]> git.ipfire.org Git - thirdparty/bash.git/blob - builtins/pushd.def
f47b2941ac909291db123824c248758e4037d8ad
[thirdparty/bash.git] / builtins / pushd.def
1 This file is pushd.def, from which is created pushd.c. It implements the
2 builtins "pushd", "popd", and "dirs" in Bash.
3
4 Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
5
6 This file is part of GNU Bash, the Bourne Again SHell.
7
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.
12
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.
17
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.
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]
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.
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
43 dir adds DIR to the directory stack at the top, making it the
44 new current working directory.
45
46 You 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]
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.
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
68 You 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]
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.
78
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.
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
111 #include <errno.h>
112
113 #include <tilde/tilde.h>
114
115 #include "../shell.h"
116 #include "maxpath.h"
117 #include "common.h"
118 #include "builtext.h"
119
120 #ifdef LOADABLE_BUILTIN
121 # include "builtins.h"
122 #endif
123
124 #if !defined (errno)
125 extern int errno;
126 #endif /* !errno */
127
128 static char *m_badarg = "%s: bad argument";
129
130 /* The list of remembered directories. */
131 static char **pushd_directory_list = (char **)NULL;
132
133 /* Number of existing slots in this list. */
134 static int directory_list_size;
135
136 /* Offset to the end of the list. */
137 static int directory_list_offset;
138
139 static void pushd_error __P((int, char *));
140 static void clear_directory_stack __P((void));
141 static int cd_to_string __P((char *));
142 static int change_to_temp __P((char *));
143 static void add_dirstack_element __P((char *));
144 static int get_dirstack_index __P((long, int, int *));
145
146 #define NOCD 0x01
147 #define ROTATE 0x02
148 #define LONGFORM 0x04
149 #define CLEARSTAK 0x08
150
151 int
152 pushd_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, '-'))
189 {
190 list = list->next;
191 break;
192 }
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);
271 if (flags & NOCD)
272 free (current_directory);
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. */
285 int
286 popd_builtin (list)
287 WORD_LIST *list;
288 {
289 register int i;
290 long which;
291 int flags;
292 char direction;
293 char *which_word;
294
295 which_word = (char *)NULL;
296 for (flags = 0, which = 0, direction = '+'; list; list = list->next)
297 {
298 if (ISOPTION (list->word->word, 'n'))
299 {
300 flags |= NOCD;
301 }
302 else if (ISOPTION (list->word->word, '-'))
303 {
304 list = list->next;
305 break;
306 }
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 }
315 which_word = list->word->word;
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 {
329 pushd_error (directory_list_offset, which_word ? which_word : "");
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])
338 : EXECUTION_SUCCESS;
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. */
362 int
363 dirs_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, '-'))
389 {
390 list = list->next;
391 break;
392 }
393 else if (*list->word->word == '+' || *list->word->word == '-')
394 {
395 int sign;
396 if (legal_number (w = list->word->word + 1, &i) == 0)
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
471 static void
472 pushd_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
484 static void
485 clear_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. */
497 static int
498 cd_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
510 static int
511 change_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
524 static void
525 add_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
538 static int
539 get_dirstack_index (ind, sign, indexp)
540 long ind;
541 int sign, *indexp;
542 {
543 if (indexp)
544 *indexp = sign > 0 ? 1 : 2;
545
546 /* dirs +0 prints the current working directory. */
547 /* dirs -0 prints last element in directory stack */
548 if (ind == 0 && sign > 0)
549 return 0;
550 else if (ind == directory_list_offset)
551 {
552 if (indexp)
553 *indexp = sign > 0 ? 2 : 1;
554 return 0;
555 }
556 else if (ind >= 0 && ind <= directory_list_offset)
557 return (sign > 0 ? directory_list_offset - ind : ind);
558 else
559 return -1;
560 }
561
562 /* Used by the tilde expansion code. */
563 char *
564 get_dirstack_from_string (string)
565 char *string;
566 {
567 int ind, sign, index_flag;
568 long i;
569
570 sign = 1;
571 if (*string == '-' || *string == '+')
572 {
573 sign = (*string == '-') ? -1 : 1;
574 string++;
575 }
576 if (legal_number (string, &i) == 0)
577 return ((char *)NULL);
578
579 index_flag = 0;
580 ind = get_dirstack_index (i, sign, &index_flag);
581 if (index_flag && (ind < 0 || ind > directory_list_offset))
582 return ((char *)NULL);
583 if (index_flag == 0 || (index_flag == 1 && ind == 0))
584 return (get_string_value ("PWD"));
585 else
586 return (pushd_directory_list[ind]);
587 }
588
589 #ifdef INCLUDE_UNUSED
590 char *
591 get_dirstack_element (ind, sign)
592 long ind;
593 int sign;
594 {
595 int i;
596
597 i = get_dirstack_index (ind, sign, (int *)NULL);
598 return (i < 0 || i > directory_list_offset) ? (char *)NULL
599 : pushd_directory_list[i];
600 }
601 #endif
602
603 void
604 set_dirstack_element (ind, sign, value)
605 long ind;
606 int sign;
607 char *value;
608 {
609 int i;
610
611 i = get_dirstack_index (ind, sign, (int *)NULL);
612 if (ind == 0 || i < 0 || i > directory_list_offset)
613 return;
614 free (pushd_directory_list[i]);
615 pushd_directory_list[i] = savestring (value);
616 }
617
618 WORD_LIST *
619 get_directory_stack ()
620 {
621 register int i;
622 WORD_LIST *ret;
623 char *d, *t;
624
625 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
626 {
627 d = polite_directory_format (pushd_directory_list[i]);
628 ret = make_word_list (make_word (d), ret);
629 }
630 /* Now the current directory. */
631 d = get_working_directory ("dirstack");
632 i = 0; /* sentinel to decide whether or not to free d */
633 if (d == 0)
634 d = ".";
635 else
636 {
637 t = polite_directory_format (d);
638 /* polite_directory_format sometimes returns its argument unchanged.
639 If it does not, we can free d right away. If it does, we need to
640 mark d to be deleted later. */
641 if (t != d)
642 {
643 free (d);
644 d = t;
645 }
646 else /* t == d, so d is what we want */
647 i = 1;
648 }
649 ret = make_word_list (make_word (d), ret);
650 if (i)
651 free (d);
652 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
653 }
654
655 #ifdef LOADABLE_BUILTIN
656 static char *dirs_doc[] = {
657 "Display the list of currently remembered directories. Directories",
658 "find their way onto the list with the `pushd' command; you can get",
659 "back up through the list with the `popd' command.",
660 "",
661 "The -l flag specifies that `dirs' should not print shorthand versions",
662 "of directories which are relative to your home directory. This means",
663 "that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag",
664 "causes `dirs' to print the directory stack with one entry per line,",
665 "prepending the directory name with its position in the stack. The -p",
666 "flag does the same thing, but the stack position is not prepended.",
667 "The -c flag clears the directory stack by deleting all of the elements.",
668 "",
669 "+N displays the Nth entry counting from the left of the list shown by",
670 " dirs when invoked without options, starting with zero.",
671 "",
672 "-N displays the Nth entry counting from the right of the list shown by",
673 " dirs when invoked without options, starting with zero.",
674 (char *)NULL
675 };
676
677 static char *pushd_doc[] = {
678 "Adds a directory to the top of the directory stack, or rotates",
679 "the stack, making the new top of the stack the current working",
680 "directory. With no arguments, exchanges the top two directories.",
681 "",
682 "+N Rotates the stack so that the Nth directory (counting",
683 " from the left of the list shown by `dirs', starting with",
684 " zero) is at the top.",
685 "",
686 "-N Rotates the stack so that the Nth directory (counting",
687 " from the right of the list shown by `dirs', starting with",
688 " zero) is at the top.",
689 "",
690 "-n suppress the normal change of directory when adding directories",
691 " to the stack, so only the stack is manipulated.",
692 "",
693 "dir adds DIR to the directory stack at the top, making it the",
694 " new current working directory.",
695 "",
696 "You can see the directory stack with the `dirs' command.",
697 (char *)NULL
698 };
699
700 static char *popd_doc[] = {
701 "Removes entries from the directory stack. With no arguments,",
702 "removes the top directory from the stack, and cd's to the new",
703 "top directory.",
704 "",
705 "+N removes the Nth entry counting from the left of the list",
706 " shown by `dirs', starting with zero. For example: `popd +0'",
707 " removes the first directory, `popd +1' the second.",
708 "",
709 "-N removes the Nth entry counting from the right of the list",
710 " shown by `dirs', starting with zero. For example: `popd -0'",
711 " removes the last directory, `popd -1' the next to last.",
712 "",
713 "-n suppress the normal change of directory when removing directories",
714 " from the stack, so only the stack is manipulated.",
715 "",
716 "You can see the directory stack with the `dirs' command.",
717 (char *)NULL
718 };
719
720 struct builtin pushd_struct = {
721 "pushd",
722 pushd_builtin,
723 BUILTIN_ENABLED,
724 pushd_doc,
725 "pushd [+N | -N] [-n] [dir]",
726 0
727 };
728
729 struct builtin popd_struct = {
730 "popd",
731 popd_builtin,
732 BUILTIN_ENABLED,
733 popd_doc,
734 "popd [+N | -N] [-n]",
735 0
736 };
737
738 struct builtin dirs_struct = {
739 "dirs",
740 dirs_builtin,
741 BUILTIN_ENABLED,
742 dirs_doc,
743 "dirs [-clpv] [+N] [-N]",
744 0
745 };
746 #endif /* LOADABLE_BUILTIN */
747
748 #endif /* PUSHD_AND_POPD */