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, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27
28#ifndef HAVE_DIRNAME
29
30#include "compat.h"
31#include <string.h>
32
33/* Unoptimised version of glibc memrchr().
34 * This is considered fast enough, as only this compat
35 * version of dirname() depends on it.
36 */
37static const char *
38__memrchr(const char *str, int c, size_t n)
39{
40 const char *end = str;
41
42 end += n - 1; /* Go to the end of the string */
43 while (end >= str)
44 {
45 if (c == *end)
46 {
47 return end;
48 }
49 else
50 {
51 end--;
52 }
53 }
54 return NULL;
55}
56
57/* Modified version based on glibc-2.14.1 by Ulrich Drepper <drepper@akkadia.org>
58 * This version is extended to handle both / and \ in path names.
59 */
60char *
61dirname(char *path)
62{
63 static const char dot[] = ".";
64 char *last_slash;
65 char separator = '/';
66
67 /* Find last '/'. */
68 last_slash = path != NULL ? strrchr(path, '/') : NULL;
69 /* If NULL, check for \ instead ... might be Windows a path */
70 if (!last_slash)
71 {
72 last_slash = path != NULL ? strrchr(path, '\\') : NULL;
73 separator = last_slash ? '\\' : '/'; /* Change the separator if \ was found */
74 }
75
76 if (last_slash != NULL && last_slash != path && last_slash[1] == '\0')
77 {
78 /* Determine whether all remaining characters are slashes. */
79 char *runp;
80
81 for (runp = last_slash; runp != path; --runp)
82 {
83 if (runp[-1] != separator)
84 {
85 break;
86 }
87 }
88
89 /* The '/' is the last character, we have to look further. */
90 if (runp != path)
91 {
92 last_slash = (char *)__memrchr(path, separator, runp - path);
93 }
94 }
95
96 if (last_slash != NULL)
97 {
98 /* Determine whether all remaining characters are slashes. */
99 char *runp;
100
101 for (runp = last_slash; runp != path; --runp)
102 {
103 if (runp[-1] != separator)
104 {
105 break;
106 }
107 }
108
109 /* Terminate the path. */
110 if (runp == path)
111 {
112 /* The last slash is the first character in the string. We have to
113 * return "/". As a special case we have to return "//" if there
114 * are exactly two slashes at the beginning of the string. See
115 * XBD 4.10 Path Name Resolution for more information. */
116 if (last_slash == path + 1)
117 {
118 ++last_slash;
119 }
120 else
121 {
122 last_slash = path + 1;
123 }
124 }
125 else
126 {
127 last_slash = runp;
128 }
129
130 last_slash[0] = '\0';
131 }
132 else
133 {
134 /* This assignment is ill-designed but the XPG specs require to
135 * return a string containing "." in any case no directory part is
136 * found and so a static and constant string is required. */
137 path = (char *)dot;
138 }
139
140 return path;
141}
142
143#endif /* HAVE_DIRNAME */
static const char * __memrchr(const char *str, int c, size_t n)
char * dirname(char *path)