OpenVPN
compat-dirname.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2011 - David Sommerseth <davids@redhat.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28
29#ifndef HAVE_DIRNAME
30
31#include "compat.h"
32#include <string.h>
33
34/* Unoptimised version of glibc memrchr().
35 * This is considered fast enough, as only this compat
36 * version of dirname() depends on it.
37 */
38static const char *
39__memrchr(const char *str, int c, size_t n)
40{
41 const char *end = str;
42
43 end += n - 1; /* Go to the end of the string */
44 while (end >= str)
45 {
46 if (c == *end)
47 {
48 return end;
49 }
50 else
51 {
52 end--;
53 }
54 }
55 return NULL;
56}
57
58/* Modified version based on glibc-2.14.1 by Ulrich Drepper <drepper@akkadia.org>
59 * This version is extended to handle both / and \ in path names.
60 */
61char *
62dirname(char *path)
63{
64 static const char dot[] = ".";
65 char *last_slash;
66 char separator = '/';
67
68 /* Find last '/'. */
69 last_slash = path != NULL ? strrchr(path, '/') : NULL;
70 /* If NULL, check for \ instead ... might be Windows a path */
71 if (!last_slash)
72 {
73 last_slash = path != NULL ? strrchr(path, '\\') : NULL;
74 separator = last_slash ? '\\' : '/'; /* Change the separator if \ was found */
75 }
76
77 if (last_slash != NULL && last_slash != path && last_slash[1] == '\0')
78 {
79 /* Determine whether all remaining characters are slashes. */
80 char *runp;
81
82 for (runp = last_slash; runp != path; --runp)
83 {
84 if (runp[-1] != separator)
85 {
86 break;
87 }
88 }
89
90 /* The '/' is the last character, we have to look further. */
91 if (runp != path)
92 {
93 last_slash = (char *) __memrchr(path, separator, runp - path);
94 }
95 }
96
97 if (last_slash != NULL)
98 {
99 /* Determine whether all remaining characters are slashes. */
100 char *runp;
101
102 for (runp = last_slash; runp != path; --runp)
103 {
104 if (runp[-1] != separator)
105 {
106 break;
107 }
108 }
109
110 /* Terminate the path. */
111 if (runp == path)
112 {
113 /* The last slash is the first character in the string. We have to
114 * return "/". As a special case we have to return "//" if there
115 * are exactly two slashes at the beginning of the string. See
116 * XBD 4.10 Path Name Resolution for more information. */
117 if (last_slash == path + 1)
118 {
119 ++last_slash;
120 }
121 else
122 {
123 last_slash = path + 1;
124 }
125 }
126 else
127 {
128 last_slash = runp;
129 }
130
131 last_slash[0] = '\0';
132 }
133 else
134 {
135 /* This assignment is ill-designed but the XPG specs require to
136 * return a string containing "." in any case no directory part is
137 * found and so a static and constant string is required. */
138 path = (char *) dot;
139 }
140
141 return path;
142}
143
144#endif /* HAVE_DIRNAME */
static const char * __memrchr(const char *str, int c, size_t n)
char * dirname(char *path)