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