Imported from ../bash-2.02.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 1, 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, 675 Mass Ave, Cambridge, MA 02139, 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 #if !defined (errno)
121 extern int errno;
122 #endif /* !errno */
123
124 static char *m_badarg = "%s: bad argument";
125
126 /* The list of remembered directories. */
127 static char **pushd_directory_list = (char **)NULL;
128
129 /* Number of existing slots in this list. */
130 static int directory_list_size;
131
132 /* Offset to the end of the list. */
133 static int directory_list_offset;
134
135 static void pushd_error ();
136 static void clear_directory_stack ();
137 static int cd_to_string ();
138 static int change_to_temp ();
139 static int get_dirstack_index ();
140 static void add_dirstack_element ();
141
142 #define NOCD            0x01
143 #define ROTATE          0x02
144 #define LONGFORM        0x04
145 #define CLEARSTAK       0x08
146
147 int
148 pushd_builtin (list)
149      WORD_LIST *list;
150 {
151   char *temp, *current_directory, *top;
152   int j, flags;
153   long num;
154   char direction;
155
156   /* If there is no argument list then switch current and
157      top of list. */
158   if (list == 0)
159     {
160       if (directory_list_offset == 0)
161         {
162           builtin_error ("no other directory");
163           return (EXECUTION_FAILURE);
164         }
165
166       current_directory = get_working_directory ("pushd");
167       if (current_directory == 0)
168         return (EXECUTION_FAILURE);
169
170       j = directory_list_offset - 1;
171       temp = pushd_directory_list[j];
172       pushd_directory_list[j] = current_directory;
173       j = change_to_temp (temp);
174       free (temp);
175       return j;
176     }
177
178   for (flags = 0; list; list = list->next)
179     {
180       if (ISOPTION (list->word->word, 'n'))
181         {
182           flags |= NOCD;
183         }
184       else if (ISOPTION (list->word->word, '-'))
185         {
186           list = list->next;
187           break;
188         }
189       else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
190         /* Let `pushd -' work like it used to. */
191         break;
192       else if (((direction = list->word->word[0]) == '+') || direction == '-')
193         {
194           if (legal_number (list->word->word + 1, &num) == 0)
195             {
196               builtin_error (m_badarg, list->word->word);
197               builtin_usage ();
198               return (EXECUTION_FAILURE);
199             }
200
201           if (direction == '-')
202             num = directory_list_offset - num;
203
204           if (num > directory_list_offset || num < 0)
205             {
206               pushd_error (directory_list_offset, list->word->word);
207               return (EXECUTION_FAILURE);
208             }
209           flags |= ROTATE;
210         }
211       else if (*list->word->word == '-')
212         {
213           bad_option (list->word->word);
214           builtin_usage ();
215           return (EXECUTION_FAILURE);
216         }
217       else
218         break;
219     }
220
221   if (flags & ROTATE)
222     {
223       /* Rotate the stack num times.  Remember, the current
224          directory acts like it is part of the stack. */
225       temp = get_working_directory ("pushd");
226
227       if (num == 0)
228         {
229           j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
230           free (temp);
231           return j;
232         }
233
234       do
235         {
236           top = pushd_directory_list[directory_list_offset - 1];
237
238           for (j = directory_list_offset - 2; j > -1; j--)
239             pushd_directory_list[j + 1] = pushd_directory_list[j];
240
241           pushd_directory_list[j + 1] = temp;
242
243           temp = top;
244           num--;
245         }
246       while (num);
247
248       j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
249       free (temp);
250       return j;
251     }
252
253   if (list == 0)
254     return (EXECUTION_SUCCESS);
255
256   /* Change to the directory in list->word->word.  Save the current
257      directory on the top of the stack. */
258   current_directory = get_working_directory ("pushd");
259   if (current_directory == 0)
260     return (EXECUTION_FAILURE);
261
262   j = ((flags & NOCD) == 0) ? cd_builtin (list) : EXECUTION_SUCCESS;
263   if (j == EXECUTION_SUCCESS)
264     {
265       add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
266       dirs_builtin ((WORD_LIST *)NULL);
267       if (flags & NOCD)
268         free (current_directory);
269       return (EXECUTION_SUCCESS);
270     }
271   else
272     {
273       free (current_directory);
274       return (EXECUTION_FAILURE);
275     }
276 }
277
278 /* Pop the directory stack, and then change to the new top of the stack.
279    If LIST is non-null it should consist of a word +N or -N, which says
280    what element to delete from the stack.  The default is the top one. */
281 int
282 popd_builtin (list)
283      WORD_LIST *list;
284 {
285   register int i;
286   long which;
287   int flags;
288   char direction;
289   char *which_word;
290
291   which_word = (char *)NULL;
292   for (flags = 0, which = 0L, direction = '+'; list; list = list->next)
293     {
294       if (ISOPTION (list->word->word, 'n'))
295         {
296           flags |= NOCD;
297         }
298       else if (ISOPTION (list->word->word, '-'))
299         {
300           list = list->next;
301           break;
302         }
303       else if (((direction = list->word->word[0]) == '+') || direction == '-')
304         {
305           if (legal_number (list->word->word + 1, &which) == 0)
306             {
307               builtin_error (m_badarg, list->word->word);
308               builtin_usage ();
309               return (EXECUTION_FAILURE);
310             }
311           which_word = list->word->word;
312         }
313       else if (*list->word->word == '-')
314         {
315           bad_option (list->word->word);
316           builtin_usage ();
317           return (EXECUTION_FAILURE);
318         }
319       else
320         break;
321     }
322
323   if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
324     {
325       pushd_error (directory_list_offset, which_word ? which_word : "");
326       return (EXECUTION_FAILURE);
327     }
328
329   /* Handle case of no specification, or top of stack specification. */
330   if ((direction == '+' && which == 0) ||
331       (direction == '-' && which == directory_list_offset))
332     {
333       i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
334                                 : EXECUTION_SUCCESS;
335       if (i != EXECUTION_SUCCESS)
336         return (i);
337       free (pushd_directory_list[--directory_list_offset]);
338     }
339   else
340     {
341       /* Since an offset other than the top directory was specified,
342          remove that directory from the list and shift the remainder
343          of the list into place. */
344       i = (direction == '+') ? directory_list_offset - which : which;
345       free (pushd_directory_list[i]);
346       directory_list_offset--;
347
348       /* Shift the remainder of the list into place. */
349       for (; i < directory_list_offset; i++)
350         pushd_directory_list[i] = pushd_directory_list[i + 1];
351     }
352
353   dirs_builtin ((WORD_LIST *)NULL);
354   return (EXECUTION_SUCCESS);
355 }
356
357 /* Print the current list of directories on the directory stack. */
358 int
359 dirs_builtin (list)
360      WORD_LIST *list;
361 {
362   int flags, desired_index, index_flag, vflag;
363   long i;
364   char *temp, *w;
365
366   for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
367     {
368       if (ISOPTION (list->word->word, 'l'))
369         {
370           flags |= LONGFORM;
371         }
372       else if (ISOPTION (list->word->word, 'c'))
373         {
374           flags |= CLEARSTAK;
375         }
376       else if (ISOPTION (list->word->word, 'v'))
377         {
378           vflag |= 2;
379         }
380       else if (ISOPTION (list->word->word, 'p'))
381         {
382           vflag |= 1;
383         }
384       else if (ISOPTION (list->word->word, '-'))
385         {
386           list = list->next;
387           break;
388         }
389       else if (*list->word->word == '+' || *list->word->word == '-')
390         {
391           int sign;
392           if (legal_number (w = list->word->word + 1, &i) == 0)
393             {
394               builtin_error (m_badarg, list->word->word);
395               builtin_usage ();
396               return (EXECUTION_FAILURE);
397             }
398           sign = (*list->word->word == '+') ? 1 : -1;
399           desired_index = get_dirstack_index (i, sign, &index_flag);
400         }
401       else
402         {
403           bad_option (list->word->word);
404           builtin_usage ();
405           return (EXECUTION_FAILURE);
406         }
407     }
408
409   if (flags & CLEARSTAK)
410     {
411       clear_directory_stack ();
412       return (EXECUTION_SUCCESS);
413     }
414
415   if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
416     {
417       pushd_error (directory_list_offset, w);
418       return (EXECUTION_FAILURE);
419     }
420
421 #define DIRSTACK_FORMAT(temp) \
422   (flags & LONGFORM) ? temp : polite_directory_format (temp)
423
424   /* The first directory printed is always the current working directory. */
425   if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
426     {
427       temp = get_working_directory ("dirs");
428       if (temp == 0)
429         temp = savestring ("<no current directory>");
430       if (vflag & 2)
431         printf ("%2d  %s", 0, DIRSTACK_FORMAT (temp));
432       else
433         printf ("%s", DIRSTACK_FORMAT (temp));
434       free (temp);
435       if (index_flag)
436         {
437           putchar ('\n');
438           return EXECUTION_SUCCESS;
439         }
440     }
441
442 #define DIRSTACK_ENTRY(i) \
443   (flags & LONGFORM) ? pushd_directory_list[i] \
444                      : polite_directory_format (pushd_directory_list[i])
445
446   /* Now print the requested directory stack entries. */
447   if (index_flag)
448     {
449       if (vflag & 2)
450         printf ("%2d  %s", directory_list_offset - desired_index,
451                            DIRSTACK_ENTRY (desired_index));
452       else
453         printf ("%s", DIRSTACK_ENTRY (desired_index));
454     }
455   else
456     for (i = directory_list_offset - 1; i >= 0; i--)
457       if (vflag >= 2)
458         printf ("\n%2d  %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
459       else
460         printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
461
462   putchar ('\n');
463   fflush (stdout);
464   return (EXECUTION_SUCCESS);
465 }
466
467 static void
468 pushd_error (offset, arg)
469      int offset;
470      char *arg;
471 {
472   if (offset == 0)
473     builtin_error ("directory stack empty");
474   else if (arg)
475     builtin_error ("%s: bad directory stack index", arg);
476   else
477     builtin_error ("bad directory stack index");
478 }
479
480 static void
481 clear_directory_stack ()
482 {
483   register int i;
484
485   for (i = 0; i < directory_list_offset; i++)
486     free (pushd_directory_list[i]);
487   directory_list_offset = 0;
488 }
489
490 /* Switch to the directory in NAME.  This uses the cd_builtin to do the work,
491    so if the result is EXECUTION_FAILURE then an error message has already
492    been printed. */
493 static int
494 cd_to_string (name)
495      char *name;
496 {
497   WORD_LIST *tlist;
498   int result;
499
500   tlist = make_word_list (make_word (name), NULL);
501   result = cd_builtin (tlist);
502   dispose_words (tlist);
503   return (result);
504 }
505
506 static int
507 change_to_temp (temp)
508      char *temp;
509 {
510   int tt;
511
512   tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
513
514   if (tt == EXECUTION_SUCCESS)
515     dirs_builtin ((WORD_LIST *)NULL);
516
517   return (tt);
518 }
519
520 static void
521 add_dirstack_element (dir)
522      char *dir;
523 {
524   int j;
525
526   if (directory_list_offset == directory_list_size)
527     {
528       j = (directory_list_size += 10) * sizeof (char *);
529       pushd_directory_list = (char **)xrealloc (pushd_directory_list, j);
530     }
531   pushd_directory_list[directory_list_offset++] = dir;
532 }
533
534 static int
535 get_dirstack_index (ind, sign, indexp)
536      int ind, sign, *indexp;
537 {
538   if (indexp)
539     *indexp = sign > 0 ? 1 : 2;
540
541   /* dirs +0 prints the current working directory. */
542   /* dirs -0 prints last element in directory stack */
543   if (ind == 0 && sign > 0)
544     return 0;
545   else if (ind == directory_list_offset)
546     {
547       if (indexp)
548         *indexp = sign > 0 ? 2 : 1;
549       return 0;
550     }
551   else
552     return (sign > 0 ? directory_list_offset - ind : ind);
553 }
554
555 /* Used by the tilde expansion code. */
556 char *
557 get_dirstack_from_string (string)
558      char *string;
559 {
560   int ind, sign, index_flag;
561   long i;
562
563   sign = 1;
564   if (*string == '-' || *string == '+')
565     {
566       sign = (*string == '-') ? -1 : 1;
567       string++;
568     }
569   if (legal_number (string, &i) == 0)
570     return ((char *)NULL);
571
572   index_flag = 0;
573   ind = get_dirstack_index (i, sign, &index_flag);
574   if (index_flag && (ind < 0 || ind > directory_list_offset))
575     return ((char *)NULL);
576   if (index_flag == 0 || (index_flag == 1 && ind == 0))
577     return (get_string_value ("PWD"));
578   else
579     return (pushd_directory_list[ind]);
580 }
581
582 #ifdef INCLUDE_UNUSED
583 char *
584 get_dirstack_element (ind, sign)
585      int ind, sign;
586 {
587   int i;
588
589   i = get_dirstack_index (ind, sign, (int *)NULL);
590   return (i < 0 || i > directory_list_offset) ? (char *)NULL
591                                               : pushd_directory_list[i];
592 }
593 #endif
594
595 void
596 set_dirstack_element (ind, sign, value)
597      int ind, sign;
598      char *value;
599 {
600   int i;
601
602   i = get_dirstack_index (ind, sign, (int *)NULL);
603   if (ind == 0 || i < 0 || i > directory_list_offset)
604     return;
605   free (pushd_directory_list[i]);
606   pushd_directory_list[i] = savestring (value);
607 }
608
609 WORD_LIST *
610 get_directory_stack ()
611 {
612   register int i;
613   WORD_LIST *ret;
614   char *d, *t;
615
616   for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
617     {
618       d = polite_directory_format (pushd_directory_list[i]);
619       ret = make_word_list (make_word (d), ret);
620     }
621   /* Now the current directory. */
622   d = get_working_directory ("dirstack");
623   i = 0;        /* sentinel to decide whether or not to free d */
624   if (d == 0)
625     d = ".";
626   else
627     {
628       t = polite_directory_format (d);
629       /* polite_directory_format sometimes returns its argument unchanged.
630          If it does not, we can free d right away.  If it does, we need to
631          mark d to be deleted later. */
632       if (t != d)
633         {
634           free (d);
635           d = t;
636         }
637       else /* t == d, so d is what we want */
638         i = 1;
639     }
640   ret = make_word_list (make_word (d), ret);
641   if (i)
642     free (d);
643   return ret;   /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
644 }
645 #endif /* PUSHD_AND_POPD */