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