Patch-ID: bash32-017
[bash.git] / builtins / pushd.def
blob86c0bddb50dae47dc0de0974af829a97d1698f43
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
11 version.
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
16 for more details.
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.
22 $PRODUCES pushd.c
24 $BUILTIN pushd
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
34 zero) is at the top.
36 -N Rotates the stack so that the Nth directory (counting
37 from the right of the list shown by `dirs', starting with
38 zero) is at the top.
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.
47 $END
49 $BUILTIN popd
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
55 top directory.
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.
69 $END
71 $BUILTIN dirs
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.
92 $END
94 #include <config.h>
96 #if defined (PUSHD_AND_POPD)
97 #include <stdio.h>
98 #ifndef _MINIX
99 # include <sys/param.h>
100 #endif
102 #if defined (HAVE_UNISTD_H)
103 # ifdef _MINIX
104 # include <sys/types.h>
105 # endif
106 # include <unistd.h>
107 #endif
109 #include "../bashansi.h"
110 #include "../bashintl.h"
112 #include <errno.h>
114 #include <tilde/tilde.h>
116 #include "../shell.h"
117 #include "maxpath.h"
118 #include "common.h"
119 #include "builtext.h"
121 #ifdef LOADABLE_BUILTIN
122 # include "builtins.h"
123 #endif
125 #if !defined (errno)
126 extern int errno;
127 #endif /* !errno */
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 *));
145 #define NOCD 0x01
146 #define ROTATE 0x02
147 #define LONGFORM 0x04
148 #define CLEARSTAK 0x08
151 pushd_builtin (list)
152 WORD_LIST *list;
154 WORD_LIST *orig_list;
155 char *temp, *current_directory, *top;
156 int j, flags, skipopt;
157 intmax_t num;
158 char direction;
160 orig_list = list;
161 if (list && list->word && ISOPTION (list->word->word, '-'))
163 list = list->next;
164 skipopt = 1;
166 else
167 skipopt = 0;
169 /* If there is no argument list then switch current and
170 top of list. */
171 if (list == 0)
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);
187 free (temp);
188 return j;
191 for (flags = 0; skipopt == 0 && list; list = list->next)
193 if (ISOPTION (list->word->word, 'n'))
195 flags |= NOCD;
197 else if (ISOPTION (list->word->word, '-'))
199 list = list->next;
200 break;
202 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
203 /* Let `pushd -' work like it used to. */
204 break;
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);
210 builtin_usage ();
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);
222 flags |= ROTATE;
224 else if (*list->word->word == '-')
226 sh_invalidopt (list->word->word);
227 builtin_usage ();
228 return (EXECUTION_FAILURE);
230 else
231 break;
234 if (flags & ROTATE)
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");
240 if (num == 0)
242 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
243 free (temp);
244 return j;
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;
256 temp = top;
257 num--;
259 while (num);
261 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
262 free (temp);
263 return j;
266 if (list == 0)
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);
280 if (flags & NOCD)
281 free (current_directory);
282 return (EXECUTION_SUCCESS);
284 else
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. */
295 popd_builtin (list)
296 WORD_LIST *list;
298 register int i;
299 intmax_t which;
300 int flags;
301 char direction;
302 char *which_word;
304 which_word = (char *)NULL;
305 for (flags = 0, which = 0, direction = '+'; list; list = list->next)
307 if (ISOPTION (list->word->word, 'n'))
309 flags |= NOCD;
311 else if (ISOPTION (list->word->word, '-'))
313 list = list->next;
314 break;
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);
321 builtin_usage ();
322 return (EXECUTION_FAILURE);
324 which_word = list->word->word;
326 else if (*list->word->word == '-')
328 sh_invalidopt (list->word->word);
329 builtin_usage ();
330 return (EXECUTION_FAILURE);
332 else
333 break;
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])
347 : EXECUTION_SUCCESS;
348 if (i != EXECUTION_SUCCESS)
349 return (i);
350 free (pushd_directory_list[--directory_list_offset]);
352 else
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. */
372 dirs_builtin (list)
373 WORD_LIST *list;
375 int flags, desired_index, index_flag, vflag;
376 intmax_t i;
377 char *temp, *w;
379 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
381 if (ISOPTION (list->word->word, 'l'))
383 flags |= LONGFORM;
385 else if (ISOPTION (list->word->word, 'c'))
387 flags |= CLEARSTAK;
389 else if (ISOPTION (list->word->word, 'v'))
391 vflag |= 2;
393 else if (ISOPTION (list->word->word, 'p'))
395 vflag |= 1;
397 else if (ISOPTION (list->word->word, '-'))
399 list = list->next;
400 break;
402 else if (*list->word->word == '+' || *list->word->word == '-')
404 int sign;
405 if (legal_number (w = list->word->word + 1, &i) == 0)
407 sh_invalidnum (list->word->word);
408 builtin_usage ();
409 return (EXECUTION_FAILURE);
411 sign = (*list->word->word == '+') ? 1 : -1;
412 desired_index = get_dirstack_index (i, sign, &index_flag);
414 else
416 sh_invalidopt (list->word->word);
417 builtin_usage ();
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");
441 if (temp == 0)
442 temp = savestring (_("<no current directory>"));
443 if (vflag & 2)
444 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
445 else
446 printf ("%s", DIRSTACK_FORMAT (temp));
447 free (temp);
448 if (index_flag)
450 putchar ('\n');
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. */
460 if (index_flag)
462 if (vflag & 2)
463 printf ("%2d %s", directory_list_offset - desired_index,
464 DIRSTACK_ENTRY (desired_index));
465 else
466 printf ("%s", DIRSTACK_ENTRY (desired_index));
468 else
469 for (i = directory_list_offset - 1; i >= 0; i--)
470 if (vflag >= 2)
471 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
472 else
473 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
475 putchar ('\n');
476 fflush (stdout);
477 return (EXECUTION_SUCCESS);
480 static void
481 pushd_error (offset, arg)
482 int offset;
483 char *arg;
485 if (offset == 0)
486 builtin_error ("directory stack empty");
487 else
488 sh_erange (arg, "directory stack index");
491 static void
492 clear_directory_stack ()
494 register int i;
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
503 been printed. */
504 static int
505 cd_to_string (name)
506 char *name;
508 WORD_LIST *tlist;
509 WORD_LIST *dir;
510 int result;
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);
516 return (result);
519 static int
520 change_to_temp (temp)
521 char *temp;
523 int tt;
525 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
527 if (tt == EXECUTION_SUCCESS)
528 dirs_builtin ((WORD_LIST *)NULL);
530 return (tt);
533 static void
534 add_dirstack_element (dir)
535 char *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;
542 static int
543 get_dirstack_index (ind, sign, indexp)
544 intmax_t ind;
545 int sign, *indexp;
547 if (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)
553 return 0;
554 else if (ind == directory_list_offset)
556 if (indexp)
557 *indexp = sign > 0 ? 2 : 1;
558 return 0;
560 else if (ind >= 0 && ind <= directory_list_offset)
561 return (sign > 0 ? directory_list_offset - ind : ind);
562 else
563 return -1;
566 /* Used by the tilde expansion code. */
567 char *
568 get_dirstack_from_string (string)
569 char *string;
571 int ind, sign, index_flag;
572 intmax_t i;
574 sign = 1;
575 if (*string == '-' || *string == '+')
577 sign = (*string == '-') ? -1 : 1;
578 string++;
580 if (legal_number (string, &i) == 0)
581 return ((char *)NULL);
583 index_flag = 0;
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"));
589 else
590 return (pushd_directory_list[ind]);
593 #ifdef INCLUDE_UNUSED
594 char *
595 get_dirstack_element (ind, sign)
596 intmax_t ind;
597 int sign;
599 int i;
601 i = get_dirstack_index (ind, sign, (int *)NULL);
602 return (i < 0 || i > directory_list_offset) ? (char *)NULL
603 : pushd_directory_list[i];
605 #endif
607 void
608 set_dirstack_element (ind, sign, value)
609 intmax_t ind;
610 int sign;
611 char *value;
613 int i;
615 i = get_dirstack_index (ind, sign, (int *)NULL);
616 if (ind == 0 || i < 0 || i > directory_list_offset)
617 return;
618 free (pushd_directory_list[i]);
619 pushd_directory_list[i] = savestring (value);
622 WORD_LIST *
623 get_directory_stack (flags)
624 int flags;
626 register int i;
627 WORD_LIST *ret;
628 char *d, *t;
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 */
639 if (d == 0)
640 d = ".";
641 else
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. */
647 if (t != d)
649 free (d);
650 d = t;
652 else /* t == d, so d is what we want */
653 i = 1;
655 ret = make_word_list (make_word (d), ret);
656 if (i)
657 free (d);
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."),
666 N_(" "),
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."),
674 N_(" "),
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."),
677 N_(" "),
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."),
680 (char *)NULL
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."),
687 N_(" "),
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."),
691 N_(" "),
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."),
695 N_(" "),
696 N_("-n suppress the normal change of directory when adding directories"),
697 N_(" to the stack, so only the stack is manipulated."),
698 N_(" "),
699 N_("dir adds DIR to the directory stack at the top, making it the"),
700 N_(" new current working directory."),
701 N_(" "),
702 N_("You can see the directory stack with the `dirs' command."),
703 (char *)NULL
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."),
710 N_(" "),
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."),
714 N_(" "),
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."),
718 N_(" "),
719 N_("-n suppress the normal change of directory when removing directories"),
720 N_(" from the stack, so only the stack is manipulated."),
721 N_(" "),
722 N_("You can see the directory stack with the `dirs' command."),
723 (char *)NULL
726 struct builtin pushd_struct = {
727 "pushd",
728 pushd_builtin,
729 BUILTIN_ENABLED,
730 pushd_doc,
731 "pushd [+N | -N] [-n] [dir]",
735 struct builtin popd_struct = {
736 "popd",
737 popd_builtin,
738 BUILTIN_ENABLED,
739 popd_doc,
740 "popd [+N | -N] [-n]",
744 struct builtin dirs_struct = {
745 "dirs",
746 dirs_builtin,
747 BUILTIN_ENABLED,
748 dirs_doc,
749 "dirs [-clpv] [+N] [-N]",
752 #endif /* LOADABLE_BUILTIN */
754 #endif /* PUSHD_AND_POPD */