#ifndef lint static char sccsid[] = "@(#)abspath.c 9.3 88/01/19 Copyright 1984 Sun Micro"; #endif /* * Copyright (c) 1984 by Sun Microsystems, Inc. */ /*-Convert a pathname to an absolute one, if it is absolute already, it is returned in the buffer unchanged, otherwise leading "./"s will be removed, the name of the current working directory will be prepended, and "../"s will be resolved. In a moment of weakness, I have implemented the cshell ~ filename convention. ~/foobar will have the ~ replaced by the home directory of the current user. ~user/foobar will have the ~user replaced by the home directory of the named user. This should really be in the kernel (or be replaced by a better kernel mechanism). Doing file name expansion like this in a user-level program leads to some very distasteful non-uniformities. Another fit of dementia has led me to implement the expansion of shell environment variables. $HOME/mbox is the same as ~/mbox. If the environment variable a = "foo" and b = "bar" then: $a => foo $a$b => foobar $a.c => foo.c xxx$a => xxxfoo ${a}! => foo! To handle a bizarre remote file name syntax, if a file name begins with '[' abspath won't change it. */ #include #ifdef REF #include #endif #include #include #include static char curwd[200]; /* the current working directory is * remembered here. chdir()'s are trapped * and this gets updated. */ abspath(nm, buf) /* input name in nm, absolute pathname * output to buf. returns -1 if the * pathname cannot be successfully * converted (only happens if the current * directory cannot be found) */ char *nm, *buf; { register char *s, *d; char lnm[1000]; s = nm; d = lnm; while (*d++ = *s) if (*s++ == '$') { register char *start = d; register braces = *s == '{'; register char *value; while (*d++ = *s) if (braces ? *s == '}' : !isalnum(*s)) break; else s++; *--d = 0; value = (char *) getenv(braces ? start + 1 : start); if (value) { for (d = start - 1; *d++ = *value++;); d--; if (braces && *s) s++; } } d = buf; nm = lnm; if (nm[0] == '~') /* prefix ~ */ if (nm[1] == '/' || nm[1] == 0) /* ~/filename */ if (s = (char *) getenv("HOME")) { if (*++nm) nm++; } else s = ""; else { /* ~user/filename */ register char *nnm; register struct passwd *pw; for (s = nm; *s && *s != '/'; s++); nnm = *s ? s + 1 : s; *s = 0; pw = (struct passwd *) getpwnam(nm + 1); if (pw == 0) s = ""; else { nm = nnm; s = pw->pw_dir; } } else { s = curwd; if (*s == 0) getwd(curwd); } while (*d++ = *s++); *(d - 1) = '/'; s = nm; if (*s == '/' || *s == '[') d = buf; while (*d++ = *s++); *(d - 1) = '/'; *d = '\0'; d = buf; s = buf; while (*s) if ((*d++ = *s++) == '/' && d > buf + 1) { register char *t = d - 2; switch (*t) { case '/': /* found // in the name */ --d; break; case '.': switch (*--t) { case '/': /* found /./ in the name */ d -= 2; break; case '.': if (*--t == '/') { /* found /../ */ while (t > buf && *--t != '/'); d = t + 1; } break; } break; } } if (*(d - 1) == '/' && d > buf + 1) d--; *d = '\0'; return 0; }