(memrchr): Declare if necessary.
[gnulib.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 ISSLASH
47 # define ISSLASH(C) ((C) == '/')
48 #endif
49
50 #define BACKSLASH_IS_PATH_SEPARATOR ISSLASH ('\\')
51
52 /* Return the leading directories part of PATH,
53    allocated with malloc.  If out of memory, return 0.
54    Works properly even if there are trailing slashes
55    (by effectively ignoring them).  */
56
57 char *
58 dir_name (const char *path)
59 {
60   char *newpath;
61   char *slash;
62   int length;                   /* Length of result, not including NUL.  */
63
64   slash = strrchr (path, '/');
65   if (BACKSLASH_IS_PATH_SEPARATOR)
66     {
67       char *b = strrchr (path, '\\');
68       if (b && slash < b)
69         slash = b;
70     }
71
72   /* If the last byte of PATH is a slash, decrement SLASH until it's
73      pointing at the leftmost in a sequence of trailing slashes.  */
74   if (slash && slash[1] == 0)
75     {
76       while (path < slash && ISSLASH (slash[-1]))
77         {
78           --slash;
79         }
80
81       if (path < slash)
82         {
83           slash = memrchr (path, '/', slash - path);
84           if (BACKSLASH_IS_PATH_SEPARATOR)
85             {
86               char *b = memrchr (path, '\\', slash - path);
87               if (b && slash < b)
88                 slash = b;
89             }
90         }
91     }
92
93   if (slash == 0)
94     {
95       /* File is in the current directory.  */
96       path = ".";
97       length = 1;
98     }
99   else
100     {
101       /* Remove any trailing slashes from the result.  */
102       if (BACKSLASH_IS_PATH_SEPARATOR)
103         {
104           const char *lim = ((path[0] >= 'A' && path[0] <= 'z'
105                               && path[1] == ':')
106                              ? path + 2 : path);
107
108           /* If canonicalized "d:/path", leave alone the root case "d:/".  */
109           while (slash > lim && ISSLASH (*slash))
110             --slash;
111         }
112       else
113         {
114           while (slash > path && ISSLASH (*slash))
115             --slash;
116         }
117
118       length = slash - path + 1;
119     }
120
121   newpath = (char *) malloc (length + 1);
122   if (newpath == 0)
123     return 0;
124   strncpy (newpath, path, length);
125   newpath[length] = 0;
126   return newpath;
127 }
128
129 #ifdef TEST_DIRNAME
130 /*
131
132 Run the test like this (expect no output):
133   gcc -DHAVE_CONFIG_H -DTEST_DIRNAME -I.. -O -Wall memrchr.c dirname.c
134   sed -n '/^BEGIN-DATA$/,/^END-DATA$/p' dirname.c|grep -v DATA|./a.out
135
136 BEGIN-DATA
137 foo//// .
138 bar/foo//// bar
139 foo/ .
140 / /
141 . .
142 a .
143 END-DATA
144
145 */
146
147 # define MAX_BUFF_LEN 1024
148 # include <stdio.h>
149 # include <stdlib.h>
150
151 int
152 main ()
153 {
154   char buff[MAX_BUFF_LEN + 1];
155
156   buff[MAX_BUFF_LEN] = 0;
157   while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
158     {
159       char path[MAX_BUFF_LEN];
160       char expected_result[MAX_BUFF_LEN];
161       char *result;
162       sscanf (buff, "%s %s", path, expected_result);
163       result = dir_name (path);
164       if (strcmp (result, expected_result))
165         printf ("%s: got %s, expected %s\n", path, result, expected_result);
166     }
167   exit (0);
168
169 }
170 #endif