No specific user configuration
[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-2013 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 #if defined (HAVE_SYS_PARAM_H)
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 (EX_USAGE);
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 (EX_USAGE);
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 (EX_USAGE);
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 (EX_USAGE);
351         }
352       else if (*list->word->word)
353         {
354           builtin_error (_("%s: invalid argument"), list->word->word);
355           builtin_usage ();
356           return (EX_USAGE);
357         }
358       else
359         break;
360     }
361
362   if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
363     {
364       pushd_error (directory_list_offset, which_word ? which_word : "");
365       return (EXECUTION_FAILURE);
366     }
367
368   /* Handle case of no specification, or top of stack specification. */
369   if ((direction == '+' && which == 0) ||
370       (direction == '-' && which == directory_list_offset))
371     {
372       i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
373                                 : EXECUTION_SUCCESS;
374       if (i != EXECUTION_SUCCESS)
375         return (i);
376       free (pushd_directory_list[--directory_list_offset]);
377     }
378   else
379     {
380       /* Since an offset other than the top directory was specified,
381          remove that directory from the list and shift the remainder
382          of the list into place. */
383       i = (direction == '+') ? directory_list_offset - which : which;
384       free (pushd_directory_list[i]);
385       directory_list_offset--;
386
387       /* Shift the remainder of the list into place. */
388       for (; i < directory_list_offset; i++)
389         pushd_directory_list[i] = pushd_directory_list[i + 1];
390     }
391
392   dirs_builtin ((WORD_LIST *)NULL);
393   return (EXECUTION_SUCCESS);
394 }
395
396 /* Print the current list of directories on the directory stack. */
397 int
398 dirs_builtin (list)
399      WORD_LIST *list;
400 {
401   int flags, desired_index, index_flag, vflag;
402   intmax_t i;
403   char *temp, *w;
404
405   for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
406     {
407       if (ISOPTION (list->word->word, 'l'))
408         {
409           flags |= LONGFORM;
410         }
411       else if (ISOPTION (list->word->word, 'c'))
412         {
413           flags |= CLEARSTAK;
414         }
415       else if (ISOPTION (list->word->word, 'v'))
416         {
417           vflag |= 2;
418         }
419       else if (ISOPTION (list->word->word, 'p'))
420         {
421           vflag |= 1;
422         }
423       else if (ISOPTION (list->word->word, '-'))
424         {
425           list = list->next;
426           break;
427         }
428       else if (*list->word->word == '+' || *list->word->word == '-')
429         {
430           int sign;
431           if (legal_number (w = list->word->word + 1, &i) == 0)
432             {
433               sh_invalidnum (list->word->word);
434               builtin_usage ();
435               return (EX_USAGE);
436             }
437           sign = (*list->word->word == '+') ? 1 : -1;
438           desired_index = get_dirstack_index (i, sign, &index_flag);
439         }
440       else
441         {
442           sh_invalidopt (list->word->word);
443           builtin_usage ();
444           return (EX_USAGE);
445         }
446     }
447
448   if (flags & CLEARSTAK)
449     {
450       clear_directory_stack ();
451       return (EXECUTION_SUCCESS);
452     }
453
454   if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
455     {
456       pushd_error (directory_list_offset, w);
457       return (EXECUTION_FAILURE);
458     }
459
460 #define DIRSTACK_FORMAT(temp) \
461   (flags & LONGFORM) ? temp : polite_directory_format (temp)
462
463   /* The first directory printed is always the current working directory. */
464   if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
465     {
466       temp = get_working_directory ("dirs");
467       if (temp == 0)
468         temp = savestring (_("<no current directory>"));
469       if (vflag & 2)
470         printf ("%2d  %s", 0, DIRSTACK_FORMAT (temp));
471       else
472         printf ("%s", DIRSTACK_FORMAT (temp));
473       free (temp);
474       if (index_flag)
475         {
476           putchar ('\n');
477           return (sh_chkwrite (EXECUTION_SUCCESS));
478         }
479     }
480
481 #define DIRSTACK_ENTRY(i) \
482   (flags & LONGFORM) ? pushd_directory_list[i] \
483                      : polite_directory_format (pushd_directory_list[i])
484
485   /* Now print the requested directory stack entries. */
486   if (index_flag)
487     {
488       if (vflag & 2)
489         printf ("%2d  %s", directory_list_offset - desired_index,
490                            DIRSTACK_ENTRY (desired_index));
491       else
492         printf ("%s", DIRSTACK_ENTRY (desired_index));
493     }
494   else
495     for (i = directory_list_offset - 1; i >= 0; i--)
496       if (vflag >= 2)
497         printf ("\n%2d  %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
498       else
499         printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
500
501   putchar ('\n');
502
503   return (sh_chkwrite (EXECUTION_SUCCESS));
504 }
505
506 static void
507 pushd_error (offset, arg)
508      int offset;
509      char *arg;
510 {
511   if (offset == 0)
512     builtin_error (_("directory stack empty"));
513   else
514     sh_erange (arg, _("directory stack index"));
515 }
516
517 static void
518 clear_directory_stack ()
519 {
520   register int i;
521
522   for (i = 0; i < directory_list_offset; i++)
523     free (pushd_directory_list[i]);
524   directory_list_offset = 0;
525 }
526
527 /* Switch to the directory in NAME.  This uses the cd_builtin to do the work,
528    so if the result is EXECUTION_FAILURE then an error message has already
529    been printed. */
530 static int
531 cd_to_string (name)
532      char *name;
533 {
534   WORD_LIST *tlist;
535   WORD_LIST *dir;
536   int result;
537
538   dir = make_word_list (make_word (name), NULL);
539   tlist = make_word_list (make_word ("--"), dir);
540   result = cd_builtin (tlist);
541   dispose_words (tlist);
542   return (result);
543 }
544
545 static int
546 change_to_temp (temp)
547      char *temp;
548 {
549   int tt;
550
551   tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
552
553   if (tt == EXECUTION_SUCCESS)
554     dirs_builtin ((WORD_LIST *)NULL);
555
556   return (tt);
557 }
558
559 static void
560 add_dirstack_element (dir)
561      char *dir;
562 {
563   if (directory_list_offset == directory_list_size)
564     pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10);
565   pushd_directory_list[directory_list_offset++] = dir;
566 }
567
568 static int
569 get_dirstack_index (ind, sign, indexp)
570      intmax_t ind;
571      int sign, *indexp;
572 {
573   if (indexp)
574     *indexp = sign > 0 ? 1 : 2;
575
576   /* dirs +0 prints the current working directory. */
577   /* dirs -0 prints last element in directory stack */
578   if (ind == 0 && sign > 0)
579     return 0;
580   else if (ind == directory_list_offset)
581     {
582       if (indexp)
583         *indexp = sign > 0 ? 2 : 1;
584       return 0;
585     }
586   else if (ind >= 0 && ind <= directory_list_offset)
587     return (sign > 0 ? directory_list_offset - ind : ind);
588   else
589     return -1;
590 }
591
592 /* Used by the tilde expansion code. */
593 char *
594 get_dirstack_from_string (string)
595      char *string;
596 {
597   int ind, sign, index_flag;
598   intmax_t i;
599
600   sign = 1;
601   if (*string == '-' || *string == '+')
602     {
603       sign = (*string == '-') ? -1 : 1;
604       string++;
605     }
606   if (legal_number (string, &i) == 0)
607     return ((char *)NULL);
608
609   index_flag = 0;
610   ind = get_dirstack_index (i, sign, &index_flag);
611   if (index_flag && (ind < 0 || ind > directory_list_offset))
612     return ((char *)NULL);
613   if (index_flag == 0 || (index_flag == 1 && ind == 0))
614     return (get_string_value ("PWD"));
615   else
616     return (pushd_directory_list[ind]);
617 }
618
619 #ifdef INCLUDE_UNUSED
620 char *
621 get_dirstack_element (ind, sign)
622      intmax_t ind;
623      int sign;
624 {
625   int i;
626
627   i = get_dirstack_index (ind, sign, (int *)NULL);
628   return (i < 0 || i > directory_list_offset) ? (char *)NULL
629                                               : pushd_directory_list[i];
630 }
631 #endif
632
633 void
634 set_dirstack_element (ind, sign, value)
635      intmax_t ind;
636      int  sign;
637      char *value;
638 {
639   int i;
640
641   i = get_dirstack_index (ind, sign, (int *)NULL);
642   if (ind == 0 || i < 0 || i > directory_list_offset)
643     return;
644   free (pushd_directory_list[i]);
645   pushd_directory_list[i] = savestring (value);
646 }
647
648 WORD_LIST *
649 get_directory_stack (flags)
650      int flags;
651 {
652   register int i;
653   WORD_LIST *ret;
654   char *d, *t;
655
656   for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
657     {
658       d = (flags&1) ? polite_directory_format (pushd_directory_list[i])
659                     : pushd_directory_list[i];
660       ret = make_word_list (make_word (d), ret);
661     }
662   /* Now the current directory. */
663   d = get_working_directory ("dirstack");
664   i = 0;        /* sentinel to decide whether or not to free d */
665   if (d == 0)
666     d = ".";
667   else
668     {
669       t = polite_directory_format (d);
670       /* polite_directory_format sometimes returns its argument unchanged.
671          If it does not, we can free d right away.  If it does, we need to
672          mark d to be deleted later. */
673       if (t != d)
674         {
675           free (d);
676           d = t;
677         }
678       else /* t == d, so d is what we want */
679         i = 1;
680     }
681   ret = make_word_list (make_word (d), ret);
682   if (i)
683     free (d);
684   return ret;   /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
685 }
686
687 #ifdef LOADABLE_BUILTIN
688 char * const dirs_doc[] = {
689 N_("Display the list of currently remembered directories.  Directories\n\
690     find their way onto the list with the `pushd' command; you can get\n\
691     back up through the list with the `popd' command.\n\
692     \n\
693     Options:\n\
694       -c        clear the directory stack by deleting all of the elements\n\
695       -l        do not print tilde-prefixed versions of directories relative\n\
696         to your home directory\n\
697       -p        print the directory stack with one entry per line\n\
698       -v        print the directory stack with one entry per line prefixed\n\
699         with its position in the stack\n\
700     \n\
701     Arguments:\n\
702       +N        Displays the Nth entry counting from the left of the list shown by\n\
703         dirs when invoked without options, starting with zero.\n\
704     \n\
705       -N        Displays the Nth entry counting from the right of the list shown by\n\
706         dirs when invoked without options, starting with zero."),
707   (char *)NULL
708 };
709
710 char * const pushd_doc[] = {
711 N_("Adds a directory to the top of the directory stack, or rotates\n\
712     the stack, making the new top of the stack the current working\n\
713     directory.  With no arguments, exchanges the top two directories.\n\
714     \n\
715     Options:\n\
716       -n        Suppresses the normal change of directory when adding\n\
717         directories to the stack, so only the stack is manipulated.\n\
718     \n\
719     Arguments:\n\
720       +N        Rotates the stack so that the Nth directory (counting\n\
721         from the left of the list shown by `dirs', starting with\n\
722         zero) is at the top.\n\
723     \n\
724       -N        Rotates the stack so that the Nth directory (counting\n\
725         from the right of the list shown by `dirs', starting with\n\
726         zero) is at the top.\n\
727     \n\
728       dir       Adds DIR to the directory stack at the top, making it the\n\
729         new current working directory.\n\
730     \n\
731     The `dirs' builtin displays the directory stack."),
732   (char *)NULL
733 };
734
735 char * const popd_doc[] = {
736 N_("Removes entries from the directory stack.  With no arguments, removes\n\
737     the top directory from the stack, and changes to the new top directory.\n\
738     \n\
739     Options:\n\
740       -n        Suppresses the normal change of directory when removing\n\
741         directories from the stack, so only the stack is manipulated.\n\
742     \n\
743     Arguments:\n\
744       +N        Removes the Nth entry counting from the left of the list\n\
745         shown by `dirs', starting with zero.  For example: `popd +0'\n\
746         removes the first directory, `popd +1' the second.\n\
747     \n\
748       -N        Removes the Nth entry counting from the right of the list\n\
749         shown by `dirs', starting with zero.  For example: `popd -0'\n\
750         removes the last directory, `popd -1' the next to last.\n\
751     \n\
752     The `dirs' builtin displays the directory stack."),
753   (char *)NULL
754 };
755
756 struct builtin pushd_struct = {
757         "pushd",
758         pushd_builtin,
759         BUILTIN_ENABLED,
760         pushd_doc,
761         "pushd [+N | -N] [-n] [dir]",
762         0
763 };
764
765 struct builtin popd_struct = {
766         "popd",
767         popd_builtin,
768         BUILTIN_ENABLED,
769         popd_doc,
770         "popd [+N | -N] [-n]",
771         0
772 };
773
774 struct builtin dirs_struct = {
775         "dirs",
776         dirs_builtin,
777         BUILTIN_ENABLED,
778         dirs_doc,
779         "dirs [-clpv] [+N] [-N]",
780         0
781 };
782 #endif /* LOADABLE_BUILTIN */
783
784 #endif /* PUSHD_AND_POPD */