1*2718b568SThomas Cort /* $NetBSD: path.c,v 1.8 2009/04/25 05:11:37 lukem Exp $ */
2*2718b568SThomas Cort #include <sys/cdefs.h>
3*2718b568SThomas Cort
4*2718b568SThomas Cort #ifndef lint
5*2718b568SThomas Cort __RCSID("$NetBSD: path.c,v 1.8 2009/04/25 05:11:37 lukem Exp $");
6*2718b568SThomas Cort #endif
7*2718b568SThomas Cort
8*2718b568SThomas Cort
9*2718b568SThomas Cort #include "sh.h"
10*2718b568SThomas Cort #include "ksh_stat.h"
11*2718b568SThomas Cort
12*2718b568SThomas Cort /*
13*2718b568SThomas Cort * Contains a routine to search a : separated list of
14*2718b568SThomas Cort * paths (a la CDPATH) and make appropriate file names.
15*2718b568SThomas Cort * Also contains a routine to simplify .'s and ..'s out of
16*2718b568SThomas Cort * a path name.
17*2718b568SThomas Cort *
18*2718b568SThomas Cort * Larry Bouzane (larry@cs.mun.ca)
19*2718b568SThomas Cort */
20*2718b568SThomas Cort
21*2718b568SThomas Cort #ifdef S_ISLNK
22*2718b568SThomas Cort static char *do_phys_path ARGS((XString *, char *, const char *));
23*2718b568SThomas Cort #endif /* S_ISLNK */
24*2718b568SThomas Cort
25*2718b568SThomas Cort /*
26*2718b568SThomas Cort * Makes a filename into result using the following algorithm.
27*2718b568SThomas Cort * - make result NULL
28*2718b568SThomas Cort * - if file starts with '/', append file to result & set cdpathp to NULL
29*2718b568SThomas Cort * - if file starts with ./ or ../ append cwd and file to result
30*2718b568SThomas Cort * and set cdpathp to NULL
31*2718b568SThomas Cort * - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
32*2718b568SThomas Cort * then cwd is appended to result.
33*2718b568SThomas Cort * - the first element of cdpathp is appended to result
34*2718b568SThomas Cort * - file is appended to result
35*2718b568SThomas Cort * - cdpathp is set to the start of the next element in cdpathp (or NULL
36*2718b568SThomas Cort * if there are no more elements.
37*2718b568SThomas Cort * The return value indicates whether a non-null element from cdpathp
38*2718b568SThomas Cort * was appended to result.
39*2718b568SThomas Cort */
40*2718b568SThomas Cort int
make_path(cwd,file,cdpathp,xsp,phys_pathp)41*2718b568SThomas Cort make_path(cwd, file, cdpathp, xsp, phys_pathp)
42*2718b568SThomas Cort const char *cwd;
43*2718b568SThomas Cort const char *file;
44*2718b568SThomas Cort char **cdpathp; /* & of : separated list */
45*2718b568SThomas Cort XString *xsp;
46*2718b568SThomas Cort int *phys_pathp;
47*2718b568SThomas Cort {
48*2718b568SThomas Cort int rval = 0;
49*2718b568SThomas Cort int use_cdpath = 1;
50*2718b568SThomas Cort char *plist;
51*2718b568SThomas Cort int len;
52*2718b568SThomas Cort int plen = 0;
53*2718b568SThomas Cort char *xp = Xstring(*xsp, xp);
54*2718b568SThomas Cort
55*2718b568SThomas Cort if (!file)
56*2718b568SThomas Cort file = null;
57*2718b568SThomas Cort
58*2718b568SThomas Cort if (!ISRELPATH(file)) {
59*2718b568SThomas Cort *phys_pathp = 0;
60*2718b568SThomas Cort use_cdpath = 0;
61*2718b568SThomas Cort } else {
62*2718b568SThomas Cort if (file[0] == '.') {
63*2718b568SThomas Cort char c = file[1];
64*2718b568SThomas Cort
65*2718b568SThomas Cort if (c == '.')
66*2718b568SThomas Cort c = file[2];
67*2718b568SThomas Cort if (ISDIRSEP(c) || c == '\0')
68*2718b568SThomas Cort use_cdpath = 0;
69*2718b568SThomas Cort }
70*2718b568SThomas Cort
71*2718b568SThomas Cort plist = *cdpathp;
72*2718b568SThomas Cort if (!plist)
73*2718b568SThomas Cort use_cdpath = 0;
74*2718b568SThomas Cort else if (use_cdpath) {
75*2718b568SThomas Cort char *pend;
76*2718b568SThomas Cort
77*2718b568SThomas Cort for (pend = plist; *pend && *pend != PATHSEP; pend++)
78*2718b568SThomas Cort ;
79*2718b568SThomas Cort plen = pend - plist;
80*2718b568SThomas Cort *cdpathp = *pend ? ++pend : (char *) 0;
81*2718b568SThomas Cort }
82*2718b568SThomas Cort
83*2718b568SThomas Cort if ((use_cdpath == 0 || !plen || ISRELPATH(plist))
84*2718b568SThomas Cort && (cwd && *cwd))
85*2718b568SThomas Cort {
86*2718b568SThomas Cort len = strlen(cwd);
87*2718b568SThomas Cort XcheckN(*xsp, xp, len);
88*2718b568SThomas Cort memcpy(xp, cwd, len);
89*2718b568SThomas Cort xp += len;
90*2718b568SThomas Cort if (!ISDIRSEP(cwd[len - 1]))
91*2718b568SThomas Cort Xput(*xsp, xp, DIRSEP);
92*2718b568SThomas Cort }
93*2718b568SThomas Cort *phys_pathp = Xlength(*xsp, xp);
94*2718b568SThomas Cort if (use_cdpath && plen) {
95*2718b568SThomas Cort XcheckN(*xsp, xp, plen);
96*2718b568SThomas Cort memcpy(xp, plist, plen);
97*2718b568SThomas Cort xp += plen;
98*2718b568SThomas Cort if (!ISDIRSEP(plist[plen - 1]))
99*2718b568SThomas Cort Xput(*xsp, xp, DIRSEP);
100*2718b568SThomas Cort rval = 1;
101*2718b568SThomas Cort }
102*2718b568SThomas Cort }
103*2718b568SThomas Cort
104*2718b568SThomas Cort len = strlen(file) + 1;
105*2718b568SThomas Cort XcheckN(*xsp, xp, len);
106*2718b568SThomas Cort memcpy(xp, file, len);
107*2718b568SThomas Cort
108*2718b568SThomas Cort if (!use_cdpath)
109*2718b568SThomas Cort *cdpathp = (char *) 0;
110*2718b568SThomas Cort
111*2718b568SThomas Cort return rval;
112*2718b568SThomas Cort }
113*2718b568SThomas Cort
114*2718b568SThomas Cort /*
115*2718b568SThomas Cort * Simplify pathnames containing "." and ".." entries.
116*2718b568SThomas Cort * ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
117*2718b568SThomas Cort */
118*2718b568SThomas Cort void
simplify_path(pathx)119*2718b568SThomas Cort simplify_path(pathx)
120*2718b568SThomas Cort char *pathx;
121*2718b568SThomas Cort {
122*2718b568SThomas Cort char *cur;
123*2718b568SThomas Cort char *t;
124*2718b568SThomas Cort int isrooted;
125*2718b568SThomas Cort char *very_start = pathx;
126*2718b568SThomas Cort char *start;
127*2718b568SThomas Cort
128*2718b568SThomas Cort if (!*pathx)
129*2718b568SThomas Cort return;
130*2718b568SThomas Cort
131*2718b568SThomas Cort if ((isrooted = ISROOTEDPATH(pathx)))
132*2718b568SThomas Cort very_start++;
133*2718b568SThomas Cort #if defined (OS2) || defined (__CYGWIN__)
134*2718b568SThomas Cort if (pathx[0] && pathx[1] == ':') /* skip a: */
135*2718b568SThomas Cort very_start += 2;
136*2718b568SThomas Cort #endif /* OS2 || __CYGWIN__ */
137*2718b568SThomas Cort
138*2718b568SThomas Cort /* Before After
139*2718b568SThomas Cort * /foo/ /foo
140*2718b568SThomas Cort * /foo/../../bar /bar
141*2718b568SThomas Cort * /foo/./blah/.. /foo
142*2718b568SThomas Cort * . .
143*2718b568SThomas Cort * .. ..
144*2718b568SThomas Cort * ./foo foo
145*2718b568SThomas Cort * foo/../../../bar ../../bar
146*2718b568SThomas Cort * OS2 and CYGWIN:
147*2718b568SThomas Cort * a:/foo/../.. a:/
148*2718b568SThomas Cort * a:. a:
149*2718b568SThomas Cort * a:.. a:..
150*2718b568SThomas Cort * a:foo/../../blah a:../blah
151*2718b568SThomas Cort */
152*2718b568SThomas Cort
153*2718b568SThomas Cort #ifdef __CYGWIN__
154*2718b568SThomas Cort /* preserve leading double-slash on pathnames (for UNC paths) */
155*2718b568SThomas Cort if (pathx[0] && ISDIRSEP(pathx[0]) && pathx[1] && ISDIRSEP(pathx[1]))
156*2718b568SThomas Cort very_start++;
157*2718b568SThomas Cort #endif /* __CYGWIN__ */
158*2718b568SThomas Cort
159*2718b568SThomas Cort for (cur = t = start = very_start; ; ) {
160*2718b568SThomas Cort /* treat multiple '/'s as one '/' */
161*2718b568SThomas Cort while (ISDIRSEP(*t))
162*2718b568SThomas Cort t++;
163*2718b568SThomas Cort
164*2718b568SThomas Cort if (*t == '\0') {
165*2718b568SThomas Cort if (cur == pathx)
166*2718b568SThomas Cort /* convert empty path to dot */
167*2718b568SThomas Cort *cur++ = '.';
168*2718b568SThomas Cort *cur = '\0';
169*2718b568SThomas Cort break;
170*2718b568SThomas Cort }
171*2718b568SThomas Cort
172*2718b568SThomas Cort if (t[0] == '.') {
173*2718b568SThomas Cort if (!t[1] || ISDIRSEP(t[1])) {
174*2718b568SThomas Cort t += 1;
175*2718b568SThomas Cort continue;
176*2718b568SThomas Cort } else if (t[1] == '.' && (!t[2] || ISDIRSEP(t[2]))) {
177*2718b568SThomas Cort if (!isrooted && cur == start) {
178*2718b568SThomas Cort if (cur != very_start)
179*2718b568SThomas Cort *cur++ = DIRSEP;
180*2718b568SThomas Cort *cur++ = '.';
181*2718b568SThomas Cort *cur++ = '.';
182*2718b568SThomas Cort start = cur;
183*2718b568SThomas Cort } else if (cur != start)
184*2718b568SThomas Cort while (--cur > start && !ISDIRSEP(*cur))
185*2718b568SThomas Cort ;
186*2718b568SThomas Cort t += 2;
187*2718b568SThomas Cort continue;
188*2718b568SThomas Cort }
189*2718b568SThomas Cort }
190*2718b568SThomas Cort
191*2718b568SThomas Cort if (cur != very_start)
192*2718b568SThomas Cort *cur++ = DIRSEP;
193*2718b568SThomas Cort
194*2718b568SThomas Cort /* find/copy next component of pathname */
195*2718b568SThomas Cort while (*t && !ISDIRSEP(*t))
196*2718b568SThomas Cort *cur++ = *t++;
197*2718b568SThomas Cort }
198*2718b568SThomas Cort }
199*2718b568SThomas Cort
200*2718b568SThomas Cort
201*2718b568SThomas Cort void
set_current_wd(pathx)202*2718b568SThomas Cort set_current_wd(pathx)
203*2718b568SThomas Cort char *pathx;
204*2718b568SThomas Cort {
205*2718b568SThomas Cort int len;
206*2718b568SThomas Cort char *p = pathx;
207*2718b568SThomas Cort
208*2718b568SThomas Cort if (!p && !(p = ksh_get_wd((char *) 0, 0)))
209*2718b568SThomas Cort p = null;
210*2718b568SThomas Cort
211*2718b568SThomas Cort len = strlen(p) + 1;
212*2718b568SThomas Cort
213*2718b568SThomas Cort if (len > current_wd_size)
214*2718b568SThomas Cort current_wd = aresize(current_wd, current_wd_size = len, APERM);
215*2718b568SThomas Cort memcpy(current_wd, p, len);
216*2718b568SThomas Cort if (p != pathx && p != null)
217*2718b568SThomas Cort afree(p, ATEMP);
218*2718b568SThomas Cort }
219*2718b568SThomas Cort
220*2718b568SThomas Cort #ifdef S_ISLNK
221*2718b568SThomas Cort char *
get_phys_path(pathx)222*2718b568SThomas Cort get_phys_path(pathx)
223*2718b568SThomas Cort const char *pathx;
224*2718b568SThomas Cort {
225*2718b568SThomas Cort XString xs;
226*2718b568SThomas Cort char *xp;
227*2718b568SThomas Cort
228*2718b568SThomas Cort Xinit(xs, xp, strlen(pathx) + 1, ATEMP);
229*2718b568SThomas Cort
230*2718b568SThomas Cort xp = do_phys_path(&xs, xp, pathx);
231*2718b568SThomas Cort
232*2718b568SThomas Cort if (!xp)
233*2718b568SThomas Cort return (char *) 0;
234*2718b568SThomas Cort
235*2718b568SThomas Cort if (Xlength(xs, xp) == 0)
236*2718b568SThomas Cort Xput(xs, xp, DIRSEP);
237*2718b568SThomas Cort Xput(xs, xp, '\0');
238*2718b568SThomas Cort
239*2718b568SThomas Cort return Xclose(xs, xp);
240*2718b568SThomas Cort }
241*2718b568SThomas Cort
242*2718b568SThomas Cort static char *
do_phys_path(xsp,xp,pathx)243*2718b568SThomas Cort do_phys_path(xsp, xp, pathx)
244*2718b568SThomas Cort XString *xsp;
245*2718b568SThomas Cort char *xp;
246*2718b568SThomas Cort const char *pathx;
247*2718b568SThomas Cort {
248*2718b568SThomas Cort const char *p, *q;
249*2718b568SThomas Cort int len, llen;
250*2718b568SThomas Cort int savepos;
251*2718b568SThomas Cort char lbuf[PATH];
252*2718b568SThomas Cort
253*2718b568SThomas Cort Xcheck(*xsp, xp);
254*2718b568SThomas Cort for (p = pathx; p; p = q) {
255*2718b568SThomas Cort while (ISDIRSEP(*p))
256*2718b568SThomas Cort p++;
257*2718b568SThomas Cort if (!*p)
258*2718b568SThomas Cort break;
259*2718b568SThomas Cort len = (q = ksh_strchr_dirsep(p)) ? q - p : (int)strlen(p);
260*2718b568SThomas Cort if (len == 1 && p[0] == '.')
261*2718b568SThomas Cort continue;
262*2718b568SThomas Cort if (len == 2 && p[0] == '.' && p[1] == '.') {
263*2718b568SThomas Cort while (xp > Xstring(*xsp, xp)) {
264*2718b568SThomas Cort xp--;
265*2718b568SThomas Cort if (ISDIRSEP(*xp))
266*2718b568SThomas Cort break;
267*2718b568SThomas Cort }
268*2718b568SThomas Cort continue;
269*2718b568SThomas Cort }
270*2718b568SThomas Cort
271*2718b568SThomas Cort savepos = Xsavepos(*xsp, xp);
272*2718b568SThomas Cort Xput(*xsp, xp, DIRSEP);
273*2718b568SThomas Cort XcheckN(*xsp, xp, len + 1);
274*2718b568SThomas Cort memcpy(xp, p, len);
275*2718b568SThomas Cort xp += len;
276*2718b568SThomas Cort *xp = '\0';
277*2718b568SThomas Cort
278*2718b568SThomas Cort llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1);
279*2718b568SThomas Cort if (llen < 0) {
280*2718b568SThomas Cort /* EINVAL means it wasn't a symlink... */
281*2718b568SThomas Cort if (errno != EINVAL)
282*2718b568SThomas Cort return (char *) 0;
283*2718b568SThomas Cort continue;
284*2718b568SThomas Cort }
285*2718b568SThomas Cort lbuf[llen] = '\0';
286*2718b568SThomas Cort
287*2718b568SThomas Cort /* If absolute path, start from scratch.. */
288*2718b568SThomas Cort xp = ISABSPATH(lbuf) ? Xstring(*xsp, xp)
289*2718b568SThomas Cort : Xrestpos(*xsp, xp, savepos);
290*2718b568SThomas Cort if (!(xp = do_phys_path(xsp, xp, lbuf)))
291*2718b568SThomas Cort return (char *) 0;
292*2718b568SThomas Cort }
293*2718b568SThomas Cort return xp;
294*2718b568SThomas Cort }
295*2718b568SThomas Cort #endif /* S_ISLNK */
296*2718b568SThomas Cort
297*2718b568SThomas Cort #ifdef TEST
298*2718b568SThomas Cort
main(argc,argv)299*2718b568SThomas Cort main(argc, argv)
300*2718b568SThomas Cort {
301*2718b568SThomas Cort int rv;
302*2718b568SThomas Cort char *cp, cdpath[256], pwd[256], file[256], result[256];
303*2718b568SThomas Cort
304*2718b568SThomas Cort printf("enter CDPATH: "); gets(cdpath);
305*2718b568SThomas Cort printf("enter PWD: "); gets(pwd);
306*2718b568SThomas Cort while (1) {
307*2718b568SThomas Cort if (printf("Enter file: "), gets(file) == 0)
308*2718b568SThomas Cort return 0;
309*2718b568SThomas Cort cp = cdpath;
310*2718b568SThomas Cort do {
311*2718b568SThomas Cort rv = make_path(pwd, file, &cp, result, sizeof(result));
312*2718b568SThomas Cort printf("make_path returns (%d), \"%s\" ", rv, result);
313*2718b568SThomas Cort simplify_path(result);
314*2718b568SThomas Cort printf("(simpifies to \"%s\")\n", result);
315*2718b568SThomas Cort } while (cp);
316*2718b568SThomas Cort }
317*2718b568SThomas Cort }
318*2718b568SThomas Cort #endif /* TEST */
319