76ef14ba3ec77f72a082d7077e6193f80b8a427b
[platform/upstream/bash.git] / builtins / cd.def
1 This file is cd.def, from which is created cd.c.  It implements the
2 builtins "cd" and "pwd" in Bash.
3
4 Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
5
6 This file is part of GNU Bash, the Bourne Again SHell.
7
8 Bash is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 1, or (at your option) any later
11 version.
12
13 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with Bash; see the file COPYING.  If not, write to the Free Software
20 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 $PRODUCES cd.c
23 #include <config.h>
24
25 #if defined (HAVE_UNISTD_H)
26 #  ifdef _MINIX
27 #    include <sys/types.h>
28 #  endif
29 #  include <unistd.h>
30 #endif
31
32 #include "../bashtypes.h"
33 #include "../posixdir.h"
34 #include "../posixstat.h"
35 #ifndef _MINIX
36 #include <sys/param.h>
37 #endif
38
39 #include <stdio.h>
40
41 #include "../bashansi.h"
42
43 #include <errno.h>
44 #include <tilde/tilde.h>
45
46 #include "../shell.h"
47 #include "../flags.h"
48 #include "../maxpath.h"
49 #include "common.h"
50 #include "bashgetopt.h"
51
52 #if !defined (errno)
53 extern int errno;
54 #endif /* !errno */
55
56 extern int posixly_correct, interactive;
57 extern int array_needs_making;
58 extern char *bash_getcwd_errstr;
59
60 static int change_to_directory ();
61
62 static char *cdspell ();
63 static int spname (), mindist (), spdist ();
64
65 /* Change this to 1 to get cd spelling correction by default. */
66 int cdspelling = 0;
67
68 int cdable_vars;
69
70 $BUILTIN cd
71 $FUNCTION cd_builtin
72 $SHORT_DOC cd [-PL] [dir]
73 Change the current directory to DIR.  The variable $HOME is the
74 default DIR.  The variable CDPATH defines the search path for
75 the directory containing DIR.  Alternative directory names in CDPATH
76 are separated by a colon (:).  A null directory name is the same as
77 the current directory, i.e. `.'.  If DIR begins with a slash (/),
78 then CDPATH is not used.  If the directory is not found, and the
79 shell option `cdable_vars' is set, then try the word as a variable
80 name.  If that variable has a value, then cd to the value of that
81 variable.  The -P option says to use the physical directory structure
82 instead of following symbolic links; the -L option forces symbolic links
83 to be followed.
84 $END
85
86 /* Take PATH, an element from $CDPATH, and DIR, a directory name, and paste
87    them together into PATH/DIR.  Tilde expansion is performed on PATH if
88    DOTILDE is non-zero.  If PATH is the empty string, it is converted to
89    `./', since a null element in $CDPATH means the current directory. */
90 static char *
91 mkpath (path, dir, dotilde)
92      char *path, *dir;
93      int dotilde;
94 {
95   int dirlen, pathlen;
96   char *ret, *xpath;
97
98   if (*path == '\0')
99     {
100       xpath = xmalloc (2);
101       xpath[0] = '.';
102       xpath[1] = '\0';
103       pathlen = 1;
104     }
105   else
106     {
107       xpath = (dotilde && *path == '~') ? bash_tilde_expand (path) : path;
108       pathlen = strlen (xpath);
109     }
110
111   dirlen = strlen (dir);
112   ret = xmalloc (2 + dirlen + pathlen);
113   strcpy (ret, xpath);
114   if (xpath[pathlen - 1] != '/')
115     {
116       ret[pathlen++] = '/';
117       ret[pathlen] = '\0';
118     }
119   strcpy (ret + pathlen, dir);
120   if (xpath != path)
121     free (xpath);
122   return (ret);
123 }
124
125 static int
126 bindpwd (no_symlinks)
127      int no_symlinks;
128 {
129   char *dirname, *pwdvar;
130   int old_symlinks, old_anm;
131   SHELL_VAR *tvar;
132
133   if (no_symlinks)
134     {
135       old_symlinks = no_symbolic_links;
136       no_symbolic_links = 1;
137       dirname = get_working_directory ("cd");
138       no_symbolic_links = old_symlinks;
139     }
140   else
141     dirname = get_working_directory ("cd");
142
143   bind_variable ("OLDPWD", get_string_value ("PWD"));
144
145   old_anm = array_needs_making;
146   tvar = bind_variable ("PWD", dirname);
147   /* This is an efficiency hack.  If PWD is exported, we will need to
148      remake the exported environment every time we change directories.
149      If there is no other reason to make the exported environment, just
150      update PWD in place and mark the exported environment as no longer
151      needing a remake. */
152   if (old_anm == 0 && array_needs_making && exported_p (tvar))
153     {
154       pwdvar = xmalloc (STRLEN (dirname) + 5);  /* 5 = "PWD" + '=' + '\0' */
155       strcpy (pwdvar, "PWD=");
156       if (dirname)
157         strcpy (pwdvar + 4, dirname);
158       add_or_supercede_exported_var (pwdvar, 0);
159       array_needs_making = 0;
160     }
161
162   FREE (dirname);
163   return (EXECUTION_SUCCESS);
164 }
165
166 /* This builtin is ultimately the way that all user-visible commands should
167    change the current working directory.  It is called by cd_to_string (),
168    so the programming interface is simple, and it handles errors and
169    restrictions properly. */
170 int
171 cd_builtin (list)
172      WORD_LIST *list;
173 {
174   char *dirname, *cdpath, *path, *temp;
175   int path_index, no_symlinks, opt;
176   struct stat sb;
177
178 #if defined (RESTRICTED_SHELL)
179   if (restricted)
180     {
181       builtin_error ("restricted");
182       return (EXECUTION_FAILURE);
183     }
184 #endif /* RESTRICTED_SHELL */
185
186   no_symlinks = no_symbolic_links;
187   reset_internal_getopt ();
188   while ((opt = internal_getopt (list, "LP")) != -1)
189     {
190       switch (opt)
191         {
192         case 'P':
193           no_symlinks = 1;
194           break;
195         case 'L':
196           no_symlinks = 0;
197           break;
198         default:
199           builtin_usage ();
200           return (EXECUTION_FAILURE);
201         }
202     }
203   list = loptend;
204
205   if (list == 0)
206     {
207       /* `cd' without arguments is equivalent to `cd $HOME' */
208       dirname = get_string_value ("HOME");
209
210       if (dirname == 0)
211         {
212           builtin_error ("HOME not set");
213           return (EXECUTION_FAILURE);
214         }
215
216       if (change_to_directory (dirname, no_symlinks) == 0)
217         {
218           builtin_error ("%s: %s", dirname, strerror (errno));
219           return (EXECUTION_FAILURE);
220         }
221     }
222   else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
223     {
224       /* This is `cd -', equivalent to `cd $OLDPWD' */
225       dirname = get_string_value ("OLDPWD");
226
227       if (dirname == 0 || change_to_directory (dirname, no_symlinks) == 0)
228         {
229           if (dirname == 0)
230             builtin_error ("OLDPWD not set");
231           else
232             builtin_error ("%s: %s", dirname, strerror (errno));
233           return (EXECUTION_FAILURE);
234         }
235       if (interactive)
236         printf ("%s\n", dirname);
237     }
238   else
239     {
240       dirname = list->word->word;
241
242       if (absolute_pathname (dirname) == 0 && (cdpath = get_string_value ("CDPATH")))
243         {
244           /* Find directory in $CDPATH. */
245           path_index = 0;
246           while ((path = extract_colon_unit (cdpath, &path_index)))
247             {
248               /* OPT is 1 if the path element is non-empty */
249               opt = path[0] != '\0';
250               temp = mkpath (path, dirname, 1);
251               free (path);
252
253               if (stat (temp, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
254                 {
255                   free (temp);
256                   continue;
257                 }
258
259               if (change_to_directory (temp, no_symlinks))
260                 {
261                   /* POSIX.2 says that if a nonempty directory from CDPATH
262                      is used to find the directory to change to, the new
263                      directory name is echoed to stdout, whether or not
264                      the shell is interactive. */
265                   if (opt)
266                     printf ("%s\n", no_symlinks ? temp : the_current_working_directory);
267
268                   free (temp);
269                   /* Posix.2 says that after using CDPATH, the resultant
270                      value of $PWD will not contain symlinks. */
271                   return (bindpwd (posixly_correct || no_symlinks));
272                 }
273               else
274                 free (temp);
275             }
276
277           /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
278              try the current directory, so we just punt now with an error
279              message if POSIXLY_CORRECT is non-zero.  The check for cdpath[0]
280              is so we don't mistakenly treat a CDPATH value of "" as not
281              specifying the current directory. */
282           if (posixly_correct && cdpath[0])
283             {
284               builtin_error ("%s: %s", dirname, strerror (ENOENT));
285               return (EXECUTION_FAILURE);
286             }
287         }
288
289       if (change_to_directory (dirname, no_symlinks))
290         return (bindpwd (no_symlinks));
291
292       /* If the user requests it, then perhaps this is the name of
293          a shell variable, whose value contains the directory to
294          change to.  If that is the case, then change to that
295          directory. */
296       if (cdable_vars)
297         {
298           temp = get_string_value (dirname);
299           if (temp && change_to_directory (temp, no_symlinks))
300             {
301               printf ("%s\n", temp);
302               return (bindpwd (no_symlinks));
303             }
304         }
305
306       /* If the user requests it, try to find a directory name similar in
307          spelling to the one requested, in case the user made a simple
308          typo.  This is similar to the UNIX 8th and 9th Edition shells. */
309       if (interactive && cdspelling)
310         {
311           temp = cdspell (dirname);
312           if (temp && change_to_directory (temp, no_symlinks))
313             {
314               printf ("%s\n", temp);
315               free (temp);
316               return (bindpwd (no_symlinks));
317             }
318           else
319             FREE (temp);
320         }
321
322       builtin_error ("%s: %s", dirname, strerror (errno));
323       return (EXECUTION_FAILURE);
324     }
325
326   return (bindpwd (no_symlinks));
327 }
328
329 $BUILTIN pwd
330 $FUNCTION pwd_builtin
331 $SHORT_DOC pwd [-PL]
332 Print the current working directory.  With the -P option, pwd prints
333 the physical directory, without any symbolic links; the -L option
334 makes pwd follow symbolic links.
335 $END
336
337 /* Non-zero means that pwd always prints the physical directory, without
338    symbolic links. */
339 static int verbatim_pwd;
340
341 /* Print the name of the current working directory. */
342 int
343 pwd_builtin (list)
344      WORD_LIST *list;
345 {
346   char *directory, *buffer;
347   int opt;
348
349   verbatim_pwd = no_symbolic_links;
350   reset_internal_getopt ();
351   while ((opt = internal_getopt (list, "LP")) != -1)
352     {
353       switch (opt)
354         {
355         case 'P':
356           verbatim_pwd = 1;
357           break;
358         case 'L':
359           verbatim_pwd = 0;
360           break;
361         default:
362           builtin_usage ();
363           return (EXECUTION_FAILURE);
364         }
365     }
366   list = loptend;
367
368   if (verbatim_pwd)
369     {
370       buffer = xmalloc (PATH_MAX);
371       directory = getcwd (buffer, PATH_MAX);
372
373       if (directory == 0)
374         {
375           builtin_error ("%s: %s", bash_getcwd_errstr, strerror (errno));
376           free (buffer);
377         }
378     }
379   else
380     directory = get_working_directory ("pwd");
381
382   if (directory)
383     {
384       printf ("%s\n", directory);
385       fflush (stdout);
386       free (directory);
387       return (EXECUTION_SUCCESS);
388     }
389   else
390     return (EXECUTION_FAILURE);
391 }
392
393 /* Do the work of changing to the directory NEWDIR.  Handle symbolic
394    link following, etc. */
395
396 static int
397 change_to_directory (newdir, nolinks)
398      char *newdir;
399      int nolinks;
400 {
401   char *t;
402
403   if (nolinks == 0)
404     {
405       int chdir_return = 0;
406       char *tdir = (char *)NULL;
407
408       if (the_current_working_directory == 0)
409         {
410           t = get_working_directory ("cd_links");
411           FREE (t);
412         }
413
414       if (the_current_working_directory)
415         t = make_absolute (newdir, the_current_working_directory);
416       else
417         t = savestring (newdir);
418
419       /* TDIR is the canonicalized absolute pathname of the NEWDIR. */
420       tdir = canonicalize_pathname (t);
421
422       /* Use the canonicalized version of NEWDIR, or, if canonicalization
423          failed, use the non-canonical form. */
424       if (tdir && *tdir)
425         free (t);
426       else
427         {
428           FREE (tdir);
429           tdir = t;
430         }
431
432       if (chdir (tdir) < 0)
433         {
434           int err;
435
436           chdir_return = 0;
437           free (tdir);
438
439           err = errno;
440
441           /* We failed changing to the canonicalized directory name.  Try
442              what the user passed verbatim.  If we succeed, reinitialize
443              the_current_working_directory. */
444           if (chdir (newdir) == 0)
445             {
446               chdir_return = 1;
447               if (the_current_working_directory)
448                 {
449                   free (the_current_working_directory);
450                   the_current_working_directory = (char *)NULL;
451                 }
452
453               tdir = get_working_directory ("cd");
454               FREE (tdir);
455             }
456           else
457             errno = err;
458         }
459       else
460         {
461           chdir_return = 1;
462
463           FREE (the_current_working_directory);
464           the_current_working_directory = tdir;
465         }
466
467       return (chdir_return);
468     }
469   else
470     return (chdir (newdir) == 0);
471 }
472
473 /* Code for cd spelling correction.  Original patch submitted by
474    Neil Russel (caret@c-side.com). */
475
476 static char *
477 cdspell (dirname)
478      char *dirname;
479 {
480   int n;
481   char *guess;
482
483   n = (strlen (dirname) * 3 + 1) / 2 + 1;
484   guess = xmalloc (n);
485
486   switch (spname (dirname, guess))
487     {
488     case -1:
489     default:
490       free (guess);
491       return (char *)NULL;
492     case 0:
493     case 1:
494       return guess;
495     }
496 }
497
498 /*
499  * `spname' and its helpers are inspired by the code in "The UNIX
500  * Programming Environment, Kernighan & Pike, Prentice-Hall 1984",
501  * pages 209 - 213.
502  */
503
504 /*
505  *      `spname' -- return a correctly spelled filename
506  *
507  *      int spname(char * oldname, char * newname)
508  *      Returns:  -1 if no reasonable match found
509  *                 0 if exact match found
510  *                 1 if corrected
511  *      Stores corrected name in `newname'.
512  */
513 static int
514 spname(oldname, newname)
515      char *oldname;
516      char *newname;
517 {
518   char *op, *np, *p;
519   char guess[PATH_MAX + 1], best[PATH_MAX + 1];
520
521   op = oldname;
522   np = newname;
523   for (;;)
524     {
525       while (*op == '/')    /* Skip slashes */
526         *np++ = *op++;
527       *np = '\0';
528
529       if (*op == '\0')    /* Exact or corrected */
530         {
531           /* `.' is rarely the right thing. */
532           if (oldname[1] == '\0' && newname[1] == '\0' &&
533                 oldname[0] != '.' && newname[0] == '.')
534             return -1;
535           return strcmp(oldname, newname) != 0;
536         }
537
538       /* Copy next component into guess */
539       for (p = guess; *op != '/' && *op != '\0'; op++)
540         if (p < guess + PATH_MAX)
541           *p++ = *op;
542       *p = '\0';
543
544       if (mindist(newname, guess, best) >= 3)
545         return -1;  /* Hopeless */
546
547       /*
548        *  Add to end of newname
549        */
550       for (p = best; *np = *p++; np++)
551         ;
552     }
553 }
554
555 /*
556  *  Search directory for a guess
557  */
558 static int
559 mindist(dir, guess, best)
560      char *dir;
561      char *guess;
562      char *best;
563 {
564   DIR *fd;
565   struct dirent *dp;
566   int dist, x;
567
568   dist = 3;    /* Worst distance */
569   if (*dir == '\0')
570     dir = ".";
571
572   if ((fd = opendir(dir)) == NULL)
573     return dist;
574
575   while ((dp = readdir(fd)) != NULL)
576     {
577       /*
578        *  Look for a better guess.  If the new guess is as
579        *  good as the current one, we take it.  This way,
580        *  any single character match will be a better match
581        *  than ".".
582        */
583       x = spdist(dp->d_name, guess);
584       if (x <= dist && x != 3)
585         {
586           strcpy(best, dp->d_name);
587           dist = x;
588           if (dist == 0)    /* Exact match */
589             break;
590         }
591     }
592   (void)closedir(fd);
593
594   /* Don't return `.' */
595   if (best[0] == '.' && best[1] == '\0')
596     dist = 3;
597   return dist;
598 }
599
600 /*
601  *  `spdist' -- return the "distance" between two names.
602  *
603  *  int spname(char * oldname, char * newname)
604  *  Returns:  0 if strings are identical
605  *      1 if two characters are transposed
606  *      2 if one character is wrong, added or deleted
607  *      3 otherwise
608  */
609 static int
610 spdist(cur, new)
611      char *cur, *new;
612 {
613   while (*cur == *new)
614     {
615       if (*cur == '\0')
616         return 0;    /* Exact match */
617       cur++;
618       new++;
619     }
620
621   if (*cur)
622     {
623       if (*new)
624         {
625           if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0)
626             return 1;  /* Transposition */
627
628           if (strcmp (cur + 1, new + 1) == 0)
629             return 2;  /* One character mismatch */
630         }
631
632       if (strcmp(&cur[1], &new[0]) == 0)
633         return 2;    /* Extra character */
634     }
635
636   if (*new && strcmp(cur, new + 1) == 0)
637     return 2;      /* Missing character */
638
639   return 3;
640 }