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') is at the top.
35 -N Rotates the stack so that the Nth directory (counting
36 from the right) is at the top.
38 -n suppress the normal change of directory when adding directories
39 to the stack, so only the stack is manipulated.
41 dir adds DIR to the directory stack at the top, making it the
42 new current working directory.
44 You can see the directory stack with the `dirs' command.
48 $FUNCTION popd_builtin
49 $DEPENDS_ON PUSHD_AND_POPD
50 $SHORT_DOC popd [+N | -N] [-n]
51 Removes entries from the directory stack. With no arguments,
52 removes the top directory from the stack, and cd's to the new
55 +N removes the Nth entry counting from the left of the list
56 shown by `dirs', starting with zero. For example: `popd +0'
57 removes the first directory, `popd +1' the second.
59 -N removes the Nth entry counting from the right of the list
60 shown by `dirs', starting with zero. For example: `popd -0'
61 removes the last directory, `popd -1' the next to last.
63 -n suppress the normal change of directory when removing directories
64 from the stack, so only the stack is manipulated.
66 You can see the directory stack with the `dirs' command.
70 $FUNCTION dirs_builtin
71 $DEPENDS_ON PUSHD_AND_POPD
72 $SHORT_DOC dirs [-clpv] [+N] [-N]
73 Display the list of currently remembered directories. Directories
74 find their way onto the list with the `pushd' command; you can get
75 back up through the list with the `popd' command.
77 The -l flag specifies that `dirs' should not print shorthand versions
78 of directories which are relative to your home directory. This means
79 that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag
80 causes `dirs' to print the directory stack with one entry per line,
81 prepending the directory name with its position in the stack. The -p
82 flag does the same thing, but the stack position is not prepended.
83 The -c flag clears the directory stack by deleting all of the elements.
85 +N displays the Nth entry counting from the left of the list shown by
86 dirs when invoked without options, starting with zero.
88 -N displays the Nth entry counting from the right of the list shown by
89 dirs when invoked without options, starting with zero.
94 #if defined (PUSHD_AND_POPD)
96 #include <sys/param.h>
98 #if defined (HAVE_UNISTD_H)
102 #include "../bashansi.h"
106 #include <tilde/tilde.h>
108 #include "../shell.h"
109 #include "../maxpath.h"
111 #include "builtext.h"
117 static char *m_badarg = "%s: bad argument";
119 /* The list of remembered directories. */
120 static char **pushd_directory_list = (char **)NULL;
122 /* Number of existing slots in this list. */
123 static int directory_list_size;
125 /* Offset to the end of the list. */
126 static int directory_list_offset;
128 static void pushd_error ();
129 static void clear_directory_stack ();
130 static int cd_to_string ();
131 static int change_to_temp ();
132 static int get_dirstack_index ();
133 static void add_dirstack_element ();
137 #define LONGFORM 0x04
138 #define CLEARSTAK 0x08
144 char *temp, *current_directory, *top;
149 /* If there is no argument list then switch current and
153 if (directory_list_offset == 0)
155 builtin_error ("no other directory");
156 return (EXECUTION_FAILURE);
159 current_directory = get_working_directory ("pushd");
160 if (current_directory == 0)
161 return (EXECUTION_FAILURE);
163 j = directory_list_offset - 1;
164 temp = pushd_directory_list[j];
165 pushd_directory_list[j] = current_directory;
166 j = change_to_temp (temp);
171 for (flags = 0; list; list = list->next)
173 if (ISOPTION (list->word->word, 'n'))
177 else if (ISOPTION (list->word->word, '-'))
182 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
183 /* Let `pushd -' work like it used to. */
185 else if (((direction = list->word->word[0]) == '+') || direction == '-')
187 if (legal_number (list->word->word + 1, &num) == 0)
189 builtin_error (m_badarg, list->word->word);
191 return (EXECUTION_FAILURE);
194 if (direction == '-')
195 num = directory_list_offset - num;
197 if (num > directory_list_offset || num < 0)
199 pushd_error (directory_list_offset, list->word->word);
200 return (EXECUTION_FAILURE);
204 else if (*list->word->word == '-')
206 bad_option (list->word->word);
208 return (EXECUTION_FAILURE);
216 /* Rotate the stack num times. Remember, the current
217 directory acts like it is part of the stack. */
218 temp = get_working_directory ("pushd");
222 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
229 top = pushd_directory_list[directory_list_offset - 1];
231 for (j = directory_list_offset - 2; j > -1; j--)
232 pushd_directory_list[j + 1] = pushd_directory_list[j];
234 pushd_directory_list[j + 1] = temp;
241 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
247 return (EXECUTION_SUCCESS);
249 /* Change to the directory in list->word->word. Save the current
250 directory on the top of the stack. */
251 current_directory = get_working_directory ("pushd");
252 if (current_directory == 0)
253 return (EXECUTION_FAILURE);
255 j = ((flags & NOCD) == 0) ? cd_builtin (list) : EXECUTION_SUCCESS;
256 if (j == EXECUTION_SUCCESS)
258 add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
259 dirs_builtin ((WORD_LIST *)NULL);
260 return (EXECUTION_SUCCESS);
264 free (current_directory);
265 return (EXECUTION_FAILURE);
269 /* Pop the directory stack, and then change to the new top of the stack.
270 If LIST is non-null it should consist of a word +N or -N, which says
271 what element to delete from the stack. The default is the top one. */
281 for (flags = 0, which = 0L, direction = '+'; list; list = list->next)
283 if (ISOPTION (list->word->word, 'n'))
287 else if (ISOPTION (list->word->word, '-'))
292 else if (((direction = list->word->word[0]) == '+') || direction == '-')
294 if (legal_number (list->word->word + 1, &which) == 0)
296 builtin_error (m_badarg, list->word->word);
298 return (EXECUTION_FAILURE);
301 else if (*list->word->word == '-')
303 bad_option (list->word->word);
305 return (EXECUTION_FAILURE);
311 if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
313 pushd_error (directory_list_offset, list ? list->word->word : "");
314 return (EXECUTION_FAILURE);
317 /* Handle case of no specification, or top of stack specification. */
318 if ((direction == '+' && which == 0) ||
319 (direction == '-' && which == directory_list_offset))
321 i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
323 if (i != EXECUTION_SUCCESS)
325 free (pushd_directory_list[--directory_list_offset]);
329 /* Since an offset other than the top directory was specified,
330 remove that directory from the list and shift the remainder
331 of the list into place. */
332 i = (direction == '+') ? directory_list_offset - which : which;
333 free (pushd_directory_list[i]);
334 directory_list_offset--;
336 /* Shift the remainder of the list into place. */
337 for (; i < directory_list_offset; i++)
338 pushd_directory_list[i] = pushd_directory_list[i + 1];
341 dirs_builtin ((WORD_LIST *)NULL);
342 return (EXECUTION_SUCCESS);
345 /* Print the current list of directories on the directory stack. */
350 int flags, desired_index, index_flag, vflag;
354 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
356 if (ISOPTION (list->word->word, 'l'))
360 else if (ISOPTION (list->word->word, 'c'))
364 else if (ISOPTION (list->word->word, 'v'))
368 else if (ISOPTION (list->word->word, 'p'))
372 else if (ISOPTION (list->word->word, '-'))
377 else if (*list->word->word == '+' || *list->word->word == '-')
380 if (legal_number (w = list->word->word + 1, &i) == 0)
382 builtin_error (m_badarg, list->word->word);
384 return (EXECUTION_FAILURE);
386 sign = (*list->word->word == '+') ? 1 : -1;
387 desired_index = get_dirstack_index (i, sign, &index_flag);
391 bad_option (list->word->word);
393 return (EXECUTION_FAILURE);
397 if (flags & CLEARSTAK)
399 clear_directory_stack ();
400 return (EXECUTION_SUCCESS);
403 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
405 pushd_error (directory_list_offset, w);
406 return (EXECUTION_FAILURE);
409 #define DIRSTACK_FORMAT(temp) \
410 (flags & LONGFORM) ? temp : polite_directory_format (temp)
412 /* The first directory printed is always the current working directory. */
413 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
415 temp = get_working_directory ("dirs");
417 temp = savestring ("<no current directory>");
419 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
421 printf ("%s", DIRSTACK_FORMAT (temp));
426 return EXECUTION_SUCCESS;
430 #define DIRSTACK_ENTRY(i) \
431 (flags & LONGFORM) ? pushd_directory_list[i] \
432 : polite_directory_format (pushd_directory_list[i])
434 /* Now print the requested directory stack entries. */
438 printf ("%2d %s", directory_list_offset - desired_index,
439 DIRSTACK_ENTRY (desired_index));
441 printf ("%s", DIRSTACK_ENTRY (desired_index));
444 for (i = directory_list_offset - 1; i >= 0; i--)
446 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
448 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
452 return (EXECUTION_SUCCESS);
456 pushd_error (offset, arg)
461 builtin_error ("directory stack empty");
463 builtin_error ("%s: bad directory stack index", arg);
465 builtin_error ("bad directory stack index");
469 clear_directory_stack ()
473 for (i = 0; i < directory_list_offset; i++)
474 free (pushd_directory_list[i]);
475 directory_list_offset = 0;
478 /* Switch to the directory in NAME. This uses the cd_builtin to do the work,
479 so if the result is EXECUTION_FAILURE then an error message has already
488 tlist = make_word_list (make_word (name), NULL);
489 result = cd_builtin (tlist);
490 dispose_words (tlist);
495 change_to_temp (temp)
500 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
502 if (tt == EXECUTION_SUCCESS)
503 dirs_builtin ((WORD_LIST *)NULL);
509 add_dirstack_element (dir)
514 if (directory_list_offset == directory_list_size)
516 j = (directory_list_size += 10) * sizeof (char *);
517 pushd_directory_list = (char **)xrealloc (pushd_directory_list, j);
519 pushd_directory_list[directory_list_offset++] = dir;
523 get_dirstack_index (ind, sign, indexp)
524 int ind, sign, *indexp;
527 *indexp = sign > 0 ? 1 : 2;
529 /* dirs +0 prints the current working directory. */
530 /* dirs -0 prints last element in directory stack */
531 if (ind == 0 && sign > 0)
533 else if (ind == directory_list_offset)
536 *indexp = sign > 0 ? 2 : 1;
540 return (sign > 0 ? directory_list_offset - ind : ind);
544 get_dirstack_element (ind, sign)
549 i = get_dirstack_index (ind, sign, (int *)NULL);
550 return (i < 0 || i > directory_list_offset) ? (char *)NULL
551 : pushd_directory_list[i];
555 set_dirstack_element (ind, sign, value)
561 i = get_dirstack_index (ind, sign, (int *)NULL);
562 if (ind == 0 || i < 0 || i > directory_list_offset)
564 free (pushd_directory_list[i]);
565 pushd_directory_list[i] = savestring (value);
569 get_directory_stack ()
575 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
577 d = polite_directory_format (pushd_directory_list[i]);
578 ret = make_word_list (make_word (d), ret);
580 /* Now the current directory. */
581 d = get_working_directory ("dirstack");
582 i = 0; /* sentinel to decide whether or not to free d */
587 t = polite_directory_format (d);
588 /* polite_directory_format sometimes returns its argument unchanged.
589 If it does not, we can free d right away. If it does, we need to
590 mark d to be deleted later. */
596 else /* t == d, so d is what we want */
599 ret = make_word_list (make_word (d), ret);
602 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
604 #endif /* PUSHD_AND_POPD */