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