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