]> git.saurik.com Git - bison.git/blob - lib/dirname.c
94f8c8936ed6744b235b03aa97d4772d529f0b68
[bison.git] / lib / dirname.c
1 /* dirname.c -- return all but the last element in a path
2 Copyright (C) 1990, 1998, 2000 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18 #if HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #ifdef STDC_HEADERS
23 # include <stdlib.h>
24 #else
25 char *malloc ();
26 #endif
27 #if defined STDC_HEADERS || defined HAVE_STRING_H
28 # include <string.h>
29 #else
30 # include <strings.h>
31 # ifndef strrchr
32 # define strrchr rindex
33 # endif
34 #endif
35 #include <assert.h>
36
37 #ifndef HAVE_DECL_MEMRCHR
38 "this configure-time declaration test was not run"
39 #endif
40 #if !HAVE_DECL_MEMRCHR
41 void *memrchr ();
42 #endif
43
44 #include "dirname.h"
45
46 #ifndef FILESYSTEM_PREFIX_LEN
47 # define FILESYSTEM_PREFIX_LEN(Filename) 0
48 #endif
49
50 #ifndef ISSLASH
51 # define ISSLASH(C) ((C) == '/')
52 #endif
53
54 #define BACKSLASH_IS_PATH_SEPARATOR ISSLASH ('\\')
55
56 /* Return the length of `dirname (PATH)' and set *RESULT to point
57 to PATH or to `"."', as appropriate. Works properly even if
58 there are trailing slashes (by effectively ignoring them).
59 WARNING: This function doesn't work for cwd-relative names like
60 `a:foo' that are specified with a drive-letter prefix. That case
61 is handled in the caller. */
62 static size_t
63 dir_name_r (char const *path, char const **result)
64 {
65 char const *slash;
66 size_t length; /* Length of result, not including NUL. */
67
68 slash = strrchr (path, '/');
69 if (BACKSLASH_IS_PATH_SEPARATOR)
70 {
71 char const *b = strrchr (path, '\\');
72 if (b && slash < b)
73 slash = b;
74 }
75
76 /* If the last byte of PATH is a slash, decrement SLASH until it's
77 pointing at the leftmost in a sequence of trailing slashes. */
78 if (slash && slash[1] == 0)
79 {
80 while (path < slash && ISSLASH (slash[-1]))
81 {
82 --slash;
83 }
84
85 if (path < slash)
86 {
87 size_t len = slash - path;
88 slash = memrchr (path, '/', len);
89 if (BACKSLASH_IS_PATH_SEPARATOR)
90 {
91 char const *b = memrchr (path, '\\', len);
92 if (b && slash < b)
93 slash = b;
94 }
95 }
96 }
97
98 if (slash == 0)
99 {
100 /* File is in the current directory. */
101
102 length = FILESYSTEM_PREFIX_LEN (path);
103
104 if (length == 0)
105 {
106 path = ".";
107 length = 1;
108 }
109 }
110 else
111 {
112 /* Remove any trailing slashes from the result. If we have a
113 canonicalized "d:/path", leave alone the root case "d:/". */
114 char const *lim = path + FILESYSTEM_PREFIX_LEN (path);
115
116 while (lim < slash && ISSLASH (*slash))
117 --slash;
118
119 length = slash - path + 1;
120 }
121
122 *result = path;
123 return length;
124 }
125
126 /* Return the leading directories part of PATH,
127 allocated with malloc. If out of memory, return 0.
128 Works properly even if there are trailing slashes
129 (by effectively ignoring them). */
130
131 char *
132 dir_name (char const *path)
133 {
134 char const *result;
135 size_t length = dir_name_r (path, &result);
136 int append_dot = (length && length == FILESYSTEM_PREFIX_LEN (newpath));
137 char *newpath = (char *) malloc (length + append_dot + 1);
138 if (newpath == 0)
139 return 0;
140 strncpy (newpath, result, length);
141 /* If PATH is "d:foo", return "d:.", the CWD on drive d: */
142 if (append_dot)
143 newpath[length++] = '.';
144 newpath[length] = 0;
145 return newpath;
146 }
147
148 #ifdef TEST_DIRNAME
149 /*
150
151 Run the test like this (expect no output):
152 gcc -DHAVE_CONFIG_H -DTEST_DIRNAME -I.. -O -Wall memrchr.c dirname.c
153 sed -n '/^BEGIN-DATA$/,/^END-DATA$/p' dirname.c|grep -v DATA|./a.out
154
155 BEGIN-DATA
156 foo//// .
157 bar/foo//// bar
158 foo/ .
159 / /
160 . .
161 a .
162 END-DATA
163
164 */
165
166 # define MAX_BUFF_LEN 1024
167 # include <stdio.h>
168 # include <stdlib.h>
169
170 int
171 main ()
172 {
173 char buff[MAX_BUFF_LEN + 1];
174
175 buff[MAX_BUFF_LEN] = 0;
176 while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
177 {
178 char path[MAX_BUFF_LEN];
179 char expected_result[MAX_BUFF_LEN];
180 char const *result;
181 sscanf (buff, "%s %s", path, expected_result);
182 result = dir_name (path);
183 if (strcmp (result, expected_result))
184 printf ("%s: got %s, expected %s\n", path, result, expected_result);
185 }
186 exit (0);
187
188 }
189 #endif