Imported from ../bash-2.02.tar.gz.
[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", 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. */
280           if (posixly_correct)
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 }