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