b6bf692f7c1aded5c3e3bbd3569e74a688d70134
[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   old_anm = array_needs_making;
144   pwdvar = get_string_value ("PWD");
145
146   tvar = bind_variable ("OLDPWD", pwdvar);
147   if (old_anm == 0 && array_needs_making && exported_p (tvar))
148     {
149       update_export_env_inplace ("OLDPWD=", 7, pwdvar);
150       array_needs_making = 0;
151     }
152
153   tvar = bind_variable ("PWD", dirname);
154   if (old_anm == 0 && array_needs_making && exported_p (tvar))
155     {
156       update_export_env_inplace ("PWD=", 4, dirname);
157       array_needs_making = 0;
158     }
159
160   FREE (dirname);
161   return (EXECUTION_SUCCESS);
162 }
163
164 /* This builtin is ultimately the way that all user-visible commands should
165    change the current working directory.  It is called by cd_to_string (),
166    so the programming interface is simple, and it handles errors and
167    restrictions properly. */
168 int
169 cd_builtin (list)
170      WORD_LIST *list;
171 {
172   char *dirname, *cdpath, *path, *temp;
173   int path_index, no_symlinks, opt;
174   struct stat sb;
175
176 #if defined (RESTRICTED_SHELL)
177   if (restricted)
178     {
179       builtin_error ("restricted");
180       return (EXECUTION_FAILURE);
181     }
182 #endif /* RESTRICTED_SHELL */
183
184   no_symlinks = no_symbolic_links;
185   reset_internal_getopt ();
186   while ((opt = internal_getopt (list, "LP")) != -1)
187     {
188       switch (opt)
189         {
190         case 'P':
191           no_symlinks = 1;
192           break;
193         case 'L':
194           no_symlinks = 0;
195           break;
196         default:
197           builtin_usage ();
198           return (EXECUTION_FAILURE);
199         }
200     }
201   list = loptend;
202
203   if (list == 0)
204     {
205       /* `cd' without arguments is equivalent to `cd $HOME' */
206       dirname = get_string_value ("HOME");
207
208       if (dirname == 0)
209         {
210           builtin_error ("HOME not set");
211           return (EXECUTION_FAILURE);
212         }
213
214       if (change_to_directory (dirname, no_symlinks) == 0)
215         {
216           builtin_error ("%s: %s", dirname, strerror (errno));
217           return (EXECUTION_FAILURE);
218         }
219     }
220   else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
221     {
222       /* This is `cd -', equivalent to `cd $OLDPWD' */
223       dirname = get_string_value ("OLDPWD");
224
225       if (dirname == 0 || change_to_directory (dirname, no_symlinks) == 0)
226         {
227           if (dirname == 0)
228             builtin_error ("OLDPWD not set");
229           else
230             builtin_error ("%s: %s", dirname, strerror (errno));
231           return (EXECUTION_FAILURE);
232         }
233       if (interactive)
234         printf ("%s\n", dirname);
235     }
236   else
237     {
238       dirname = list->word->word;
239
240       if (absolute_pathname (dirname) == 0 && (cdpath = get_string_value ("CDPATH")))
241         {
242           /* Find directory in $CDPATH. */
243           path_index = 0;
244           while (path = extract_colon_unit (cdpath, &path_index))
245             {
246               /* OPT is 1 if the path element is non-empty */
247               opt = path[0] != '\0';
248               temp = mkpath (path, dirname, 1);
249               free (path);
250
251               if (stat (temp, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
252                 {
253                   free (temp);
254                   continue;
255                 }
256
257               if (change_to_directory (temp, no_symlinks))
258                 {
259                   /* POSIX.2 says that if a nonempty directory from CDPATH
260                      is used to find the directory to change to, the new
261                      directory name is echoed to stdout, whether or not
262                      the shell is interactive. */
263                   if (opt)
264                     printf ("%s\n", no_symlinks ? temp : the_current_working_directory);
265
266                   free (temp);
267                   /* Posix.2 says that after using CDPATH, the resultant
268                      value of $PWD will not contain symlinks. */
269                   return (bindpwd (posixly_correct || no_symlinks));
270                 }
271               else
272                 free (temp);
273             }
274
275           /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
276              try the current directory, so we just punt now with an error
277              message if POSIXLY_CORRECT is non-zero.  The check for cdpath[0]
278              is so we don't mistakenly treat a CDPATH value of "" as not
279              specifying the current directory. */
280           if (posixly_correct && cdpath[0])
281             {
282               builtin_error ("%s: %s", dirname, strerror (ENOENT));
283               return (EXECUTION_FAILURE);
284             }
285         }
286
287       if (change_to_directory (dirname, no_symlinks))
288         return (bindpwd (no_symlinks));
289
290       /* If the user requests it, then perhaps this is the name of
291          a shell variable, whose value contains the directory to
292          change to.  If that is the case, then change to that
293          directory. */
294       if (cdable_vars)
295         {
296           temp = get_string_value (dirname);
297           if (temp && change_to_directory (temp, no_symlinks))
298             {
299               printf ("%s\n", temp);
300               return (bindpwd (no_symlinks));
301             }
302         }
303
304       /* If the user requests it, try to find a directory name similar in
305          spelling to the one requested, in case the user made a simple
306          typo.  This is similar to the UNIX 8th and 9th Edition shells. */
307       if (interactive && cdspelling)
308         {
309           temp = cdspell (dirname);
310           if (temp && change_to_directory (temp, no_symlinks))
311             {
312               printf ("%s\n", temp);
313               free (temp);
314               return (bindpwd (no_symlinks));
315             }
316           else
317             FREE (temp);
318         }
319
320       builtin_error ("%s: %s", dirname, strerror (errno));
321       return (EXECUTION_FAILURE);
322     }
323
324   return (bindpwd (no_symlinks));
325 }
326
327 $BUILTIN pwd
328 $FUNCTION pwd_builtin
329 $SHORT_DOC pwd [-PL]
330 Print the current working directory.  With the -P option, pwd prints
331 the physical directory, without any symbolic links; the -L option
332 makes pwd follow symbolic links.
333 $END
334
335 /* Non-zero means that pwd always prints the physical directory, without
336    symbolic links. */
337 static int verbatim_pwd;
338
339 /* Print the name of the current working directory. */
340 int
341 pwd_builtin (list)
342      WORD_LIST *list;
343 {
344   char *directory, *buffer;
345   int opt;
346
347   verbatim_pwd = no_symbolic_links;
348   reset_internal_getopt ();
349   while ((opt = internal_getopt (list, "LP")) != -1)
350     {
351       switch (opt)
352         {
353         case 'P':
354           verbatim_pwd = 1;
355           break;
356         case 'L':
357           verbatim_pwd = 0;
358           break;
359         default:
360           builtin_usage ();
361           return (EXECUTION_FAILURE);
362         }
363     }
364   list = loptend;
365
366   if (verbatim_pwd)
367     {
368       buffer = xmalloc (PATH_MAX);
369       directory = getcwd (buffer, PATH_MAX);
370
371       if (directory == 0)
372         {
373           builtin_error ("%s: %s", bash_getcwd_errstr, strerror (errno));
374           free (buffer);
375         }
376     }
377   else
378     directory = get_working_directory ("pwd");
379
380   if (directory)
381     {
382       printf ("%s\n", directory);
383       fflush (stdout);
384       free (directory);
385       return (EXECUTION_SUCCESS);
386     }
387   else
388     return (EXECUTION_FAILURE);
389 }
390
391 /* Do the work of changing to the directory NEWDIR.  Handle symbolic
392    link following, etc. */
393
394 static int
395 change_to_directory (newdir, nolinks)
396      char *newdir;
397      int nolinks;
398 {
399   char *t;
400
401   if (nolinks == 0)
402     {
403       int chdir_return = 0;
404       char *tdir = (char *)NULL;
405
406       if (the_current_working_directory == 0)
407         {
408           t = get_working_directory ("cd_links");
409           FREE (t);
410         }
411
412       if (the_current_working_directory)
413         t = make_absolute (newdir, the_current_working_directory);
414       else
415         t = savestring (newdir);
416
417       /* TDIR is the canonicalized absolute pathname of the NEWDIR. */
418       tdir = canonicalize_pathname (t);
419
420       /* Use the canonicalized version of NEWDIR, or, if canonicalization
421          failed, use the non-canonical form. */
422       if (tdir && *tdir)
423         free (t);
424       else
425         {
426           FREE (tdir);
427           tdir = t;
428         }
429
430       if (chdir (tdir) < 0)
431         {
432           int err;
433
434           chdir_return = 0;
435           free (tdir);
436
437           err = errno;
438
439           /* We failed changing to the canonicalized directory name.  Try
440              what the user passed verbatim.  If we succeed, reinitialize
441              the_current_working_directory. */
442           if (chdir (newdir) == 0)
443             {
444               chdir_return = 1;
445               if (the_current_working_directory)
446                 {
447                   free (the_current_working_directory);
448                   the_current_working_directory = (char *)NULL;
449                 }
450
451               tdir = get_working_directory ("cd");
452               FREE (tdir);
453             }
454           else
455             errno = err;
456         }
457       else
458         {
459           chdir_return = 1;
460
461           FREE (the_current_working_directory);
462           the_current_working_directory = tdir;
463         }
464
465       return (chdir_return);
466     }
467   else
468     return (chdir (newdir) == 0);
469 }
470
471 /* Code for cd spelling correction.  Original patch submitted by
472    Neil Russel (caret@c-side.com). */
473
474 static char *
475 cdspell (dirname)
476      char *dirname;
477 {
478   int n;
479   char *guess;
480
481   n = (strlen (dirname) * 3 + 1) / 2 + 1;
482   guess = xmalloc (n);
483
484   switch (spname (dirname, guess))
485     {
486     case -1:
487     default:
488       free (guess);
489       return (char *)NULL;
490     case 0:
491     case 1:
492       return guess;
493     }
494 }
495
496 /*
497  * `spname' and its helpers are inspired by the code in "The UNIX
498  * Programming Environment, Kernighan & Pike, Prentice-Hall 1984",
499  * pages 209 - 213.
500  */
501
502 /*
503  *      `spname' -- return a correctly spelled filename
504  *
505  *      int spname(char * oldname, char * newname)
506  *      Returns:  -1 if no reasonable match found
507  *                 0 if exact match found
508  *                 1 if corrected
509  *      Stores corrected name in `newname'.
510  */
511 static int
512 spname(oldname, newname)
513      char *oldname;
514      char *newname;
515 {
516   char *op, *np, *p;
517   char guess[PATH_MAX + 1], best[PATH_MAX + 1];
518
519   op = oldname;
520   np = newname;
521   for (;;)
522     {
523       while (*op == '/')    /* Skip slashes */
524         *np++ = *op++;
525       *np = '\0';
526
527       if (*op == '\0')    /* Exact or corrected */
528         {
529           /* `.' is rarely the right thing. */
530           if (oldname[1] == '\0' && newname[1] == '\0' &&
531                 oldname[0] != '.' && newname[0] == '.')
532             return -1;
533           return strcmp(oldname, newname) != 0;
534         }
535
536       /* Copy next component into guess */
537       for (p = guess; *op != '/' && *op != '\0'; op++)
538         if (p < guess + PATH_MAX)
539           *p++ = *op;
540       *p = '\0';
541
542       if (mindist(newname, guess, best) >= 3)
543         return -1;  /* Hopeless */
544
545       /*
546        *  Add to end of newname
547        */
548       for (p = best; *np = *p++; np++)
549         ;
550     }
551 }
552
553 /*
554  *  Search directory for a guess
555  */
556 static int
557 mindist(dir, guess, best)
558      char *dir;
559      char *guess;
560      char *best;
561 {
562   DIR *fd;
563   struct dirent *dp;
564   int dist, x;
565
566   dist = 3;    /* Worst distance */
567   if (*dir == '\0')
568     dir = ".";
569
570   if ((fd = opendir(dir)) == NULL)
571     return dist;
572
573   while ((dp = readdir(fd)) != NULL)
574     {
575       /*
576        *  Look for a better guess.  If the new guess is as
577        *  good as the current one, we take it.  This way,
578        *  any single character match will be a better match
579        *  than ".".
580        */
581       x = spdist(dp->d_name, guess);
582       if (x <= dist && x != 3)
583         {
584           strcpy(best, dp->d_name);
585           dist = x;
586           if (dist == 0)    /* Exact match */
587             break;
588         }
589     }
590   (void)closedir(fd);
591
592   /* Don't return `.' */
593   if (best[0] == '.' && best[1] == '\0')
594     dist = 3;
595   return dist;
596 }
597
598 /*
599  *  `spdist' -- return the "distance" between two names.
600  *
601  *  int spname(char * oldname, char * newname)
602  *  Returns:  0 if strings are identical
603  *      1 if two characters are transposed
604  *      2 if one character is wrong, added or deleted
605  *      3 otherwise
606  */
607 static int
608 spdist(cur, new)
609      char *cur, *new;
610 {
611   while (*cur == *new)
612     {
613       if (*cur == '\0')
614         return 0;    /* Exact match */
615       cur++;
616       new++;
617     }
618
619   if (*cur)
620     {
621       if (*new)
622         {
623           if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0)
624             return 1;  /* Transposition */
625
626           if (strcmp (cur + 1, new + 1) == 0)
627             return 2;  /* One character mismatch */
628         }
629
630       if (strcmp(&cur[1], &new[0]) == 0)
631         return 2;    /* Extra character */
632     }
633
634   if (*new && strcmp(cur, new + 1) == 0)
635     return 2;      /* Missing character */
636
637   return 3;
638 }