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-2013 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 shown by
105 dirs when invoked without options, starting with zero.
107 -N Displays the Nth entry counting from the right of the list shown by
108 dirs when invoked without options, starting with zero.
111 Returns success unless an invalid option is supplied or an error occurs.
116 #if defined (PUSHD_AND_POPD)
118 #if defined (HAVE_SYS_PARAM_H)
119 # include <sys/param.h>
122 #if defined (HAVE_UNISTD_H)
124 # include <sys/types.h>
129 #include "../bashansi.h"
130 #include "../bashintl.h"
134 #include <tilde/tilde.h>
136 #include "../shell.h"
139 #include "builtext.h"
141 #ifdef LOADABLE_BUILTIN
142 # include "builtins.h"
149 /* The list of remembered directories. */
150 static char **pushd_directory_list = (char **)NULL;
152 /* Number of existing slots in this list. */
153 static int directory_list_size;
155 /* Offset to the end of the list. */
156 static int directory_list_offset;
158 static void pushd_error __P((int, char *));
159 static void clear_directory_stack __P((void));
160 static int cd_to_string __P((char *));
161 static int change_to_temp __P((char *));
162 static void add_dirstack_element __P((char *));
163 static int get_dirstack_index __P((intmax_t, int, int *));
167 #define LONGFORM 0x04
168 #define CLEARSTAK 0x08
174 WORD_LIST *orig_list;
175 char *temp, *current_directory, *top;
176 int j, flags, skipopt;
181 if (list && list->word && ISOPTION (list->word->word, '-'))
189 /* If there is no argument list then switch current and
193 if (directory_list_offset == 0)
195 builtin_error (_("no other directory"));
196 return (EXECUTION_FAILURE);
199 current_directory = get_working_directory ("pushd");
200 if (current_directory == 0)
201 return (EXECUTION_FAILURE);
203 j = directory_list_offset - 1;
204 temp = pushd_directory_list[j];
205 pushd_directory_list[j] = current_directory;
206 j = change_to_temp (temp);
211 for (flags = 0; skipopt == 0 && list; list = list->next)
213 if (ISOPTION (list->word->word, 'n'))
217 else if (ISOPTION (list->word->word, '-'))
222 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
223 /* Let `pushd -' work like it used to. */
225 else if (((direction = list->word->word[0]) == '+') || direction == '-')
227 if (legal_number (list->word->word + 1, &num) == 0)
229 sh_invalidnum (list->word->word);
234 if (direction == '-')
235 num = directory_list_offset - num;
237 if (num > directory_list_offset || num < 0)
239 pushd_error (directory_list_offset, list->word->word);
240 return (EXECUTION_FAILURE);
244 else if (*list->word->word == '-')
246 sh_invalidopt (list->word->word);
256 /* Rotate the stack num times. Remember, the current
257 directory acts like it is part of the stack. */
258 temp = get_working_directory ("pushd");
262 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
269 top = pushd_directory_list[directory_list_offset - 1];
271 for (j = directory_list_offset - 2; j > -1; j--)
272 pushd_directory_list[j + 1] = pushd_directory_list[j];
274 pushd_directory_list[j + 1] = temp;
281 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
287 return (EXECUTION_SUCCESS);
289 /* Change to the directory in list->word->word. Save the current
290 directory on the top of the stack. */
291 current_directory = get_working_directory ("pushd");
292 if (current_directory == 0)
293 return (EXECUTION_FAILURE);
295 j = ((flags & NOCD) == 0) ? cd_builtin (skipopt ? orig_list : list) : EXECUTION_SUCCESS;
296 if (j == EXECUTION_SUCCESS)
298 add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
299 dirs_builtin ((WORD_LIST *)NULL);
301 free (current_directory);
302 return (EXECUTION_SUCCESS);
306 free (current_directory);
307 return (EXECUTION_FAILURE);
311 /* Pop the directory stack, and then change to the new top of the stack.
312 If LIST is non-null it should consist of a word +N or -N, which says
313 what element to delete from the stack. The default is the top one. */
324 which_word = (char *)NULL;
325 for (flags = 0, which = 0, direction = '+'; list; list = list->next)
327 if (ISOPTION (list->word->word, 'n'))
331 else if (ISOPTION (list->word->word, '-'))
336 else if (((direction = list->word->word[0]) == '+') || direction == '-')
338 if (legal_number (list->word->word + 1, &which) == 0)
340 sh_invalidnum (list->word->word);
344 which_word = list->word->word;
346 else if (*list->word->word == '-')
348 sh_invalidopt (list->word->word);
352 else if (*list->word->word)
354 builtin_error (_("%s: invalid argument"), list->word->word);
362 if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
364 pushd_error (directory_list_offset, which_word ? which_word : "");
365 return (EXECUTION_FAILURE);
368 /* Handle case of no specification, or top of stack specification. */
369 if ((direction == '+' && which == 0) ||
370 (direction == '-' && which == directory_list_offset))
372 i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
374 if (i != EXECUTION_SUCCESS)
376 free (pushd_directory_list[--directory_list_offset]);
380 /* Since an offset other than the top directory was specified,
381 remove that directory from the list and shift the remainder
382 of the list into place. */
383 i = (direction == '+') ? directory_list_offset - which : which;
384 free (pushd_directory_list[i]);
385 directory_list_offset--;
387 /* Shift the remainder of the list into place. */
388 for (; i < directory_list_offset; i++)
389 pushd_directory_list[i] = pushd_directory_list[i + 1];
392 dirs_builtin ((WORD_LIST *)NULL);
393 return (EXECUTION_SUCCESS);
396 /* Print the current list of directories on the directory stack. */
401 int flags, desired_index, index_flag, vflag;
405 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
407 if (ISOPTION (list->word->word, 'l'))
411 else if (ISOPTION (list->word->word, 'c'))
415 else if (ISOPTION (list->word->word, 'v'))
419 else if (ISOPTION (list->word->word, 'p'))
423 else if (ISOPTION (list->word->word, '-'))
428 else if (*list->word->word == '+' || *list->word->word == '-')
431 if (legal_number (w = list->word->word + 1, &i) == 0)
433 sh_invalidnum (list->word->word);
437 sign = (*list->word->word == '+') ? 1 : -1;
438 desired_index = get_dirstack_index (i, sign, &index_flag);
442 sh_invalidopt (list->word->word);
448 if (flags & CLEARSTAK)
450 clear_directory_stack ();
451 return (EXECUTION_SUCCESS);
454 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
456 pushd_error (directory_list_offset, w);
457 return (EXECUTION_FAILURE);
460 #define DIRSTACK_FORMAT(temp) \
461 (flags & LONGFORM) ? temp : polite_directory_format (temp)
463 /* The first directory printed is always the current working directory. */
464 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
466 temp = get_working_directory ("dirs");
468 temp = savestring (_("<no current directory>"));
470 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
472 printf ("%s", DIRSTACK_FORMAT (temp));
477 return (sh_chkwrite (EXECUTION_SUCCESS));
481 #define DIRSTACK_ENTRY(i) \
482 (flags & LONGFORM) ? pushd_directory_list[i] \
483 : polite_directory_format (pushd_directory_list[i])
485 /* Now print the requested directory stack entries. */
489 printf ("%2d %s", directory_list_offset - desired_index,
490 DIRSTACK_ENTRY (desired_index));
492 printf ("%s", DIRSTACK_ENTRY (desired_index));
495 for (i = directory_list_offset - 1; i >= 0; i--)
497 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
499 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
503 return (sh_chkwrite (EXECUTION_SUCCESS));
507 pushd_error (offset, arg)
512 builtin_error (_("directory stack empty"));
514 sh_erange (arg, _("directory stack index"));
518 clear_directory_stack ()
522 for (i = 0; i < directory_list_offset; i++)
523 free (pushd_directory_list[i]);
524 directory_list_offset = 0;
527 /* Switch to the directory in NAME. This uses the cd_builtin to do the work,
528 so if the result is EXECUTION_FAILURE then an error message has already
538 dir = make_word_list (make_word (name), NULL);
539 tlist = make_word_list (make_word ("--"), dir);
540 result = cd_builtin (tlist);
541 dispose_words (tlist);
546 change_to_temp (temp)
551 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
553 if (tt == EXECUTION_SUCCESS)
554 dirs_builtin ((WORD_LIST *)NULL);
560 add_dirstack_element (dir)
563 if (directory_list_offset == directory_list_size)
564 pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10);
565 pushd_directory_list[directory_list_offset++] = dir;
569 get_dirstack_index (ind, sign, indexp)
574 *indexp = sign > 0 ? 1 : 2;
576 /* dirs +0 prints the current working directory. */
577 /* dirs -0 prints last element in directory stack */
578 if (ind == 0 && sign > 0)
580 else if (ind == directory_list_offset)
583 *indexp = sign > 0 ? 2 : 1;
586 else if (ind >= 0 && ind <= directory_list_offset)
587 return (sign > 0 ? directory_list_offset - ind : ind);
592 /* Used by the tilde expansion code. */
594 get_dirstack_from_string (string)
597 int ind, sign, index_flag;
601 if (*string == '-' || *string == '+')
603 sign = (*string == '-') ? -1 : 1;
606 if (legal_number (string, &i) == 0)
607 return ((char *)NULL);
610 ind = get_dirstack_index (i, sign, &index_flag);
611 if (index_flag && (ind < 0 || ind > directory_list_offset))
612 return ((char *)NULL);
613 if (index_flag == 0 || (index_flag == 1 && ind == 0))
614 return (get_string_value ("PWD"));
616 return (pushd_directory_list[ind]);
619 #ifdef INCLUDE_UNUSED
621 get_dirstack_element (ind, sign)
627 i = get_dirstack_index (ind, sign, (int *)NULL);
628 return (i < 0 || i > directory_list_offset) ? (char *)NULL
629 : pushd_directory_list[i];
634 set_dirstack_element (ind, sign, value)
641 i = get_dirstack_index (ind, sign, (int *)NULL);
642 if (ind == 0 || i < 0 || i > directory_list_offset)
644 free (pushd_directory_list[i]);
645 pushd_directory_list[i] = savestring (value);
649 get_directory_stack (flags)
656 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
658 d = (flags&1) ? polite_directory_format (pushd_directory_list[i])
659 : pushd_directory_list[i];
660 ret = make_word_list (make_word (d), ret);
662 /* Now the current directory. */
663 d = get_working_directory ("dirstack");
664 i = 0; /* sentinel to decide whether or not to free d */
669 t = polite_directory_format (d);
670 /* polite_directory_format sometimes returns its argument unchanged.
671 If it does not, we can free d right away. If it does, we need to
672 mark d to be deleted later. */
678 else /* t == d, so d is what we want */
681 ret = make_word_list (make_word (d), ret);
684 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
687 #ifdef LOADABLE_BUILTIN
688 char * const dirs_doc[] = {
689 N_("Display the list of currently remembered directories. Directories\n\
690 find their way onto the list with the `pushd' command; you can get\n\
691 back up through the list with the `popd' command.\n\
694 -c clear the directory stack by deleting all of the elements\n\
695 -l do not print tilde-prefixed versions of directories relative\n\
696 to your home directory\n\
697 -p print the directory stack with one entry per line\n\
698 -v print the directory stack with one entry per line prefixed\n\
699 with its position in the stack\n\
702 +N Displays the Nth entry counting from the left of the list shown by\n\
703 dirs when invoked without options, starting with zero.\n\
705 -N Displays the Nth entry counting from the right of the list shown by\n\
706 dirs when invoked without options, starting with zero."),
710 char * const pushd_doc[] = {
711 N_("Adds a directory to the top of the directory stack, or rotates\n\
712 the stack, making the new top of the stack the current working\n\
713 directory. With no arguments, exchanges the top two directories.\n\
716 -n Suppresses the normal change of directory when adding\n\
717 directories to the stack, so only the stack is manipulated.\n\
720 +N Rotates the stack so that the Nth directory (counting\n\
721 from the left of the list shown by `dirs', starting with\n\
722 zero) is at the top.\n\
724 -N Rotates the stack so that the Nth directory (counting\n\
725 from the right of the list shown by `dirs', starting with\n\
726 zero) is at the top.\n\
728 dir Adds DIR to the directory stack at the top, making it the\n\
729 new current working directory.\n\
731 The `dirs' builtin displays the directory stack."),
735 char * const popd_doc[] = {
736 N_("Removes entries from the directory stack. With no arguments, removes\n\
737 the top directory from the stack, and changes to the new top directory.\n\
740 -n Suppresses the normal change of directory when removing\n\
741 directories from the stack, so only the stack is manipulated.\n\
744 +N Removes the Nth entry counting from the left of the list\n\
745 shown by `dirs', starting with zero. For example: `popd +0'\n\
746 removes the first directory, `popd +1' the second.\n\
748 -N Removes the Nth entry counting from the right of the list\n\
749 shown by `dirs', starting with zero. For example: `popd -0'\n\
750 removes the last directory, `popd -1' the next to last.\n\
752 The `dirs' builtin displays the directory stack."),
756 struct builtin pushd_struct = {
761 "pushd [+N | -N] [-n] [dir]",
765 struct builtin popd_struct = {
770 "popd [+N | -N] [-n]",
774 struct builtin dirs_struct = {
779 "dirs [-clpv] [+N] [-N]",
782 #endif /* LOADABLE_BUILTIN */
784 #endif /* PUSHD_AND_POPD */