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, 1989, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, 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"
113 #include <tilde/tilde.h>
115 #include "../shell.h"
116 #include "../maxpath.h"
118 #include "builtext.h"
120 #ifdef LOADABLE_BUILTIN
121 # include "builtins.h"
128 static char *m_badarg = "%s: bad argument";
130 /* The list of remembered directories. */
131 static char **pushd_directory_list = (char **)NULL;
133 /* Number of existing slots in this list. */
134 static int directory_list_size;
136 /* Offset to the end of the list. */
137 static int directory_list_offset;
139 static void pushd_error ();
140 static void clear_directory_stack ();
141 static int cd_to_string ();
142 static int change_to_temp ();
143 static int get_dirstack_index ();
144 static void add_dirstack_element ();
148 #define LONGFORM 0x04
149 #define CLEARSTAK 0x08
155 char *temp, *current_directory, *top;
160 /* If there is no argument list then switch current and
164 if (directory_list_offset == 0)
166 builtin_error ("no other directory");
167 return (EXECUTION_FAILURE);
170 current_directory = get_working_directory ("pushd");
171 if (current_directory == 0)
172 return (EXECUTION_FAILURE);
174 j = directory_list_offset - 1;
175 temp = pushd_directory_list[j];
176 pushd_directory_list[j] = current_directory;
177 j = change_to_temp (temp);
182 for (flags = 0; list; list = list->next)
184 if (ISOPTION (list->word->word, 'n'))
188 else if (ISOPTION (list->word->word, '-'))
193 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
194 /* Let `pushd -' work like it used to. */
196 else if (((direction = list->word->word[0]) == '+') || direction == '-')
198 if (legal_number (list->word->word + 1, &num) == 0)
200 builtin_error (m_badarg, list->word->word);
202 return (EXECUTION_FAILURE);
205 if (direction == '-')
206 num = directory_list_offset - num;
208 if (num > directory_list_offset || num < 0)
210 pushd_error (directory_list_offset, list->word->word);
211 return (EXECUTION_FAILURE);
215 else if (*list->word->word == '-')
217 bad_option (list->word->word);
219 return (EXECUTION_FAILURE);
227 /* Rotate the stack num times. Remember, the current
228 directory acts like it is part of the stack. */
229 temp = get_working_directory ("pushd");
233 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
240 top = pushd_directory_list[directory_list_offset - 1];
242 for (j = directory_list_offset - 2; j > -1; j--)
243 pushd_directory_list[j + 1] = pushd_directory_list[j];
245 pushd_directory_list[j + 1] = temp;
252 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
258 return (EXECUTION_SUCCESS);
260 /* Change to the directory in list->word->word. Save the current
261 directory on the top of the stack. */
262 current_directory = get_working_directory ("pushd");
263 if (current_directory == 0)
264 return (EXECUTION_FAILURE);
266 j = ((flags & NOCD) == 0) ? cd_builtin (list) : EXECUTION_SUCCESS;
267 if (j == EXECUTION_SUCCESS)
269 add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
270 dirs_builtin ((WORD_LIST *)NULL);
272 free (current_directory);
273 return (EXECUTION_SUCCESS);
277 free (current_directory);
278 return (EXECUTION_FAILURE);
282 /* Pop the directory stack, and then change to the new top of the stack.
283 If LIST is non-null it should consist of a word +N or -N, which says
284 what element to delete from the stack. The default is the top one. */
295 which_word = (char *)NULL;
296 for (flags = 0, which = 0L, direction = '+'; list; list = list->next)
298 if (ISOPTION (list->word->word, 'n'))
302 else if (ISOPTION (list->word->word, '-'))
307 else if (((direction = list->word->word[0]) == '+') || direction == '-')
309 if (legal_number (list->word->word + 1, &which) == 0)
311 builtin_error (m_badarg, list->word->word);
313 return (EXECUTION_FAILURE);
315 which_word = list->word->word;
317 else if (*list->word->word == '-')
319 bad_option (list->word->word);
321 return (EXECUTION_FAILURE);
327 if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
329 pushd_error (directory_list_offset, which_word ? which_word : "");
330 return (EXECUTION_FAILURE);
333 /* Handle case of no specification, or top of stack specification. */
334 if ((direction == '+' && which == 0) ||
335 (direction == '-' && which == directory_list_offset))
337 i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
339 if (i != EXECUTION_SUCCESS)
341 free (pushd_directory_list[--directory_list_offset]);
345 /* Since an offset other than the top directory was specified,
346 remove that directory from the list and shift the remainder
347 of the list into place. */
348 i = (direction == '+') ? directory_list_offset - which : which;
349 free (pushd_directory_list[i]);
350 directory_list_offset--;
352 /* Shift the remainder of the list into place. */
353 for (; i < directory_list_offset; i++)
354 pushd_directory_list[i] = pushd_directory_list[i + 1];
357 dirs_builtin ((WORD_LIST *)NULL);
358 return (EXECUTION_SUCCESS);
361 /* Print the current list of directories on the directory stack. */
366 int flags, desired_index, index_flag, vflag;
370 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
372 if (ISOPTION (list->word->word, 'l'))
376 else if (ISOPTION (list->word->word, 'c'))
380 else if (ISOPTION (list->word->word, 'v'))
384 else if (ISOPTION (list->word->word, 'p'))
388 else if (ISOPTION (list->word->word, '-'))
393 else if (*list->word->word == '+' || *list->word->word == '-')
396 if (legal_number (w = list->word->word + 1, &i) == 0)
398 builtin_error (m_badarg, list->word->word);
400 return (EXECUTION_FAILURE);
402 sign = (*list->word->word == '+') ? 1 : -1;
403 desired_index = get_dirstack_index (i, sign, &index_flag);
407 bad_option (list->word->word);
409 return (EXECUTION_FAILURE);
413 if (flags & CLEARSTAK)
415 clear_directory_stack ();
416 return (EXECUTION_SUCCESS);
419 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
421 pushd_error (directory_list_offset, w);
422 return (EXECUTION_FAILURE);
425 #define DIRSTACK_FORMAT(temp) \
426 (flags & LONGFORM) ? temp : polite_directory_format (temp)
428 /* The first directory printed is always the current working directory. */
429 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
431 temp = get_working_directory ("dirs");
433 temp = savestring ("<no current directory>");
435 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
437 printf ("%s", DIRSTACK_FORMAT (temp));
442 return EXECUTION_SUCCESS;
446 #define DIRSTACK_ENTRY(i) \
447 (flags & LONGFORM) ? pushd_directory_list[i] \
448 : polite_directory_format (pushd_directory_list[i])
450 /* Now print the requested directory stack entries. */
454 printf ("%2d %s", directory_list_offset - desired_index,
455 DIRSTACK_ENTRY (desired_index));
457 printf ("%s", DIRSTACK_ENTRY (desired_index));
460 for (i = directory_list_offset - 1; i >= 0; i--)
462 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
464 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
468 return (EXECUTION_SUCCESS);
472 pushd_error (offset, arg)
477 builtin_error ("directory stack empty");
479 builtin_error ("%s: bad directory stack index", arg);
481 builtin_error ("bad directory stack index");
485 clear_directory_stack ()
489 for (i = 0; i < directory_list_offset; i++)
490 free (pushd_directory_list[i]);
491 directory_list_offset = 0;
494 /* Switch to the directory in NAME. This uses the cd_builtin to do the work,
495 so if the result is EXECUTION_FAILURE then an error message has already
504 tlist = make_word_list (make_word (name), NULL);
505 result = cd_builtin (tlist);
506 dispose_words (tlist);
511 change_to_temp (temp)
516 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
518 if (tt == EXECUTION_SUCCESS)
519 dirs_builtin ((WORD_LIST *)NULL);
525 add_dirstack_element (dir)
530 if (directory_list_offset == directory_list_size)
532 j = (directory_list_size += 10) * sizeof (char *);
533 pushd_directory_list = (char **)xrealloc (pushd_directory_list, j);
535 pushd_directory_list[directory_list_offset++] = dir;
539 get_dirstack_index (ind, sign, indexp)
540 int ind, sign, *indexp;
543 *indexp = sign > 0 ? 1 : 2;
545 /* dirs +0 prints the current working directory. */
546 /* dirs -0 prints last element in directory stack */
547 if (ind == 0 && sign > 0)
549 else if (ind == directory_list_offset)
552 *indexp = sign > 0 ? 2 : 1;
556 return (sign > 0 ? directory_list_offset - ind : ind);
559 /* Used by the tilde expansion code. */
561 get_dirstack_from_string (string)
564 int ind, sign, index_flag;
568 if (*string == '-' || *string == '+')
570 sign = (*string == '-') ? -1 : 1;
573 if (legal_number (string, &i) == 0)
574 return ((char *)NULL);
577 ind = get_dirstack_index (i, sign, &index_flag);
578 if (index_flag && (ind < 0 || ind > directory_list_offset))
579 return ((char *)NULL);
580 if (index_flag == 0 || (index_flag == 1 && ind == 0))
581 return (get_string_value ("PWD"));
583 return (pushd_directory_list[ind]);
586 #ifdef INCLUDE_UNUSED
588 get_dirstack_element (ind, sign)
593 i = get_dirstack_index (ind, sign, (int *)NULL);
594 return (i < 0 || i > directory_list_offset) ? (char *)NULL
595 : pushd_directory_list[i];
600 set_dirstack_element (ind, sign, value)
606 i = get_dirstack_index (ind, sign, (int *)NULL);
607 if (ind == 0 || i < 0 || i > directory_list_offset)
609 free (pushd_directory_list[i]);
610 pushd_directory_list[i] = savestring (value);
614 get_directory_stack ()
620 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
622 d = polite_directory_format (pushd_directory_list[i]);
623 ret = make_word_list (make_word (d), ret);
625 /* Now the current directory. */
626 d = get_working_directory ("dirstack");
627 i = 0; /* sentinel to decide whether or not to free d */
632 t = polite_directory_format (d);
633 /* polite_directory_format sometimes returns its argument unchanged.
634 If it does not, we can free d right away. If it does, we need to
635 mark d to be deleted later. */
641 else /* t == d, so d is what we want */
644 ret = make_word_list (make_word (d), ret);
647 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
650 #ifdef LOADABLE_BUILTIN
651 static char *dirs_doc[] = {
652 "Display the list of currently remembered directories. Directories",
653 "find their way onto the list with the `pushd' command; you can get",
654 "back up through the list with the `popd' command.",
656 "The -l flag specifies that `dirs' should not print shorthand versions",
657 "of directories which are relative to your home directory. This means",
658 "that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag",
659 "causes `dirs' to print the directory stack with one entry per line,",
660 "prepending the directory name with its position in the stack. The -p",
661 "flag does the same thing, but the stack position is not prepended.",
662 "The -c flag clears the directory stack by deleting all of the elements.",
664 "+N displays the Nth entry counting from the left of the list shown by",
665 " dirs when invoked without options, starting with zero.",
667 "-N displays the Nth entry counting from the right of the list shown by",
668 " dirs when invoked without options, starting with zero.",
672 static char *pushd_doc[] = {
673 "Adds a directory to the top of the directory stack, or rotates",
674 "the stack, making the new top of the stack the current working",
675 "directory. With no arguments, exchanges the top two directories.",
677 "+N Rotates the stack so that the Nth directory (counting",
678 " from the left of the list shown by `dirs', starting with",
679 " zero) is at the top.",
681 "-N Rotates the stack so that the Nth directory (counting",
682 " from the right of the list shown by `dirs', starting with",
683 " zero) is at the top.",
685 "-n suppress the normal change of directory when adding directories",
686 " to the stack, so only the stack is manipulated.",
688 "dir adds DIR to the directory stack at the top, making it the",
689 " new current working directory.",
691 "You can see the directory stack with the `dirs' command.",
695 static char *popd_doc[] = {
696 "Removes entries from the directory stack. With no arguments,",
697 "removes the top directory from the stack, and cd's to the new",
700 "+N removes the Nth entry counting from the left of the list",
701 " shown by `dirs', starting with zero. For example: `popd +0'",
702 " removes the first directory, `popd +1' the second.",
704 "-N removes the Nth entry counting from the right of the list",
705 " shown by `dirs', starting with zero. For example: `popd -0'",
706 " removes the last directory, `popd -1' the next to last.",
708 "-n suppress the normal change of directory when removing directories",
709 " from the stack, so only the stack is manipulated.",
711 "You can see the directory stack with the `dirs' command.",
715 struct builtin pushd_struct = {
720 "pushd [+N | -N] [-n] [dir]",
724 struct builtin popd_struct = {
729 "popd [+N | -N] [-n]",
733 struct builtin dirs_struct = {
738 "dirs [-clpv] [+N] [-N]",
741 #endif /* LOADABLE_BUILTIN */
743 #endif /* PUSHD_AND_POPD */