]> git.ipfire.org Git - thirdparty/bash.git/blame - builtins/pushd.def
Imported from ../bash-2.05.tar.gz.
[thirdparty/bash.git] / builtins / pushd.def
CommitLineData
ccc6cda3
JA
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, 1989, 1991 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
bb70624e 10Software Foundation; either version 2, or (at your option) any later
ccc6cda3
JA
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
bb70624e 20Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
ccc6cda3
JA
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
d166f048
JA
33 from the left of the list shown by `dirs', starting with
34 zero) is at the top.
ccc6cda3
JA
35
36-N Rotates the stack so that the Nth directory (counting
d166f048
JA
37 from the right of the list shown by `dirs', starting with
38 zero) is at the top.
ccc6cda3
JA
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>
cce855bc
JA
98#ifndef _MINIX
99# include <sys/param.h>
100#endif
ccc6cda3
JA
101
102#if defined (HAVE_UNISTD_H)
cce855bc
JA
103# ifdef _MINIX
104# include <sys/types.h>
105# endif
ccc6cda3
JA
106# include <unistd.h>
107#endif
108
109#include "../bashansi.h"
110
111#include <errno.h>
112
113#include <tilde/tilde.h>
114
115#include "../shell.h"
bb70624e 116#include "maxpath.h"
ccc6cda3
JA
117#include "common.h"
118#include "builtext.h"
119
b72432fd
JA
120#ifdef LOADABLE_BUILTIN
121# include "builtins.h"
122#endif
123
ccc6cda3
JA
124#if !defined (errno)
125extern int errno;
126#endif /* !errno */
127
128static char *m_badarg = "%s: bad argument";
129
130/* The list of remembered directories. */
131static char **pushd_directory_list = (char **)NULL;
132
133/* Number of existing slots in this list. */
134static int directory_list_size;
135
136/* Offset to the end of the list. */
137static int directory_list_offset;
138
139static void pushd_error ();
140static void clear_directory_stack ();
141static int cd_to_string ();
142static int change_to_temp ();
143static int get_dirstack_index ();
144static void add_dirstack_element ();
145
146#define NOCD 0x01
147#define ROTATE 0x02
148#define LONGFORM 0x04
149#define CLEARSTAK 0x08
150
151int
152pushd_builtin (list)
153 WORD_LIST *list;
154{
155 char *temp, *current_directory, *top;
156 int j, flags;
157 long num;
158 char direction;
159
160 /* If there is no argument list then switch current and
161 top of list. */
162 if (list == 0)
163 {
164 if (directory_list_offset == 0)
165 {
166 builtin_error ("no other directory");
167 return (EXECUTION_FAILURE);
168 }
169
170 current_directory = get_working_directory ("pushd");
171 if (current_directory == 0)
172 return (EXECUTION_FAILURE);
173
174 j = directory_list_offset - 1;
175 temp = pushd_directory_list[j];
176 pushd_directory_list[j] = current_directory;
177 j = change_to_temp (temp);
178 free (temp);
179 return j;
180 }
181
182 for (flags = 0; list; list = list->next)
183 {
184 if (ISOPTION (list->word->word, 'n'))
185 {
186 flags |= NOCD;
187 }
188 else if (ISOPTION (list->word->word, '-'))
28ef6c31
JA
189 {
190 list = list->next;
191 break;
192 }
ccc6cda3
JA
193 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
194 /* Let `pushd -' work like it used to. */
195 break;
196 else if (((direction = list->word->word[0]) == '+') || direction == '-')
197 {
198 if (legal_number (list->word->word + 1, &num) == 0)
199 {
200 builtin_error (m_badarg, list->word->word);
201 builtin_usage ();
202 return (EXECUTION_FAILURE);
203 }
204
205 if (direction == '-')
206 num = directory_list_offset - num;
207
208 if (num > directory_list_offset || num < 0)
209 {
210 pushd_error (directory_list_offset, list->word->word);
211 return (EXECUTION_FAILURE);
212 }
213 flags |= ROTATE;
214 }
215 else if (*list->word->word == '-')
216 {
217 bad_option (list->word->word);
218 builtin_usage ();
219 return (EXECUTION_FAILURE);
220 }
221 else
222 break;
223 }
224
225 if (flags & ROTATE)
226 {
227 /* Rotate the stack num times. Remember, the current
228 directory acts like it is part of the stack. */
229 temp = get_working_directory ("pushd");
230
231 if (num == 0)
232 {
233 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
234 free (temp);
235 return j;
236 }
237
238 do
239 {
240 top = pushd_directory_list[directory_list_offset - 1];
241
242 for (j = directory_list_offset - 2; j > -1; j--)
243 pushd_directory_list[j + 1] = pushd_directory_list[j];
244
245 pushd_directory_list[j + 1] = temp;
246
247 temp = top;
248 num--;
249 }
250 while (num);
251
252 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
253 free (temp);
254 return j;
255 }
256
257 if (list == 0)
258 return (EXECUTION_SUCCESS);
259
260 /* Change to the directory in list->word->word. Save the current
261 directory on the top of the stack. */
262 current_directory = get_working_directory ("pushd");
263 if (current_directory == 0)
264 return (EXECUTION_FAILURE);
265
266 j = ((flags & NOCD) == 0) ? cd_builtin (list) : EXECUTION_SUCCESS;
267 if (j == EXECUTION_SUCCESS)
268 {
269 add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
270 dirs_builtin ((WORD_LIST *)NULL);
d166f048
JA
271 if (flags & NOCD)
272 free (current_directory);
ccc6cda3
JA
273 return (EXECUTION_SUCCESS);
274 }
275 else
276 {
277 free (current_directory);
278 return (EXECUTION_FAILURE);
279 }
280}
281
282/* Pop the directory stack, and then change to the new top of the stack.
283 If LIST is non-null it should consist of a word +N or -N, which says
284 what element to delete from the stack. The default is the top one. */
285int
286popd_builtin (list)
287 WORD_LIST *list;
288{
289 register int i;
290 long which;
291 int flags;
292 char direction;
d166f048 293 char *which_word;
ccc6cda3 294
d166f048 295 which_word = (char *)NULL;
ccc6cda3
JA
296 for (flags = 0, which = 0L, direction = '+'; list; list = list->next)
297 {
298 if (ISOPTION (list->word->word, 'n'))
28ef6c31
JA
299 {
300 flags |= NOCD;
301 }
ccc6cda3 302 else if (ISOPTION (list->word->word, '-'))
28ef6c31
JA
303 {
304 list = list->next;
305 break;
306 }
ccc6cda3
JA
307 else if (((direction = list->word->word[0]) == '+') || direction == '-')
308 {
309 if (legal_number (list->word->word + 1, &which) == 0)
310 {
311 builtin_error (m_badarg, list->word->word);
312 builtin_usage ();
313 return (EXECUTION_FAILURE);
314 }
d166f048 315 which_word = list->word->word;
ccc6cda3
JA
316 }
317 else if (*list->word->word == '-')
318 {
319 bad_option (list->word->word);
320 builtin_usage ();
321 return (EXECUTION_FAILURE);
322 }
323 else
324 break;
325 }
326
327 if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
328 {
d166f048 329 pushd_error (directory_list_offset, which_word ? which_word : "");
ccc6cda3
JA
330 return (EXECUTION_FAILURE);
331 }
332
333 /* Handle case of no specification, or top of stack specification. */
334 if ((direction == '+' && which == 0) ||
335 (direction == '-' && which == directory_list_offset))
336 {
337 i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
28ef6c31 338 : EXECUTION_SUCCESS;
ccc6cda3
JA
339 if (i != EXECUTION_SUCCESS)
340 return (i);
341 free (pushd_directory_list[--directory_list_offset]);
342 }
343 else
344 {
345 /* Since an offset other than the top directory was specified,
346 remove that directory from the list and shift the remainder
347 of the list into place. */
348 i = (direction == '+') ? directory_list_offset - which : which;
349 free (pushd_directory_list[i]);
350 directory_list_offset--;
351
352 /* Shift the remainder of the list into place. */
353 for (; i < directory_list_offset; i++)
354 pushd_directory_list[i] = pushd_directory_list[i + 1];
355 }
356
357 dirs_builtin ((WORD_LIST *)NULL);
358 return (EXECUTION_SUCCESS);
359}
360
361/* Print the current list of directories on the directory stack. */
362int
363dirs_builtin (list)
364 WORD_LIST *list;
365{
366 int flags, desired_index, index_flag, vflag;
367 long i;
368 char *temp, *w;
369
370 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
371 {
372 if (ISOPTION (list->word->word, 'l'))
373 {
374 flags |= LONGFORM;
375 }
376 else if (ISOPTION (list->word->word, 'c'))
377 {
378 flags |= CLEARSTAK;
379 }
380 else if (ISOPTION (list->word->word, 'v'))
381 {
382 vflag |= 2;
383 }
384 else if (ISOPTION (list->word->word, 'p'))
385 {
386 vflag |= 1;
387 }
388 else if (ISOPTION (list->word->word, '-'))
28ef6c31
JA
389 {
390 list = list->next;
391 break;
392 }
ccc6cda3 393 else if (*list->word->word == '+' || *list->word->word == '-')
28ef6c31
JA
394 {
395 int sign;
396 if (legal_number (w = list->word->word + 1, &i) == 0)
ccc6cda3
JA
397 {
398 builtin_error (m_badarg, list->word->word);
399 builtin_usage ();
400 return (EXECUTION_FAILURE);
401 }
402 sign = (*list->word->word == '+') ? 1 : -1;
403 desired_index = get_dirstack_index (i, sign, &index_flag);
404 }
405 else
406 {
407 bad_option (list->word->word);
408 builtin_usage ();
409 return (EXECUTION_FAILURE);
410 }
411 }
412
413 if (flags & CLEARSTAK)
414 {
415 clear_directory_stack ();
416 return (EXECUTION_SUCCESS);
417 }
418
419 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
420 {
421 pushd_error (directory_list_offset, w);
422 return (EXECUTION_FAILURE);
423 }
424
425#define DIRSTACK_FORMAT(temp) \
426 (flags & LONGFORM) ? temp : polite_directory_format (temp)
427
428 /* The first directory printed is always the current working directory. */
429 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
430 {
431 temp = get_working_directory ("dirs");
432 if (temp == 0)
433 temp = savestring ("<no current directory>");
434 if (vflag & 2)
435 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
436 else
437 printf ("%s", DIRSTACK_FORMAT (temp));
438 free (temp);
439 if (index_flag)
440 {
441 putchar ('\n');
442 return EXECUTION_SUCCESS;
443 }
444 }
445
446#define DIRSTACK_ENTRY(i) \
447 (flags & LONGFORM) ? pushd_directory_list[i] \
448 : polite_directory_format (pushd_directory_list[i])
449
450 /* Now print the requested directory stack entries. */
451 if (index_flag)
452 {
453 if (vflag & 2)
454 printf ("%2d %s", directory_list_offset - desired_index,
455 DIRSTACK_ENTRY (desired_index));
456 else
457 printf ("%s", DIRSTACK_ENTRY (desired_index));
458 }
459 else
460 for (i = directory_list_offset - 1; i >= 0; i--)
461 if (vflag >= 2)
462 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
463 else
464 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
465
466 putchar ('\n');
467 fflush (stdout);
468 return (EXECUTION_SUCCESS);
469}
470
471static void
472pushd_error (offset, arg)
473 int offset;
474 char *arg;
475{
476 if (offset == 0)
477 builtin_error ("directory stack empty");
478 else if (arg)
479 builtin_error ("%s: bad directory stack index", arg);
480 else
481 builtin_error ("bad directory stack index");
482}
483
484static void
485clear_directory_stack ()
486{
487 register int i;
488
489 for (i = 0; i < directory_list_offset; i++)
490 free (pushd_directory_list[i]);
491 directory_list_offset = 0;
492}
493
494/* Switch to the directory in NAME. This uses the cd_builtin to do the work,
495 so if the result is EXECUTION_FAILURE then an error message has already
496 been printed. */
497static int
498cd_to_string (name)
499 char *name;
500{
501 WORD_LIST *tlist;
502 int result;
503
504 tlist = make_word_list (make_word (name), NULL);
505 result = cd_builtin (tlist);
506 dispose_words (tlist);
507 return (result);
508}
509
510static int
511change_to_temp (temp)
512 char *temp;
513{
514 int tt;
515
516 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
517
518 if (tt == EXECUTION_SUCCESS)
519 dirs_builtin ((WORD_LIST *)NULL);
520
521 return (tt);
522}
523
524static void
525add_dirstack_element (dir)
526 char *dir;
527{
528 int j;
529
530 if (directory_list_offset == directory_list_size)
531 {
532 j = (directory_list_size += 10) * sizeof (char *);
533 pushd_directory_list = (char **)xrealloc (pushd_directory_list, j);
534 }
535 pushd_directory_list[directory_list_offset++] = dir;
536}
537
538static int
539get_dirstack_index (ind, sign, indexp)
540 int ind, sign, *indexp;
541{
542 if (indexp)
543 *indexp = sign > 0 ? 1 : 2;
544
545 /* dirs +0 prints the current working directory. */
546 /* dirs -0 prints last element in directory stack */
547 if (ind == 0 && sign > 0)
548 return 0;
549 else if (ind == directory_list_offset)
550 {
551 if (indexp)
552 *indexp = sign > 0 ? 2 : 1;
553 return 0;
554 }
555 else
556 return (sign > 0 ? directory_list_offset - ind : ind);
557}
558
cce855bc
JA
559/* Used by the tilde expansion code. */
560char *
561get_dirstack_from_string (string)
562 char *string;
563{
564 int ind, sign, index_flag;
565 long i;
566
567 sign = 1;
568 if (*string == '-' || *string == '+')
569 {
570 sign = (*string == '-') ? -1 : 1;
571 string++;
572 }
573 if (legal_number (string, &i) == 0)
574 return ((char *)NULL);
575
576 index_flag = 0;
577 ind = get_dirstack_index (i, sign, &index_flag);
578 if (index_flag && (ind < 0 || ind > directory_list_offset))
579 return ((char *)NULL);
580 if (index_flag == 0 || (index_flag == 1 && ind == 0))
581 return (get_string_value ("PWD"));
582 else
583 return (pushd_directory_list[ind]);
584}
585
586#ifdef INCLUDE_UNUSED
ccc6cda3
JA
587char *
588get_dirstack_element (ind, sign)
589 int ind, sign;
590{
591 int i;
592
593 i = get_dirstack_index (ind, sign, (int *)NULL);
594 return (i < 0 || i > directory_list_offset) ? (char *)NULL
595 : pushd_directory_list[i];
596}
cce855bc 597#endif
ccc6cda3
JA
598
599void
600set_dirstack_element (ind, sign, value)
601 int ind, sign;
602 char *value;
603{
604 int i;
605
606 i = get_dirstack_index (ind, sign, (int *)NULL);
607 if (ind == 0 || i < 0 || i > directory_list_offset)
608 return;
609 free (pushd_directory_list[i]);
610 pushd_directory_list[i] = savestring (value);
611}
612
613WORD_LIST *
614get_directory_stack ()
615{
616 register int i;
617 WORD_LIST *ret;
618 char *d, *t;
619
620 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
621 {
622 d = polite_directory_format (pushd_directory_list[i]);
623 ret = make_word_list (make_word (d), ret);
624 }
625 /* Now the current directory. */
626 d = get_working_directory ("dirstack");
627 i = 0; /* sentinel to decide whether or not to free d */
628 if (d == 0)
629 d = ".";
630 else
631 {
632 t = polite_directory_format (d);
633 /* polite_directory_format sometimes returns its argument unchanged.
634 If it does not, we can free d right away. If it does, we need to
635 mark d to be deleted later. */
636 if (t != d)
637 {
638 free (d);
639 d = t;
640 }
641 else /* t == d, so d is what we want */
642 i = 1;
643 }
644 ret = make_word_list (make_word (d), ret);
645 if (i)
646 free (d);
647 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
648}
b72432fd
JA
649
650#ifdef LOADABLE_BUILTIN
651static char *dirs_doc[] = {
652 "Display the list of currently remembered directories. Directories",
653 "find their way onto the list with the `pushd' command; you can get",
654 "back up through the list with the `popd' command.",
655 "",
656 "The -l flag specifies that `dirs' should not print shorthand versions",
657 "of directories which are relative to your home directory. This means",
658 "that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag",
659 "causes `dirs' to print the directory stack with one entry per line,",
660 "prepending the directory name with its position in the stack. The -p",
661 "flag does the same thing, but the stack position is not prepended.",
662 "The -c flag clears the directory stack by deleting all of the elements.",
663 "",
664 "+N displays the Nth entry counting from the left of the list shown by",
665 " dirs when invoked without options, starting with zero.",
666 "",
667 "-N displays the Nth entry counting from the right of the list shown by",
668 " dirs when invoked without options, starting with zero.",
669 (char *)NULL
670};
671
672static char *pushd_doc[] = {
673 "Adds a directory to the top of the directory stack, or rotates",
674 "the stack, making the new top of the stack the current working",
675 "directory. With no arguments, exchanges the top two directories.",
676 "",
677 "+N Rotates the stack so that the Nth directory (counting",
678 " from the left of the list shown by `dirs', starting with",
679 " zero) is at the top.",
680 "",
681 "-N Rotates the stack so that the Nth directory (counting",
682 " from the right of the list shown by `dirs', starting with",
683 " zero) is at the top.",
684 "",
685 "-n suppress the normal change of directory when adding directories",
686 " to the stack, so only the stack is manipulated.",
687 "",
688 "dir adds DIR to the directory stack at the top, making it the",
689 " new current working directory.",
690 "",
691 "You can see the directory stack with the `dirs' command.",
692 (char *)NULL
693};
694
695static char *popd_doc[] = {
696 "Removes entries from the directory stack. With no arguments,",
697 "removes the top directory from the stack, and cd's to the new",
698 "top directory.",
699 "",
700 "+N removes the Nth entry counting from the left of the list",
701 " shown by `dirs', starting with zero. For example: `popd +0'",
702 " removes the first directory, `popd +1' the second.",
703 "",
704 "-N removes the Nth entry counting from the right of the list",
705 " shown by `dirs', starting with zero. For example: `popd -0'",
706 " removes the last directory, `popd -1' the next to last.",
707 "",
708 "-n suppress the normal change of directory when removing directories",
709 " from the stack, so only the stack is manipulated.",
710 "",
711 "You can see the directory stack with the `dirs' command.",
712 (char *)NULL
713};
714
715struct builtin pushd_struct = {
716 "pushd",
717 pushd_builtin,
718 BUILTIN_ENABLED,
719 pushd_doc,
720 "pushd [+N | -N] [-n] [dir]",
721 0
722};
723
724struct builtin popd_struct = {
725 "popd",
726 popd_builtin,
727 BUILTIN_ENABLED,
728 popd_doc,
729 "popd [+N | -N] [-n]",
730 0
731};
732
733struct builtin dirs_struct = {
734 "dirs",
735 dirs_builtin,
736 BUILTIN_ENABLED,
737 dirs_doc,
738 "dirs [-clpv] [+N] [-N]",
739 0
740};
741#endif /* LOADABLE_BUILTIN */
742
ccc6cda3 743#endif /* PUSHD_AND_POPD */