338f6942821da41b8edb17e79b8c771f4b48b740
[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", "pwd", "pushd", "popd", and "dirs" 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 $PRODUCES cd.c
23
24 #include <stdio.h>
25 #include <sys/param.h>
26
27 #if defined (HAVE_STRING_H)
28 #  include <string.h>
29 #else /* !HAVE_STRING_H */
30 #  include <strings.h>
31 #endif /* !HAVE_STRING_H */
32
33 #include <errno.h>
34 #include <tilde/tilde.h>
35
36 #include "../shell.h"
37 #include "../flags.h"
38 #include "../maxpath.h"
39 #include "common.h"
40
41 #if !defined (errno)
42 extern int errno;
43 #endif /* !errno */
44
45 static int change_to_directory (), cd_to_string ();
46
47 $BUILTIN cd
48 $FUNCTION cd_builtin
49 $SHORT_DOC cd [dir]
50 Change the current directory to DIR.  The variable $HOME is the
51 default DIR.  The variable $CDPATH defines the search path for
52 the directory containing DIR.  Alternative directory names are
53 separated by a colon (:).  A null directory name is the same as
54 the current directory, i.e. `.'.  If DIR begins with a slash (/),
55 then $CDPATH is not used.  If the directory is not found, and the
56 shell variable `cdable_vars' exists, then try the word as a variable
57 name.  If that variable has a value, then cd to the value of that
58 variable.
59 $END
60
61 /* This builtin is ultimately the way that all user-visible commands should
62    change the current working directory.  It is called by cd_to_string (),
63    so the programming interface is simple, and it handles errors and
64    restrictions properly. */
65 int
66 cd_builtin (list)
67      WORD_LIST *list;
68 {
69   char *dirname;
70
71 #if defined (RESTRICTED_SHELL)
72   if (restricted)
73     {
74       builtin_error ("restricted");
75       return (EXECUTION_FAILURE);
76     }
77 #endif /* RESTRICTED_SHELL */
78
79   if (list)
80     {
81       char *extract_colon_unit ();
82       char *path_string = get_string_value ("CDPATH");
83       char *path;
84       int path_index = 0, dirlen, pathlen;
85
86       dirname = list->word->word;
87
88       if (path_string && !absolute_pathname (dirname))
89         {
90           while ((path = extract_colon_unit (path_string, &path_index)))
91             {
92               char *dir;
93
94               if (*path == '~')
95                 {
96                   char *te_string = tilde_expand (path);
97
98                   free (path);
99                   path = te_string;
100                 }
101
102               if (!*path)
103                 {
104                   free (path);
105                   path = xmalloc (2);
106                   path[0] = '.';        /* by definition. */
107                   path[1] = '\0';
108                 }
109
110               dirlen = strlen (dirname);
111               pathlen = strlen (path);
112               dir = xmalloc (2 + dirlen + pathlen);
113               strcpy (dir, path);
114               if (path[pathlen - 1] != '/')
115                 {
116                   dir[pathlen++] = '/';
117                   dir[pathlen] = '\0';
118                 }
119               strcpy (dir + pathlen, dirname);
120               free (path);
121
122               if (change_to_directory (dir))
123                 {
124                   /* replaces (strncmp (dir, "./", 2) != 0) */
125                   if (dir[0] != '.' || dir[1] != '/')
126                     printf ("%s\n", dir);
127
128                   free (dir);
129                   goto bind_and_exit;
130                 }
131               else
132                 free (dir);
133             }
134         }
135
136       if (!change_to_directory (dirname))
137         {
138           /* Maybe this is `cd -', equivalent to `cd $OLDPWD' */
139           if (dirname[0] == '-' && dirname[1] == '\0')
140             {
141               char *t = get_string_value ("OLDPWD");
142
143               if (t && change_to_directory (t))
144                 goto bind_and_exit;
145             }
146
147           /* If the user requests it, then perhaps this is the name of
148              a shell variable, whose value contains the directory to
149              change to.  If that is the case, then change to that
150              directory. */
151           if (find_variable ("cdable_vars"))
152             {
153               char *t = get_string_value (dirname);
154
155               if (t && change_to_directory (t))
156                 {
157                   printf ("%s\n", t);
158                   goto bind_and_exit;
159                 }
160             }
161
162           file_error (dirname);
163           return (EXECUTION_FAILURE);
164         }
165       goto bind_and_exit;
166     }
167   else
168     {
169       dirname = get_string_value ("HOME");
170
171       if (!dirname)
172         return (EXECUTION_FAILURE);
173
174       if (!change_to_directory (dirname))
175         {
176           file_error (dirname);
177           return (EXECUTION_FAILURE);
178         }
179
180     bind_and_exit:
181       {
182         char *directory;
183
184         directory = get_working_directory ("cd");
185
186         bind_variable ("OLDPWD", get_string_value ("PWD"));
187         bind_variable ("PWD", directory);
188
189         FREE (directory);
190       }
191       return (EXECUTION_SUCCESS);
192     }
193 }
194
195 $BUILTIN pwd
196 $FUNCTION pwd_builtin
197 $SHORT_DOC pwd
198 Print the current working directory.
199 $END
200
201 /* Non-zero means that pwd always give verbatim directory, regardless of
202    symbolic link following. */
203 static int verbatim_pwd;
204
205 /* Print the name of the current working directory. */
206 pwd_builtin (list)
207      WORD_LIST *list;
208 {
209   char *directory, *s;
210
211 #if 0
212   no_args (list);
213 #else
214   verbatim_pwd = no_symbolic_links;
215   if (list && (s = list->word->word) && s[0] == '-' && s[1] == 'P' && !s[2])
216     verbatim_pwd = 1;
217 #endif
218
219   if (verbatim_pwd)
220     {
221       char *buffer = xmalloc (MAXPATHLEN);
222       directory = getwd (buffer);
223
224       if (!directory)
225         {
226           builtin_error ("%s", buffer);
227           free (buffer);
228         }
229     }
230   else
231     directory = get_working_directory ("pwd");
232
233   if (directory)
234     {
235       printf ("%s\n", directory);
236       fflush (stdout);
237       free (directory);
238       return (EXECUTION_SUCCESS);
239     }
240   else
241     return (EXECUTION_FAILURE);
242 }
243
244 $BUILTIN pushd
245 $FUNCTION pushd_builtin
246 $DEPENDS_ON PUSHD_AND_POPD
247 $SHORT_DOC pushd [dir | +n | -n]
248 Adds a directory to the top of the directory stack, or rotates
249 the stack, making the new top of the stack the current working
250 directory.  With no arguments, exchanges the top two directories.
251
252 +n      Rotates the stack so that the Nth directory (counting
253         from the left of the list shown by `dirs') is at the top.
254
255 -n      Rotates the stack so that the Nth directory (counting
256         from the right) is at the top.
257
258 dir     adds DIR to the directory stack at the top, making it the
259         new current working directory.
260
261 You can see the directory stack with the `dirs' command.
262 $END
263
264 #if defined (PUSHD_AND_POPD)
265 /* Some useful commands whose behaviour has been observed in Csh. */
266
267 /* The list of remembered directories. */
268 static char **pushd_directory_list = (char **)NULL;
269
270 /* Number of existing slots in this list. */
271 static int directory_list_size = 0;
272
273 /* Offset to the end of the list. */
274 static int directory_list_offset = 0;
275
276 pushd_builtin (list)
277      WORD_LIST *list;
278 {
279   char *temp, *current_directory;
280   int j = directory_list_offset - 1;
281   char direction = '+';
282
283   /* If there is no argument list then switch current and
284      top of list. */
285   if (!list)
286     {
287       if (!directory_list_offset)
288         {
289           builtin_error ("No other directory");
290           return (EXECUTION_FAILURE);
291         }
292
293       current_directory = get_working_directory ("pushd");
294       if (!current_directory)
295         return (EXECUTION_FAILURE);
296
297       temp = pushd_directory_list[j];
298       pushd_directory_list[j] = current_directory;
299       goto change_to_temp;
300     }
301   else
302     {
303       direction = *(list->word->word);
304       if (direction == '+' || direction == '-')
305         {
306           int num;
307           if (1 == sscanf (&(list->word->word)[1], "%d", &num))
308             {
309               if (direction == '-')
310                 num = directory_list_offset - num;
311
312               if (num > directory_list_offset || num < 0)
313                 {
314                   if (!directory_list_offset)
315                     builtin_error ("Directory stack empty");
316                   else
317                     builtin_error ("Stack contains only %d directories",
318                                     directory_list_offset + 1);
319                   return (EXECUTION_FAILURE);
320                 }
321               else
322                 {
323                   /* Rotate the stack num times.  Remember, the
324                      current directory acts like it is part of the
325                      stack. */
326                   temp = get_working_directory ("pushd");
327
328                   if (!num)
329                     goto change_to_temp;
330
331                   do
332                     {
333                       char *top =
334                         pushd_directory_list[directory_list_offset - 1];
335
336                       for (j = directory_list_offset - 2; j > -1; j--)
337                         pushd_directory_list[j + 1] = pushd_directory_list[j];
338
339                       pushd_directory_list[j + 1] = temp;
340
341                       temp = top;
342                       num--;
343                     }
344                   while (num);
345
346                   temp = savestring (temp);
347                 change_to_temp:
348                   {
349                     int tt = EXECUTION_FAILURE;
350
351                     if (temp)
352                       {
353                         tt = cd_to_string (temp);
354                         free (temp);
355                       }
356
357                     if ((tt == EXECUTION_SUCCESS))
358                       dirs_builtin ((WORD_LIST *)NULL);
359
360                     return (tt);
361                   }
362                 }
363             }
364         }
365
366       /* Change to the directory in list->word->word.  Save the current
367          directory on the top of the stack. */
368       current_directory = get_working_directory ("pushd");
369       if (!current_directory)
370         return (EXECUTION_FAILURE);
371
372       if (cd_builtin (list) == EXECUTION_SUCCESS)
373         {
374           if (directory_list_offset == directory_list_size)
375             {
376               pushd_directory_list = (char **)
377                 xrealloc (pushd_directory_list,
378                           (directory_list_size += 10) * sizeof (char *));
379             }
380           pushd_directory_list[directory_list_offset++] = current_directory;
381
382           dirs_builtin ((WORD_LIST *)NULL);
383
384           return (EXECUTION_SUCCESS);
385         }
386       else
387         {
388           free (current_directory);
389           return (EXECUTION_FAILURE);
390         }
391     }
392 }
393 #endif /* PUSHD_AND_POPD */
394
395 $BUILTIN dirs
396 $FUNCTION dirs_builtin
397 $DEPENDS_ON PUSHD_AND_POPD
398 $SHORT_DOC dirs [-l]
399 Display the list of currently remembered directories.  Directories
400 find their way onto the list with the `pushd' command; you can get
401 back up through the list with the `popd' command.
402
403 The -l flag specifies that `dirs' should not print shorthand versions
404 of directories which are relative to your home directory.  This means
405 that `~/bin' might be displayed as `/homes/bfox/bin'.
406 $END
407
408 #if defined (PUSHD_AND_POPD)
409 /* Print the current list of directories on the directory stack. */
410 dirs_builtin (list)
411      WORD_LIST *list;
412 {
413   int i, format, desired_index, index_flag;
414   char *temp, *w;
415
416   format = index_flag = 0;
417   desired_index = -1;
418   /* Maybe do long form or print specific dir stack entry? */
419   while (list)
420     {
421       if (strcmp (list->word->word, "-l") == 0)
422         {
423           format++;
424           list = list->next;
425         }
426       else if (*list->word->word == '+' && all_digits (list->word->word + 1))
427         {
428           w = list->word->word + 1;
429           index_flag = 1;
430           i = atoi (w);
431           /* dirs +0 prints the current working directory. */
432           if (i == 0)
433             desired_index = i;
434           else if (i == directory_list_offset)
435             {
436               desired_index = 0;
437               index_flag = 2;
438             }
439           else
440             desired_index = directory_list_offset - i;
441           list = list->next;
442         }
443       else if (*list->word->word == '-' && all_digits (list->word->word + 1))
444         {
445           w = list->word->word + 1;
446           i = atoi (w);
447           index_flag = 2;
448           /* dirs -X where X is directory_list_offset prints the current
449              working directory. */
450           if (i == directory_list_offset)
451             {
452               index_flag = 1;
453               desired_index = 0;
454             }
455           else
456             desired_index = i;
457           list = list->next;
458         }
459       else
460         {
461           bad_option (list->word->word);
462           return (EXECUTION_FAILURE);
463         }
464     }
465
466   if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
467     {
468       if (directory_list_offset == 0)
469         builtin_error ("directory stack empty");
470       else
471         builtin_error ("%s: bad directory stack index", w);
472       return (EXECUTION_FAILURE);
473     }
474
475   /* The first directory printed is always the current working directory. */
476   if (!index_flag || (index_flag == 1 && desired_index == 0))
477     {
478       temp = get_working_directory ("dirs");
479       if (!temp)
480         temp = savestring ("<no directory>");
481       printf ("%s", format ? temp : polite_directory_format (temp));
482       free (temp);
483       if (index_flag)
484         {
485           putchar ('\n');
486           return EXECUTION_SUCCESS;
487         }
488     }
489
490 #define DIRSTACK_ENTRY(i) \
491         format ? pushd_directory_list[i] \
492                : polite_directory_format (pushd_directory_list[i])
493
494   /* Now print the requested directory stack entries. */
495   if (index_flag)
496     printf ("%s", DIRSTACK_ENTRY (desired_index));
497   else
498     for (i = (directory_list_offset - 1); i > -1; i--)
499       printf (" %s", DIRSTACK_ENTRY (i));
500
501   putchar ('\n');
502   fflush (stdout);
503   return (EXECUTION_SUCCESS);
504 }
505 #endif /* PUSHD_AND_POPD */
506
507 $BUILTIN popd
508 $FUNCTION popd_builtin
509 $DEPENDS_ON PUSHD_AND_POPD
510 $SHORT_DOC popd [+n | -n]
511 Removes entries from the directory stack.  With no arguments,
512 removes the top directory from the stack, and cd's to the new
513 top directory.
514
515 +n      removes the Nth entry counting from the left of the list
516         shown by `dirs', starting with zero.  For example: `popd +0'
517         removes the first directory, `popd +1' the second.
518
519 -n      removes the Nth entry counting from the right of the list
520         shown by `dirs', starting with zero.  For example: `popd -0'
521         removes the last directory, `popd -1' the next to last.
522
523 You can see the directory stack with the `dirs' command.
524 $END
525
526 #if defined (PUSHD_AND_POPD)
527 /* Pop the directory stack, and then change to the new top of the stack.
528    If LIST is non-null it should consist of a word +N or -N, which says
529    what element to delete from the stack.  The default is the top one. */
530 popd_builtin (list)
531      WORD_LIST *list;
532 {
533   register int i;
534   int which = 0;
535   char direction = '+';
536
537   if (list)
538     {
539       direction = *(list->word->word);
540
541       if ((direction != '+' && direction != '-') ||
542           (1 != sscanf (&((list->word->word)[1]), "%d", &which)))
543         {
544           builtin_error ("bad arg `%s'", list->word->word);
545           return (EXECUTION_FAILURE);
546         }
547     }
548
549   if (which > directory_list_offset || (!directory_list_offset && !which))
550     {
551       if (!directory_list_offset)
552         builtin_error ("Directory stack empty");
553       else
554         builtin_error ("Stack contains only %d directories",
555                         directory_list_offset + 1);
556       return (EXECUTION_FAILURE);
557     }
558
559   /* Handle case of no specification, or top of stack specification. */
560   if ((direction == '+' && which == 0) ||
561       (direction == '-' && which == directory_list_offset))
562     {
563       i = cd_to_string (pushd_directory_list[directory_list_offset - 1]);
564       if (i != EXECUTION_SUCCESS)
565         return (i);
566       free (pushd_directory_list[--directory_list_offset]);
567     }
568   else
569     {
570       /* Since an offset other than the top directory was specified,
571          remove that directory from the list and shift the remainder
572          of the list into place. */
573
574       if (direction == '+')
575         i = directory_list_offset - which;
576       else
577         i = which;
578
579       free (pushd_directory_list[i]);
580       directory_list_offset--;
581
582       /* Shift the remainder of the list into place. */
583       for (; i < directory_list_offset; i++)
584         pushd_directory_list[i] = pushd_directory_list[i + 1];
585     }
586
587   dirs_builtin ((WORD_LIST *)NULL);
588
589   return (EXECUTION_SUCCESS);
590 }
591 #endif /* PUSHD_AND_POPD */
592
593 /* Do the work of changing to the directory NEWDIR.  Handle symbolic
594    link following, etc. */
595
596 static int
597 change_to_directory (newdir)
598      char *newdir;
599 {
600   char *t;
601
602   if (!no_symbolic_links)
603     {
604       int chdir_return = 0;
605       char *tdir = (char *)NULL;
606
607       if (!the_current_working_directory)
608         {
609           t = get_working_directory ("cd_links");
610           FREE (t);
611         }
612
613       if (the_current_working_directory)
614         t = make_absolute (newdir, the_current_working_directory);
615       else
616         t = savestring (newdir);
617
618       /* TDIR is the canonicalized absolute pathname of the NEWDIR. */
619       tdir = canonicalize_pathname (t);
620
621       /* Use the canonicalized version of NEWDIR, or, if canonicalization
622          failed, use the non-canonical form. */
623       if (tdir && *tdir)
624         free (t);
625       else
626         {
627           FREE (tdir);
628
629           tdir = t;
630         }
631
632       if (chdir (tdir) < 0)
633         {
634           int err;
635
636           chdir_return = 0;
637           free (tdir);
638
639           err = errno;
640
641           /* We failed changing to the canonicalized directory name.  Try
642              what the user passed verbatim.  If we succeed, reinitialize
643              the_current_working_directory. */
644           if (chdir (newdir) == 0)
645             {
646               chdir_return = 1;
647               if (the_current_working_directory)
648                 {
649                   free (the_current_working_directory);
650                   the_current_working_directory = (char *)NULL;
651                 }
652
653               tdir = get_working_directory ("cd");
654               FREE (tdir);
655             }
656           else
657             errno = err;
658         }
659       else
660         {
661           chdir_return = 1;
662
663           FREE (the_current_working_directory);
664           the_current_working_directory = tdir;
665         }
666
667       return (chdir_return);
668     }
669   else
670     {
671       if (chdir (newdir) < 0)
672         return (0);
673       else
674         return (1);
675     }
676 }
677
678 /* Switch to the directory in NAME.  This uses the cd_builtin to do the work,
679    so if the result is EXECUTION_FAILURE then an error message has already
680    been printed. */
681 static int
682 cd_to_string (name)
683      char *name;
684 {
685   WORD_LIST *tlist = make_word_list (make_word (name), NULL);
686   int result = (cd_builtin (tlist));
687   dispose_words (tlist);
688   return (result);
689 }