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 (flags
)
630 for (ret
= (WORD_LIST *)NULL
, i
= 0; i
< directory_list_offset
; i
++)
632 d
= (flags
&1) ?
polite_directory_format (pushd_directory_list
[i
])
633 : pushd_directory_list
[i
];
634 ret
= make_word_list (make_word (d
), ret
);
636 /* Now the current directory.
*/
637 d
= get_working_directory ("dirstack");
638 i
= 0; /* sentinel to decide whether or not to free d
*/
643 t
= polite_directory_format (d
);
644 /* polite_directory_format sometimes returns its argument unchanged.
645 If it does not
, we can free d right away. If it does
, we need to
646 mark d to be deleted later.
*/
652 else
/* t
== d
, so d is what we want
*/
655 ret
= make_word_list (make_word (d
), ret
);
658 return ret
; /* was (REVERSE_LIST (ret
, (WORD_LIST *)
); */
661 #ifdef LOADABLE_BUILTIN
662 char
* const dirs_doc
[] = {
663 N_("Display the list of currently remembered directories. Directories"),
664 N_("find their way onto the list with the `pushd' command; you can get"),
665 N_("back up through the list with the `popd' command."),
667 N_("The -l flag specifies that `dirs' should not print shorthand versions"),
668 N_("of directories which are relative to your home directory. This means"),
669 N_("that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag"),
670 N_("causes `dirs' to print the directory stack with one entry per line,"),
671 N_("prepending the directory name with its position in the stack. The -p"),
672 N_("flag does the same thing, but the stack position is not prepended."),
673 N_("The -c flag clears the directory stack by deleting all of the elements."),
675 N_("+N displays the Nth entry counting from the left of the list shown by"),
676 N_(" dirs when invoked without options, starting with zero."),
678 N_("-N displays the Nth entry counting from the right of the list shown by"),
679 N_(" dirs when invoked without options, starting with zero."),
683 char
* const pushd_doc
[] = {
684 N_("Adds a directory to the top of the directory stack, or rotates"),
685 N_("the stack, making the new top of the stack the current working"),
686 N_("directory. With no arguments, exchanges the top two directories."),
688 N_("+N Rotates the stack so that the Nth directory (counting"),
689 N_(" from the left of the list shown by `dirs', starting with"),
690 N_(" zero) is at the top."),
692 N_("-N Rotates the stack so that the Nth directory (counting"),
693 N_(" from the right of the list shown by `dirs', starting with"),
694 N_(" zero) is at the top."),
696 N_("-n suppress the normal change of directory when adding directories"),
697 N_(" to the stack, so only the stack is manipulated."),
699 N_("dir adds DIR to the directory stack at the top, making it the"),
700 N_(" new current working directory."),
702 N_("You can see the directory stack with the `dirs' command."),
706 char
* const popd_doc
[] = {
707 N_("Removes entries from the directory stack. With no arguments,"),
708 N_("removes the top directory from the stack, and cd's to the new"),
709 N_("top directory."),
711 N_("+N removes the Nth entry counting from the left of the list"),
712 N_(" shown by `dirs', starting with zero. For example: `popd +0'"),
713 N_(" removes the first directory, `popd +1' the second."),
715 N_("-N removes the Nth entry counting from the right of the list"),
716 N_(" shown by `dirs', starting with zero. For example: `popd -0'"),
717 N_(" removes the last directory, `popd -1' the next to last."),
719 N_("-n suppress the normal change of directory when removing directories"),
720 N_(" from the stack, so only the stack is manipulated."),
722 N_("You can see the directory stack with the `dirs' command."),
726 struct builtin pushd_struct
= {
731 "pushd [+N | -N] [-n] [dir]",
735 struct builtin popd_struct
= {
740 "popd [+N | -N] [-n]",
744 struct builtin dirs_struct
= {
749 "dirs [-clpv] [+N] [-N]",
752 #endif
/* LOADABLE_BUILTIN
*/
754 #endif
/* PUSHD_AND_POPD
*/