]>
Commit | Line | Data |
---|---|---|
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 |