1This file is cd.def, from which is created cd.c.  It implements the
2builtins "cd" and "pwd" in Bash.
3
4Copyright (C) 1987-2005 Free Software Foundation, Inc.
5
6This file is part of GNU Bash, the Bourne Again SHell.
7
8Bash is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13Bash is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with Bash; see the file COPYING.  If not, write to the Free Software
20Foundation, 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)
54extern int errno;
55#endif /* !errno */
56
57extern int posixly_correct;
58extern int array_needs_making;
59extern char *bash_getcwd_errstr;
60
61static int bindpwd __P((int));
62static void setpwd __P((char *));
63static char *resetpwd __P((char *));
64static int change_to_directory __P((char *, int));
65
66static char *cdspell __P((char *));
67
68/* Change this to 1 to get cd spelling correction by default. */
69int cdspelling = 0;
70
71int cdable_vars;
72
73$BUILTIN cd
74$FUNCTION cd_builtin
75$SHORT_DOC cd [-L|-P] [dir]
76Change the current directory to DIR.  The variable $HOME is the
77default DIR.  The variable CDPATH defines the search path for
78the directory containing DIR.  Alternative directory names in CDPATH
79are separated by a colon (:).  A null directory name is the same as
80the current directory, i.e. `.'.  If DIR begins with a slash (/),
81then CDPATH is not used.  If the directory is not found, and the
82shell option `cdable_vars' is set, then try the word as a variable
83name.  If that variable has a value, then cd to the value of that
84variable.  The -P option says to use the physical directory structure
85instead of following symbolic links; the -L option forces symbolic links
86to be followed.
87$END
88
89/* Just set $PWD, don't change OLDPWD.  Used by `pwd -P' in posix mode. */
90static void
91setpwd (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
106static int
107bindpwd (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 () */
139static char *
140resetpwd (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. */
160int
161cd_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]
320Print the current working directory.  With the -P option, pwd prints
321the physical directory, without any symbolic links; the -L option
322makes pwd follow symbolic links.
323$END
324
325/* Non-zero means that pwd always prints the physical directory, without
326   symbolic links. */
327static int verbatim_pwd;
328
329/* Print the name of the current working directory. */
330int
331pwd_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
398static int
399change_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
506static char *
507cdspell (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}
527