1 /*	$NetBSD: sane_basename.c,v 1.1.1.1 2009/06/23 10:09:00 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	sane_basename 3
6 /* SUMMARY
7 /*	split pathname into last component and parent directory
8 /* SYNOPSIS
9 /*	#include <stringops.h>
10 /*
11 /*	char	*sane_basename(buf, path)
12 /*	VSTRING	*buf;
13 /*	const char *path;
14 /*
15 /*	char	*sane_dirname(buf, path)
16 /*	VSTRING	*buf;
17 /*	const char *path;
18 /* DESCRIPTION
19 /*	These functions split a pathname into its last component
20 /*	and its parent directory, excluding any trailing "/"
21 /*	characters from the input. The result is a pointer to "/"
22 /*	when the input is all "/" characters, or a pointer to "."
23 /*	when the input is a null pointer or zero-length string.
24 /*
25 /*	sane_basename() and sane_dirname() differ as follows
26 /*	from standard basename() and dirname() implementations:
27 /* .IP \(bu
28 /*	They can use caller-provided storage or private storage.
29 /* .IP \(bu
30 /*	They never modify their input.
31 /* .PP
32 /*	sane_basename() returns a pointer to string with the last
33 /*	pathname component.
34 /*
35 /*	sane_dirname() returns a pointer to string with the parent
36 /*	directory. The result is a pointer to "." when the input
37 /*	contains no '/' character.
38 /*
39 /*	Arguments:
40 /* .IP buf
41 /*	Result storage. If a null pointer is specified, each function
42 /*	uses its own private memory that is overwritten upon each call.
43 /* .IP path
44 /*	The input pathname.
45 /* LICENSE
46 /* .ad
47 /* .fi
48 /*	The Secure Mailer license must be distributed with this
49 /*	software.
50 /* AUTHOR(S)
51 /*	Wietse Venema
52 /*	IBM T.J. Watson Research
53 /*	P.O. Box 704
54 /*	Yorktown Heights, NY 10598, USA
55 /*--*/
56 
57 /* System library. */
58 
59 #include <sys_defs.h>
60 #include <string.h>
61 
62 /* Utility library. */
63 
64 #include <vstring.h>
65 #include <stringops.h>
66 
67 #define STR(x)	vstring_str(x)
68 
69 /* sane_basename - skip directory prefix */
70 
71 char   *sane_basename(VSTRING *bp, const char *path)
72 {
73     static VSTRING *buf;
74     const char *first;
75     const char *last;
76 
77     /*
78      * Your buffer or mine?
79      */
80     if (bp == 0) {
81 	bp = buf;
82 	if (bp == 0)
83 	    bp = buf = vstring_alloc(10);
84     }
85 
86     /*
87      * Special case: return "." for null or zero-length input.
88      */
89     if (path == 0 || *path == 0)
90 	return (STR(vstring_strcpy(bp, ".")));
91 
92     /*
93      * Remove trailing '/' characters from input. Return "/" if input is all
94      * '/' characters.
95      */
96     last = path + strlen(path) - 1;
97     while (*last == '/') {
98 	if (last == path)
99 	    return (STR(vstring_strcpy(bp, "/")));
100 	last--;
101     }
102 
103     /*
104      * The pathname does not end in '/'. Skip to last '/' character if any.
105      */
106     first = last - 1;
107     while (first >= path && *first != '/')
108 	first--;
109 
110     return (STR(vstring_strncpy(bp, first + 1, last - first)));
111 }
112 
113 /* sane_dirname - keep directory prefix */
114 
115 char   *sane_dirname(VSTRING *bp, const char *path)
116 {
117     static VSTRING *buf;
118     const char *last;
119 
120     /*
121      * Your buffer or mine?
122      */
123     if (bp == 0) {
124 	bp = buf;
125 	if (bp == 0)
126 	    bp = buf = vstring_alloc(10);
127     }
128 
129     /*
130      * Special case: return "." for null or zero-length input.
131      */
132     if (path == 0 || *path == 0)
133 	return (STR(vstring_strcpy(bp, ".")));
134 
135     /*
136      * Remove trailing '/' characters from input. Return "/" if input is all
137      * '/' characters.
138      */
139     last = path + strlen(path) - 1;
140     while (*last == '/') {
141 	if (last == path)
142 	    return (STR(vstring_strcpy(bp, "/")));
143 	last--;
144     }
145 
146     /*
147      * This pathname does not end in '/'. Skip to last '/' character if any.
148      */
149     while (last >= path && *last != '/')
150 	last--;
151     if (last < path)				/* no '/' */
152 	return (STR(vstring_strcpy(bp, ".")));
153 
154     /*
155      * Strip trailing '/' characters from dirname (not strictly needed).
156      */
157     while (last > path && *last == '/')
158 	last--;
159 
160     return (STR(vstring_strncpy(bp, path, last - path + 1)));
161 }
162 
163 #ifdef TEST
164 #include <vstring_vstream.h>
165 
166 int     main(int argc, char **argv)
167 {
168     VSTRING *buf = vstring_alloc(10);
169     char   *dir;
170     char   *base;
171 
172     while (vstring_get_nonl(buf, VSTREAM_IN) > 0) {
173 	dir = sane_dirname((VSTRING *) 0, STR(buf));
174 	base = sane_basename((VSTRING *) 0, STR(buf));
175 	vstream_printf("input=\"%s\" dir=\"%s\" base=\"%s\"\n",
176 		       STR(buf), dir, base);
177     }
178     vstream_fflush(VSTREAM_OUT);
179     vstring_free(buf);
180     return (0);
181 }
182 
183 #endif
184