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