]> git.ipfire.org Git - thirdparty/bash.git/blob - builtins/cd.def
Imported from ../bash-1.14.7.tar.gz.
[thirdparty/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 }