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)
98 #include <sys/param.h>
100 #if defined (HAVE_UNISTD_H)
104 #include "../bashansi.h"
108 #include <tilde/tilde.h>
110 #include "../shell.h"
111 #include "../maxpath.h"
113 #include "builtext.h"
119 static char *m_badarg = "%s: bad argument";
121 /* The list of remembered directories. */
122 static char **pushd_directory_list = (char **)NULL;
124 /* Number of existing slots in this list. */
125 static int directory_list_size;
127 /* Offset to the end of the list. */
128 static int directory_list_offset;
130 static void pushd_error ();
131 static void clear_directory_stack ();
132 static int cd_to_string ();
133 static int change_to_temp ();
134 static int get_dirstack_index ();
135 static void add_dirstack_element ();
139 #define LONGFORM 0x04
140 #define CLEARSTAK 0x08
146 char *temp, *current_directory, *top;
151 /* If there is no argument list then switch current and
155 if (directory_list_offset == 0)
157 builtin_error ("no other directory");
158 return (EXECUTION_FAILURE);
161 current_directory = get_working_directory ("pushd");
162 if (current_directory == 0)
163 return (EXECUTION_FAILURE);
165 j = directory_list_offset - 1;
166 temp = pushd_directory_list[j];
167 pushd_directory_list[j] = current_directory;
168 j = change_to_temp (temp);
173 for (flags = 0; list; list = list->next)
175 if (ISOPTION (list->word->word, 'n'))
179 else if (ISOPTION (list->word->word, '-'))
184 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
185 /* Let `pushd -' work like it used to. */
187 else if (((direction = list->word->word[0]) == '+') || direction == '-')
189 if (legal_number (list->word->word + 1, &num) == 0)
191 builtin_error (m_badarg, list->word->word);
193 return (EXECUTION_FAILURE);
196 if (direction == '-')
197 num = directory_list_offset - num;
199 if (num > directory_list_offset || num < 0)
201 pushd_error (directory_list_offset, list->word->word);
202 return (EXECUTION_FAILURE);
206 else if (*list->word->word == '-')
208 bad_option (list->word->word);
210 return (EXECUTION_FAILURE);
218 /* Rotate the stack num times. Remember, the current
219 directory acts like it is part of the stack. */
220 temp = get_working_directory ("pushd");
224 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
231 top = pushd_directory_list[directory_list_offset - 1];
233 for (j = directory_list_offset - 2; j > -1; j--)
234 pushd_directory_list[j + 1] = pushd_directory_list[j];
236 pushd_directory_list[j + 1] = temp;
243 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
249 return (EXECUTION_SUCCESS);
251 /* Change to the directory in list->word->word. Save the current
252 directory on the top of the stack. */
253 current_directory = get_working_directory ("pushd");
254 if (current_directory == 0)
255 return (EXECUTION_FAILURE);
257 j = ((flags & NOCD) == 0) ? cd_builtin (list) : EXECUTION_SUCCESS;
258 if (j == EXECUTION_SUCCESS)
260 add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
261 dirs_builtin ((WORD_LIST *)NULL);
263 free (current_directory);
264 return (EXECUTION_SUCCESS);
268 free (current_directory);
269 return (EXECUTION_FAILURE);
273 /* Pop the directory stack, and then change to the new top of the stack.
274 If LIST is non-null it should consist of a word +N or -N, which says
275 what element to delete from the stack. The default is the top one. */
286 which_word = (char *)NULL;
287 for (flags = 0, which = 0L, direction = '+'; list; list = list->next)
289 if (ISOPTION (list->word->word, 'n'))
293 else if (ISOPTION (list->word->word, '-'))
298 else if (((direction = list->word->word[0]) == '+') || direction == '-')
300 if (legal_number (list->word->word + 1, &which) == 0)
302 builtin_error (m_badarg, list->word->word);
304 return (EXECUTION_FAILURE);
306 which_word = list->word->word;
308 else if (*list->word->word == '-')
310 bad_option (list->word->word);
312 return (EXECUTION_FAILURE);
318 if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
320 pushd_error (directory_list_offset, which_word ? which_word : "");
321 return (EXECUTION_FAILURE);
324 /* Handle case of no specification, or top of stack specification. */
325 if ((direction == '+' && which == 0) ||
326 (direction == '-' && which == directory_list_offset))
328 i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
330 if (i != EXECUTION_SUCCESS)
332 free (pushd_directory_list[--directory_list_offset]);
336 /* Since an offset other than the top directory was specified,
337 remove that directory from the list and shift the remainder
338 of the list into place. */
339 i = (direction == '+') ? directory_list_offset - which : which;
340 free (pushd_directory_list[i]);
341 directory_list_offset--;
343 /* Shift the remainder of the list into place. */
344 for (; i < directory_list_offset; i++)
345 pushd_directory_list[i] = pushd_directory_list[i + 1];
348 dirs_builtin ((WORD_LIST *)NULL);
349 return (EXECUTION_SUCCESS);
352 /* Print the current list of directories on the directory stack. */
357 int flags, desired_index, index_flag, vflag;
361 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
363 if (ISOPTION (list->word->word, 'l'))
367 else if (ISOPTION (list->word->word, 'c'))
371 else if (ISOPTION (list->word->word, 'v'))
375 else if (ISOPTION (list->word->word, 'p'))
379 else if (ISOPTION (list->word->word, '-'))
384 else if (*list->word->word == '+' || *list->word->word == '-')
387 if (legal_number (w = list->word->word + 1, &i) == 0)
389 builtin_error (m_badarg, list->word->word);
391 return (EXECUTION_FAILURE);
393 sign = (*list->word->word == '+') ? 1 : -1;
394 desired_index = get_dirstack_index (i, sign, &index_flag);
398 bad_option (list->word->word);
400 return (EXECUTION_FAILURE);
404 if (flags & CLEARSTAK)
406 clear_directory_stack ();
407 return (EXECUTION_SUCCESS);
410 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
412 pushd_error (directory_list_offset, w);
413 return (EXECUTION_FAILURE);
416 #define DIRSTACK_FORMAT(temp) \
417 (flags & LONGFORM) ? temp : polite_directory_format (temp)
419 /* The first directory printed is always the current working directory. */
420 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
422 temp = get_working_directory ("dirs");
424 temp = savestring ("<no current directory>");
426 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
428 printf ("%s", DIRSTACK_FORMAT (temp));
433 return EXECUTION_SUCCESS;
437 #define DIRSTACK_ENTRY(i) \
438 (flags & LONGFORM) ? pushd_directory_list[i] \
439 : polite_directory_format (pushd_directory_list[i])
441 /* Now print the requested directory stack entries. */
445 printf ("%2d %s", directory_list_offset - desired_index,
446 DIRSTACK_ENTRY (desired_index));
448 printf ("%s", DIRSTACK_ENTRY (desired_index));
451 for (i = directory_list_offset - 1; i >= 0; i--)
453 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
455 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
459 return (EXECUTION_SUCCESS);
463 pushd_error (offset, arg)
468 builtin_error ("directory stack empty");
470 builtin_error ("%s: bad directory stack index", arg);
472 builtin_error ("bad directory stack index");
476 clear_directory_stack ()
480 for (i = 0; i < directory_list_offset; i++)
481 free (pushd_directory_list[i]);
482 directory_list_offset = 0;
485 /* Switch to the directory in NAME. This uses the cd_builtin to do the work,
486 so if the result is EXECUTION_FAILURE then an error message has already
495 tlist = make_word_list (make_word (name), NULL);
496 result = cd_builtin (tlist);
497 dispose_words (tlist);
502 change_to_temp (temp)
507 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
509 if (tt == EXECUTION_SUCCESS)
510 dirs_builtin ((WORD_LIST *)NULL);
516 add_dirstack_element (dir)
521 if (directory_list_offset == directory_list_size)
523 j = (directory_list_size += 10) * sizeof (char *);
524 pushd_directory_list = (char **)xrealloc (pushd_directory_list, j);
526 pushd_directory_list[directory_list_offset++] = dir;
530 get_dirstack_index (ind, sign, indexp)
531 int ind, sign, *indexp;
534 *indexp = sign > 0 ? 1 : 2;
536 /* dirs +0 prints the current working directory. */
537 /* dirs -0 prints last element in directory stack */
538 if (ind == 0 && sign > 0)
540 else if (ind == directory_list_offset)
543 *indexp = sign > 0 ? 2 : 1;
547 return (sign > 0 ? directory_list_offset - ind : ind);
551 get_dirstack_element (ind, sign)
556 i = get_dirstack_index (ind, sign, (int *)NULL);
557 return (i < 0 || i > directory_list_offset) ? (char *)NULL
558 : pushd_directory_list[i];
562 set_dirstack_element (ind, sign, value)
568 i = get_dirstack_index (ind, sign, (int *)NULL);
569 if (ind == 0 || i < 0 || i > directory_list_offset)
571 free (pushd_directory_list[i]);
572 pushd_directory_list[i] = savestring (value);
576 get_directory_stack ()
582 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
584 d = polite_directory_format (pushd_directory_list[i]);
585 ret = make_word_list (make_word (d), ret);
587 /* Now the current directory. */
588 d = get_working_directory ("dirstack");
589 i = 0; /* sentinel to decide whether or not to free d */
594 t = polite_directory_format (d);
595 /* polite_directory_format sometimes returns its argument unchanged.
596 If it does not, we can free d right away. If it does, we need to
597 mark d to be deleted later. */
603 else /* t == d, so d is what we want */
606 ret = make_word_list (make_word (d), ret);
609 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
611 #endif /* PUSHD_AND_POPD */