Subversion Repositories HelenOS

Rev

Rev 2714 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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