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-2015 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 __P((int, char *));
161 static void clear_directory_stack __P((void));
162 static int cd_to_string __P((char *));
163 static int change_to_temp __P((char *));
164 static void add_dirstack_element __P((char *));
165 static int get_dirstack_index __P((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 || (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 free (pushd_directory_list[i]);
391 directory_list_offset--;
393 /* Shift the remainder of the list into place. */
394 for (; i < directory_list_offset; i++)
395 pushd_directory_list[i] = pushd_directory_list[i + 1];
398 dirs_builtin ((WORD_LIST *)NULL);
399 return (EXECUTION_SUCCESS);
402 /* Print the current list of directories on the directory stack. */
407 int flags, desired_index, index_flag, vflag;
411 CHECK_HELPOPT (list);
412 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
414 if (ISOPTION (list->word->word, 'l'))
418 else if (ISOPTION (list->word->word, 'c'))
422 else if (ISOPTION (list->word->word, 'v'))
426 else if (ISOPTION (list->word->word, 'p'))
430 else if (ISOPTION (list->word->word, '-'))
435 else if (*list->word->word == '+' || *list->word->word == '-')
438 if (legal_number (w = list->word->word + 1, &i) == 0)
440 sh_invalidnum (list->word->word);
444 sign = (*list->word->word == '+') ? 1 : -1;
445 desired_index = get_dirstack_index (i, sign, &index_flag);
449 sh_invalidopt (list->word->word);
455 if (flags & CLEARSTAK)
457 clear_directory_stack ();
458 return (EXECUTION_SUCCESS);
461 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
463 pushd_error (directory_list_offset, w);
464 return (EXECUTION_FAILURE);
467 #define DIRSTACK_FORMAT(temp) \
468 (flags & LONGFORM) ? temp : polite_directory_format (temp)
470 /* The first directory printed is always the current working directory. */
471 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
473 temp = get_working_directory ("dirs");
475 temp = savestring (_("<no current directory>"));
477 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
479 printf ("%s", DIRSTACK_FORMAT (temp));
484 return (sh_chkwrite (EXECUTION_SUCCESS));
488 #define DIRSTACK_ENTRY(i) \
489 (flags & LONGFORM) ? pushd_directory_list[i] \
490 : polite_directory_format (pushd_directory_list[i])
492 /* Now print the requested directory stack entries. */
496 printf ("%2d %s", directory_list_offset - desired_index,
497 DIRSTACK_ENTRY (desired_index));
499 printf ("%s", DIRSTACK_ENTRY (desired_index));
502 for (i = directory_list_offset - 1; i >= 0; i--)
504 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
506 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
510 return (sh_chkwrite (EXECUTION_SUCCESS));
514 pushd_error (offset, arg)
519 builtin_error (_("directory stack empty"));
521 sh_erange (arg, _("directory stack index"));
525 clear_directory_stack ()
529 for (i = 0; i < directory_list_offset; i++)
530 free (pushd_directory_list[i]);
531 directory_list_offset = 0;
534 /* Switch to the directory in NAME. This uses the cd_builtin to do the work,
535 so if the result is EXECUTION_FAILURE then an error message has already
545 dir = make_word_list (make_word (name), NULL);
546 tlist = make_word_list (make_word ("--"), dir);
547 result = cd_builtin (tlist);
548 dispose_words (tlist);
553 change_to_temp (temp)
558 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
560 if (tt == EXECUTION_SUCCESS)
561 dirs_builtin ((WORD_LIST *)NULL);
567 add_dirstack_element (dir)
570 if (directory_list_offset == directory_list_size)
571 pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10);
572 pushd_directory_list[directory_list_offset++] = dir;
576 get_dirstack_index (ind, sign, indexp)
581 *indexp = sign > 0 ? 1 : 2;
583 /* dirs +0 prints the current working directory. */
584 /* dirs -0 prints last element in directory stack */
585 if (ind == 0 && sign > 0)
587 else if (ind == directory_list_offset)
590 *indexp = sign > 0 ? 2 : 1;
593 else if (ind >= 0 && ind <= directory_list_offset)
594 return (sign > 0 ? directory_list_offset - ind : ind);
599 /* Used by the tilde expansion code. */
601 get_dirstack_from_string (string)
604 int ind, sign, index_flag;
608 if (*string == '-' || *string == '+')
610 sign = (*string == '-') ? -1 : 1;
613 if (legal_number (string, &i) == 0)
614 return ((char *)NULL);
617 ind = get_dirstack_index (i, sign, &index_flag);
618 if (index_flag && (ind < 0 || ind > directory_list_offset))
619 return ((char *)NULL);
620 if (index_flag == 0 || (index_flag == 1 && ind == 0))
621 return (get_string_value ("PWD"));
623 return (pushd_directory_list[ind]);
626 #ifdef INCLUDE_UNUSED
628 get_dirstack_element (ind, sign)
634 i = get_dirstack_index (ind, sign, (int *)NULL);
635 return (i < 0 || i > directory_list_offset) ? (char *)NULL
636 : pushd_directory_list[i];
641 set_dirstack_element (ind, sign, value)
648 i = get_dirstack_index (ind, sign, (int *)NULL);
649 if (ind == 0 || i < 0 || i > directory_list_offset)
651 free (pushd_directory_list[i]);
652 pushd_directory_list[i] = savestring (value);
656 get_directory_stack (flags)
663 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
665 d = (flags&1) ? polite_directory_format (pushd_directory_list[i])
666 : pushd_directory_list[i];
667 ret = make_word_list (make_word (d), ret);
669 /* Now the current directory. */
670 d = get_working_directory ("dirstack");
671 i = 0; /* sentinel to decide whether or not to free d */
676 t = polite_directory_format (d);
677 /* polite_directory_format sometimes returns its argument unchanged.
678 If it does not, we can free d right away. If it does, we need to
679 mark d to be deleted later. */
685 else /* t == d, so d is what we want */
688 ret = make_word_list (make_word (d), ret);
691 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
694 #ifdef LOADABLE_BUILTIN
695 char * const dirs_doc[] = {
696 N_("Display the list of currently remembered directories. Directories\n\
697 find their way onto the list with the `pushd' command; you can get\n\
698 back up through the list with the `popd' command.\n\
701 -c clear the directory stack by deleting all of the elements\n\
702 -l do not print tilde-prefixed versions of directories relative\n\
703 to your home directory\n\
704 -p print the directory stack with one entry per line\n\
705 -v print the directory stack with one entry per line prefixed\n\
706 with its position in the stack\n\
709 +N Displays the Nth entry counting from the left of the list shown by\n\
710 dirs when invoked without options, starting with zero.\n\
712 -N Displays the Nth entry counting from the right of the list shown by\n\
713 dirs when invoked without options, starting with zero."),
717 char * const pushd_doc[] = {
718 N_("Adds a directory to the top of the directory stack, or rotates\n\
719 the stack, making the new top of the stack the current working\n\
720 directory. With no arguments, exchanges the top two directories.\n\
723 -n Suppresses the normal change of directory when adding\n\
724 directories to the stack, so only the stack is manipulated.\n\
727 +N Rotates the stack so that the Nth directory (counting\n\
728 from the left of the list shown by `dirs', starting with\n\
729 zero) is at the top.\n\
731 -N Rotates the stack so that the Nth directory (counting\n\
732 from the right of the list shown by `dirs', starting with\n\
733 zero) is at the top.\n\
735 dir Adds DIR to the directory stack at the top, making it the\n\
736 new current working directory.\n\
738 The `dirs' builtin displays the directory stack."),
742 char * const popd_doc[] = {
743 N_("Removes entries from the directory stack. With no arguments, removes\n\
744 the top directory from the stack, and changes to the new top directory.\n\
747 -n Suppresses the normal change of directory when removing\n\
748 directories from the stack, so only the stack is manipulated.\n\
751 +N Removes the Nth entry counting from the left of the list\n\
752 shown by `dirs', starting with zero. For example: `popd +0'\n\
753 removes the first directory, `popd +1' the second.\n\
755 -N Removes the Nth entry counting from the right of the list\n\
756 shown by `dirs', starting with zero. For example: `popd -0'\n\
757 removes the last directory, `popd -1' the next to last.\n\
759 The `dirs' builtin displays the directory stack."),
763 struct builtin pushd_struct = {
768 "pushd [+N | -N] [-n] [dir]",
772 struct builtin popd_struct = {
777 "popd [+N | -N] [-n]",
781 struct builtin dirs_struct = {
786 "dirs [-clpv] [+N] [-N]",
789 #endif /* LOADABLE_BUILTIN */
791 #endif /* PUSHD_AND_POPD */