source: vendor/bash/3.1/builtins/pushd.def

Last change on this file was 3228, checked in by bird, 18 years ago

bash 3.1

File size: 19.9 KB
Line 
1This file is pushd.def, from which is created pushd.c. It implements the
2builtins "pushd", "popd", and "dirs" in Bash.
3
4Copyright (C) 1987-2004 Free Software Foundation, Inc.
5
6This file is part of GNU Bash, the Bourne Again SHell.
7
8Bash is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13Bash is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with Bash; see the file COPYING. If not, write to the Free Software
20Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
21
22$PRODUCES pushd.c
23
24$BUILTIN pushd
25$FUNCTION pushd_builtin
26$DEPENDS_ON PUSHD_AND_POPD
27$SHORT_DOC pushd [dir | +N | -N] [-n]
28Adds a directory to the top of the directory stack, or rotates
29the stack, making the new top of the stack the current working
30directory. With no arguments, exchanges the top two directories.
31
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.
35
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.
39
40-n suppress the normal change of directory when adding directories
41 to the stack, so only the stack is manipulated.
42
43dir adds DIR to the directory stack at the top, making it the
44 new current working directory.
45
46You can see the directory stack with the `dirs' command.
47$END
48
49$BUILTIN popd
50$FUNCTION popd_builtin
51$DEPENDS_ON PUSHD_AND_POPD
52$SHORT_DOC popd [+N | -N] [-n]
53Removes entries from the directory stack. With no arguments,
54removes the top directory from the stack, and cd's to the new
55top directory.
56
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.
60
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.
64
65-n suppress the normal change of directory when removing directories
66 from the stack, so only the stack is manipulated.
67
68You can see the directory stack with the `dirs' command.
69$END
70
71$BUILTIN dirs
72$FUNCTION dirs_builtin
73$DEPENDS_ON PUSHD_AND_POPD
74$SHORT_DOC dirs [-clpv] [+N] [-N]
75Display the list of currently remembered directories. Directories
76find their way onto the list with the `pushd' command; you can get
77back up through the list with the `popd' command.
78
79The -l flag specifies that `dirs' should not print shorthand versions
80of directories which are relative to your home directory. This means
81that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag
82causes `dirs' to print the directory stack with one entry per line,
83prepending the directory name with its position in the stack. The -p
84flag does the same thing, but the stack position is not prepended.
85The -c flag clears the directory stack by deleting all of the elements.
86
87+N displays the Nth entry counting from the left of the list shown by
88 dirs when invoked without options, starting with zero.
89
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
93
94#include <config.h>
95
96#if defined (PUSHD_AND_POPD)
97#include <stdio.h>
98#ifndef _MINIX
99# include <sys/param.h>
100#endif
101
102#if defined (HAVE_UNISTD_H)
103# ifdef _MINIX
104# include <sys/types.h>
105# endif
106# include <unistd.h>
107#endif
108
109#include "../bashansi.h"
110#include "../bashintl.h"
111
112#include <errno.h>
113
114#include <tilde/tilde.h>
115
116#include "../shell.h"
117#include "maxpath.h"
118#include "common.h"
119#include "builtext.h"
120
121#ifdef LOADABLE_BUILTIN
122# include "builtins.h"
123#endif
124
125#if !defined (errno)
126extern int errno;
127#endif /* !errno */
128
129/* The list of remembered directories. */
130static char **pushd_directory_list = (char **)NULL;
131
132/* Number of existing slots in this list. */
133static int directory_list_size;
134
135/* Offset to the end of the list. */
136static int directory_list_offset;
137
138static void pushd_error __P((int, char *));
139static void clear_directory_stack __P((void));
140static int cd_to_string __P((char *));
141static int change_to_temp __P((char *));
142static void add_dirstack_element __P((char *));
143static int get_dirstack_index __P((intmax_t, int, int *));
144
145#define NOCD 0x01
146#define ROTATE 0x02
147#define LONGFORM 0x04
148#define CLEARSTAK 0x08
149
150int
151pushd_builtin (list)
152 WORD_LIST *list;
153{
154 WORD_LIST *orig_list;
155 char *temp, *current_directory, *top;
156 int j, flags, skipopt;
157 intmax_t num;
158 char direction;
159
160 orig_list = list;
161 if (list && list->word && ISOPTION (list->word->word, '-'))
162 {
163 list = list->next;
164 skipopt = 1;
165 }
166 else
167 skipopt = 0;
168
169 /* If there is no argument list then switch current and
170 top of list. */
171 if (list == 0)
172 {
173 if (directory_list_offset == 0)
174 {
175 builtin_error (_("no other directory"));
176 return (EXECUTION_FAILURE);
177 }
178
179 current_directory = get_working_directory ("pushd");
180 if (current_directory == 0)
181 return (EXECUTION_FAILURE);
182
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;
189 }
190
191 for (flags = 0; skipopt == 0 && list; list = list->next)
192 {
193 if (ISOPTION (list->word->word, 'n'))
194 {
195 flags |= NOCD;
196 }
197 else if (ISOPTION (list->word->word, '-'))
198 {
199 list = list->next;
200 break;
201 }
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 == '-')
206 {
207 if (legal_number (list->word->word + 1, &num) == 0)
208 {
209 sh_invalidnum (list->word->word);
210 builtin_usage ();
211 return (EXECUTION_FAILURE);
212 }
213
214 if (direction == '-')
215 num = directory_list_offset - num;
216
217 if (num > directory_list_offset || num < 0)
218 {
219 pushd_error (directory_list_offset, list->word->word);
220 return (EXECUTION_FAILURE);
221 }
222 flags |= ROTATE;
223 }
224 else if (*list->word->word == '-')
225 {
226 sh_invalidopt (list->word->word);
227 builtin_usage ();
228 return (EXECUTION_FAILURE);
229 }
230 else
231 break;
232 }
233
234 if (flags & ROTATE)
235 {
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");
239
240 if (num == 0)
241 {
242 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
243 free (temp);
244 return j;
245 }
246
247 do
248 {
249 top = pushd_directory_list[directory_list_offset - 1];
250
251 for (j = directory_list_offset - 2; j > -1; j--)
252 pushd_directory_list[j + 1] = pushd_directory_list[j];
253
254 pushd_directory_list[j + 1] = temp;
255
256 temp = top;
257 num--;
258 }
259 while (num);
260
261 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
262 free (temp);
263 return j;
264 }
265
266 if (list == 0)
267 return (EXECUTION_SUCCESS);
268
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);
274
275 j = ((flags & NOCD) == 0) ? cd_builtin (skipopt ? orig_list : list) : EXECUTION_SUCCESS;
276 if (j == EXECUTION_SUCCESS)
277 {
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);
283 }
284 else
285 {
286 free (current_directory);
287 return (EXECUTION_FAILURE);
288 }
289}
290
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. */
294int
295popd_builtin (list)
296 WORD_LIST *list;
297{
298 register int i;
299 intmax_t which;
300 int flags;
301 char direction;
302 char *which_word;
303
304 which_word = (char *)NULL;
305 for (flags = 0, which = 0, direction = '+'; list; list = list->next)
306 {
307 if (ISOPTION (list->word->word, 'n'))
308 {
309 flags |= NOCD;
310 }
311 else if (ISOPTION (list->word->word, '-'))
312 {
313 list = list->next;
314 break;
315 }
316 else if (((direction = list->word->word[0]) == '+') || direction == '-')
317 {
318 if (legal_number (list->word->word + 1, &which) == 0)
319 {
320 sh_invalidnum (list->word->word);
321 builtin_usage ();
322 return (EXECUTION_FAILURE);
323 }
324 which_word = list->word->word;
325 }
326 else if (*list->word->word == '-')
327 {
328 sh_invalidopt (list->word->word);
329 builtin_usage ();
330 return (EXECUTION_FAILURE);
331 }
332 else
333 break;
334 }
335
336 if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
337 {
338 pushd_error (directory_list_offset, which_word ? which_word : "");
339 return (EXECUTION_FAILURE);
340 }
341
342 /* Handle case of no specification, or top of stack specification. */
343 if ((direction == '+' && which == 0) ||
344 (direction == '-' && which == directory_list_offset))
345 {
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]);
351 }
352 else
353 {
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--;
360
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];
364 }
365
366 dirs_builtin ((WORD_LIST *)NULL);
367 return (EXECUTION_SUCCESS);
368}
369
370/* Print the current list of directories on the directory stack. */
371int
372dirs_builtin (list)
373 WORD_LIST *list;
374{
375 int flags, desired_index, index_flag, vflag;
376 intmax_t i;
377 char *temp, *w;
378
379 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
380 {
381 if (ISOPTION (list->word->word, 'l'))
382 {
383 flags |= LONGFORM;
384 }
385 else if (ISOPTION (list->word->word, 'c'))
386 {
387 flags |= CLEARSTAK;
388 }
389 else if (ISOPTION (list->word->word, 'v'))
390 {
391 vflag |= 2;
392 }
393 else if (ISOPTION (list->word->word, 'p'))
394 {
395 vflag |= 1;
396 }
397 else if (ISOPTION (list->word->word, '-'))
398 {
399 list = list->next;
400 break;
401 }
402 else if (*list->word->word == '+' || *list->word->word == '-')
403 {
404 int sign;
405 if (legal_number (w = list->word->word + 1, &i) == 0)
406 {
407 sh_invalidnum (list->word->word);
408 builtin_usage ();
409 return (EXECUTION_FAILURE);
410 }
411 sign = (*list->word->word == '+') ? 1 : -1;
412 desired_index = get_dirstack_index (i, sign, &index_flag);
413 }
414 else
415 {
416 sh_invalidopt (list->word->word);
417 builtin_usage ();
418 return (EXECUTION_FAILURE);
419 }
420 }
421
422 if (flags & CLEARSTAK)
423 {
424 clear_directory_stack ();
425 return (EXECUTION_SUCCESS);
426 }
427
428 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
429 {
430 pushd_error (directory_list_offset, w);
431 return (EXECUTION_FAILURE);
432 }
433
434#define DIRSTACK_FORMAT(temp) \
435 (flags & LONGFORM) ? temp : polite_directory_format (temp)
436
437 /* The first directory printed is always the current working directory. */
438 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
439 {
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)
449 {
450 putchar ('\n');
451 return EXECUTION_SUCCESS;
452 }
453 }
454
455#define DIRSTACK_ENTRY(i) \
456 (flags & LONGFORM) ? pushd_directory_list[i] \
457 : polite_directory_format (pushd_directory_list[i])
458
459 /* Now print the requested directory stack entries. */
460 if (index_flag)
461 {
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));
467 }
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));
474
475 putchar ('\n');
476 fflush (stdout);
477 return (EXECUTION_SUCCESS);
478}
479
480static void
481pushd_error (offset, arg)
482 int offset;
483 char *arg;
484{
485 if (offset == 0)
486 builtin_error ("directory stack empty");
487 else
488 sh_erange (arg, "directory stack index");
489}
490
491static void
492clear_directory_stack ()
493{
494 register int i;
495
496 for (i = 0; i < directory_list_offset; i++)
497 free (pushd_directory_list[i]);
498 directory_list_offset = 0;
499}
500
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. */
504static int
505cd_to_string (name)
506 char *name;
507{
508 WORD_LIST *tlist;
509 WORD_LIST *dir;
510 int result;
511
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);
517}
518
519static int
520change_to_temp (temp)
521 char *temp;
522{
523 int tt;
524
525 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
526
527 if (tt == EXECUTION_SUCCESS)
528 dirs_builtin ((WORD_LIST *)NULL);
529
530 return (tt);
531}
532
533static void
534add_dirstack_element (dir)
535 char *dir;
536{
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;
540}
541
542static int
543get_dirstack_index (ind, sign, indexp)
544 intmax_t ind;
545 int sign, *indexp;
546{
547 if (indexp)
548 *indexp = sign > 0 ? 1 : 2;
549
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)
555 {
556 if (indexp)
557 *indexp = sign > 0 ? 2 : 1;
558 return 0;
559 }
560 else if (ind >= 0 && ind <= directory_list_offset)
561 return (sign > 0 ? directory_list_offset - ind : ind);
562 else
563 return -1;
564}
565
566/* Used by the tilde expansion code. */
567char *
568get_dirstack_from_string (string)
569 char *string;
570{
571 int ind, sign, index_flag;
572 intmax_t i;
573
574 sign = 1;
575 if (*string == '-' || *string == '+')
576 {
577 sign = (*string == '-') ? -1 : 1;
578 string++;
579 }
580 if (legal_number (string, &i) == 0)
581 return ((char *)NULL);
582
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]);
591}
592
593#ifdef INCLUDE_UNUSED
594char *
595get_dirstack_element (ind, sign)
596 intmax_t ind;
597 int sign;
598{
599 int i;
600
601 i = get_dirstack_index (ind, sign, (int *)NULL);
602 return (i < 0 || i > directory_list_offset) ? (char *)NULL
603 : pushd_directory_list[i];
604}
605#endif
606
607void
608set_dirstack_element (ind, sign, value)
609 intmax_t ind;
610 int sign;
611 char *value;
612{
613 int i;
614
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);
620}
621
622WORD_LIST *
623get_directory_stack ()
624{
625 register int i;
626 WORD_LIST *ret;
627 char *d, *t;
628
629 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
630 {
631 d = polite_directory_format (pushd_directory_list[i]);
632 ret = make_word_list (make_word (d), ret);
633 }
634 /* Now the current directory. */
635 d = get_working_directory ("dirstack");
636 i = 0; /* sentinel to decide whether or not to free d */
637 if (d == 0)
638 d = ".";
639 else
640 {
641 t = polite_directory_format (d);
642 /* polite_directory_format sometimes returns its argument unchanged.
643 If it does not, we can free d right away. If it does, we need to
644 mark d to be deleted later. */
645 if (t != d)
646 {
647 free (d);
648 d = t;
649 }
650 else /* t == d, so d is what we want */
651 i = 1;
652 }
653 ret = make_word_list (make_word (d), ret);
654 if (i)
655 free (d);
656 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
657}
658
659#ifdef LOADABLE_BUILTIN
660static char * const dirs_doc[] = {
661 N_("Display the list of currently remembered directories. Directories"),
662 N_("find their way onto the list with the `pushd' command; you can get"),
663 N_("back up through the list with the `popd' command."),
664 N_(" "),
665 N_("The -l flag specifies that `dirs' should not print shorthand versions"),
666 N_("of directories which are relative to your home directory. This means"),
667 N_("that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag"),
668 N_("causes `dirs' to print the directory stack with one entry per line,"),
669 N_("prepending the directory name with its position in the stack. The -p"),
670 N_("flag does the same thing, but the stack position is not prepended."),
671 N_("The -c flag clears the directory stack by deleting all of the elements."),
672 N_(" "),
673 N_("+N displays the Nth entry counting from the left of the list shown by"),
674 N_(" dirs when invoked without options, starting with zero."),
675 N_(" "),
676 N_("-N displays the Nth entry counting from the right of the list shown by"),
677 N_(" dirs when invoked without options, starting with zero."),
678 (char *)NULL
679};
680
681static char * const pushd_doc[] = {
682 N_("Adds a directory to the top of the directory stack, or rotates"),
683 N_("the stack, making the new top of the stack the current working"),
684 N_("directory. With no arguments, exchanges the top two directories."),
685 N_(" "),
686 N_("+N Rotates the stack so that the Nth directory (counting"),
687 N_(" from the left of the list shown by `dirs', starting with"),
688 N_(" zero) is at the top."),
689 N_(" "),
690 N_("-N Rotates the stack so that the Nth directory (counting"),
691 N_(" from the right of the list shown by `dirs', starting with"),
692 N_(" zero) is at the top."),
693 N_(" "),
694 N_("-n suppress the normal change of directory when adding directories"),
695 N_(" to the stack, so only the stack is manipulated."),
696 N_(" "),
697 N_("dir adds DIR to the directory stack at the top, making it the"),
698 N_(" new current working directory."),
699 N_(" "),
700 N_("You can see the directory stack with the `dirs' command."),
701 (char *)NULL
702};
703
704static char * const popd_doc[] = {
705 N_("Removes entries from the directory stack. With no arguments,"),
706 N_("removes the top directory from the stack, and cd's to the new"),
707 N_("top directory."),
708 N_(" "),
709 N_("+N removes the Nth entry counting from the left of the list"),
710 N_(" shown by `dirs', starting with zero. For example: `popd +0'"),
711 N_(" removes the first directory, `popd +1' the second."),
712 N_(" "),
713 N_("-N removes the Nth entry counting from the right of the list"),
714 N_(" shown by `dirs', starting with zero. For example: `popd -0'"),
715 N_(" removes the last directory, `popd -1' the next to last."),
716 N_(" "),
717 N_("-n suppress the normal change of directory when removing directories"),
718 N_(" from the stack, so only the stack is manipulated."),
719 N_(" "),
720 N_("You can see the directory stack with the `dirs' command."),
721 (char *)NULL
722};
723
724struct builtin pushd_struct = {
725 "pushd",
726 pushd_builtin,
727 BUILTIN_ENABLED,
728 pushd_doc,
729 "pushd [+N | -N] [-n] [dir]",
730 0
731};
732
733struct builtin popd_struct = {
734 "popd",
735 popd_builtin,
736 BUILTIN_ENABLED,
737 popd_doc,
738 "popd [+N | -N] [-n]",
739 0
740};
741
742struct builtin dirs_struct = {
743 "dirs",
744 dirs_builtin,
745 BUILTIN_ENABLED,
746 dirs_doc,
747 "dirs [-clpv] [+N] [-N]",
748 0
749};
750#endif /* LOADABLE_BUILTIN */
751
752#endif /* PUSHD_AND_POPD */
Note: See TracBrowser for help on using the repository browser.