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