Imported from ../bash-3.2.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-2005 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 #include "../bashintl.h"
43
44 #include <errno.h>
45 #include <tilde/tilde.h>
46
47 #include "../shell.h"
48 #include "../flags.h"
49 #include "maxpath.h"
50 #include "common.h"
51 #include "bashgetopt.h"
52
53 #if !defined (errno)
54 extern int errno;
55 #endif /* !errno */
56
57 extern int posixly_correct;
58 extern int array_needs_making;
59 extern char *bash_getcwd_errstr;
60
61 static int bindpwd __P((int));
62 static void setpwd __P((char *));
63 static char *resetpwd __P((char *));
64 static int change_to_directory __P((char *, int));
65
66 static char *cdspell __P((char *));
67
68 /* Change this to 1 to get cd spelling correction by default. */
69 int cdspelling = 0;
70
71 int cdable_vars;
72
73 $BUILTIN cd
74 $FUNCTION cd_builtin
75 $SHORT_DOC cd [-L|-P] [dir]
76 Change the current directory to DIR.  The variable $HOME is the
77 default DIR.  The variable CDPATH defines the search path for
78 the directory containing DIR.  Alternative directory names in CDPATH
79 are separated by a colon (:).  A null directory name is the same as
80 the current directory, i.e. `.'.  If DIR begins with a slash (/),
81 then CDPATH is not used.  If the directory is not found, and the
82 shell option `cdable_vars' is set, then try the word as a variable
83 name.  If that variable has a value, then cd to the value of that
84 variable.  The -P option says to use the physical directory structure
85 instead of following symbolic links; the -L option forces symbolic links
86 to be followed.
87 $END
88
89 /* Just set $PWD, don't change OLDPWD.  Used by `pwd -P' in posix mode. */
90 static void
91 setpwd (dirname)
92      char *dirname;
93 {
94   int old_anm;
95   SHELL_VAR *tvar;
96
97   old_anm = array_needs_making;
98   tvar = bind_variable ("PWD", dirname ? dirname : "", 0);
99   if (old_anm == 0 && array_needs_making && exported_p (tvar))
100     {
101       update_export_env_inplace ("PWD=", 4, dirname ? dirname : "");
102       array_needs_making = 0;
103     }
104 }
105
106 static int
107 bindpwd (no_symlinks)
108      int no_symlinks;
109 {
110   char *dirname, *pwdvar;
111   int old_anm;
112   SHELL_VAR *tvar;
113
114 #define tcwd the_current_working_directory
115   dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd)
116                  : get_working_directory ("cd");
117 #undef tcwd
118
119   old_anm = array_needs_making;
120   pwdvar = get_string_value ("PWD");
121
122   tvar = bind_variable ("OLDPWD", pwdvar, 0);
123   if (old_anm == 0 && array_needs_making && exported_p (tvar))
124     {
125       update_export_env_inplace ("OLDPWD=", 7, pwdvar);
126       array_needs_making = 0;
127     }
128
129   setpwd (dirname);
130
131   if (dirname && dirname != the_current_working_directory)
132     free (dirname);
133
134   return (EXECUTION_SUCCESS);
135 }
136
137 /* Call get_working_directory to reset the value of
138    the_current_working_directory () */
139 static char *
140 resetpwd (caller)
141      char *caller;
142 {
143   char *tdir;
144       
145   FREE (the_current_working_directory);
146   the_current_working_directory = (char *)NULL;
147   tdir = get_working_directory (caller);
148   return (tdir);
149 }
150
151 #define LCD_DOVARS      0x001
152 #define LCD_DOSPELL     0x002
153 #define LCD_PRINTPATH   0x004
154 #define LCD_FREEDIRNAME 0x010
155
156 /* This builtin is ultimately the way that all user-visible commands should
157    change the current working directory.  It is called by cd_to_string (),
158    so the programming interface is simple, and it handles errors and
159    restrictions properly. */
160 int
161 cd_builtin (list)
162      WORD_LIST *list;
163 {
164   char *dirname, *cdpath, *path, *temp;
165   int path_index, no_symlinks, opt, lflag;
166
167 #if defined (RESTRICTED_SHELL)
168   if (restricted)
169     {
170       sh_restricted ((char *)NULL);
171       return (EXECUTION_FAILURE);
172     }
173 #endif /* RESTRICTED_SHELL */
174
175   no_symlinks = no_symbolic_links;
176   reset_internal_getopt ();
177   while ((opt = internal_getopt (list, "LP")) != -1)
178     {
179       switch (opt)
180         {
181         case 'P':
182           no_symlinks = 1;
183           break;
184         case 'L':
185           no_symlinks = 0;
186           break;
187         default:
188           builtin_usage ();
189           return (EXECUTION_FAILURE);
190         }
191     }
192   list = loptend;
193
194   lflag = (cdable_vars ? LCD_DOVARS : 0) |
195           ((interactive && cdspelling) ? LCD_DOSPELL : 0);
196
197   if (list == 0)
198     {
199       /* `cd' without arguments is equivalent to `cd $HOME' */
200       dirname = get_string_value ("HOME");
201
202       if (dirname == 0)
203         {
204           builtin_error (_("HOME not set"));
205           return (EXECUTION_FAILURE);
206         }
207       lflag = 0;
208     }
209   else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
210     {
211       /* This is `cd -', equivalent to `cd $OLDPWD' */
212       dirname = get_string_value ("OLDPWD");
213
214       if (dirname == 0)
215         {
216           builtin_error (_("OLDPWD not set"));
217           return (EXECUTION_FAILURE);
218         }
219 #if 0
220       lflag = interactive ? LCD_PRINTPATH : 0;
221 #else
222       lflag = LCD_PRINTPATH;            /* According to SUSv3 */
223 #endif
224     }
225   else if (absolute_pathname (list->word->word))
226     dirname = list->word->word;
227   else if (cdpath = get_string_value ("CDPATH"))
228     {
229       dirname = list->word->word;
230
231       /* Find directory in $CDPATH. */
232       path_index = 0;
233       while (path = extract_colon_unit (cdpath, &path_index))
234         {
235           /* OPT is 1 if the path element is non-empty */
236           opt = path[0] != '\0';
237           temp = sh_makepath (path, dirname, MP_DOTILDE);
238           free (path);
239
240           if (change_to_directory (temp, no_symlinks))
241             {
242               /* POSIX.2 says that if a nonempty directory from CDPATH
243                  is used to find the directory to change to, the new
244                  directory name is echoed to stdout, whether or not
245                  the shell is interactive. */
246               if (opt && (path = no_symlinks ? temp : the_current_working_directory))
247                 printf ("%s\n", path);
248
249               free (temp);
250 #if 0
251               /* Posix.2 says that after using CDPATH, the resultant
252                  value of $PWD will not contain `.' or `..'. */
253               return (bindpwd (posixly_correct || no_symlinks));
254 #else
255               return (bindpwd (no_symlinks));
256 #endif
257             }
258           else
259             free (temp);
260         }
261
262       /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
263          try the current directory, so we just punt now with an error
264          message if POSIXLY_CORRECT is non-zero.  The check for cdpath[0]
265          is so we don't mistakenly treat a CDPATH value of "" as not
266          specifying the current directory. */
267       if (posixly_correct && cdpath[0])
268         {
269           builtin_error ("%s: %s", dirname, strerror (ENOENT));
270           return (EXECUTION_FAILURE);
271         }
272     }
273   else
274     dirname = list->word->word;
275
276   /* When we get here, DIRNAME is the directory to change to.  If we
277      chdir successfully, just return. */
278   if (change_to_directory (dirname, no_symlinks))
279     {
280       if (lflag & LCD_PRINTPATH)
281         printf ("%s\n", dirname);
282       return (bindpwd (no_symlinks));
283     }
284
285   /* If the user requests it, then perhaps this is the name of
286      a shell variable, whose value contains the directory to
287      change to. */
288   if (lflag & LCD_DOVARS)
289     {
290       temp = get_string_value (dirname);
291       if (temp && change_to_directory (temp, no_symlinks))
292         {
293           printf ("%s\n", temp);
294           return (bindpwd (no_symlinks));
295         }
296     }
297
298   /* If the user requests it, try to find a directory name similar in
299      spelling to the one requested, in case the user made a simple
300      typo.  This is similar to the UNIX 8th and 9th Edition shells. */
301   if (lflag & LCD_DOSPELL)
302     {
303       temp = cdspell (dirname);
304       if (temp && change_to_directory (temp, no_symlinks))
305         {
306           printf ("%s\n", temp);
307           return (bindpwd (no_symlinks));
308         }
309       else
310         FREE (temp);
311     }
312
313   builtin_error ("%s: %s", dirname, strerror (errno));
314   return (EXECUTION_FAILURE);
315 }
316
317 $BUILTIN pwd
318 $FUNCTION pwd_builtin
319 $SHORT_DOC pwd [-LP]
320 Print the current working directory.  With the -P option, pwd prints
321 the physical directory, without any symbolic links; the -L option
322 makes pwd follow symbolic links.
323 $END
324
325 /* Non-zero means that pwd always prints the physical directory, without
326    symbolic links. */
327 static int verbatim_pwd;
328
329 /* Print the name of the current working directory. */
330 int
331 pwd_builtin (list)
332      WORD_LIST *list;
333 {
334   char *directory;
335   int opt, pflag;
336
337   verbatim_pwd = no_symbolic_links;
338   pflag = 0;
339   reset_internal_getopt ();
340   while ((opt = internal_getopt (list, "LP")) != -1)
341     {
342       switch (opt)
343         {
344         case 'P':
345           verbatim_pwd = pflag = 1;
346           break;
347         case 'L':
348           verbatim_pwd = 0;
349           break;
350         default:
351           builtin_usage ();
352           return (EXECUTION_FAILURE);
353         }
354     }
355   list = loptend;
356
357 #define tcwd the_current_working_directory
358
359   directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd)
360                    : get_working_directory ("pwd");
361
362   /* Try again using getcwd() if canonicalization fails (for instance, if
363      the file system has changed state underneath bash). */
364   if ((tcwd && directory == 0) ||
365       (posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0))
366     directory = resetpwd ("pwd");
367
368 #undef tcwd
369
370   if (directory)
371     {
372       printf ("%s\n", directory);
373       /* This is dumb but posix-mandated. */
374       if (posixly_correct && pflag)
375         setpwd (directory);
376       if (directory != the_current_working_directory)
377         free (directory);
378       fflush (stdout);
379       if (ferror (stdout))
380         {
381           sh_wrerror ();
382           clearerr (stdout);
383           return (EXECUTION_FAILURE);
384         }
385
386       return (EXECUTION_SUCCESS);
387     }
388   else
389     return (EXECUTION_FAILURE);
390 }
391
392 /* Do the work of changing to the directory NEWDIR.  Handle symbolic
393    link following, etc.  This function *must* return with
394    the_current_working_directory either set to NULL (in which case
395    getcwd() will eventually be called), or set to a string corresponding
396    to the working directory.  Return 1 on success, 0 on failure. */
397
398 static int
399 change_to_directory (newdir, nolinks)
400      char *newdir;
401      int nolinks;
402 {
403   char *t, *tdir;
404   int err, canon_failed, r, ndlen, dlen;
405
406   tdir = (char *)NULL;
407
408   if (the_current_working_directory == 0)
409     {
410       t = get_working_directory ("chdir");
411       FREE (t);
412     }
413
414   t = make_absolute (newdir, the_current_working_directory);
415
416   /* TDIR is either the canonicalized absolute pathname of NEWDIR
417      (nolinks == 0) or the absolute physical pathname of NEWDIR
418      (nolinks != 0). */
419   tdir = nolinks ? sh_physpath (t, 0)
420                  : sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
421
422   ndlen = strlen (newdir);
423   dlen = strlen (t);
424
425   /* Use the canonicalized version of NEWDIR, or, if canonicalization
426      failed, use the non-canonical form. */
427   canon_failed = 0;
428   if (tdir && *tdir)
429     free (t);
430   else
431     {
432       FREE (tdir);
433       tdir = t;
434       canon_failed = 1;
435     }
436
437   /* In POSIX mode, if we're resolving symlinks logically and sh_canonpath
438      returns NULL (because it checks the path, it will return NULL if the
439      resolved path doesn't exist), fail immediately. */
440   if (posixly_correct && nolinks == 0 && canon_failed && (errno != ENAMETOOLONG || ndlen > PATH_MAX))
441     {
442 #if defined ENAMETOOLONG
443       if (errno != ENOENT && errno != ENAMETOOLONG)
444 #else
445       if (errno != ENOENT)
446 #endif
447         errno = ENOTDIR;
448       free (tdir);
449       return (0);
450     }
451
452   /* If the chdir succeeds, update the_current_working_directory. */
453   if (chdir (nolinks ? newdir : tdir) == 0)
454     {
455       /* If canonicalization failed, but the chdir succeeded, reset the
456          shell's idea of the_current_working_directory. */
457       if (canon_failed)
458         {
459           t = resetpwd ("cd");
460           if (t == 0)
461             set_working_directory (tdir);
462         }
463       else
464         set_working_directory (tdir);
465
466       free (tdir);
467       return (1);
468     }
469
470   /* We failed to change to the appropriate directory name.  If we tried
471      what the user passed (nolinks != 0), punt now. */
472   if (nolinks)
473     {
474       free (tdir);
475       return (0);
476     }
477
478   err = errno;
479
480   /* We're not in physical mode (nolinks == 0), but we failed to change to
481      the canonicalized directory name (TDIR).  Try what the user passed
482      verbatim. If we succeed, reinitialize the_current_working_directory. */
483   if (chdir (newdir) == 0)
484     {
485       t = resetpwd ("cd");
486       if (t == 0)
487         set_working_directory (tdir);
488       else
489         free (t);
490
491       r = 1;
492     }
493   else
494     {
495       errno = err;
496       r = 0;
497     }
498
499   free (tdir);
500   return r;
501 }
502
503 /* Code for cd spelling correction.  Original patch submitted by
504    Neil Russel (caret@c-side.com). */
505
506 static char *
507 cdspell (dirname)
508      char *dirname;
509 {
510   int n;
511   char *guess;
512
513   n = (strlen (dirname) * 3 + 1) / 2 + 1;
514   guess = (char *)xmalloc (n);
515
516   switch (spname (dirname, guess))
517     {
518     case -1:
519     default:
520       free (guess);
521       return (char *)NULL;
522     case 0:
523     case 1:
524       return guess;
525     }
526 }