]> git.ipfire.org Git - thirdparty/bash.git/blob - builtins/pushd.def
Imported from ../bash-2.05.tar.gz.
[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 ();
140 static void clear_directory_stack ();
141 static int cd_to_string ();
142 static int change_to_temp ();
143 static int get_dirstack_index ();
144 static void add_dirstack_element ();
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 = 0L, 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 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
559 /* Used by the tilde expansion code. */
560 char *
561 get_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
587 char *
588 get_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 }
597 #endif
598
599 void
600 set_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
613 WORD_LIST *
614 get_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 }
649
650 #ifdef LOADABLE_BUILTIN
651 static 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
672 static 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
695 static 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
715 struct builtin pushd_struct = {
716 "pushd",
717 pushd_builtin,
718 BUILTIN_ENABLED,
719 pushd_doc,
720 "pushd [+N | -N] [-n] [dir]",
721 0
722 };
723
724 struct builtin popd_struct = {
725 "popd",
726 popd_builtin,
727 BUILTIN_ENABLED,
728 popd_doc,
729 "popd [+N | -N] [-n]",
730 0
731 };
732
733 struct 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
743 #endif /* PUSHD_AND_POPD */