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-2004 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"
110 #include "../bashintl.h"
114 #include <tilde/tilde.h>
116 #include "../shell.h"
119 #include "builtext.h"
121 #ifdef LOADABLE_BUILTIN
122 # include "builtins.h"
129 /* The list of remembered directories. */
130 static char **pushd_directory_list = (char **)NULL;
132 /* Number of existing slots in this list. */
133 static int directory_list_size;
135 /* Offset to the end of the list. */
136 static int directory_list_offset;
138 static void pushd_error __P((int, char *));
139 static void clear_directory_stack __P((void));
140 static int cd_to_string __P((char *));
141 static int change_to_temp __P((char *));
142 static void add_dirstack_element __P((char *));
143 static int get_dirstack_index __P((intmax_t, int, int *));
147 #define LONGFORM 0x04
148 #define CLEARSTAK 0x08
154 WORD_LIST *orig_list;
155 char *temp, *current_directory, *top;
156 int j, flags, skipopt;
161 if (list && list->word && ISOPTION (list->word->word, '-'))
169 /* If there is no argument list then switch current and
173 if (directory_list_offset == 0)
175 builtin_error (_("no other directory"));
176 return (EXECUTION_FAILURE);
179 current_directory = get_working_directory ("pushd");
180 if (current_directory == 0)
181 return (EXECUTION_FAILURE);
183 j = directory_list_offset - 1;
184 temp = pushd_directory_list[j];
185 pushd_directory_list[j] = current_directory;
186 j = change_to_temp (temp);
191 for (flags = 0; skipopt == 0 && list; list = list->next)
193 if (ISOPTION (list->word->word, 'n'))
197 else if (ISOPTION (list->word->word, '-'))
202 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
203 /* Let `pushd -' work like it used to. */
205 else if (((direction = list->word->word[0]) == '+') || direction == '-')
207 if (legal_number (list->word->word + 1, &num) == 0)
209 sh_invalidnum (list->word->word);
211 return (EXECUTION_FAILURE);
214 if (direction == '-')
215 num = directory_list_offset - num;
217 if (num > directory_list_offset || num < 0)
219 pushd_error (directory_list_offset, list->word->word);
220 return (EXECUTION_FAILURE);
224 else if (*list->word->word == '-')
226 sh_invalidopt (list->word->word);
228 return (EXECUTION_FAILURE);
236 /* Rotate the stack num times. Remember, the current
237 directory acts like it is part of the stack. */
238 temp = get_working_directory ("pushd");
242 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
249 top = pushd_directory_list[directory_list_offset - 1];
251 for (j = directory_list_offset - 2; j > -1; j--)
252 pushd_directory_list[j + 1] = pushd_directory_list[j];
254 pushd_directory_list[j + 1] = temp;
261 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
267 return (EXECUTION_SUCCESS);
269 /* Change to the directory in list->word->word. Save the current
270 directory on the top of the stack. */
271 current_directory = get_working_directory ("pushd");
272 if (current_directory == 0)
273 return (EXECUTION_FAILURE);
275 j = ((flags & NOCD) == 0) ? cd_builtin (skipopt ? orig_list : list) : EXECUTION_SUCCESS;
276 if (j == EXECUTION_SUCCESS)
278 add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
279 dirs_builtin ((WORD_LIST *)NULL);
281 free (current_directory);
282 return (EXECUTION_SUCCESS);
286 free (current_directory);
287 return (EXECUTION_FAILURE);
291 /* Pop the directory stack, and then change to the new top of the stack.
292 If LIST is non-null it should consist of a word +N or -N, which says
293 what element to delete from the stack. The default is the top one. */
304 which_word = (char *)NULL;
305 for (flags = 0, which = 0, direction = '+'; list; list = list->next)
307 if (ISOPTION (list->word->word, 'n'))
311 else if (ISOPTION (list->word->word, '-'))
316 else if (((direction = list->word->word[0]) == '+') || direction == '-')
318 if (legal_number (list->word->word + 1, &which) == 0)
320 sh_invalidnum (list->word->word);
322 return (EXECUTION_FAILURE);
324 which_word = list->word->word;
326 else if (*list->word->word == '-')
328 sh_invalidopt (list->word->word);
330 return (EXECUTION_FAILURE);
336 if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
338 pushd_error (directory_list_offset, which_word ? which_word : "");
339 return (EXECUTION_FAILURE);
342 /* Handle case of no specification, or top of stack specification. */
343 if ((direction == '+' && which == 0) ||
344 (direction == '-' && which == directory_list_offset))
346 i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
348 if (i != EXECUTION_SUCCESS)
350 free (pushd_directory_list[--directory_list_offset]);
354 /* Since an offset other than the top directory was specified,
355 remove that directory from the list and shift the remainder
356 of the list into place. */
357 i = (direction == '+') ? directory_list_offset - which : which;
358 free (pushd_directory_list[i]);
359 directory_list_offset--;
361 /* Shift the remainder of the list into place. */
362 for (; i < directory_list_offset; i++)
363 pushd_directory_list[i] = pushd_directory_list[i + 1];
366 dirs_builtin ((WORD_LIST *)NULL);
367 return (EXECUTION_SUCCESS);
370 /* Print the current list of directories on the directory stack. */
375 int flags, desired_index, index_flag, vflag;
379 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
381 if (ISOPTION (list->word->word, 'l'))
385 else if (ISOPTION (list->word->word, 'c'))
389 else if (ISOPTION (list->word->word, 'v'))
393 else if (ISOPTION (list->word->word, 'p'))
397 else if (ISOPTION (list->word->word, '-'))
402 else if (*list->word->word == '+' || *list->word->word == '-')
405 if (legal_number (w = list->word->word + 1, &i) == 0)
407 sh_invalidnum (list->word->word);
409 return (EXECUTION_FAILURE);
411 sign = (*list->word->word == '+') ? 1 : -1;
412 desired_index = get_dirstack_index (i, sign, &index_flag);
416 sh_invalidopt (list->word->word);
418 return (EXECUTION_FAILURE);
422 if (flags & CLEARSTAK)
424 clear_directory_stack ();
425 return (EXECUTION_SUCCESS);
428 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
430 pushd_error (directory_list_offset, w);
431 return (EXECUTION_FAILURE);
434 #define DIRSTACK_FORMAT(temp) \
435 (flags & LONGFORM) ? temp : polite_directory_format (temp)
437 /* The first directory printed is always the current working directory. */
438 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
440 temp = get_working_directory ("dirs");
442 temp = savestring (_("<no current directory>"));
444 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
446 printf ("%s", DIRSTACK_FORMAT (temp));
451 return EXECUTION_SUCCESS;
455 #define DIRSTACK_ENTRY(i) \
456 (flags & LONGFORM) ? pushd_directory_list[i] \
457 : polite_directory_format (pushd_directory_list[i])
459 /* Now print the requested directory stack entries. */
463 printf ("%2d %s", directory_list_offset - desired_index,
464 DIRSTACK_ENTRY (desired_index));
466 printf ("%s", DIRSTACK_ENTRY (desired_index));
469 for (i = directory_list_offset - 1; i >= 0; i--)
471 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
473 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
477 return (EXECUTION_SUCCESS);
481 pushd_error (offset, arg)
486 builtin_error ("directory stack empty");
488 sh_erange (arg, "directory stack index");
492 clear_directory_stack ()
496 for (i = 0; i < directory_list_offset; i++)
497 free (pushd_directory_list[i]);
498 directory_list_offset = 0;
501 /* Switch to the directory in NAME. This uses the cd_builtin to do the work,
502 so if the result is EXECUTION_FAILURE then an error message has already
512 dir = make_word_list (make_word (name), NULL);
513 tlist = make_word_list (make_word ("--"), dir);
514 result = cd_builtin (tlist);
515 dispose_words (tlist);
520 change_to_temp (temp)
525 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
527 if (tt == EXECUTION_SUCCESS)
528 dirs_builtin ((WORD_LIST *)NULL);
534 add_dirstack_element (dir)
537 if (directory_list_offset == directory_list_size)
538 pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10);
539 pushd_directory_list[directory_list_offset++] = dir;
543 get_dirstack_index (ind, sign, indexp)
548 *indexp = sign > 0 ? 1 : 2;
550 /* dirs +0 prints the current working directory. */
551 /* dirs -0 prints last element in directory stack */
552 if (ind == 0 && sign > 0)
554 else if (ind == directory_list_offset)
557 *indexp = sign > 0 ? 2 : 1;
560 else if (ind >= 0 && ind <= directory_list_offset)
561 return (sign > 0 ? directory_list_offset - ind : ind);
566 /* Used by the tilde expansion code. */
568 get_dirstack_from_string (string)
571 int ind, sign, index_flag;
575 if (*string == '-' || *string == '+')
577 sign = (*string == '-') ? -1 : 1;
580 if (legal_number (string, &i) == 0)
581 return ((char *)NULL);
584 ind = get_dirstack_index (i, sign, &index_flag);
585 if (index_flag && (ind < 0 || ind > directory_list_offset))
586 return ((char *)NULL);
587 if (index_flag == 0 || (index_flag == 1 && ind == 0))
588 return (get_string_value ("PWD"));
590 return (pushd_directory_list[ind]);
593 #ifdef INCLUDE_UNUSED
595 get_dirstack_element (ind, sign)
601 i = get_dirstack_index (ind, sign, (int *)NULL);
602 return (i < 0 || i > directory_list_offset) ? (char *)NULL
603 : pushd_directory_list[i];
608 set_dirstack_element (ind, sign, value)
615 i = get_dirstack_index (ind, sign, (int *)NULL);
616 if (ind == 0 || i < 0 || i > directory_list_offset)
618 free (pushd_directory_list[i]);
619 pushd_directory_list[i] = savestring (value);
623 get_directory_stack ()
629 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
631 d = polite_directory_format (pushd_directory_list[i]);
632 ret = make_word_list (make_word (d), ret);
634 /* Now the current directory. */
635 d = get_working_directory ("dirstack");
636 i = 0; /* sentinel to decide whether or not to free d */
641 t = polite_directory_format (d);
642 /* polite_directory_format sometimes returns its argument unchanged.
643 If it does not, we can free d right away. If it does, we need to
644 mark d to be deleted later. */
650 else /* t == d, so d is what we want */
653 ret = make_word_list (make_word (d), ret);
656 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
659 #ifdef LOADABLE_BUILTIN
660 static char * const dirs_doc[] = {
661 N_("Display the list of currently remembered directories. Directories"),
662 N_("find their way onto the list with the `pushd' command; you can get"),
663 N_("back up through the list with the `popd' command."),
665 N_("The -l flag specifies that `dirs' should not print shorthand versions"),
666 N_("of directories which are relative to your home directory. This means"),
667 N_("that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag"),
668 N_("causes `dirs' to print the directory stack with one entry per line,"),
669 N_("prepending the directory name with its position in the stack. The -p"),
670 N_("flag does the same thing, but the stack position is not prepended."),
671 N_("The -c flag clears the directory stack by deleting all of the elements."),
673 N_("+N displays the Nth entry counting from the left of the list shown by"),
674 N_(" dirs when invoked without options, starting with zero."),
676 N_("-N displays the Nth entry counting from the right of the list shown by"),
677 N_(" dirs when invoked without options, starting with zero."),
681 static char * const pushd_doc[] = {
682 N_("Adds a directory to the top of the directory stack, or rotates"),
683 N_("the stack, making the new top of the stack the current working"),
684 N_("directory. With no arguments, exchanges the top two directories."),
686 N_("+N Rotates the stack so that the Nth directory (counting"),
687 N_(" from the left of the list shown by `dirs', starting with"),
688 N_(" zero) is at the top."),
690 N_("-N Rotates the stack so that the Nth directory (counting"),
691 N_(" from the right of the list shown by `dirs', starting with"),
692 N_(" zero) is at the top."),
694 N_("-n suppress the normal change of directory when adding directories"),
695 N_(" to the stack, so only the stack is manipulated."),
697 N_("dir adds DIR to the directory stack at the top, making it the"),
698 N_(" new current working directory."),
700 N_("You can see the directory stack with the `dirs' command."),
704 static char * const popd_doc[] = {
705 N_("Removes entries from the directory stack. With no arguments,"),
706 N_("removes the top directory from the stack, and cd's to the new"),
707 N_("top directory."),
709 N_("+N removes the Nth entry counting from the left of the list"),
710 N_(" shown by `dirs', starting with zero. For example: `popd +0'"),
711 N_(" removes the first directory, `popd +1' the second."),
713 N_("-N removes the Nth entry counting from the right of the list"),
714 N_(" shown by `dirs', starting with zero. For example: `popd -0'"),
715 N_(" removes the last directory, `popd -1' the next to last."),
717 N_("-n suppress the normal change of directory when removing directories"),
718 N_(" from the stack, so only the stack is manipulated."),
720 N_("You can see the directory stack with the `dirs' command."),
724 struct builtin pushd_struct = {
729 "pushd [+N | -N] [-n] [dir]",
733 struct builtin popd_struct = {
738 "popd [+N | -N] [-n]",
742 struct builtin dirs_struct = {
747 "dirs [-clpv] [+N] [-N]",
750 #endif /* LOADABLE_BUILTIN */
752 #endif /* PUSHD_AND_POPD */