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"
124 static char *m_badarg = "%s: bad argument";
126 /* The list of remembered directories. */
127 static char **pushd_directory_list = (char **)NULL;
129 /* Number of existing slots in this list. */
130 static int directory_list_size;
132 /* Offset to the end of the list. */
133 static int directory_list_offset;
135 static void pushd_error ();
136 static void clear_directory_stack ();
137 static int cd_to_string ();
138 static int change_to_temp ();
139 static int get_dirstack_index ();
140 static void add_dirstack_element ();
144 #define LONGFORM 0x04
145 #define CLEARSTAK 0x08
151 char *temp, *current_directory, *top;
156 /* If there is no argument list then switch current and
160 if (directory_list_offset == 0)
162 builtin_error ("no other directory");
163 return (EXECUTION_FAILURE);
166 current_directory = get_working_directory ("pushd");
167 if (current_directory == 0)
168 return (EXECUTION_FAILURE);
170 j = directory_list_offset - 1;
171 temp = pushd_directory_list[j];
172 pushd_directory_list[j] = current_directory;
173 j = change_to_temp (temp);
178 for (flags = 0; list; list = list->next)
180 if (ISOPTION (list->word->word, 'n'))
184 else if (ISOPTION (list->word->word, '-'))
189 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
190 /* Let `pushd -' work like it used to. */
192 else if (((direction = list->word->word[0]) == '+') || direction == '-')
194 if (legal_number (list->word->word + 1, &num) == 0)
196 builtin_error (m_badarg, list->word->word);
198 return (EXECUTION_FAILURE);
201 if (direction == '-')
202 num = directory_list_offset - num;
204 if (num > directory_list_offset || num < 0)
206 pushd_error (directory_list_offset, list->word->word);
207 return (EXECUTION_FAILURE);
211 else if (*list->word->word == '-')
213 bad_option (list->word->word);
215 return (EXECUTION_FAILURE);
223 /* Rotate the stack num times. Remember, the current
224 directory acts like it is part of the stack. */
225 temp = get_working_directory ("pushd");
229 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
236 top = pushd_directory_list[directory_list_offset - 1];
238 for (j = directory_list_offset - 2; j > -1; j--)
239 pushd_directory_list[j + 1] = pushd_directory_list[j];
241 pushd_directory_list[j + 1] = temp;
248 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
254 return (EXECUTION_SUCCESS);
256 /* Change to the directory in list->word->word. Save the current
257 directory on the top of the stack. */
258 current_directory = get_working_directory ("pushd");
259 if (current_directory == 0)
260 return (EXECUTION_FAILURE);
262 j = ((flags & NOCD) == 0) ? cd_builtin (list) : EXECUTION_SUCCESS;
263 if (j == EXECUTION_SUCCESS)
265 add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
266 dirs_builtin ((WORD_LIST *)NULL);
268 free (current_directory);
269 return (EXECUTION_SUCCESS);
273 free (current_directory);
274 return (EXECUTION_FAILURE);
278 /* Pop the directory stack, and then change to the new top of the stack.
279 If LIST is non-null it should consist of a word +N or -N, which says
280 what element to delete from the stack. The default is the top one. */
291 which_word = (char *)NULL;
292 for (flags = 0, which = 0L, direction = '+'; list; list = list->next)
294 if (ISOPTION (list->word->word, 'n'))
298 else if (ISOPTION (list->word->word, '-'))
303 else if (((direction = list->word->word[0]) == '+') || direction == '-')
305 if (legal_number (list->word->word + 1, &which) == 0)
307 builtin_error (m_badarg, list->word->word);
309 return (EXECUTION_FAILURE);
311 which_word = list->word->word;
313 else if (*list->word->word == '-')
315 bad_option (list->word->word);
317 return (EXECUTION_FAILURE);
323 if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
325 pushd_error (directory_list_offset, which_word ? which_word : "");
326 return (EXECUTION_FAILURE);
329 /* Handle case of no specification, or top of stack specification. */
330 if ((direction == '+' && which == 0) ||
331 (direction == '-' && which == directory_list_offset))
333 i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
335 if (i != EXECUTION_SUCCESS)
337 free (pushd_directory_list[--directory_list_offset]);
341 /* Since an offset other than the top directory was specified,
342 remove that directory from the list and shift the remainder
343 of the list into place. */
344 i = (direction == '+') ? directory_list_offset - which : which;
345 free (pushd_directory_list[i]);
346 directory_list_offset--;
348 /* Shift the remainder of the list into place. */
349 for (; i < directory_list_offset; i++)
350 pushd_directory_list[i] = pushd_directory_list[i + 1];
353 dirs_builtin ((WORD_LIST *)NULL);
354 return (EXECUTION_SUCCESS);
357 /* Print the current list of directories on the directory stack. */
362 int flags, desired_index, index_flag, vflag;
366 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
368 if (ISOPTION (list->word->word, 'l'))
372 else if (ISOPTION (list->word->word, 'c'))
376 else if (ISOPTION (list->word->word, 'v'))
380 else if (ISOPTION (list->word->word, 'p'))
384 else if (ISOPTION (list->word->word, '-'))
389 else if (*list->word->word == '+' || *list->word->word == '-')
392 if (legal_number (w = list->word->word + 1, &i) == 0)
394 builtin_error (m_badarg, list->word->word);
396 return (EXECUTION_FAILURE);
398 sign = (*list->word->word == '+') ? 1 : -1;
399 desired_index = get_dirstack_index (i, sign, &index_flag);
403 bad_option (list->word->word);
405 return (EXECUTION_FAILURE);
409 if (flags & CLEARSTAK)
411 clear_directory_stack ();
412 return (EXECUTION_SUCCESS);
415 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
417 pushd_error (directory_list_offset, w);
418 return (EXECUTION_FAILURE);
421 #define DIRSTACK_FORMAT(temp) \
422 (flags & LONGFORM) ? temp : polite_directory_format (temp)
424 /* The first directory printed is always the current working directory. */
425 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
427 temp = get_working_directory ("dirs");
429 temp = savestring ("<no current directory>");
431 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
433 printf ("%s", DIRSTACK_FORMAT (temp));
438 return EXECUTION_SUCCESS;
442 #define DIRSTACK_ENTRY(i) \
443 (flags & LONGFORM) ? pushd_directory_list[i] \
444 : polite_directory_format (pushd_directory_list[i])
446 /* Now print the requested directory stack entries. */
450 printf ("%2d %s", directory_list_offset - desired_index,
451 DIRSTACK_ENTRY (desired_index));
453 printf ("%s", DIRSTACK_ENTRY (desired_index));
456 for (i = directory_list_offset - 1; i >= 0; i--)
458 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
460 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
464 return (EXECUTION_SUCCESS);
468 pushd_error (offset, arg)
473 builtin_error ("directory stack empty");
475 builtin_error ("%s: bad directory stack index", arg);
477 builtin_error ("bad directory stack index");
481 clear_directory_stack ()
485 for (i = 0; i < directory_list_offset; i++)
486 free (pushd_directory_list[i]);
487 directory_list_offset = 0;
490 /* Switch to the directory in NAME. This uses the cd_builtin to do the work,
491 so if the result is EXECUTION_FAILURE then an error message has already
500 tlist = make_word_list (make_word (name), NULL);
501 result = cd_builtin (tlist);
502 dispose_words (tlist);
507 change_to_temp (temp)
512 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
514 if (tt == EXECUTION_SUCCESS)
515 dirs_builtin ((WORD_LIST *)NULL);
521 add_dirstack_element (dir)
526 if (directory_list_offset == directory_list_size)
528 j = (directory_list_size += 10) * sizeof (char *);
529 pushd_directory_list = (char **)xrealloc (pushd_directory_list, j);
531 pushd_directory_list[directory_list_offset++] = dir;
535 get_dirstack_index (ind, sign, indexp)
536 int ind, sign, *indexp;
539 *indexp = sign > 0 ? 1 : 2;
541 /* dirs +0 prints the current working directory. */
542 /* dirs -0 prints last element in directory stack */
543 if (ind == 0 && sign > 0)
545 else if (ind == directory_list_offset)
548 *indexp = sign > 0 ? 2 : 1;
552 return (sign > 0 ? directory_list_offset - ind : ind);
555 /* Used by the tilde expansion code. */
557 get_dirstack_from_string (string)
560 int ind, sign, index_flag;
564 if (*string == '-' || *string == '+')
566 sign = (*string == '-') ? -1 : 1;
569 if (legal_number (string, &i) == 0)
570 return ((char *)NULL);
573 ind = get_dirstack_index (i, sign, &index_flag);
574 if (index_flag && (ind < 0 || ind > directory_list_offset))
575 return ((char *)NULL);
576 if (index_flag == 0 || (index_flag == 1 && ind == 0))
577 return (get_string_value ("PWD"));
579 return (pushd_directory_list[ind]);
582 #ifdef INCLUDE_UNUSED
584 get_dirstack_element (ind, sign)
589 i = get_dirstack_index (ind, sign, (int *)NULL);
590 return (i < 0 || i > directory_list_offset) ? (char *)NULL
591 : pushd_directory_list[i];
596 set_dirstack_element (ind, sign, value)
602 i = get_dirstack_index (ind, sign, (int *)NULL);
603 if (ind == 0 || i < 0 || i > directory_list_offset)
605 free (pushd_directory_list[i]);
606 pushd_directory_list[i] = savestring (value);
610 get_directory_stack ()
616 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
618 d = polite_directory_format (pushd_directory_list[i]);
619 ret = make_word_list (make_word (d), ret);
621 /* Now the current directory. */
622 d = get_working_directory ("dirstack");
623 i = 0; /* sentinel to decide whether or not to free d */
628 t = polite_directory_format (d);
629 /* polite_directory_format sometimes returns its argument unchanged.
630 If it does not, we can free d right away. If it does, we need to
631 mark d to be deleted later. */
637 else /* t == d, so d is what we want */
640 ret = make_word_list (make_word (d), ret);
643 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
645 #endif /* PUSHD_AND_POPD */