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