#include "sh.h"
#include "sh.dir.h"
#include "sh.tconst.h"
struct directory *dfind(tchar *);
tchar *dfollow(tchar *);
tchar *dcanon(tchar *, tchar *);
void dtildepr(tchar *, tchar *);
void dfree(struct directory *);
void dnewcwd(struct directory *);
int didchdir;
bool loginsh;
bool havhash2;
struct varent shvhed;
struct directory *dcwd;
struct directory dhead;
int printd;
static tchar *fakev[] = { S_dirs, NOSTR };
char xhash2[HSHSIZ / 8];
void
dinit(tchar *hp)
{
tchar *cp;
struct directory *dp;
tchar path[MAXPATHLEN];
#ifdef TRACE
tprintf("TRACE- dinit()\n");
#endif
if (loginsh && hp && *hp)
cp = hp;
else {
cp = getwd_(path);
if (cp == NULL) {
printf("Warning: cannot determine current directory\n");
cp = S_DOT;
}
}
dp = (struct directory *)xcalloc(sizeof (struct directory), 1);
dp->di_name = savestr(cp);
dp->di_count = 0;
dhead.di_next = dhead.di_prev = dp;
dp->di_next = dp->di_prev = &dhead;
printd = 0;
dnewcwd(dp);
}
void
dodirs(tchar **v)
{
struct directory *dp;
bool lflag;
tchar *hp = value(S_home);
#ifdef TRACE
tprintf("TRACE- dodirs()\n");
#endif
if (*hp == '\0')
hp = NOSTR;
if (*++v != NOSTR)
if (eq(*v, S_MINl ) && *++v == NOSTR)
lflag = 1;
else
error("Usage: dirs [ -l ]");
else
lflag = 0;
dp = dcwd;
do {
if (dp == &dhead)
continue;
if (!lflag && hp != NOSTR) {
dtildepr(hp, dp->di_name);
} else
printf("%t", dp->di_name);
printf(" ");
} while ((dp = dp->di_prev) != dcwd);
printf("\n");
}
void
dtildepr(tchar *home, tchar *dir)
{
#ifdef TRACE
tprintf("TRACE- dtildepr()\n");
#endif
if (!eq(home, S_SLASH ) && prefix(home, dir))
printf("~%t", dir + strlen_(home));
else
printf("%t", dir);
}
void
dochngd(tchar **v)
{
tchar *cp;
struct directory *dp;
#ifdef TRACE
tprintf("TRACE- dochngd()\n");
#endif
printd = 0;
if (*++v == NOSTR) {
if ((cp = value(S_home)) == NOSTR || *cp == 0)
bferr("No home directory");
if (chdir_(cp) < 0)
bferr("Can't change to home directory");
cp = savestr(cp);
} else if ((dp = dfind(*v)) != 0) {
printd = 1;
if (chdir_(dp->di_name) < 0)
Perror(dp->di_name);
dcwd->di_prev->di_next = dcwd->di_next;
dcwd->di_next->di_prev = dcwd->di_prev;
goto flushcwd;
} else
cp = dfollow(*v);
dp = (struct directory *)xcalloc(sizeof (struct directory), 1);
dp->di_name = cp;
dp->di_count = 0;
dp->di_next = dcwd->di_next;
dp->di_prev = dcwd->di_prev;
dp->di_prev->di_next = dp;
dp->di_next->di_prev = dp;
flushcwd:
dfree(dcwd);
dnewcwd(dp);
}
tchar *
dfollow(tchar *cp)
{
tchar *dp;
struct varent *c;
int cdhashval, cdhashval1;
int index;
int slash;
tchar *fullpath;
tchar *slashcp;
#ifdef TRACE
tprintf("TRACE- dfollow()\n");
#endif
cp = globone(cp);
if (chdir_(cp) >= 0)
goto gotcha;
slash = any('/', cp);
if (cp[0] != '/'
&& !prefix(S_DOTSLA , cp)
&& !prefix(S_DOTDOTSLA , cp)
&& (c = adrof(S_cdpath))
&& (!havhash2 || slash)) {
tchar **cdp;
tchar *p;
tchar buf[MAXPATHLEN];
for (cdp = c->vec; *cdp; cdp++) {
for (dp = buf, p = *cdp; *dp++ = *p++; )
;
dp[-1] = '/';
for (p = cp; *dp++ = *p++; )
;
if (chdir_(buf) >= 0) {
printd = 1;
xfree(cp);
cp = savestr(buf);
goto gotcha;
}
}
}
if (cp[0] != '/'
&& !prefix(S_DOTSLA , cp)
&& !prefix(S_DOTDOTSLA , cp)
&& (c = adrof(S_cdpath))
&& havhash2 && !slash) {
tchar **pv;
if (c == 0 || c->vec[0] == 0)
pv = justabs;
else
pv = c->vec;
slashcp = strspl(S_SLASH, cp);
cdhashval = hashname(cp);
index = 0;
do {
if (pv[0][0] == '/') {
cdhashval1 = hash(cdhashval, index);
if (bit(xhash2, cdhashval1)) {
fullpath = strspl(*pv, slashcp);
if (chdir_(fullpath) >= 0) {
printd = 1;
xfree(cp);
cp = savestr(fullpath);
xfree(slashcp);
xfree(fullpath);
goto gotcha;
}
}
}
else {
tchar *p;
tchar buf[MAXPATHLEN];
for (dp = buf, p = *pv; *dp++ = *p++; )
;
dp[-1] = '/';
for (p = cp; *dp++ = *p++; )
;
if (chdir_(buf) >= 0) {
printd = 1;
xfree(cp);
cp = savestr(buf);
xfree(slashcp);
goto gotcha;
}
}
pv++;
index++;
} while (*pv);
}
dp = value(cp);
if ((dp[0] == '/' || dp[0] == '.') && chdir_(dp) >= 0) {
xfree(cp);
cp = savestr(dp);
printd = 1;
goto gotcha;
}
xfree(cp);
Perror(cp);
gotcha:
if (*cp != '/') {
tchar *p, *q;
int cwdlen;
int len;
if ((cwdlen = (strlen_(dcwd->di_name))) == 1) {
if (*dcwd->di_name == '/')
cwdlen = 0;
else
{
tchar path[MAXPATHLEN];
p = getwd_(path);
if (p == NULL)
error("cannot determine current directory");
else
{
xfree(dcwd->di_name);
dcwd->di_name = savestr(p);
xfree(cp);
cp = savestr(p);
return dcanon(cp, cp);
}
}
}
len = strlen_(cp);
dp = (tchar *)xalloc((unsigned)(cwdlen + len + 2) * sizeof (tchar));
for (p = dp, q = dcwd->di_name; *p++ = *q++; )
;
if (cwdlen)
p[-1] = '/';
else
p--;
for (q = cp; *p++ = *q++; )
;
xfree(cp);
cp = dp;
dp += cwdlen;
} else
dp = cp;
return dcanon(cp, dp);
}
void
dopushd(tchar **v)
{
struct directory *dp;
#ifdef TRACE
tprintf("TRACE- dopushd()\n");
#endif
printd = 1;
if (*++v == NOSTR) {
if ((dp = dcwd->di_prev) == &dhead)
dp = dhead.di_prev;
if (dp == dcwd)
bferr("No other directory");
if (chdir_(dp->di_name) < 0)
Perror(dp->di_name);
dp->di_prev->di_next = dp->di_next;
dp->di_next->di_prev = dp->di_prev;
dp->di_next = dcwd->di_next;
dp->di_prev = dcwd;
dcwd->di_next->di_prev = dp;
dcwd->di_next = dp;
} else if (dp = dfind(*v)) {
if (chdir_(dp->di_name) < 0)
Perror(dp->di_name);
} else {
tchar *cp;
cp = dfollow(*v);
dp = (struct directory *)xcalloc(sizeof (struct directory), 1);
dp->di_name = cp;
dp->di_count = 0;
dp->di_prev = dcwd;
dp->di_next = dcwd->di_next;
dcwd->di_next = dp;
dp->di_next->di_prev = dp;
}
dnewcwd(dp);
}
struct directory *
dfind(tchar *cp)
{
struct directory *dp;
int i;
tchar *ep;
#ifdef TRACE
tprintf("TRACE- dfind()\n");
#endif
if (*cp++ != '+')
return (0);
for (ep = cp; digit(*ep); ep++)
continue;
if (*ep)
return (0);
i = getn(cp);
if (i <= 0)
return (0);
for (dp = dcwd; i != 0; i--) {
if ((dp = dp->di_prev) == &dhead)
dp = dp->di_prev;
if (dp == dcwd)
bferr("Directory stack not that deep");
}
return (dp);
}
void
dopopd(tchar **v)
{
struct directory *dp, *p;
#ifdef TRACE
tprintf("TRACE- dopopd()\n");
#endif
printd = 1;
if (*++v == NOSTR)
dp = dcwd;
else if ((dp = dfind(*v)) == 0)
bferr("Invalid argument");
if (dp->di_prev == &dhead && dp->di_next == &dhead)
bferr("Directory stack empty");
if (dp == dcwd) {
if ((p = dp->di_prev) == &dhead)
p = dhead.di_prev;
if (chdir_(p->di_name) < 0)
Perror(p->di_name);
}
dp->di_prev->di_next = dp->di_next;
dp->di_next->di_prev = dp->di_prev;
if (dp == dcwd)
dnewcwd(p);
else
dodirs(fakev);
dfree(dp);
}
void
dfree(struct directory *dp)
{
#ifdef TRACE
tprintf("TRACE- dfree()\n");
#endif
if (dp->di_count != 0)
dp->di_next = dp->di_prev = 0;
else
xfree(dp->di_name), xfree((tchar *)dp);
}
tchar *
dcanon(tchar *cp, tchar *p)
{
tchar *sp;
tchar *p1,
*p2;
bool slash, dotdot, hardpaths;
#ifdef TRACE
tprintf("TRACE- dcannon()\n");
#endif
if (*cp != '/')
abort();
if (hardpaths = (adrof(S_hardpaths) != NULL)) {
p = cp;
}
while (*p) {
sp = p;
while (*++p == '/')
;
if (p != ++sp)
for (p1 = sp, p2 = p; *p1++ = *p2++; )
;
p = sp;
slash = 0;
if (*p)
while (*++p)
if (*p == '/') {
slash = 1;
*p = '\0';
break;
}
if (*sp == '\0') {
if (--sp == cp)
break;
else
*sp = '\0';
continue;
}
if (sp[0] == '.' && sp[1] == '\0') {
if (slash) {
for (p1 = sp, p2 = p + 1; *p1++ = *p2++; )
;
p = --sp;
} else if (--sp != cp)
*sp = '\0';
continue;
}
dotdot = sp[0] == '.' && sp[1] == '.' && sp[2] == '\0';
if (hardpaths || dotdot) {
tchar link[MAXPATHLEN];
int cc;
tchar *newcp;
sp--;
if (! hardpaths)
*sp = '\0';
if ((hardpaths || sp > cp) &&
(cc = readlink_(cp, link, MAXPATHLEN)) >= 0) {
if (slash)
*p = '/';
if (! hardpaths) {
*(p = sp) = '/';
}
for (p1 = p; *p1++; )
;
if (*link != '/') {
if (! hardpaths) {
while (*--sp != '/')
;
}
sp++;
*sp = '\0';
p1 = newcp = (tchar *)xalloc((unsigned)
((sp - cp) + cc + (p1 - p)) * sizeof (tchar));
for (p2 = cp; *p1++ = *p2++; )
;
for (p1--, p2 = link; *p1++ = *p2++; )
;
for (p1--, p2 = p; *p1++ = *p2++; )
;
p = sp - cp - 1 + newcp;
} else {
p1 = newcp = (tchar *)xalloc((unsigned)
(cc + (p1 - p))*sizeof (tchar));
for (p2 = link; *p1++ = *p2++; )
;
for (p1--, p2 = p; *p1++ = *p2++; )
;
p = newcp;
}
xfree(cp);
cp = newcp;
continue;
}
if (! hardpaths)
*sp = '/';
}
if (dotdot) {
if (sp != cp)
while (*--sp != '/')
;
if (slash) {
for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++; )
;
p = sp;
} else if (cp == sp)
*++sp = '\0';
else
*sp = '\0';
continue;
}
if (slash)
*p = '/';
}
return cp;
}
void
dnewcwd(struct directory *dp)
{
#ifdef TRACE
tprintf("TRACE- dnewcwd()\n");
#endif
dcwd = dp;
#ifdef notdef
#endif
didchdir = 1;
set(S_cwd, savestr(dcwd->di_name));
didchdir = 0;
local_setenv(S_PWD, dcwd->di_name);
if (printd)
dodirs(fakev);
}