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