1 This file is pushd.def, from which is created pushd.c. It implements the
2 builtins "pushd", "popd", and "dirs" in Bash.
4 Copyright (C) 1987-2002 Free Software Foundation, Inc.
6 This file is part of GNU Bash, the Bourne Again SHell.
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
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
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.
25 $FUNCTION pushd_builtin
26 $DEPENDS_ON PUSHD_AND_POPD
27 $SHORT_DOC pushd [dir | +N | -N] [-n]
28 Adds a directory to the top of the directory stack, or rotates
29 the stack, making the new top of the stack the current working
30 directory. With no arguments, exchanges the top two directories.
32 +N Rotates the stack so that the Nth directory (counting
33 from the left of the list shown by `dirs', starting with
36 -N Rotates the stack so that the Nth directory (counting
37 from the right of the list shown by `dirs', starting with
40 -n suppress the normal change of directory when adding directories
41 to the stack, so only the stack is manipulated.
43 dir adds DIR to the directory stack at the top, making it the
44 new current working directory.
46 You can see the directory stack with the `dirs' command.
50 $FUNCTION popd_builtin
51 $DEPENDS_ON PUSHD_AND_POPD
52 $SHORT_DOC popd [+N | -N] [-n]
53 Removes entries from the directory stack. With no arguments,
54 removes the top directory from the stack, and cd's to the new
57 +N removes the Nth entry counting from the left of the list
58 shown by `dirs', starting with zero. For example: `popd +0'
59 removes the first directory, `popd +1' the second.
61 -N removes the Nth entry counting from the right of the list
62 shown by `dirs', starting with zero. For example: `popd -0'
63 removes the last directory, `popd -1' the next to last.
65 -n suppress the normal change of directory when removing directories
66 from the stack, so only the stack is manipulated.
68 You can see the directory stack with the `dirs' command.
72 $FUNCTION dirs_builtin
73 $DEPENDS_ON PUSHD_AND_POPD
74 $SHORT_DOC dirs [-clpv] [+N] [-N]
75 Display the list of currently remembered directories. Directories
76 find their way onto the list with the `pushd' command; you can get
77 back up through the list with the `popd' command.
79 The -l flag specifies that `dirs' should not print shorthand versions
80 of directories which are relative to your home directory. This means
81 that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag
82 causes `dirs' to print the directory stack with one entry per line,
83 prepending the directory name with its position in the stack. The -p
84 flag does the same thing, but the stack position is not prepended.
85 The -c flag clears the directory stack by deleting all of the elements.
87 +N displays the Nth entry counting from the left of the list shown by
88 dirs when invoked without options, starting with zero.
90 -N displays the Nth entry counting from the right of the list shown by
91 dirs when invoked without options, starting with zero.
96 #if defined (PUSHD_AND_POPD)
99 # include <sys/param.h>
102 #if defined (HAVE_UNISTD_H)
104 # include <sys/types.h>
109 #include "../bashansi.h"
113 #include <tilde/tilde.h>
115 #include "../shell.h"
118 #include "builtext.h"
120 #ifdef LOADABLE_BUILTIN
121 # include "builtins.h"
128 /* The list of remembered directories. */
129 static char **pushd_directory_list = (char **)NULL;
131 /* Number of existing slots in this list. */
132 static int directory_list_size;
134 /* Offset to the end of the list. */
135 static int directory_list_offset;
137 static void pushd_error __P((int, char *));
138 static void clear_directory_stack __P((void));
139 static int cd_to_string __P((char *));
140 static int change_to_temp __P((char *));
141 static void add_dirstack_element __P((char *));
142 static int get_dirstack_index __P((intmax_t, int, int *));
146 #define LONGFORM 0x04
147 #define CLEARSTAK 0x08
153 char *temp, *current_directory, *top;
158 if (list && list->word && ISOPTION (list->word->word, '-'))
161 /* If there is no argument list then switch current and
165 if (directory_list_offset == 0)
167 builtin_error ("no other directory");
168 return (EXECUTION_FAILURE);
171 current_directory = get_working_directory ("pushd");
172 if (current_directory == 0)
173 return (EXECUTION_FAILURE);
175 j = directory_list_offset - 1;
176 temp = pushd_directory_list[j];
177 pushd_directory_list[j] = current_directory;
178 j = change_to_temp (temp);
183 for (flags = 0; list; list = list->next)
185 if (ISOPTION (list->word->word, 'n'))
189 else if (ISOPTION (list->word->word, '-'))
194 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
195 /* Let `pushd -' work like it used to. */
197 else if (((direction = list->word->word[0]) == '+') || direction == '-')
199 if (legal_number (list->word->word + 1, &num) == 0)
201 sh_invalidnum (list->word->word);
203 return (EXECUTION_FAILURE);
206 if (direction == '-')
207 num = directory_list_offset - num;
209 if (num > directory_list_offset || num < 0)
211 pushd_error (directory_list_offset, list->word->word);
212 return (EXECUTION_FAILURE);
216 else if (*list->word->word == '-')
218 sh_invalidopt (list->word->word);
220 return (EXECUTION_FAILURE);
228 /* Rotate the stack num times. Remember, the current
229 directory acts like it is part of the stack. */
230 temp = get_working_directory ("pushd");
234 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
241 top = pushd_directory_list[directory_list_offset - 1];
243 for (j = directory_list_offset - 2; j > -1; j--)
244 pushd_directory_list[j + 1] = pushd_directory_list[j];
246 pushd_directory_list[j + 1] = temp;
253 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
259 return (EXECUTION_SUCCESS);
261 /* Change to the directory in list->word->word. Save the current
262 directory on the top of the stack. */
263 current_directory = get_working_directory ("pushd");
264 if (current_directory == 0)
265 return (EXECUTION_FAILURE);
267 j = ((flags & NOCD) == 0) ? cd_builtin (list) : EXECUTION_SUCCESS;
268 if (j == EXECUTION_SUCCESS)
270 add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
271 dirs_builtin ((WORD_LIST *)NULL);
273 free (current_directory);
274 return (EXECUTION_SUCCESS);
278 free (current_directory);
279 return (EXECUTION_FAILURE);
283 /* Pop the directory stack, and then change to the new top of the stack.
284 If LIST is non-null it should consist of a word +N or -N, which says
285 what element to delete from the stack. The default is the top one. */
296 which_word = (char *)NULL;
297 for (flags = 0, which = 0, direction = '+'; list; list = list->next)
299 if (ISOPTION (list->word->word, 'n'))
303 else if (ISOPTION (list->word->word, '-'))
308 else if (((direction = list->word->word[0]) == '+') || direction == '-')
310 if (legal_number (list->word->word + 1, &which) == 0)
312 sh_invalidnum (list->word->word);
314 return (EXECUTION_FAILURE);
316 which_word = list->word->word;
318 else if (*list->word->word == '-')
320 sh_invalidopt (list->word->word);
322 return (EXECUTION_FAILURE);
328 if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
330 pushd_error (directory_list_offset, which_word ? which_word : "");
331 return (EXECUTION_FAILURE);
334 /* Handle case of no specification, or top of stack specification. */
335 if ((direction == '+' && which == 0) ||
336 (direction == '-' && which == directory_list_offset))
338 i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
340 if (i != EXECUTION_SUCCESS)
342 free (pushd_directory_list[--directory_list_offset]);
346 /* Since an offset other than the top directory was specified,
347 remove that directory from the list and shift the remainder
348 of the list into place. */
349 i = (direction == '+') ? directory_list_offset - which : which;
350 free (pushd_directory_list[i]);
351 directory_list_offset--;
353 /* Shift the remainder of the list into place. */
354 for (; i < directory_list_offset; i++)
355 pushd_directory_list[i] = pushd_directory_list[i + 1];
358 dirs_builtin ((WORD_LIST *)NULL);
359 return (EXECUTION_SUCCESS);
362 /* Print the current list of directories on the directory stack. */
367 int flags, desired_index, index_flag, vflag;
371 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
373 if (ISOPTION (list->word->word, 'l'))
377 else if (ISOPTION (list->word->word, 'c'))
381 else if (ISOPTION (list->word->word, 'v'))
385 else if (ISOPTION (list->word->word, 'p'))
389 else if (ISOPTION (list->word->word, '-'))
394 else if (*list->word->word == '+' || *list->word->word == '-')
397 if (legal_number (w = list->word->word + 1, &i) == 0)
399 sh_invalidnum (list->word->word);
401 return (EXECUTION_FAILURE);
403 sign = (*list->word->word == '+') ? 1 : -1;
404 desired_index = get_dirstack_index (i, sign, &index_flag);
408 sh_invalidopt (list->word->word);
410 return (EXECUTION_FAILURE);
414 if (flags & CLEARSTAK)
416 clear_directory_stack ();
417 return (EXECUTION_SUCCESS);
420 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
422 pushd_error (directory_list_offset, w);
423 return (EXECUTION_FAILURE);
426 #define DIRSTACK_FORMAT(temp) \
427 (flags & LONGFORM) ? temp : polite_directory_format (temp)
429 /* The first directory printed is always the current working directory. */
430 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
432 temp = get_working_directory ("dirs");
434 temp = savestring ("<no current directory>");
436 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
438 printf ("%s", DIRSTACK_FORMAT (temp));
443 return EXECUTION_SUCCESS;
447 #define DIRSTACK_ENTRY(i) \
448 (flags & LONGFORM) ? pushd_directory_list[i] \
449 : polite_directory_format (pushd_directory_list[i])
451 /* Now print the requested directory stack entries. */
455 printf ("%2d %s", directory_list_offset - desired_index,
456 DIRSTACK_ENTRY (desired_index));
458 printf ("%s", DIRSTACK_ENTRY (desired_index));
461 for (i = directory_list_offset - 1; i >= 0; i--)
463 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
465 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
469 return (EXECUTION_SUCCESS);
473 pushd_error (offset, arg)
478 builtin_error ("directory stack empty");
480 sh_erange (arg, "directory stack index");
484 clear_directory_stack ()
488 for (i = 0; i < directory_list_offset; i++)
489 free (pushd_directory_list[i]);
490 directory_list_offset = 0;
493 /* Switch to the directory in NAME. This uses the cd_builtin to do the work,
494 so if the result is EXECUTION_FAILURE then an error message has already
503 tlist = make_word_list (make_word (name), NULL);
504 result = cd_builtin (tlist);
505 dispose_words (tlist);
510 change_to_temp (temp)
515 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
517 if (tt == EXECUTION_SUCCESS)
518 dirs_builtin ((WORD_LIST *)NULL);
524 add_dirstack_element (dir)
527 if (directory_list_offset == directory_list_size)
528 pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10);
529 pushd_directory_list[directory_list_offset++] = dir;
533 get_dirstack_index (ind, sign, indexp)
538 *indexp = sign > 0 ? 1 : 2;
540 /* dirs +0 prints the current working directory. */
541 /* dirs -0 prints last element in directory stack */
542 if (ind == 0 && sign > 0)
544 else if (ind == directory_list_offset)
547 *indexp = sign > 0 ? 2 : 1;
550 else if (ind >= 0 && ind <= directory_list_offset)
551 return (sign > 0 ? directory_list_offset - ind : ind);
556 /* Used by the tilde expansion code. */
558 get_dirstack_from_string (string)
561 int ind, sign, index_flag;
565 if (*string == '-' || *string == '+')
567 sign = (*string == '-') ? -1 : 1;
570 if (legal_number (string, &i) == 0)
571 return ((char *)NULL);
574 ind = get_dirstack_index (i, sign, &index_flag);
575 if (index_flag && (ind < 0 || ind > directory_list_offset))
576 return ((char *)NULL);
577 if (index_flag == 0 || (index_flag == 1 && ind == 0))
578 return (get_string_value ("PWD"));
580 return (pushd_directory_list[ind]);
583 #ifdef INCLUDE_UNUSED
585 get_dirstack_element (ind, sign)
591 i = get_dirstack_index (ind, sign, (int *)NULL);
592 return (i < 0 || i > directory_list_offset) ? (char *)NULL
593 : pushd_directory_list[i];
598 set_dirstack_element (ind, sign, value)
605 i = get_dirstack_index (ind, sign, (int *)NULL);
606 if (ind == 0 || i < 0 || i > directory_list_offset)
608 free (pushd_directory_list[i]);
609 pushd_directory_list[i] = savestring (value);
613 get_directory_stack ()
619 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
621 d = polite_directory_format (pushd_directory_list[i]);
622 ret = make_word_list (make_word (d), ret);
624 /* Now the current directory. */
625 d = get_working_directory ("dirstack");
626 i = 0; /* sentinel to decide whether or not to free d */
631 t = polite_directory_format (d);
632 /* polite_directory_format sometimes returns its argument unchanged.
633 If it does not, we can free d right away. If it does, we need to
634 mark d to be deleted later. */
640 else /* t == d, so d is what we want */
643 ret = make_word_list (make_word (d), ret);
646 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
649 #ifdef LOADABLE_BUILTIN
650 static char *dirs_doc[] = {
651 "Display the list of currently remembered directories. Directories",
652 "find their way onto the list with the `pushd' command; you can get",
653 "back up through the list with the `popd' command.",
655 "The -l flag specifies that `dirs' should not print shorthand versions",
656 "of directories which are relative to your home directory. This means",
657 "that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag",
658 "causes `dirs' to print the directory stack with one entry per line,",
659 "prepending the directory name with its position in the stack. The -p",
660 "flag does the same thing, but the stack position is not prepended.",
661 "The -c flag clears the directory stack by deleting all of the elements.",
663 "+N displays the Nth entry counting from the left of the list shown by",
664 " dirs when invoked without options, starting with zero.",
666 "-N displays the Nth entry counting from the right of the list shown by",
667 " dirs when invoked without options, starting with zero.",
671 static char *pushd_doc[] = {
672 "Adds a directory to the top of the directory stack, or rotates",
673 "the stack, making the new top of the stack the current working",
674 "directory. With no arguments, exchanges the top two directories.",
676 "+N Rotates the stack so that the Nth directory (counting",
677 " from the left of the list shown by `dirs', starting with",
678 " zero) is at the top.",
680 "-N Rotates the stack so that the Nth directory (counting",
681 " from the right of the list shown by `dirs', starting with",
682 " zero) is at the top.",
684 "-n suppress the normal change of directory when adding directories",
685 " to the stack, so only the stack is manipulated.",
687 "dir adds DIR to the directory stack at the top, making it the",
688 " new current working directory.",
690 "You can see the directory stack with the `dirs' command.",
694 static char *popd_doc[] = {
695 "Removes entries from the directory stack. With no arguments,",
696 "removes the top directory from the stack, and cd's to the new",
699 "+N removes the Nth entry counting from the left of the list",
700 " shown by `dirs', starting with zero. For example: `popd +0'",
701 " removes the first directory, `popd +1' the second.",
703 "-N removes the Nth entry counting from the right of the list",
704 " shown by `dirs', starting with zero. For example: `popd -0'",
705 " removes the last directory, `popd -1' the next to last.",
707 "-n suppress the normal change of directory when removing directories",
708 " from the stack, so only the stack is manipulated.",
710 "You can see the directory stack with the `dirs' command.",
714 struct builtin pushd_struct = {
719 "pushd [+N | -N] [-n] [dir]",
723 struct builtin popd_struct = {
728 "popd [+N | -N] [-n]",
732 struct builtin dirs_struct = {
737 "dirs [-clpv] [+N] [-N]",
740 #endif /* LOADABLE_BUILTIN */
742 #endif /* PUSHD_AND_POPD */