Imported from ../bash-2.04.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, 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 */