source: trunk/third/tcsh/sh.dir.c @ 12039

Revision 12039, 31.8 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12038, which included commits to RCS files with non-trunk default branches.
Line 
1/* $Header: /afs/dev.mit.edu/source/repository/third/tcsh/sh.dir.c,v 1.1.1.2 1998-10-03 21:09:57 danw Exp $ */
2/*
3 * sh.dir.c: Directory manipulation functions
4 */
5/*-
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *      This product includes software developed by the University of
20 *      California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37#include "sh.h"
38
39RCSID("$Id: sh.dir.c,v 1.1.1.2 1998-10-03 21:09:57 danw Exp $")
40
41/*
42 * C Shell - directory management
43 */
44
45static  void                     dstart         __P((const char *));
46static  struct directory        *dfind          __P((Char *));
47static  Char                    *dfollow        __P((Char *));
48static  void                     printdirs      __P((int));
49static  Char                    *dgoto          __P((Char *));
50static  void                     dnewcwd        __P((struct directory *, int));
51static  void                     dset           __P((Char *));
52static  void                     dextract       __P((struct directory *));
53static  int                      skipargs       __P((Char ***, char *, char *));
54static  void                     dgetstack      __P((void));
55
56static struct directory dhead INIT_ZERO_STRUCT;         /* "head" of loop */
57static int    printd;                   /* force name to be printed */
58
59int     bequiet = 0;            /* do not print dir stack -strike */
60
61static void
62dstart(from)
63    const char *from;
64{
65    xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from);
66}
67
68/*
69 * dinit - initialize current working directory
70 */
71void
72dinit(hp)
73    Char   *hp;
74{
75    register char *tcp;
76    register Char *cp;
77    register struct directory *dp;
78    char    path[MAXPATHLEN];
79
80    /* Don't believe the login shell home, because it may be a symlink */
81    tcp = (char *) getcwd(path, sizeof(path));
82    if (tcp == NULL || *tcp == '\0') {
83        xprintf("%s: %s\n", progname, strerror(errno));
84        if (hp && *hp) {
85            tcp = short2str(hp);
86            dstart(tcp);
87            if (chdir(tcp) == -1)
88                cp = NULL;
89            else
90                cp = Strsave(hp);
91        }
92        else
93            cp = NULL;
94        if (cp == NULL) {
95            dstart("/");
96            if (chdir("/") == -1)
97                /* I am not even try to print an error message! */
98                xexit(1);
99            cp = SAVE("/");
100        }
101    }
102    else {
103#ifdef S_IFLNK
104        struct stat swd, shp;
105
106        /*
107         * See if $HOME is the working directory we got and use that
108         */
109        if (hp && *hp &&
110            stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
111            DEV_DEV_COMPARE(swd.st_dev, shp.st_dev)  &&
112                swd.st_ino == shp.st_ino)
113            cp = Strsave(hp);
114        else {
115            char   *cwd;
116
117            /*
118             * use PWD if we have it (for subshells)
119             */
120            if ((cwd = getenv("PWD")) != NULL) {
121                if (stat(cwd, &shp) != -1 &&
122                        DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) &&
123                    swd.st_ino == shp.st_ino)
124                    tcp = cwd;
125            }
126            cp = dcanon(SAVE(tcp), STRNULL);
127        }
128#else /* S_IFLNK */
129        cp = dcanon(SAVE(tcp), STRNULL);
130#endif /* S_IFLNK */
131    }
132
133    dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
134    dp->di_name = cp;
135    dp->di_count = 0;
136    dhead.di_next = dhead.di_prev = dp;
137    dp->di_next = dp->di_prev = &dhead;
138    printd = 0;
139    dnewcwd(dp, 0);
140    set(STRdirstack, Strsave(dp->di_name), VAR_READWRITE|VAR_NOGLOB);
141}
142
143static void
144dset(dp)
145Char *dp;
146{
147    /*
148     * Don't call set() directly cause if the directory contains ` or
149     * other junk characters glob will fail.
150     */
151    set(STRowd, Strsave(varval(STRcwd)), VAR_READWRITE|VAR_NOGLOB);
152    set(STRcwd, Strsave(dp), VAR_READWRITE|VAR_NOGLOB);
153
154    tsetenv(STRPWD, dp);
155}
156
157#define DIR_PRINT       0x01    /* -p */
158#define DIR_LONG        0x02    /* -l */
159#define DIR_VERT        0x04    /* -v */
160#define DIR_LINE        0x08    /* -n */
161#define DIR_SAVE        0x10    /* -S */
162#define DIR_LOAD        0x20    /* -L */
163#define DIR_CLEAR       0x40    /* -c */
164#define DIR_OLD         0x80    /* - */
165
166static int
167skipargs(v, dstr, str)
168    Char ***v;
169    char   *dstr;
170    char   *str;
171{
172    Char  **n = *v, *s;
173
174    int dflag = 0, loop = 1;
175    for (n++; loop && *n != NULL && (*n)[0] == '-'; n++)
176        if (*(s = &((*n)[1])) == '\0')  /* test for bare "-" argument */
177            dflag |= DIR_OLD;
178        else {
179            char *p;
180            while (loop && *s != '\0')  /* examine flags */
181            {
182                if ((p = strchr(dstr, *s++)) != NULL)
183                    dflag |= (1 << (p - dstr));
184                else {
185                    stderror(ERR_DIRUS, short2str(**v), dstr, str);
186                    loop = 0;   /* break from both loops */
187                    break;
188                }
189            }
190        }
191    if (*n && (dflag & DIR_OLD))
192        stderror(ERR_DIRUS, short2str(**v), dstr, str);
193    *v = n;
194    /* make -l, -v, and -n imply -p */
195    if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE))
196        dflag |= DIR_PRINT;
197    return dflag;
198}
199
200/*
201 * dodirs - list all directories in directory loop
202 */
203/*ARGSUSED*/
204void
205dodirs(v, c)
206    Char  **v;
207    struct command *c;
208{
209    static char flags[] = "plvnSLc";
210    int dflag = skipargs(&v, flags, "");
211
212    USE(c);
213    if ((dflag & DIR_CLEAR) != 0) {
214        struct directory *dp, *fdp;
215        for (dp = dcwd->di_next; dp != dcwd; ) {
216            fdp = dp;
217            dp = dp->di_next;
218            if (fdp != &dhead)
219                dfree(fdp);
220        }
221        dhead.di_next = dhead.di_prev = dp;
222        dp->di_next = dp->di_prev = &dhead;
223    }
224    if ((dflag & DIR_LOAD) != 0)
225        loaddirs(*v);
226    else if ((dflag & DIR_SAVE) != 0)
227        recdirs(*v, 1);
228
229    if (*v && (dflag & (DIR_SAVE|DIR_LOAD)))
230        v++;
231
232    if (*v != NULL || (dflag & DIR_OLD))
233        stderror(ERR_DIRUS, "dirs", flags, "");
234    if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT))
235        printdirs(dflag);
236}
237
238static void
239printdirs(dflag)
240    int dflag;
241{
242    register struct directory *dp;
243    Char   *s, *user;
244    int     idx, len, cur;
245    extern int T_Cols;
246
247    dp = dcwd;
248    idx = 0;
249    cur = 0;
250    do {
251        if (dp == &dhead)
252            continue;
253        if (dflag & DIR_VERT) {
254            xprintf("%d\t", idx++);
255            cur = 0;
256        }
257        s = dp->di_name;               
258        user = NULL;
259        if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL)
260            len = (int) (Strlen(user) + Strlen(s) + 2);
261        else
262            len = (int) (Strlen(s) + 1);
263
264        cur += len;
265        if ((dflag & DIR_LINE) && cur >= T_Cols - 1 && len < T_Cols) {
266            xputchar('\n');
267            cur = len;
268        }
269        if (user)
270            xprintf("~%S", user);
271        xprintf("%S%c", s, (dflag & DIR_VERT) ? '\n' : ' ');
272    } while ((dp = dp->di_prev) != dcwd);
273    if (!(dflag & DIR_VERT))
274        xputchar('\n');
275}
276
277void
278dtildepr(dir)
279    Char *dir;
280{
281    Char* user;
282    if ((user = getusername(&dir)) != NULL)
283        xprintf("~%S%S", user, dir);
284    else
285        xprintf("%S", dir);
286}
287
288void
289dtilde()
290{
291    struct directory *d = dcwd;
292
293    do {
294        if (d == &dhead)
295            continue;
296        d->di_name = dcanon(d->di_name, STRNULL);
297    } while ((d = d->di_prev) != dcwd);
298
299    dset(dcwd->di_name);
300}
301
302
303/* dnormalize():
304 *      The path will be normalized if it
305 *      1) is "..",
306 *      2) or starts with "../",
307 *      3) or ends with "/..",
308 *      4) or contains the string "/../",
309 *      then it will be normalized, unless those strings are quoted.
310 *      Otherwise, a copy is made and sent back.
311 */
312Char   *
313dnormalize(cp, exp)
314    Char   *cp;
315    int exp;
316{
317
318/* return true if dp is of the form "../xxx" or "/../xxx" */
319#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
320#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
321
322#ifdef S_IFLNK
323    if (exp) {
324        int     dotdot = 0;
325        Char   *dp, *cwd, *start = cp, buf[MAXPATHLEN];
326# ifdef apollo
327        bool slashslash;
328# endif /* apollo */
329
330        /*
331         * count the number of "../xxx" or "xxx/../xxx" in the path
332         */
333        for (dp=start; *dp && *(dp+1); dp++)
334            if (IS_DOTDOT(start, dp))
335                dotdot++;
336        /*
337         * if none, we are done.
338         */
339        if (dotdot == 0)
340            return (Strsave(cp));
341
342        cwd = (Char *) xmalloc((size_t) (((int) Strlen(dcwd->di_name) + 3) *
343                                           sizeof(Char)));
344        (void) Strcpy(cwd, dcwd->di_name);
345
346        /*
347         * If the path starts with a slash, we are not relative to
348         * the current working directory.
349         */
350        if (ABSOLUTEP(start))
351            *cwd = '\0';
352# ifdef apollo
353        slashslash = cwd[0] == '/' && cwd[1] == '/';
354# endif /* apollo */
355
356        /*
357         * Ignore . and count ..'s
358         */
359        for (;;) {
360            dotdot = 0;
361            buf[0] = '\0';
362            dp = buf;
363            while (*cp)
364                if (IS_DOT(start, cp)) {
365                    if (*++cp)
366                        cp++;
367                }
368                else if (IS_DOTDOT(start, cp)) {
369                    if (buf[0])
370                        break; /* finish analyzing .././../xxx/[..] */
371                    dotdot++;
372                    cp += 2;
373                    if (*cp)
374                        cp++;
375                }
376                else
377                        *dp++ = *cp++;
378
379            *dp = '\0';
380            while (dotdot > 0)
381                if ((dp = Strrchr(cwd, '/')) != NULL) {
382# ifdef apollo
383                    if (dp == &cwd[1])
384                        slashslash = 1;
385# endif /* apollo */
386                        *dp = '\0';
387                        dotdot--;
388                }
389                else
390                    break;
391
392            if (!*cwd) {        /* too many ..'s, starts with "/" */
393                cwd[0] = '/';
394# ifdef apollo
395                cwd[1] = '/';
396                cwd[2] = '\0';
397# else /* !apollo */
398                cwd[1] = '\0';
399# endif /* apollo */
400            }
401# ifdef apollo
402            else if (slashslash && cwd[1] == '\0') {
403                cwd[1] = '/';
404                cwd[2] = '\0';
405            }
406# endif /* apollo */
407
408            if (buf[0]) {
409                if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) != '/')
410                    cwd[dotdot++] = '/';
411                cwd[dotdot] = '\0';
412                dp = Strspl(cwd, TRM(buf[0]) == '/' ? &buf[1] : buf);
413                xfree((ptr_t) cwd);
414                cwd = dp;
415                if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) == '/')
416                    cwd[--dotdot] = '\0';
417            }
418            if (!*cp)
419                break;
420        }
421        return cwd;
422    }
423#endif /* S_IFLNK */
424    return Strsave(cp);
425}
426
427
428/*
429 * dochngd - implement chdir command.
430 */
431/*ARGSUSED*/
432void
433dochngd(v, c)
434    Char  **v;
435    struct command *c;
436{
437    register Char *cp;
438    register struct directory *dp;
439    int dflag = skipargs(&v, "plvn", "[-|<dir>]");
440
441    USE(c);
442    printd = 0;
443    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
444
445    if (cp == NULL) {
446        if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
447            stderror(ERR_NAME | ERR_NOHOMEDIR);
448        if (chdir(short2str(cp)) < 0)
449            stderror(ERR_NAME | ERR_CANTCHANGE);
450        cp = Strsave(cp);
451    }
452    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
453        stderror(ERR_NAME | ERR_TOOMANY);
454        /* NOTREACHED */
455        return;
456    }
457    else if ((dp = dfind(cp)) != 0) {
458        char   *tmp;
459
460        printd = 1;
461        if (chdir(tmp = short2str(dp->di_name)) < 0)
462            stderror(ERR_SYSTEM, tmp, strerror(errno));
463        dcwd->di_prev->di_next = dcwd->di_next;
464        dcwd->di_next->di_prev = dcwd->di_prev;
465        dfree(dcwd);
466        dnewcwd(dp, dflag);
467        return;
468    }
469    else
470        if ((cp = dfollow(cp)) == NULL)
471            return;
472    dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
473    dp->di_name = cp;
474    dp->di_count = 0;
475    dp->di_next = dcwd->di_next;
476    dp->di_prev = dcwd->di_prev;
477    dp->di_prev->di_next = dp;
478    dp->di_next->di_prev = dp;
479    dfree(dcwd);
480    dnewcwd(dp, dflag);
481}
482
483static Char *
484dgoto(cp)
485    Char   *cp;
486{
487    Char   *dp;
488
489    if (!ABSOLUTEP(cp))
490    {
491        register Char *p, *q;
492        int     cwdlen;
493
494        for (p = dcwd->di_name; *p++;)
495            continue;
496        if ((cwdlen = p - dcwd->di_name - 1) == 1)      /* root */
497            cwdlen = 0;
498        for (p = cp; *p++;)
499            continue;
500        dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char)));
501        for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';)
502            continue;
503        if (cwdlen)
504            p[-1] = '/';
505        else
506            p--;                /* don't add a / after root */
507        for (q = cp; (*p++ = *q++) != '\0';)
508            continue;
509        xfree((ptr_t) cp);
510        cp = dp;
511        dp += cwdlen;
512    }
513    else
514        dp = cp;
515
516#ifdef WINNT
517    cp = SAVE(getcwd(NULL, 0));
518#else /* !WINNT */
519    cp = dcanon(cp, dp);
520#endif /* WINNT */
521    return cp;
522}
523
524/*
525 * dfollow - change to arg directory; fall back on cdpath if not valid
526 */
527static Char *
528dfollow(cp)
529    register Char *cp;
530{
531    register Char *dp;
532    struct varent *c;
533    char    ebuf[MAXPATHLEN];
534    int serrno;
535
536    cp = globone(cp, G_ERROR);
537#ifdef apollo
538    if (Strchr(cp, '`')) {
539        char *dptr, *ptr;
540        if (chdir(dptr = short2str(cp)) < 0)
541            stderror(ERR_SYSTEM, dptr, strerror(errno));
542        else if ((ptr = getcwd(ebuf, sizeof(ebuf))) && *ptr != '\0') {
543                xfree((ptr_t) cp);
544                cp = Strsave(str2short(ptr));
545                return dgoto(cp);
546        }
547        else
548            stderror(ERR_SYSTEM, dptr, ebuf);
549    }
550#endif /* apollo */
551           
552    (void) strncpy(ebuf, short2str(cp), MAXPATHLEN);
553    ebuf[MAXPATHLEN-1] = '\0';
554    /*
555     * if we are ignoring symlinks, try to fix relatives now.
556     * if we are expading symlinks, it should be done by now.
557     */
558    dp = dnormalize(cp, symlinks == SYM_IGNORE);
559    if (chdir(short2str(dp)) >= 0) {
560        xfree((ptr_t) cp);
561        return dgoto(dp);
562    }
563    else {
564        xfree((ptr_t) dp);
565        if (chdir(short2str(cp)) >= 0)
566            return dgoto(cp);
567        else if (errno != ENOENT && errno != ENOTDIR)
568            stderror(ERR_SYSTEM, ebuf, strerror(errno));
569        serrno = errno;
570    }
571
572    if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
573        && (c = adrof(STRcdpath))) {
574        Char  **cdp;
575        register Char *p;
576        Char    buf[MAXPATHLEN];
577
578        for (cdp = c->vec; *cdp; cdp++) {
579            for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';)
580                continue;
581            dp[-1] = '/';
582            for (p = cp; (*dp++ = *p++) != '\0';)
583                continue;
584            /*
585             * We always want to fix the directory here
586             * If we are normalizing symlinks
587             */
588            dp = dnormalize(buf, symlinks == SYM_IGNORE ||
589                                 symlinks == SYM_EXPAND);
590            if (chdir(short2str(dp)) >= 0) {
591                printd = 1;
592                xfree((ptr_t) cp);
593                return dgoto(dp);
594            }
595            else if (chdir(short2str(cp)) >= 0) {
596                printd = 1;
597                xfree((ptr_t) dp);
598                return dgoto(cp);
599            }
600        }
601    }
602    dp = varval(cp);
603    if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
604        xfree((ptr_t) cp);
605        cp = Strsave(dp);
606        printd = 1;
607        return dgoto(cp);
608    }
609    xfree((ptr_t) cp);
610    /*
611     * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
612     * directories we could get to.
613     */
614    if (!bequiet) {
615        stderror(ERR_SYSTEM, ebuf, strerror(serrno));
616        return (NULL);
617    }
618    else
619        return (NULL);
620}
621
622
623/*
624 * dopushd - push new directory onto directory stack.
625 *      with no arguments exchange top and second.
626 *      with numeric argument (+n) bring it to top.
627 */
628/*ARGSUSED*/
629void
630dopushd(v, c)
631    Char  **v;
632    struct command *c;
633{
634    register struct directory *dp;
635    register Char *cp;
636    int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]");
637   
638    USE(c);
639    printd = 1;
640    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
641
642    if (cp == NULL) {
643        if (adrof(STRpushdtohome)) {
644            if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
645                stderror(ERR_NAME | ERR_NOHOMEDIR);
646            if (chdir(short2str(cp)) < 0)
647                stderror(ERR_NAME | ERR_CANTCHANGE);
648            cp = Strsave(cp);   /* hmmm... PWP */
649            if ((cp = dfollow(cp)) == NULL)
650                return;
651            dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
652            dp->di_name = cp;
653            dp->di_count = 0;
654            dp->di_prev = dcwd;
655            dp->di_next = dcwd->di_next;
656            dcwd->di_next = dp;
657            dp->di_next->di_prev = dp;
658        }
659        else {
660            char   *tmp;
661
662            if ((dp = dcwd->di_prev) == &dhead)
663                dp = dhead.di_prev;
664            if (dp == dcwd)
665                stderror(ERR_NAME | ERR_NODIR);
666            if (chdir(tmp = short2str(dp->di_name)) < 0)
667                stderror(ERR_SYSTEM, tmp, strerror(errno));
668            dp->di_prev->di_next = dp->di_next;
669            dp->di_next->di_prev = dp->di_prev;
670            dp->di_next = dcwd->di_next;
671            dp->di_prev = dcwd;
672            dcwd->di_next->di_prev = dp;
673            dcwd->di_next = dp;
674        }
675    }
676    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
677        stderror(ERR_NAME | ERR_TOOMANY);
678        /* NOTREACHED */
679        return;
680    }
681    else if ((dp = dfind(cp)) != NULL) {
682        char   *tmp;
683
684        if (chdir(tmp = short2str(dp->di_name)) < 0)
685            stderror(ERR_SYSTEM, tmp, strerror(errno));
686        /*
687         * kfk - 10 Feb 1984 - added new "extraction style" pushd +n
688         */
689        if (adrof(STRdextract))
690            dextract(dp);
691    }
692    else {
693        register Char *ccp;
694
695        if ((ccp = dfollow(cp)) == NULL)
696            return;
697        dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
698        dp->di_name = ccp;
699        dp->di_count = 0;
700        dp->di_prev = dcwd;
701        dp->di_next = dcwd->di_next;
702        dcwd->di_next = dp;
703        dp->di_next->di_prev = dp;
704    }
705    dnewcwd(dp, dflag);
706}
707
708/*
709 * dfind - find a directory if specified by numeric (+n) argument
710 */
711static struct directory *
712dfind(cp)
713    register Char *cp;
714{
715    register struct directory *dp;
716    register int i;
717    register Char *ep;
718
719    if (*cp++ != '+')
720        return (0);
721    for (ep = cp; Isdigit(*ep); ep++)
722        continue;
723    if (*ep)
724        return (0);
725    i = getn(cp);
726    if (i <= 0)
727        return (0);
728    for (dp = dcwd; i != 0; i--) {
729        if ((dp = dp->di_prev) == &dhead)
730            dp = dp->di_prev;
731        if (dp == dcwd)
732            stderror(ERR_NAME | ERR_DEEP);
733    }
734    return (dp);
735}
736
737/*
738 * dopopd - pop a directory out of the directory stack
739 *      with a numeric argument just discard it.
740 */
741/*ARGSUSED*/
742void
743dopopd(v, c)
744    Char  **v;
745    struct command *c;
746{
747    Char *cp;
748    register struct directory *dp, *p = NULL;
749    int dflag = skipargs(&v, "plvn", " [-|+<n>]");
750
751    USE(c);
752    printd = 1;
753    cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
754
755    if (cp == NULL)
756        dp = dcwd;
757    else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
758        stderror(ERR_NAME | ERR_TOOMANY);
759        /* NOTREACHED */
760        return;
761    }
762    else if ((dp = dfind(cp)) == 0)
763        stderror(ERR_NAME | ERR_BADDIR);
764    if (dp->di_prev == &dhead && dp->di_next == &dhead)
765        stderror(ERR_NAME | ERR_EMPTY);
766    if (dp == dcwd) {
767        char   *tmp;
768
769        if ((p = dp->di_prev) == &dhead)
770            p = dhead.di_prev;
771        if (chdir(tmp = short2str(p->di_name)) < 0)
772            stderror(ERR_SYSTEM, tmp, strerror(errno));
773    }
774    dp->di_prev->di_next = dp->di_next;
775    dp->di_next->di_prev = dp->di_prev;
776    if (dp == dcwd) {
777        dnewcwd(p, dflag);
778    }
779    else {
780        printdirs(dflag);
781    }
782    dfree(dp);
783}
784
785/*
786 * dfree - free the directory (or keep it if it still has ref count)
787 */
788void
789dfree(dp)
790    register struct directory *dp;
791{
792
793    if (dp->di_count != 0) {
794        dp->di_next = dp->di_prev = 0;
795    }
796    else {
797        xfree((ptr_t) dp->di_name);
798        xfree((ptr_t) dp);
799    }
800}
801
802/*
803 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
804 *      we are of course assuming that the file system is standardly
805 *      constructed (always have ..'s, directories have links)
806 */
807Char   *
808dcanon(cp, p)
809    register Char *cp, *p;
810{
811    register Char *sp;
812    register Char *p1, *p2;     /* general purpose */
813    bool    slash;
814#ifdef apollo
815    bool    slashslash;
816#endif /* apollo */
817
818#ifdef S_IFLNK                  /* if we have symlinks */
819    Char    link[MAXPATHLEN];
820    char    tlink[MAXPATHLEN];
821    int     cc;
822    Char   *newcp;
823#endif /* S_IFLNK */
824
825    /*
826     * kim: if the path given is too long abort().
827     */
828    if (Strlen(cp) >= MAXPATHLEN)
829        abort();
830
831    /*
832     * christos: if the path given does not start with a slash prepend cwd. If
833     * cwd does not start with a slash or the result would be too long abort().
834     */
835    if (!ABSOLUTEP(cp)) {
836        Char    tmpdir[MAXPATHLEN];
837
838        p1 = varval(STRcwd);
839        if (p1 == STRNULL || ABSOLUTEP(p1))
840            abort();
841        if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN)
842            abort();
843        (void) Strcpy(tmpdir, p1);
844        (void) Strcat(tmpdir, STRslash);
845        (void) Strcat(tmpdir, cp);
846        xfree((ptr_t) cp);
847        cp = p = Strsave(tmpdir);
848    }
849
850#ifdef COMMENT
851    if (*cp != '/')
852        abort();
853#endif /* COMMENT */
854
855#ifdef apollo
856    slashslash = (cp[0] == '/' && cp[1] == '/');
857#endif /* apollo */
858
859    while (*p) {                /* for each component */
860        sp = p;                 /* save slash address */
861        while (*++p == '/')     /* flush extra slashes */
862            continue;
863        if (p != ++sp)
864            for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';)
865                continue;
866        p = sp;                 /* save start of component */
867        slash = 0;
868        if (*p)
869            while (*++p)        /* find next slash or end of path */
870                if (*p == '/') {
871                    slash = 1;
872                    *p = 0;
873                    break;
874                }
875
876#ifdef apollo
877        if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0')
878            slashslash = 1;
879#endif /* apollo */
880        if (*sp == '\0') {      /* if component is null */
881            if (--sp == cp)     /* if path is one char (i.e. /) */
882                break;
883            else
884                *sp = '\0';
885        }
886        else if (sp[0] == '.' && sp[1] == 0) {
887            if (slash) {
888                for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';)
889                    continue;
890                p = --sp;
891            }
892            else if (--sp != cp)
893                *sp = '\0';
894            else
895                sp[1] = '\0';
896        }
897        else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
898            /*
899             * We have something like "yyy/xxx/..", where "yyy" can be null or
900             * a path starting at /, and "xxx" is a single component. Before
901             * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
902             * symbolic link.
903             */
904            *--sp = 0;          /* form the pathname for readlink */
905#ifdef S_IFLNK                  /* if we have symlinks */
906            if (sp != cp && /* symlinks != SYM_IGNORE && */
907                (cc = readlink(short2str(cp), tlink,
908                               sizeof tlink)) >= 0) {
909                tlink[cc] = '\0';
910                (void) Strncpy(link, str2short(tlink),
911                    sizeof(link) / sizeof(Char));
912                link[sizeof(link) / sizeof(Char) - 1] = '\0';
913
914                if (slash)
915                    *p = '/';
916                /*
917                 * Point p to the '/' in "/..", and restore the '/'.
918                 */
919                *(p = sp) = '/';
920                /*
921                 * find length of p
922                 */
923                for (p1 = p; *p1++;)
924                    continue;
925                if (*link != '/') {
926                    /*
927                     * Relative path, expand it between the "yyy/" and the
928                     * "/..". First, back sp up to the character past "yyy/".
929                     */
930                    while (*--sp != '/')
931                        continue;
932                    sp++;
933                    *sp = 0;
934                    /*
935                     * New length is "yyy/" + link + "/.." and rest
936                     */
937                    p1 = newcp = (Char *) xmalloc((size_t)
938                                                (((sp - cp) + cc + (p1 - p)) *
939                                                 sizeof(Char)));
940                    /*
941                     * Copy new path into newcp
942                     */
943                    for (p2 = cp; (*p1++ = *p2++) != '\0';)
944                        continue;
945                    for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
946                        continue;
947                    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
948                        continue;
949                    /*
950                     * Restart canonicalization at expanded "/xxx".
951                     */
952                    p = sp - cp - 1 + newcp;
953                }
954                else {
955                    /*
956                     * New length is link + "/.." and rest
957                     */
958                    p1 = newcp = (Char *) xmalloc((size_t)
959                                            ((cc + (p1 - p)) * sizeof(Char)));
960                    /*
961                     * Copy new path into newcp
962                     */
963                    for (p2 = link; (*p1++ = *p2++) != '\0';)
964                        continue;
965                    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
966                        continue;
967                    /*
968                     * Restart canonicalization at beginning
969                     */
970                    p = newcp;
971                }
972                xfree((ptr_t) cp);
973                cp = newcp;
974#ifdef apollo
975                slashslash = (cp[0] == '/' && cp[1] == '/');
976#endif /* apollo */
977                continue;       /* canonicalize the link */
978            }
979#endif /* S_IFLNK */
980            *sp = '/';
981            if (sp != cp)
982                while (*--sp != '/')
983                    continue;
984            if (slash) {
985                for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';)
986                    continue;
987                p = sp;
988            }
989            else if (cp == sp)
990                *++sp = '\0';
991            else
992                *sp = '\0';
993        }
994        else {                  /* normal dir name (not . or .. or nothing) */
995
996#ifdef S_IFLNK                  /* if we have symlinks */
997            if (sp != cp && symlinks == SYM_CHASE &&
998                (cc = readlink(short2str(cp), tlink,
999                               sizeof tlink)) >= 0) {
1000                tlink[cc] = '\0';
1001                (void) Strncpy(link, str2short(tlink),
1002                    sizeof(link) / sizeof(Char));
1003                link[sizeof(link) / sizeof(Char) - 1] = '\0';
1004
1005                /*
1006                 * restore the '/'.
1007                 */
1008                if (slash)
1009                    *p = '/';
1010
1011                /*
1012                 * point sp to p (rather than backing up).
1013                 */
1014                sp = p;
1015
1016                /*
1017                 * find length of p
1018                 */
1019                for (p1 = p; *p1++;)
1020                    continue;
1021                if (*link != '/') {
1022                    /*
1023                     * Relative path, expand it between the "yyy/" and the
1024                     * remainder. First, back sp up to the character past
1025                     * "yyy/".
1026                     */
1027                    while (*--sp != '/')
1028                        continue;
1029                    sp++;
1030                    *sp = 0;
1031                    /*
1032                     * New length is "yyy/" + link + "/.." and rest
1033                     */
1034                    p1 = newcp = (Char *) xmalloc((size_t)
1035                                                  (((sp - cp) + cc + (p1 - p))
1036                                                   * sizeof(Char)));
1037                    /*
1038                     * Copy new path into newcp
1039                     */
1040                    for (p2 = cp; (*p1++ = *p2++) != '\0';)
1041                        continue;
1042                    for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
1043                        continue;
1044                    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
1045                        continue;
1046                    /*
1047                     * Restart canonicalization at expanded "/xxx".
1048                     */
1049                    p = sp - cp - 1 + newcp;
1050                }
1051                else {
1052                    /*
1053                     * New length is link + the rest
1054                     */
1055                    p1 = newcp = (Char *) xmalloc((size_t)
1056                                            ((cc + (p1 - p)) * sizeof(Char)));
1057                    /*
1058                     * Copy new path into newcp
1059                     */
1060                    for (p2 = link; (*p1++ = *p2++) != '\0';)
1061                        continue;
1062                    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
1063                        continue;
1064                    /*
1065                     * Restart canonicalization at beginning
1066                     */
1067                    p = newcp;
1068                }
1069                xfree((ptr_t) cp);
1070                cp = newcp;
1071#ifdef apollo
1072                slashslash = (cp[0] == '/' && cp[1] == '/');
1073#endif /* apollo */
1074                continue;       /* canonicalize the link */
1075            }
1076#endif /* S_IFLNK */
1077            if (slash)
1078                *p = '/';
1079        }
1080    }
1081
1082    /*
1083     * fix home...
1084     */
1085#ifdef S_IFLNK
1086    p1 = varval(STRhome);
1087    cc = (int) Strlen(p1);
1088    /*
1089     * See if we're not in a subdir of STRhome
1090     */
1091    if (p1 && *p1 == '/' && (Strncmp(p1, cp, (size_t) cc) != 0 ||
1092        (cp[cc] != '/' && cp[cc] != '\0'))) {
1093        static ino_t home_ino = (ino_t) -1;
1094        static dev_t home_dev = (dev_t) -1;
1095        static Char *home_ptr = NULL;
1096        struct stat statbuf;
1097        int found;
1098
1099        /*
1100         * Get dev and ino of STRhome
1101         */
1102        if (home_ptr != p1 &&
1103            stat(short2str(p1), &statbuf) != -1) {
1104            home_dev = statbuf.st_dev;
1105            home_ino = statbuf.st_ino;
1106            home_ptr = p1;
1107        }
1108        /*
1109         * Start comparing dev & ino backwards
1110         */
1111        p2 = Strncpy(link, cp, sizeof(link) / sizeof(Char));
1112        link[sizeof(link) / sizeof(Char) - 1] = '\0';
1113        found = 0;
1114        while (*p2 && stat(short2str(p2), &statbuf) != -1) {
1115            if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) &&
1116                        statbuf.st_ino == home_ino) {
1117                        found = 1;
1118                        break;
1119            }
1120            if ((sp = Strrchr(p2, '/')) != NULL)
1121                *sp = '\0';
1122        }
1123        /*
1124         * See if we found it
1125         */
1126        if (*p2 && found) {
1127            /*
1128             * Use STRhome to make '~' work
1129             */
1130            newcp = Strspl(p1, cp + Strlen(p2));
1131            xfree((ptr_t) cp);
1132            cp = newcp;
1133        }
1134    }
1135#endif /* S_IFLNK */
1136
1137#ifdef apollo
1138    if (slashslash) {
1139        if (cp[1] != '/') {
1140            p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char));
1141            *p = '/';
1142            (void) Strcpy(&p[1], cp);
1143            xfree((ptr_t) cp);
1144            cp = p;
1145        }
1146    }
1147    if (cp[1] == '/' && cp[2] == '/')
1148        (void) Strcpy(&cp[1], &cp[2]);
1149#endif /* apollo */
1150    return cp;
1151}
1152
1153
1154/*
1155 * dnewcwd - make a new directory in the loop the current one
1156 */
1157static void
1158dnewcwd(dp, dflag)
1159    register struct directory *dp;
1160    int dflag;
1161{
1162    int print;
1163
1164    if (adrof(STRdunique)) {
1165        struct directory *dn;
1166
1167        for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev)
1168            if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) {
1169                dn->di_next->di_prev = dn->di_prev;
1170                dn->di_prev->di_next = dn->di_next;
1171                dfree(dn);
1172                break;
1173            }
1174    }
1175    dcwd = dp;
1176    dset(dcwd->di_name);
1177    dgetstack();
1178    print = printd;             /* if printd is set, print dirstack... */
1179    if (adrof(STRpushdsilent))  /* but pushdsilent overrides printd... */
1180        print = 0;
1181    if (dflag & DIR_PRINT)      /* but DIR_PRINT overrides pushdsilent... */
1182        print = 1;
1183    if (bequiet)                /* and bequiet overrides everything */
1184        print = 0;
1185    if (print)
1186        printdirs(dflag);
1187    cwd_cmd();                  /* PWP: run the defined cwd command */
1188}
1189
1190void
1191dsetstack()
1192{
1193    Char **cp;
1194    struct varent *vp;
1195    struct directory *dn, *dp;
1196
1197    if ((vp = adrof(STRdirstack)) == NULL)
1198        return;
1199
1200    /* Free the whole stack */
1201    while ((dn = dhead.di_prev) != &dhead) {
1202        dn->di_next->di_prev = dn->di_prev;
1203        dn->di_prev->di_next = dn->di_next;
1204        if (dn != dcwd)
1205            dfree(dn);
1206    }
1207
1208    /* thread the current working directory */
1209    dhead.di_prev = dhead.di_next = dcwd;
1210    dcwd->di_next = dcwd->di_prev = &dhead;
1211
1212    /* put back the stack */
1213    for (cp = vp->vec; cp && *cp && **cp; cp++) {
1214        dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
1215        dp->di_name = Strsave(*cp);
1216        dp->di_count = 0;
1217        dp->di_prev = dcwd;
1218        dp->di_next = dcwd->di_next;
1219        dcwd->di_next = dp;
1220        dp->di_next->di_prev = dp;
1221    }
1222    dgetstack();        /* Make $dirstack reflect the current state */
1223}
1224
1225static void
1226dgetstack()
1227{
1228    int i = 0;
1229    Char **dblk, **dbp;
1230    struct directory *dn;
1231
1232    if (adrof(STRdirstack) == NULL)
1233        return;
1234
1235    for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++)
1236        continue;
1237    dbp = dblk = (Char**) xmalloc((size_t) (i + 1) * sizeof(Char *));
1238    for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++)
1239         *dbp = Strsave(dn->di_name);
1240    *dbp = NULL;
1241    setq(STRdirstack, dblk, &shvhed, VAR_READWRITE);
1242}
1243
1244/*
1245 * getstakd - added by kfk 17 Jan 1984
1246 * Support routine for the stack hack.  Finds nth directory in
1247 * the directory stack, or finds last directory in stack.
1248 */
1249int
1250getstakd(s, cnt)
1251    Char   *s;
1252    int     cnt;
1253{
1254    struct directory *dp;
1255
1256    dp = dcwd;
1257    if (cnt < 0) {              /* < 0 ==> last dir requested. */
1258        dp = dp->di_next;
1259        if (dp == &dhead)
1260            dp = dp->di_next;
1261    }
1262    else {
1263        while (cnt-- > 0) {
1264            dp = dp->di_prev;
1265            if (dp == &dhead)
1266                dp = dp->di_prev;
1267            if (dp == dcwd)
1268                return (0);
1269        }
1270    }
1271    (void) Strcpy(s, dp->di_name);
1272    return (1);
1273}
1274
1275/*
1276 * Karl Kleinpaste - 10 Feb 1984
1277 * Added dextract(), which is used in pushd +n.
1278 * Instead of just rotating the entire stack around, dextract()
1279 * lets the user have the nth dir extracted from its current
1280 * position, and pushes it onto the top.
1281 */
1282static void
1283dextract(dp)
1284    struct directory *dp;
1285{
1286    if (dp == dcwd)
1287        return;
1288    dp->di_next->di_prev = dp->di_prev;
1289    dp->di_prev->di_next = dp->di_next;
1290    dp->di_next = dcwd->di_next;
1291    dp->di_prev = dcwd;
1292    dp->di_next->di_prev = dp;
1293    dcwd->di_next = dp;
1294}
1295
1296void
1297loaddirs(fname)
1298    Char *fname;
1299{
1300    static Char *loaddirs_cmd[] = { STRsource, NULL, NULL };
1301
1302    bequiet = 1;
1303    if (fname)
1304        loaddirs_cmd[1] = fname;
1305    else if ((fname = varval(STRdirsfile)) != STRNULL)
1306        loaddirs_cmd[1] = fname;
1307    else
1308        loaddirs_cmd[1] = STRtildotdirs;
1309    dosource(loaddirs_cmd, (struct command *)0);
1310    bequiet = 0;
1311}
1312
1313/*
1314 * create a file called ~/.cshdirs which has a sequence
1315 * of pushd commands which will restore the dir stack to
1316 * its state before exit/logout. remember that the order
1317 * is reversed in the file because we are pushing.
1318 * -strike
1319 */
1320void
1321recdirs(fname, def)
1322    Char *fname;
1323    int def;
1324{
1325    int     fp, ftmp, oldidfds;
1326    int     cdflag = 0;
1327    extern struct directory *dcwd;
1328    struct directory *dp;
1329    unsigned int    num;
1330    Char   *snum;
1331    Char    qname[MAXPATHLEN*2];
1332
1333    if (fname == NULL && !def)
1334        return;
1335
1336    if (fname == NULL) {
1337        if ((fname = varval(STRdirsfile)) == STRNULL)
1338            fname = Strspl(varval(STRhome), &STRtildotdirs[1]);
1339        else
1340            fname = Strsave(fname);
1341    }
1342    else
1343        fname = globone(fname, G_ERROR);
1344               
1345    if ((fp = creat(short2str(fname), 0600)) == -1) {
1346        xfree((ptr_t) fname);
1347        return;
1348    }
1349
1350    if ((snum = varval(STRsavedirs)) == STRNULL)
1351        num = (unsigned int) ~0;
1352    else
1353        num = (unsigned int) atoi(short2str(snum));
1354
1355    oldidfds = didfds;
1356    didfds = 0;
1357    ftmp = SHOUT;
1358    SHOUT = fp;
1359
1360    dp = dcwd->di_next;
1361    do {
1362        if (dp == &dhead)
1363            continue;
1364
1365        if (cdflag == 0) {
1366            cdflag = 1;
1367            xprintf("cd %S\n", quote_meta(qname, dp->di_name));
1368        }
1369        else
1370            xprintf("pushd %S\n", quote_meta(qname, dp->di_name));
1371
1372        if (num-- == 0)
1373            break;
1374
1375    } while ((dp = dp->di_next) != dcwd->di_next);
1376
1377    (void) close(fp);
1378    SHOUT = ftmp;
1379    didfds = oldidfds;
1380    xfree((ptr_t) fname);
1381}
Note: See TracBrowser for help on using the repository browser.