home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / bin / sh / cd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-15  |  8.1 KB  |  367 lines

  1. /*-
  2.  * Copyright (c) 1991 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * This code is derived from software contributed to Berkeley by
  6.  * Kenneth Almquist.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  * 1. Redistributions of source code must retain the above copyright
  12.  *    notice, this list of conditions and the following disclaimer.
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  *    notice, this list of conditions and the following disclaimer in the
  15.  *    documentation and/or other materials provided with the distribution.
  16.  * 3. All advertising materials mentioning features or use of this software
  17.  *    must display the following acknowledgement:
  18.  *    This product includes software developed by the University of
  19.  *    California, Berkeley and its contributors.
  20.  * 4. Neither the name of the University nor the names of its contributors
  21.  *    may be used to endorse or promote products derived from this software
  22.  *    without specific prior written permission.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34.  * SUCH DAMAGE.
  35.  */
  36.  
  37. #ifndef lint
  38. static char sccsid[] = "@(#)cd.c    5.2 (Berkeley) 3/13/91";
  39. #endif /* not lint */
  40.  
  41. /*
  42.  * The cd and pwd commands.
  43.  */
  44.  
  45. #include "shell.h"
  46. #include "var.h"
  47. #include "nodes.h"    /* for jobs.h */
  48. #include "jobs.h"
  49. #include "options.h"
  50. #include "output.h"
  51. #include "memalloc.h"
  52. #include "error.h"
  53. #include "mystring.h"
  54. #include <sys/types.h>
  55. #include <sys/stat.h>
  56. #include <errno.h>
  57.  
  58.  
  59. #ifdef __STDC__
  60. STATIC int docd(char *, int);
  61. STATIC void updatepwd(char *);
  62. STATIC void getpwd(void);
  63. STATIC char *getcomponent(void);
  64. #else
  65. STATIC int docd();
  66. STATIC void updatepwd();
  67. STATIC void getpwd();
  68. STATIC char *getcomponent();
  69. #endif
  70.  
  71.  
  72. char *curdir;            /* current working directory */
  73. STATIC char *cdcomppath;
  74.  
  75. #if UDIR
  76. extern int didudir;        /* set if /u/logname expanded */
  77. #endif
  78.  
  79.  
  80. int
  81. cdcmd(argc, argv)  char **argv; {
  82.     char *dest;
  83.     char *path;
  84.     char *p;
  85.     struct stat statb;
  86.     char *padvance();
  87.  
  88.     nextopt(nullstr);
  89.     if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
  90.         error("HOME not set");
  91.     if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
  92.         path = nullstr;
  93.     while ((p = padvance(&path, dest)) != NULL) {
  94.         if (stat(p, &statb) >= 0
  95.          && (statb.st_mode & S_IFMT) == S_IFDIR
  96.          && docd(p, strcmp(p, dest)) >= 0)
  97.             return 0;
  98.     }
  99.     error("can't cd to %s", dest);
  100. }
  101.  
  102.  
  103. /*
  104.  * Actually do the chdir.  If the name refers to symbolic links, we
  105.  * compute the actual directory name before doing the cd.  In an
  106.  * interactive shell, print the directory name if "print" is nonzero
  107.  * or if the name refers to a symbolic link.  We also print the name
  108.  * if "/u/logname" was expanded in it, since this is similar to a
  109.  * symbolic link.  (The check for this breaks if the user gives the
  110.  * cd command some additional, unused arguments.)
  111.  */
  112.  
  113. #if SYMLINKS == 0
  114. STATIC int
  115. docd(dest, print)
  116.     char *dest;
  117.     {
  118. #if UDIR
  119.     if (didudir)
  120.         print = 1;
  121. #endif
  122.     INTOFF;
  123.     if (chdir(dest) < 0) {
  124.         INTON;
  125.         return -1;
  126.     }
  127.     updatepwd(dest);
  128.     INTON;
  129.     if (print && iflag)
  130.         out1fmt("%s\n", stackblock());
  131.     return 0;
  132. }
  133.  
  134. #else
  135.  
  136.  
  137.  
  138. STATIC int
  139. docd(dest, print)
  140.     char *dest;
  141.     {
  142.     register char *p;
  143.     register char *q;
  144.     char *symlink;
  145.     char *component;
  146.     struct stat statb;
  147.     int first;
  148.     int i;
  149.  
  150.     TRACE(("docd(\"%s\", %d) called\n", dest, print));
  151. #if UDIR
  152.     if (didudir)
  153.         print = 1;
  154. #endif
  155.  
  156. top:
  157.     cdcomppath = dest;
  158.     STARTSTACKSTR(p);
  159.     if (*dest == '/') {
  160.         STPUTC('/', p);
  161.         cdcomppath++;
  162.     }
  163.     first = 1;
  164.     while ((q = getcomponent()) != NULL) {
  165.         if (q[0] == '\0' || q[0] == '.' && q[1] == '\0')
  166.             continue;
  167.         if (! first)
  168.             STPUTC('/', p);
  169.         first = 0;
  170.         component = q;
  171.         while (*q)
  172.             STPUTC(*q++, p);
  173.         if (equal(component, ".."))
  174.             continue;
  175.         STACKSTRNUL(p);
  176.         if (lstat(stackblock(), &statb) < 0)
  177.             error("lstat %s failed", stackblock());
  178.         if ((statb.st_mode & S_IFMT) != S_IFLNK)
  179.             continue;
  180.  
  181.         /* Hit a symbolic link.  We have to start all over again. */
  182.         print = 1;
  183.         STPUTC('\0', p);
  184.         symlink = grabstackstr(p);
  185.         i = (int)statb.st_size + 2;        /* 2 for '/' and '\0' */
  186.         if (cdcomppath != NULL)
  187.             i += strlen(cdcomppath);
  188.         p = stalloc(i);
  189.         if (readlink(symlink, p, (int)statb.st_size) < 0) {
  190.             error("readlink %s failed", stackblock());
  191.         }
  192.         if (cdcomppath != NULL) {
  193.             p[(int)statb.st_size] = '/';
  194.             scopy(cdcomppath, p + (int)statb.st_size + 1);
  195.         } else {
  196.             p[(int)statb.st_size] = '\0';
  197.         }
  198.         if (p[0] != '/') {    /* relative path name */
  199.             char *r;
  200.             q = r = symlink;
  201.             while (*q) {
  202.                 if (*q++ == '/')
  203.                     r = q;
  204.             }
  205.             *r = '\0';
  206.             dest = stalloc(strlen(symlink) + strlen(p) + 1);
  207.             scopy(symlink, dest);
  208.             strcat(dest, p);
  209.         } else {
  210.             dest = p;
  211.         }
  212.         goto top;
  213.     }
  214.     STPUTC('\0', p);
  215.     p = grabstackstr(p);
  216.     INTOFF;
  217.     if (chdir(p) < 0) {
  218.         INTON;
  219.         return -1;
  220.     }
  221.     updatepwd(p);
  222.     INTON;
  223.     if (print && iflag)
  224.         out1fmt("%s\n", p);
  225.     return 0;
  226. }
  227. #endif /* SYMLINKS */
  228.  
  229.  
  230.  
  231. /*
  232.  * Get the next component of the path name pointed to by cdcomppath.
  233.  * This routine overwrites the string pointed to by cdcomppath.
  234.  */
  235.  
  236. STATIC char *
  237. getcomponent() {
  238.     register char *p;
  239.     char *start;
  240.  
  241.     if ((p = cdcomppath) == NULL)
  242.         return NULL;
  243.     start = cdcomppath;
  244.     while (*p != '/' && *p != '\0')
  245.         p++;
  246.     if (*p == '\0') {
  247.         cdcomppath = NULL;
  248.     } else {
  249.         *p++ = '\0';
  250.         cdcomppath = p;
  251.     }
  252.     return start;
  253. }
  254.  
  255.  
  256.  
  257. /*
  258.  * Update curdir (the name of the current directory) in response to a
  259.  * cd command.  We also call hashcd to let the routines in exec.c know
  260.  * that the current directory has changed.
  261.  */
  262.  
  263. void hashcd();
  264.  
  265. STATIC void
  266. updatepwd(dir)
  267.     char *dir;
  268.     {
  269.     char *new;
  270.     char *p;
  271.  
  272.     hashcd();                /* update command hash table */
  273.     cdcomppath = stalloc(strlen(dir) + 1);
  274.     scopy(dir, cdcomppath);
  275.     STARTSTACKSTR(new);
  276.     if (*dir != '/') {
  277.         if (curdir == NULL)
  278.             return;
  279.         p = curdir;
  280.         while (*p)
  281.             STPUTC(*p++, new);
  282.         if (p[-1] == '/')
  283.             STUNPUTC(new);
  284.     }
  285.     while ((p = getcomponent()) != NULL) {
  286.         if (equal(p, "..")) {
  287.             while (new > stackblock() && (STUNPUTC(new), *new) != '/');
  288.         } else if (*p != '\0' && ! equal(p, ".")) {
  289.             STPUTC('/', new);
  290.             while (*p)
  291.                 STPUTC(*p++, new);
  292.         }
  293.     }
  294.     if (new == stackblock())
  295.         STPUTC('/', new);
  296.     STACKSTRNUL(new);
  297.     if (curdir)
  298.         ckfree(curdir);
  299.     curdir = savestr(stackblock());
  300. }
  301.  
  302.  
  303.  
  304. int
  305. pwdcmd(argc, argv)  char **argv; {
  306.     getpwd();
  307.     out1str(curdir);
  308.     out1c('\n');
  309.     return 0;
  310. }
  311.  
  312.  
  313.  
  314. /*
  315.  * Run /bin/pwd to find out what the current directory is.  We suppress
  316.  * interrupts throughout most of this, but the user can still break out
  317.  * of it by killing the pwd program.  If we already know the current
  318.  * directory, this routine returns immediately.
  319.  */
  320.  
  321. #define MAXPWD 256
  322.  
  323. STATIC void
  324. getpwd() {
  325.     char buf[MAXPWD];
  326.     char *p;
  327.     int i;
  328.     int status;
  329.     struct job *jp;
  330.     int pip[2];
  331.  
  332.     if (curdir)
  333.         return;
  334.     INTOFF;
  335.     if (pipe(pip) < 0)
  336.         error("Pipe call failed");
  337.     jp = makejob((union node *)NULL, 1);
  338.     if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
  339.         close(pip[0]);
  340.         if (pip[1] != 1) {
  341.             close(1);
  342.             copyfd(pip[1], 1);
  343.             close(pip[1]);
  344.         }
  345.         execl("/bin/pwd", "pwd", (char *)0);
  346.         error("Cannot exec /bin/pwd");
  347.     }
  348.     close(pip[1]);
  349.     pip[1] = -1;
  350.     p = buf;
  351.     while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
  352.          || i == -1 && errno == EINTR) {
  353.         if (i > 0)
  354.             p += i;
  355.     }
  356.     close(pip[0]);
  357.     pip[0] = -1;
  358.     status = waitforjob(jp);
  359.     if (status != 0)
  360.         error((char *)0);
  361.     if (i < 0 || p == buf || p[-1] != '\n')
  362.         error("pwd command failed");
  363.     p[-1] = '\0';
  364.     curdir = savestr(buf);
  365.     INTON;
  366. }
  367.