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-2020 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
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 Bash is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Bash. If not, see <http://www.gnu.org/licenses/>.
24 $FUNCTION pushd_builtin
25 $DEPENDS_ON PUSHD_AND_POPD
26 $SHORT_DOC pushd [-n] [+N | -N | dir]
27 Add directories to stack.
29 Adds a directory to the top of the directory stack, or rotates
30 the stack, making the new top of the stack the current working
31 directory. With no arguments, exchanges the top two directories.
34 -n Suppresses the normal change of directory when adding
35 directories to the stack, so only the stack is manipulated.
38 +N Rotates the stack so that the Nth directory (counting
39 from the left of the list shown by `dirs', starting with
42 -N Rotates the stack so that the Nth directory (counting
43 from the right of the list shown by `dirs', starting with
46 dir Adds DIR to the directory stack at the top, making it the
47 new current working directory.
49 The `dirs' builtin displays the directory stack.
52 Returns success unless an invalid argument is supplied or the directory
57 $FUNCTION popd_builtin
58 $DEPENDS_ON PUSHD_AND_POPD
59 $SHORT_DOC popd [-n] [+N | -N]
60 Remove directories from stack.
62 Removes entries from the directory stack. With no arguments, removes
63 the top directory from the stack, and changes to the new top directory.
66 -n Suppresses the normal change of directory when removing
67 directories from the stack, so only the stack is manipulated.
70 +N Removes the Nth entry counting from the left of the list
71 shown by `dirs', starting with zero. For example: `popd +0'
72 removes the first directory, `popd +1' the second.
74 -N Removes the Nth entry counting from the right of the list
75 shown by `dirs', starting with zero. For example: `popd -0'
76 removes the last directory, `popd -1' the next to last.
78 The `dirs' builtin displays the directory stack.
81 Returns success unless an invalid argument is supplied or the directory
86 $FUNCTION dirs_builtin
87 $DEPENDS_ON PUSHD_AND_POPD
88 $SHORT_DOC dirs [-clpv] [+N] [-N]
89 Display directory stack.
91 Display the list of currently remembered directories. Directories
92 find their way onto the list with the `pushd' command; you can get
93 back up through the list with the `popd' command.
96 -c clear the directory stack by deleting all of the elements
97 -l do not print tilde-prefixed versions of directories relative
98 to your home directory
99 -p print the directory stack with one entry per line
100 -v print the directory stack with one entry per line prefixed
101 with its position in the stack
104 +N Displays the Nth entry counting from the left of the list
105 shown by dirs when invoked without options, starting with
108 -N Displays the Nth entry counting from the right of the list
109 shown by dirs when invoked without options, starting with
113 Returns success unless an invalid option is supplied or an error occurs.
118 #if defined (PUSHD_AND_POPD)
120 #if defined (HAVE_SYS_PARAM_H)
121 # include <sys/param.h>
124 #if defined (HAVE_UNISTD_H)
126 # include <sys/types.h>
131 #include "../bashansi.h"
132 #include "../bashintl.h"
136 #include <tilde/tilde.h>
138 #include "../shell.h"
141 #include "builtext.h"
143 #ifdef LOADABLE_BUILTIN
144 # include "builtins.h"
151 /* The list of remembered directories. */
152 static char **pushd_directory_list = (char **)NULL;
154 /* Number of existing slots in this list. */
155 static int directory_list_size;
157 /* Offset to the end of the list. */
158 static int directory_list_offset;
160 static void pushd_error PARAMS((int, char *));
161 static void clear_directory_stack PARAMS((void));
162 static int cd_to_string PARAMS((char *));
163 static int change_to_temp PARAMS((char *));
164 static void add_dirstack_element PARAMS((char *));
165 static int get_dirstack_index PARAMS((intmax_t, int, int *));
169 #define LONGFORM 0x04
170 #define CLEARSTAK 0x08
176 WORD_LIST *orig_list;
177 char *temp, *current_directory, *top;
178 int j, flags, skipopt;
184 CHECK_HELPOPT (list);
185 if (list && list->word && ISOPTION (list->word->word, '-'))
193 /* If there is no argument list then switch current and
197 if (directory_list_offset == 0)
199 builtin_error (_("no other directory"));
200 return (EXECUTION_FAILURE);
203 current_directory = get_working_directory ("pushd");
204 if (current_directory == 0)
205 return (EXECUTION_FAILURE);
207 j = directory_list_offset - 1;
208 temp = pushd_directory_list[j];
209 pushd_directory_list[j] = current_directory;
210 j = change_to_temp (temp);
215 for (flags = 0; skipopt == 0 && list; list = list->next)
217 if (ISOPTION (list->word->word, 'n'))
221 else if (ISOPTION (list->word->word, '-'))
226 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
227 /* Let `pushd -' work like it used to. */
229 else if (((direction = list->word->word[0]) == '+') || direction == '-')
231 if (legal_number (list->word->word + 1, &num) == 0)
233 sh_invalidnum (list->word->word);
238 if (direction == '-')
239 num = directory_list_offset - num;
241 if (num > directory_list_offset || num < 0)
243 pushd_error (directory_list_offset, list->word->word);
244 return (EXECUTION_FAILURE);
248 else if (*list->word->word == '-')
250 sh_invalidopt (list->word->word);
260 /* Rotate the stack num times. Remember, the current
261 directory acts like it is part of the stack. */
262 temp = get_working_directory ("pushd");
266 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
273 top = pushd_directory_list[directory_list_offset - 1];
275 for (j = directory_list_offset - 2; j > -1; j--)
276 pushd_directory_list[j + 1] = pushd_directory_list[j];
278 pushd_directory_list[j + 1] = temp;
285 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
291 return (EXECUTION_SUCCESS);
293 /* Change to the directory in list->word->word. Save the current
294 directory on the top of the stack. */
295 current_directory = get_working_directory ("pushd");
296 if (current_directory == 0)
297 return (EXECUTION_FAILURE);
299 j = ((flags & NOCD) == 0) ? cd_builtin (skipopt ? orig_list : list) : EXECUTION_SUCCESS;
300 if (j == EXECUTION_SUCCESS)
302 add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
303 dirs_builtin ((WORD_LIST *)NULL);
305 free (current_directory);
306 return (EXECUTION_SUCCESS);
310 free (current_directory);
311 return (EXECUTION_FAILURE);
315 /* Pop the directory stack, and then change to the new top of the stack.
316 If LIST is non-null it should consist of a word +N or -N, which says
317 what element to delete from the stack. The default is the top one. */
328 CHECK_HELPOPT (list);
330 which_word = (char *)NULL;
331 for (flags = 0, which = 0, direction = '+'; list; list = list->next)
333 if (ISOPTION (list->word->word, 'n'))
337 else if (ISOPTION (list->word->word, '-'))
342 else if (((direction = list->word->word[0]) == '+') || direction == '-')
344 if (legal_number (list->word->word + 1, &which) == 0)
346 sh_invalidnum (list->word->word);
350 which_word = list->word->word;
352 else if (*list->word->word == '-')
354 sh_invalidopt (list->word->word);
358 else if (*list->word->word)
360 builtin_error (_("%s: invalid argument"), list->word->word);
368 if (which > directory_list_offset || (which < -directory_list_offset) || (directory_list_offset == 0 && which == 0))
370 pushd_error (directory_list_offset, which_word ? which_word : "");
371 return (EXECUTION_FAILURE);
374 /* Handle case of no specification, or top of stack specification. */
375 if ((direction == '+' && which == 0) ||
376 (direction == '-' && which == directory_list_offset))
378 i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
380 if (i != EXECUTION_SUCCESS)
382 free (pushd_directory_list[--directory_list_offset]);
386 /* Since an offset other than the top directory was specified,
387 remove that directory from the list and shift the remainder
388 of the list into place. */
389 i = (direction == '+') ? directory_list_offset - which : which;
390 if (i < 0 || i > directory_list_offset)
392 pushd_error (directory_list_offset, which_word ? which_word : "");
393 return (EXECUTION_FAILURE);
395 free (pushd_directory_list[i]);
396 directory_list_offset--;
398 /* Shift the remainder of the list into place. */
399 for (; i < directory_list_offset; i++)
400 pushd_directory_list[i] = pushd_directory_list[i + 1];
403 dirs_builtin ((WORD_LIST *)NULL);
404 return (EXECUTION_SUCCESS);
407 /* Print the current list of directories on the directory stack. */
412 int flags, desired_index, index_flag, vflag;
416 CHECK_HELPOPT (list);
417 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
419 if (ISOPTION (list->word->word, 'l'))
423 else if (ISOPTION (list->word->word, 'c'))
427 else if (ISOPTION (list->word->word, 'v'))
431 else if (ISOPTION (list->word->word, 'p'))
435 else if (ISOPTION (list->word->word, '-'))
440 else if (*list->word->word == '+' || *list->word->word == '-')
443 if (legal_number (w = list->word->word + 1, &i) == 0)
445 sh_invalidnum (list->word->word);
449 sign = (*list->word->word == '+') ? 1 : -1;
450 desired_index = get_dirstack_index (i, sign, &index_flag);
454 sh_invalidopt (list->word->word);
460 if (flags & CLEARSTAK)
462 clear_directory_stack ();
463 return (EXECUTION_SUCCESS);
466 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
468 pushd_error (directory_list_offset, w);
469 return (EXECUTION_FAILURE);
472 #define DIRSTACK_FORMAT(temp) \
473 (flags & LONGFORM) ? temp : polite_directory_format (temp)
475 /* The first directory printed is always the current working directory. */
476 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
478 temp = get_working_directory ("dirs");
480 temp = savestring (_("<no current directory>"));
482 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
484 printf ("%s", DIRSTACK_FORMAT (temp));
489 return (sh_chkwrite (EXECUTION_SUCCESS));
493 #define DIRSTACK_ENTRY(i) \
494 (flags & LONGFORM) ? pushd_directory_list[i] \
495 : polite_directory_format (pushd_directory_list[i])
497 /* Now print the requested directory stack entries. */
501 printf ("%2d %s", directory_list_offset - desired_index,
502 DIRSTACK_ENTRY (desired_index));
504 printf ("%s", DIRSTACK_ENTRY (desired_index));
507 for (i = directory_list_offset - 1; i >= 0; i--)
509 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
511 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
515 return (sh_chkwrite (EXECUTION_SUCCESS));
519 pushd_error (offset, arg)
524 builtin_error (_("directory stack empty"));
526 sh_erange (arg, _("directory stack index"));
530 clear_directory_stack ()
534 for (i = 0; i < directory_list_offset; i++)
535 free (pushd_directory_list[i]);
536 directory_list_offset = 0;
539 /* Switch to the directory in NAME. This uses the cd_builtin to do the work,
540 so if the result is EXECUTION_FAILURE then an error message has already
550 dir = make_word_list (make_word (name), NULL);
551 tlist = make_word_list (make_word ("--"), dir);
552 result = cd_builtin (tlist);
553 dispose_words (tlist);
558 change_to_temp (temp)
563 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
565 if (tt == EXECUTION_SUCCESS)
566 dirs_builtin ((WORD_LIST *)NULL);
572 add_dirstack_element (dir)
575 if (directory_list_offset == directory_list_size)
576 pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10);
577 pushd_directory_list[directory_list_offset++] = dir;
581 get_dirstack_index (ind, sign, indexp)
586 *indexp = sign > 0 ? 1 : 2;
588 /* dirs +0 prints the current working directory. */
589 /* dirs -0 prints last element in directory stack */
590 if (ind == 0 && sign > 0)
592 else if (ind == directory_list_offset)
595 *indexp = sign > 0 ? 2 : 1;
598 else if (ind >= 0 && ind <= directory_list_offset)
599 return (sign > 0 ? directory_list_offset - ind : ind);
604 /* Used by the tilde expansion code. */
606 get_dirstack_from_string (string)
609 int ind, sign, index_flag;
613 if (*string == '-' || *string == '+')
615 sign = (*string == '-') ? -1 : 1;
618 if (legal_number (string, &i) == 0)
619 return ((char *)NULL);
622 ind = get_dirstack_index (i, sign, &index_flag);
623 if (index_flag && (ind < 0 || ind > directory_list_offset))
624 return ((char *)NULL);
625 if (index_flag == 0 || (index_flag == 1 && ind == 0))
626 return (get_string_value ("PWD"));
628 return (pushd_directory_list[ind]);
631 #ifdef INCLUDE_UNUSED
633 get_dirstack_element (ind, sign)
639 i = get_dirstack_index (ind, sign, (int *)NULL);
640 return (i < 0 || i > directory_list_offset) ? (char *)NULL
641 : pushd_directory_list[i];
646 set_dirstack_element (ind, sign, value)
653 i = get_dirstack_index (ind, sign, (int *)NULL);
654 if (ind == 0 || i < 0 || i > directory_list_offset)
656 free (pushd_directory_list[i]);
657 pushd_directory_list[i] = savestring (value);
661 get_directory_stack (flags)
668 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
670 d = (flags&1) ? polite_directory_format (pushd_directory_list[i])
671 : pushd_directory_list[i];
672 ret = make_word_list (make_word (d), ret);
674 /* Now the current directory. */
675 d = get_working_directory ("dirstack");
676 i = 0; /* sentinel to decide whether or not to free d */
681 t = (flags&1) ? polite_directory_format (d) : d;
682 /* polite_directory_format sometimes returns its argument unchanged.
683 If it does not, we can free d right away. If it does, we need to
684 mark d to be deleted later. */
690 else /* t == d, so d is what we want */
693 ret = make_word_list (make_word (d), ret);
696 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
699 #ifdef LOADABLE_BUILTIN
700 char * const dirs_doc[] = {
701 N_("Display the list of currently remembered directories. Directories\n\
702 find their way onto the list with the `pushd' command; you can get\n\
703 back up through the list with the `popd' command.\n\
706 -c clear the directory stack by deleting all of the elements\n\
707 -l do not print tilde-prefixed versions of directories relative\n\
708 to your home directory\n\
709 -p print the directory stack with one entry per line\n\
710 -v print the directory stack with one entry per line prefixed\n\
711 with its position in the stack\n\
714 +N Displays the Nth entry counting from the left of the list shown by\n\
715 dirs when invoked without options, starting with zero.\n\
717 -N Displays the Nth entry counting from the right of the list shown by\n\
718 dirs when invoked without options, starting with zero."),
722 char * const pushd_doc[] = {
723 N_("Adds a directory to the top of the directory stack, or rotates\n\
724 the stack, making the new top of the stack the current working\n\
725 directory. With no arguments, exchanges the top two directories.\n\
728 -n Suppresses the normal change of directory when adding\n\
729 directories to the stack, so only the stack is manipulated.\n\
732 +N Rotates the stack so that the Nth directory (counting\n\
733 from the left of the list shown by `dirs', starting with\n\
734 zero) is at the top.\n\
736 -N Rotates the stack so that the Nth directory (counting\n\
737 from the right of the list shown by `dirs', starting with\n\
738 zero) is at the top.\n\
740 dir Adds DIR to the directory stack at the top, making it the\n\
741 new current working directory.\n\
743 The `dirs' builtin displays the directory stack."),
747 char * const popd_doc[] = {
748 N_("Removes entries from the directory stack. With no arguments, removes\n\
749 the top directory from the stack, and changes to the new top directory.\n\
752 -n Suppresses the normal change of directory when removing\n\
753 directories from the stack, so only the stack is manipulated.\n\
756 +N Removes the Nth entry counting from the left of the list\n\
757 shown by `dirs', starting with zero. For example: `popd +0'\n\
758 removes the first directory, `popd +1' the second.\n\
760 -N Removes the Nth entry counting from the right of the list\n\
761 shown by `dirs', starting with zero. For example: `popd -0'\n\
762 removes the last directory, `popd -1' the next to last.\n\
764 The `dirs' builtin displays the directory stack."),
768 struct builtin pushd_struct = {
773 "pushd [+N | -N] [-n] [dir]",
777 struct builtin popd_struct = {
782 "popd [+N | -N] [-n]",
786 struct builtin dirs_struct = {
791 "dirs [-clpv] [+N] [-N]",
794 #endif /* LOADABLE_BUILTIN */
796 #endif /* PUSHD_AND_POPD */