summaryrefslogtreecommitdiff
path: root/path.c
diff options
context:
space:
mode:
Diffstat (limited to 'path.c')
-rw-r--r--path.c303
1 files changed, 303 insertions, 0 deletions
diff --git a/path.c b/path.c
new file mode 100644
index 0000000..bb18075
--- /dev/null
+++ b/path.c
@@ -0,0 +1,303 @@
+/* $NetBSD: path.c,v 1.13 2017/06/30 03:56:12 kamil Exp $ */
+#include <sys/cdefs.h>
+
+#ifndef lint
+__RCSID("$NetBSD: path.c,v 1.13 2017/06/30 03:56:12 kamil Exp $");
+#endif
+
+#include <sys/stat.h>
+
+#include "sh.h"
+
+/*
+ * Contains a routine to search a : separated list of
+ * paths (a la CDPATH) and make appropriate file names.
+ * Also contains a routine to simplify .'s and ..'s out of
+ * a path name.
+ *
+ * Larry Bouzane (larry@cs.mun.ca)
+ */
+
+#ifdef S_ISLNK
+static char *do_phys_path ARGS((XString *, char *, const char *));
+#endif /* S_ISLNK */
+
+/*
+ * Makes a filename into result using the following algorithm.
+ * - make result NULL
+ * - if file starts with '/', append file to result & set cdpathp to NULL
+ * - if file starts with ./ or ../ append cwd and file to result
+ * and set cdpathp to NULL
+ * - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
+ * then cwd is appended to result.
+ * - the first element of cdpathp is appended to result
+ * - file is appended to result
+ * - cdpathp is set to the start of the next element in cdpathp (or NULL
+ * if there are no more elements.
+ * The return value indicates whether a non-null element from cdpathp
+ * was appended to result.
+ */
+int
+make_path(cwd, file, cdpathp, xsp, phys_pathp)
+ const char *cwd;
+ const char *file;
+ char **cdpathp; /* & of : separated list */
+ XString *xsp;
+ int *phys_pathp;
+{
+ int rval = 0;
+ int use_cdpath = 1;
+ char *plist;
+ int len;
+ int plen = 0;
+ char *xp = Xstring(*xsp, xp);
+
+ if (!file)
+ file = null;
+
+ if (!ISRELPATH(file)) {
+ *phys_pathp = 0;
+ use_cdpath = 0;
+ } else {
+ if (file[0] == '.') {
+ char c = file[1];
+
+ if (c == '.')
+ c = file[2];
+ if (ISDIRSEP(c) || c == '\0')
+ use_cdpath = 0;
+ }
+
+ plist = *cdpathp;
+ if (!plist)
+ use_cdpath = 0;
+ else if (use_cdpath) {
+ char *pend;
+
+ for (pend = plist; *pend && *pend != PATHSEP; pend++)
+ ;
+ plen = pend - plist;
+ *cdpathp = *pend ? ++pend : (char *) 0;
+ }
+
+ if ((use_cdpath == 0 || !plen || ISRELPATH(plist))
+ && (cwd && *cwd))
+ {
+ len = strlen(cwd);
+ XcheckN(*xsp, xp, len);
+ memcpy(xp, cwd, len);
+ xp += len;
+ if (!ISDIRSEP(cwd[len - 1]))
+ Xput(*xsp, xp, DIRSEP);
+ }
+ *phys_pathp = Xlength(*xsp, xp);
+ if (use_cdpath && plen) {
+ XcheckN(*xsp, xp, plen);
+ memcpy(xp, plist, plen);
+ xp += plen;
+ if (!ISDIRSEP(plist[plen - 1]))
+ Xput(*xsp, xp, DIRSEP);
+ rval = 1;
+ }
+ }
+
+ len = strlen(file) + 1;
+ XcheckN(*xsp, xp, len);
+ memcpy(xp, file, len);
+
+ if (!use_cdpath)
+ *cdpathp = (char *) 0;
+
+ return rval;
+}
+
+/*
+ * Simplify pathnames containing "." and ".." entries.
+ * ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
+ */
+void
+simplify_path(pathx)
+ char *pathx;
+{
+ char *cur;
+ char *t;
+ int isrooted;
+ char *very_start = pathx;
+ char *start;
+
+ if (!*pathx)
+ return;
+
+ if ((isrooted = ISROOTEDPATH(pathx)))
+ very_start++;
+
+ /* Before After
+ * /foo/ /foo
+ * /foo/../../bar /bar
+ * /foo/./blah/.. /foo
+ * . .
+ * .. ..
+ * ./foo foo
+ * foo/../../../bar ../../bar
+ */
+
+ for (cur = t = start = very_start; ; ) {
+ /* treat multiple '/'s as one '/' */
+ while (ISDIRSEP(*t))
+ t++;
+
+ if (*t == '\0') {
+ if (cur == pathx)
+ /* convert empty path to dot */
+ *cur++ = '.';
+ *cur = '\0';
+ break;
+ }
+
+ if (t[0] == '.') {
+ if (!t[1] || ISDIRSEP(t[1])) {
+ t += 1;
+ continue;
+ } else if (t[1] == '.' && (!t[2] || ISDIRSEP(t[2]))) {
+ if (!isrooted && cur == start) {
+ if (cur != very_start)
+ *cur++ = DIRSEP;
+ *cur++ = '.';
+ *cur++ = '.';
+ start = cur;
+ } else if (cur != start)
+ while (--cur > start && !ISDIRSEP(*cur))
+ ;
+ t += 2;
+ continue;
+ }
+ }
+
+ if (cur != very_start)
+ *cur++ = DIRSEP;
+
+ /* find/copy next component of pathname */
+ while (*t && !ISDIRSEP(*t))
+ *cur++ = *t++;
+ }
+}
+
+
+void
+set_current_wd(pathx)
+ char *pathx;
+{
+ int len;
+ char *p = pathx;
+
+ if (!p && !(p = ksh_get_wd((char *) 0, 0)))
+ p = null;
+
+ len = strlen(p) + 1;
+
+ if (len > current_wd_size)
+ current_wd = aresize(current_wd, current_wd_size = len, APERM);
+ memcpy(current_wd, p, len);
+ if (p != pathx && p != null)
+ afree(p, ATEMP);
+}
+
+#ifdef S_ISLNK
+char *
+get_phys_path(pathx)
+ const char *pathx;
+{
+ XString xs;
+ char *xp;
+
+ Xinit(xs, xp, strlen(pathx) + 1, ATEMP);
+
+ xp = do_phys_path(&xs, xp, pathx);
+
+ if (!xp)
+ return (char *) 0;
+
+ if (Xlength(xs, xp) == 0)
+ Xput(xs, xp, DIRSEP);
+ Xput(xs, xp, '\0');
+
+ return Xclose(xs, xp);
+}
+
+static char *
+do_phys_path(xsp, xp, pathx)
+ XString *xsp;
+ char *xp;
+ const char *pathx;
+{
+ const char *p, *q;
+ int len, llen;
+ int savepos;
+ char lbuf[PATH];
+
+ Xcheck(*xsp, xp);
+ for (p = pathx; p; p = q) {
+ while (ISDIRSEP(*p))
+ p++;
+ if (!*p)
+ break;
+ len = (q = ksh_strchr_dirsep(p)) ? q - p : (int)strlen(p);
+ if (len == 1 && p[0] == '.')
+ continue;
+ if (len == 2 && p[0] == '.' && p[1] == '.') {
+ while (xp > Xstring(*xsp, xp)) {
+ xp--;
+ if (ISDIRSEP(*xp))
+ break;
+ }
+ continue;
+ }
+
+ savepos = Xsavepos(*xsp, xp);
+ Xput(*xsp, xp, DIRSEP);
+ XcheckN(*xsp, xp, len + 1);
+ memcpy(xp, p, len);
+ xp += len;
+ *xp = '\0';
+
+ llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1);
+ if (llen < 0) {
+ /* EINVAL means it wasn't a symlink... */
+ if (errno != EINVAL)
+ return (char *) 0;
+ continue;
+ }
+ lbuf[llen] = '\0';
+
+ /* If absolute path, start from scratch.. */
+ xp = ISABSPATH(lbuf) ? Xstring(*xsp, xp)
+ : Xrestpos(*xsp, xp, savepos);
+ if (!(xp = do_phys_path(xsp, xp, lbuf)))
+ return (char *) 0;
+ }
+ return xp;
+}
+#endif /* S_ISLNK */
+
+#ifdef TEST
+
+main(argc, argv)
+{
+ int rv;
+ char *cp, cdpath[256], pwd[256], file[256], result[256];
+
+ printf("enter CDPATH: "); gets(cdpath);
+ printf("enter PWD: "); gets(pwd);
+ while (1) {
+ if (printf("Enter file: "), gets(file) == 0)
+ return 0;
+ cp = cdpath;
+ do {
+ rv = make_path(pwd, file, &cp, result, sizeof(result));
+ printf("make_path returns (%d), \"%s\" ", rv, result);
+ simplify_path(result);
+ printf("(simpifies to \"%s\")\n", result);
+ } while (cp);
+ }
+}
+#endif /* TEST */