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-2009 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)
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);
231 return (EXECUTION_FAILURE);
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);
248 return (EXECUTION_FAILURE);
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);
342 return (EXECUTION_FAILURE);
344 which_word = list->word->word;
346 else if (*list->word->word == '-')
348 sh_invalidopt (list->word->word);
350 return (EXECUTION_FAILURE);
356 if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
358 pushd_error (directory_list_offset, which_word ? which_word : "");
359 return (EXECUTION_FAILURE);
362 /* Handle case of no specification, or top of stack specification. */
363 if ((direction == '+' && which == 0) ||
364 (direction == '-' && which == directory_list_offset))
366 i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
368 if (i != EXECUTION_SUCCESS)
370 free (pushd_directory_list[--directory_list_offset]);
374 /* Since an offset other than the top directory was specified,
375 remove that directory from the list and shift the remainder
376 of the list into place. */
377 i = (direction == '+') ? directory_list_offset - which : which;
378 free (pushd_directory_list[i]);
379 directory_list_offset--;
381 /* Shift the remainder of the list into place. */
382 for (; i < directory_list_offset; i++)
383 pushd_directory_list[i] = pushd_directory_list[i + 1];
386 dirs_builtin ((WORD_LIST *)NULL);
387 return (EXECUTION_SUCCESS);
390 /* Print the current list of directories on the directory stack. */
395 int flags, desired_index, index_flag, vflag;
399 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
401 if (ISOPTION (list->word->word, 'l'))
405 else if (ISOPTION (list->word->word, 'c'))
409 else if (ISOPTION (list->word->word, 'v'))
413 else if (ISOPTION (list->word->word, 'p'))
417 else if (ISOPTION (list->word->word, '-'))
422 else if (*list->word->word == '+' || *list->word->word == '-')
425 if (legal_number (w = list->word->word + 1, &i) == 0)
427 sh_invalidnum (list->word->word);
429 return (EXECUTION_FAILURE);
431 sign = (*list->word->word == '+') ? 1 : -1;
432 desired_index = get_dirstack_index (i, sign, &index_flag);
436 sh_invalidopt (list->word->word);
438 return (EXECUTION_FAILURE);
442 if (flags & CLEARSTAK)
444 clear_directory_stack ();
445 return (EXECUTION_SUCCESS);
448 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
450 pushd_error (directory_list_offset, w);
451 return (EXECUTION_FAILURE);
454 #define DIRSTACK_FORMAT(temp) \
455 (flags & LONGFORM) ? temp : polite_directory_format (temp)
457 /* The first directory printed is always the current working directory. */
458 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
460 temp = get_working_directory ("dirs");
462 temp = savestring (_("<no current directory>"));
464 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
466 printf ("%s", DIRSTACK_FORMAT (temp));
471 return (sh_chkwrite (EXECUTION_SUCCESS));
475 #define DIRSTACK_ENTRY(i) \
476 (flags & LONGFORM) ? pushd_directory_list[i] \
477 : polite_directory_format (pushd_directory_list[i])
479 /* Now print the requested directory stack entries. */
483 printf ("%2d %s", directory_list_offset - desired_index,
484 DIRSTACK_ENTRY (desired_index));
486 printf ("%s", DIRSTACK_ENTRY (desired_index));
489 for (i = directory_list_offset - 1; i >= 0; i--)
491 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
493 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
497 return (sh_chkwrite (EXECUTION_SUCCESS));
501 pushd_error (offset, arg)
506 builtin_error (_("directory stack empty"));
508 sh_erange (arg, _("directory stack index"));
512 clear_directory_stack ()
516 for (i = 0; i < directory_list_offset; i++)
517 free (pushd_directory_list[i]);
518 directory_list_offset = 0;
521 /* Switch to the directory in NAME. This uses the cd_builtin to do the work,
522 so if the result is EXECUTION_FAILURE then an error message has already
532 dir = make_word_list (make_word (name), NULL);
533 tlist = make_word_list (make_word ("--"), dir);
534 result = cd_builtin (tlist);
535 dispose_words (tlist);
540 change_to_temp (temp)
545 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
547 if (tt == EXECUTION_SUCCESS)
548 dirs_builtin ((WORD_LIST *)NULL);
554 add_dirstack_element (dir)
557 if (directory_list_offset == directory_list_size)
558 pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10);
559 pushd_directory_list[directory_list_offset++] = dir;
563 get_dirstack_index (ind, sign, indexp)
568 *indexp = sign > 0 ? 1 : 2;
570 /* dirs +0 prints the current working directory. */
571 /* dirs -0 prints last element in directory stack */
572 if (ind == 0 && sign > 0)
574 else if (ind == directory_list_offset)
577 *indexp = sign > 0 ? 2 : 1;
580 else if (ind >= 0 && ind <= directory_list_offset)
581 return (sign > 0 ? directory_list_offset - ind : ind);
586 /* Used by the tilde expansion code. */
588 get_dirstack_from_string (string)
591 int ind, sign, index_flag;
595 if (*string == '-' || *string == '+')
597 sign = (*string == '-') ? -1 : 1;
600 if (legal_number (string, &i) == 0)
601 return ((char *)NULL);
604 ind = get_dirstack_index (i, sign, &index_flag);
605 if (index_flag && (ind < 0 || ind > directory_list_offset))
606 return ((char *)NULL);
607 if (index_flag == 0 || (index_flag == 1 && ind == 0))
608 return (get_string_value ("PWD"));
610 return (pushd_directory_list[ind]);
613 #ifdef INCLUDE_UNUSED
615 get_dirstack_element (ind, sign)
621 i = get_dirstack_index (ind, sign, (int *)NULL);
622 return (i < 0 || i > directory_list_offset) ? (char *)NULL
623 : pushd_directory_list[i];
628 set_dirstack_element (ind, sign, value)
635 i = get_dirstack_index (ind, sign, (int *)NULL);
636 if (ind == 0 || i < 0 || i > directory_list_offset)
638 free (pushd_directory_list[i]);
639 pushd_directory_list[i] = savestring (value);
643 get_directory_stack (flags)
650 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
652 d = (flags&1) ? polite_directory_format (pushd_directory_list[i])
653 : pushd_directory_list[i];
654 ret = make_word_list (make_word (d), ret);
656 /* Now the current directory. */
657 d = get_working_directory ("dirstack");
658 i = 0; /* sentinel to decide whether or not to free d */
663 t = polite_directory_format (d);
664 /* polite_directory_format sometimes returns its argument unchanged.
665 If it does not, we can free d right away. If it does, we need to
666 mark d to be deleted later. */
672 else /* t == d, so d is what we want */
675 ret = make_word_list (make_word (d), ret);
678 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
681 #ifdef LOADABLE_BUILTIN
682 char * const dirs_doc[] = {
683 N_("Display the list of currently remembered directories. Directories\n\
684 find their way onto the list with the `pushd' command; you can get\n\
685 back up through the list with the `popd' command.\n\
688 -c clear the directory stack by deleting all of the elements\n\
689 -l do not print tilde-prefixed versions of directories relative\n\
690 to your home directory\n\
691 -p print the directory stack with one entry per line\n\
692 -v print the directory stack with one entry per line prefixed\n\
693 with its position in the stack\n\
696 +N Displays the Nth entry counting from the left of the list shown by\n\
697 dirs when invoked without options, starting with zero.\n\
699 -N Displays the Nth entry counting from the right of the list shown by\n\
700 dirs when invoked without options, starting with zero."),
704 char * const pushd_doc[] = {
705 N_("Adds a directory to the top of the directory stack, or rotates\n\
706 the stack, making the new top of the stack the current working\n\
707 directory. With no arguments, exchanges the top two directories.\n\
710 -n Suppresses the normal change of directory when adding\n\
711 directories to the stack, so only the stack is manipulated.\n\
714 +N Rotates the stack so that the Nth directory (counting\n\
715 from the left of the list shown by `dirs', starting with\n\
716 zero) is at the top.\n\
718 -N Rotates the stack so that the Nth directory (counting\n\
719 from the right of the list shown by `dirs', starting with\n\
720 zero) is at the top.\n\
722 dir Adds DIR to the directory stack at the top, making it the\n\
723 new current working directory.\n\
725 The `dirs' builtin displays the directory stack."),
729 char * const popd_doc[] = {
730 N_("Removes entries from the directory stack. With no arguments, removes\n\
731 the top directory from the stack, and changes to the new top directory.\n\
734 -n Suppresses the normal change of directory when removing\n\
735 directories from the stack, so only the stack is manipulated.\n\
738 +N Removes the Nth entry counting from the left of the list\n\
739 shown by `dirs', starting with zero. For example: `popd +0'\n\
740 removes the first directory, `popd +1' the second.\n\
742 -N Removes the Nth entry counting from the right of the list\n\
743 shown by `dirs', starting with zero. For example: `popd -0'\n\
744 removes the last directory, `popd -1' the next to last.\n\
746 The `dirs' builtin displays the directory stack."),
750 struct builtin pushd_struct = {
755 "pushd [+N | -N] [-n] [dir]",
759 struct builtin popd_struct = {
764 "popd [+N | -N] [-n]",
768 struct builtin dirs_struct = {
773 "dirs [-clpv] [+N] [-N]",
776 #endif /* LOADABLE_BUILTIN */
778 #endif /* PUSHD_AND_POPD */