75cd627ffc0a6fce9e107cc4849769b346ad8129
[platform/upstream/bash.git] / examples / loadables / pushd.c
1 /* pushd.c, created from pushd.def. */
2 #include <config.h>
3
4 #include <stdio.h>
5 #include <sys/param.h>
6
7 #if defined (HAVE_UNISTD_H)
8 #  include <unistd.h>
9 #endif
10
11 #include "bashansi.h"
12
13 #include <errno.h>
14
15 #include <tilde/tilde.h>
16
17 #include "shell.h"
18 #include "builtins.h"
19 #include "maxpath.h"
20 #include "common.h"
21
22 #if !defined (errno)
23 extern int errno;
24 #endif /* !errno */
25
26 static char *m_badarg = "%s: bad argument";
27
28 /* The list of remembered directories. */
29 static char **pushd_directory_list = (char **)NULL;
30
31 /* Number of existing slots in this list. */
32 static int directory_list_size;
33
34 /* Offset to the end of the list. */
35 static int directory_list_offset;
36
37 static void pushd_error ();
38 static void clear_directory_stack ();
39 static int cd_to_string ();
40 static int change_to_temp ();
41 static int get_dirstack_index ();
42 static void add_dirstack_element ();
43
44 #define NOCD            0x01
45 #define ROTATE          0x02
46 #define LONGFORM        0x04
47 #define CLEARSTAK       0x08
48
49 int
50 pushd_builtin (list)
51      WORD_LIST *list;
52 {
53   char *temp, *current_directory, *top;
54   int j, flags;
55   long num;
56   char direction;
57
58   /* If there is no argument list then switch current and
59      top of list. */
60   if (list == 0)
61     {
62       if (directory_list_offset == 0)
63         {
64           builtin_error ("no other directory");
65           return (EXECUTION_FAILURE);
66         }
67
68       current_directory = get_working_directory ("pushd");
69       if (current_directory == 0)
70         return (EXECUTION_FAILURE);
71
72       j = directory_list_offset - 1;
73       temp = pushd_directory_list[j];
74       pushd_directory_list[j] = current_directory;
75       j = change_to_temp (temp);
76       free (temp);
77       return j;
78     }
79
80   for (flags = 0; list; list = list->next)
81     {
82       if (ISOPTION (list->word->word, 'n'))
83         {
84           flags |= NOCD;
85         }
86       else if (ISOPTION (list->word->word, '-'))
87         {
88           list = list->next;
89           break;
90         }
91       else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
92         /* Let `pushd -' work like it used to. */
93         break;
94       else if (((direction = list->word->word[0]) == '+') || direction == '-')
95         {
96           if (legal_number (list->word->word + 1, &num) == 0)
97             {
98               builtin_error (m_badarg, list->word->word);
99               builtin_usage ();
100               return (EXECUTION_FAILURE);
101             }
102
103           if (direction == '-')
104             num = directory_list_offset - num;
105
106           if (num > directory_list_offset || num < 0)
107             {
108               pushd_error (directory_list_offset, list->word->word);
109               return (EXECUTION_FAILURE);
110             }
111           flags |= ROTATE;
112         }
113       else if (*list->word->word == '-')
114         {
115           bad_option (list->word->word);
116           builtin_usage ();
117           return (EXECUTION_FAILURE);
118         }
119       else
120         break;
121     }
122
123   if (flags & ROTATE)
124     {
125       /* Rotate the stack num times.  Remember, the current
126          directory acts like it is part of the stack. */
127       temp = get_working_directory ("pushd");
128
129       if (num == 0)
130         {
131           j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
132           free (temp);
133           return j;
134         }
135
136       do
137         {
138           top = pushd_directory_list[directory_list_offset - 1];
139
140           for (j = directory_list_offset - 2; j > -1; j--)
141             pushd_directory_list[j + 1] = pushd_directory_list[j];
142
143           pushd_directory_list[j + 1] = temp;
144
145           temp = top;
146           num--;
147         }
148       while (num);
149
150       j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
151       free (temp);
152       return j;
153     }
154
155   if (list == 0)
156     return (EXECUTION_SUCCESS);
157
158   /* Change to the directory in list->word->word.  Save the current
159      directory on the top of the stack. */
160   current_directory = get_working_directory ("pushd");
161   if (current_directory == 0)
162     return (EXECUTION_FAILURE);
163
164   j = ((flags & NOCD) == 0) ? cd_builtin (list) : EXECUTION_SUCCESS;
165   if (j == EXECUTION_SUCCESS)
166     {
167       add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
168       dirs_builtin ((WORD_LIST *)NULL);
169       return (EXECUTION_SUCCESS);
170     }
171   else
172     {
173       free (current_directory);
174       return (EXECUTION_FAILURE);
175     }
176 }
177
178 /* Pop the directory stack, and then change to the new top of the stack.
179    If LIST is non-null it should consist of a word +N or -N, which says
180    what element to delete from the stack.  The default is the top one. */
181 int
182 popd_builtin (list)
183      WORD_LIST *list;
184 {
185   register int i;
186   long which;
187   int flags;
188   char direction;
189
190   for (flags = 0, which = 0L, direction = '+'; list; list = list->next)
191     {
192       if (ISOPTION (list->word->word, 'n'))
193         {
194           flags |= NOCD;
195         }
196       else if (ISOPTION (list->word->word, '-'))
197         {
198           list = list->next;
199           break;
200         }
201       else if (((direction = list->word->word[0]) == '+') || direction == '-')
202         {
203           if (legal_number (list->word->word + 1, &which) == 0)
204             {
205               builtin_error (m_badarg, list->word->word);
206               builtin_usage ();
207               return (EXECUTION_FAILURE);
208             }
209         }
210       else if (*list->word->word == '-')
211         {
212           bad_option (list->word->word);
213           builtin_usage ();
214           return (EXECUTION_FAILURE);
215         }
216       else
217         break;
218     }
219
220   if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
221     {
222       pushd_error (directory_list_offset, list ? list->word->word : "");
223       return (EXECUTION_FAILURE);
224     }
225
226   /* Handle case of no specification, or top of stack specification. */
227   if ((direction == '+' && which == 0) ||
228       (direction == '-' && which == directory_list_offset))
229     {
230       i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
231                                 : EXECUTION_SUCCESS;
232       if (i != EXECUTION_SUCCESS)
233         return (i);
234       free (pushd_directory_list[--directory_list_offset]);
235     }
236   else
237     {
238       /* Since an offset other than the top directory was specified,
239          remove that directory from the list and shift the remainder
240          of the list into place. */
241       i = (direction == '+') ? directory_list_offset - which : which;
242       free (pushd_directory_list[i]);
243       directory_list_offset--;
244
245       /* Shift the remainder of the list into place. */
246       for (; i < directory_list_offset; i++)
247         pushd_directory_list[i] = pushd_directory_list[i + 1];
248     }
249
250   dirs_builtin ((WORD_LIST *)NULL);
251   return (EXECUTION_SUCCESS);
252 }
253
254 /* Print the current list of directories on the directory stack. */
255 int
256 dirs_builtin (list)
257      WORD_LIST *list;
258 {
259   int flags, desired_index, index_flag, vflag;
260   long i;
261   char *temp, *w;
262
263   for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
264     {
265       if (ISOPTION (list->word->word, 'l'))
266         {
267           flags |= LONGFORM;
268         }
269       else if (ISOPTION (list->word->word, 'c'))
270         {
271           flags |= CLEARSTAK;
272         }
273       else if (ISOPTION (list->word->word, 'v'))
274         {
275           vflag |= 2;
276         }
277       else if (ISOPTION (list->word->word, 'p'))
278         {
279           vflag |= 1;
280         }
281       else if (ISOPTION (list->word->word, '-'))
282         {
283           list = list->next;
284           break;
285         }
286       else if (*list->word->word == '+' || *list->word->word == '-')
287         {
288           int sign;
289           if (legal_number (w = list->word->word + 1, &i) == 0)
290             {
291               builtin_error (m_badarg, list->word->word);
292               builtin_usage ();
293               return (EXECUTION_FAILURE);
294             }
295           sign = (*list->word->word == '+') ? 1 : -1;
296           desired_index = get_dirstack_index (i, sign, &index_flag);
297         }
298       else
299         {
300           bad_option (list->word->word);
301           builtin_usage ();
302           return (EXECUTION_FAILURE);
303         }
304     }
305
306   if (flags & CLEARSTAK)
307     {
308       clear_directory_stack ();
309       return (EXECUTION_SUCCESS);
310     }
311
312   if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
313     {
314       pushd_error (directory_list_offset, w);
315       return (EXECUTION_FAILURE);
316     }
317
318 #define DIRSTACK_FORMAT(temp) \
319   (flags & LONGFORM) ? temp : polite_directory_format (temp)
320
321   /* The first directory printed is always the current working directory. */
322   if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
323     {
324       temp = get_working_directory ("dirs");
325       if (temp == 0)
326         temp = savestring ("<no current directory>");
327       if (vflag & 2)
328         printf ("%2d  %s", 0, DIRSTACK_FORMAT (temp));
329       else
330         printf ("%s", DIRSTACK_FORMAT (temp));
331       free (temp);
332       if (index_flag)
333         {
334           putchar ('\n');
335           return EXECUTION_SUCCESS;
336         }
337     }
338
339 #define DIRSTACK_ENTRY(i) \
340   (flags & LONGFORM) ? pushd_directory_list[i] \
341                      : polite_directory_format (pushd_directory_list[i])
342
343   /* Now print the requested directory stack entries. */
344   if (index_flag)
345     {
346       if (vflag & 2)
347         printf ("%2d  %s", directory_list_offset - desired_index,
348                            DIRSTACK_ENTRY (desired_index));
349       else
350         printf ("%s", DIRSTACK_ENTRY (desired_index));
351     }
352   else
353     for (i = directory_list_offset - 1; i >= 0; i--)
354       if (vflag >= 2)
355         printf ("\n%2d  %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
356       else
357         printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
358
359   putchar ('\n');
360   fflush (stdout);
361   return (EXECUTION_SUCCESS);
362 }
363
364 static void
365 pushd_error (offset, arg)
366      int offset;
367      char *arg;
368 {
369   if (offset == 0)
370     builtin_error ("directory stack empty");
371   else if (arg)
372     builtin_error ("%s: bad directory stack index", arg);
373   else
374     builtin_error ("bad directory stack index");
375 }
376
377 static void
378 clear_directory_stack ()
379 {
380   register int i;
381
382   for (i = 0; i < directory_list_offset; i++)
383     free (pushd_directory_list[i]);
384   directory_list_offset = 0;
385 }
386
387 /* Switch to the directory in NAME.  This uses the cd_builtin to do the work,
388    so if the result is EXECUTION_FAILURE then an error message has already
389    been printed. */
390 static int
391 cd_to_string (name)
392      char *name;
393 {
394   WORD_LIST *tlist;
395   int result;
396
397   tlist = make_word_list (make_word (name), NULL);
398   result = cd_builtin (tlist);
399   dispose_words (tlist);
400   return (result);
401 }
402
403 static int
404 change_to_temp (temp)
405      char *temp;
406 {
407   int tt;
408
409   tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
410
411   if (tt == EXECUTION_SUCCESS)
412     dirs_builtin ((WORD_LIST *)NULL);
413
414   return (tt);
415 }
416
417 static void
418 add_dirstack_element (dir)
419      char *dir;
420 {
421   int j;
422
423   if (directory_list_offset == directory_list_size)
424     {
425       j = (directory_list_size += 10) * sizeof (char *);
426       pushd_directory_list = (char **)xrealloc (pushd_directory_list, j);
427     }
428   pushd_directory_list[directory_list_offset++] = dir;
429 }
430
431 static int
432 get_dirstack_index (ind, sign, indexp)
433      int ind, sign, *indexp;
434 {
435   if (indexp)
436     *indexp = sign > 0 ? 1 : 2;
437
438   /* dirs +0 prints the current working directory. */
439   /* dirs -0 prints last element in directory stack */
440   if (ind == 0 && sign > 0)
441     return 0;
442   else if (ind == directory_list_offset)
443     {
444       if (indexp)
445         *indexp = sign > 0 ? 2 : 1;
446       return 0;
447     }
448   else
449     return (sign > 0 ? directory_list_offset - ind : ind);
450 }
451
452 char *
453 get_dirstack_element (ind, sign)
454      int ind, sign;
455 {
456   int i;
457
458   i = get_dirstack_index (ind, sign, (int *)NULL);
459   return (i < 0 || i > directory_list_offset) ? (char *)NULL
460                                               : pushd_directory_list[i];
461 }
462
463 void
464 set_dirstack_element (ind, sign, value)
465      int ind, sign;
466      char *value;
467 {
468   int i;
469
470   i = get_dirstack_index (ind, sign, (int *)NULL);
471   if (ind == 0 || i < 0 || i > directory_list_offset)
472     return;
473   free (pushd_directory_list[i]);
474   pushd_directory_list[i] = savestring (value);
475 }
476
477 WORD_LIST *
478 get_directory_stack ()
479 {
480   register int i;
481   WORD_LIST *ret;
482   char *d, *t;
483
484   for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
485     {
486       d = polite_directory_format (pushd_directory_list[i]);
487       ret = make_word_list (make_word (d), ret);
488     }
489   /* Now the current directory. */
490   d = get_working_directory ("dirstack");
491   i = 0;        /* sentinel to decide whether or not to free d */
492   if (d == 0)
493     d = ".";
494   else
495     {
496       t = polite_directory_format (d);
497       /* polite_directory_format sometimes returns its argument unchanged.
498          If it does not, we can free d right away.  If it does, we need to
499          mark d to be deleted later. */
500       if (t != d)
501         {
502           free (d);
503           d = t;
504         }
505       else /* t == d, so d is what we want */
506         i = 1;
507     }
508   ret = make_word_list (make_word (d), ret);
509   if (i)
510     free (d);
511   return ret;   /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
512 }
513
514 static char *dirs_doc[] = {
515   "Display the list of currently remembered directories.  Directories",
516   "find their way onto the list with the `pushd' command; you can get",
517   "back up through the list with the `popd' command.",
518   "",
519   "The -l flag specifies that `dirs' should not print shorthand versions",
520   "of directories which are relative to your home directory.  This means",
521   "that `~/bin' might be displayed as `/homes/bfox/bin'.  The -v flag",
522   "causes `dirs' to print the directory stack with one entry per line,",
523   "prepending the directory name with its position in the stack.  The -p",
524   "flag does the same thing, but the stack position is not prepended.",
525   "The -c flag clears the directory stack by deleting all of the elements.",
526   "",
527   "+N   displays the Nth entry counting from the left of the list shown by",
528   "     dirs when invoked without options, starting with zero.",
529   "",
530   "-N   displays the Nth entry counting from the right of the list shown by",
531   "     dirs when invoked without options, starting with zero.",
532   (char *)NULL
533 };
534
535 static char *pushd_doc[] = {
536   "Adds a directory to the top of the directory stack, or rotates",
537   "the stack, making the new top of the stack the current working",
538   "directory.  With no arguments, exchanges the top two directories.",
539   "",
540   "+N   Rotates the stack so that the Nth directory (counting",
541   "     from the left of the list shown by `dirs') is at the top.",
542   "",
543   "-N   Rotates the stack so that the Nth directory (counting",
544   "     from the right) is at the top.",
545   "",
546   "-n   suppress the normal change of directory when adding directories",
547   "     to the stack, so only the stack is manipulated.",
548   "",
549   "dir  adds DIR to the directory stack at the top, making it the",
550   "     new current working directory.",
551   "",
552   "You can see the directory stack with the `dirs' command.",
553   (char *)NULL
554 };
555
556 static char *popd_doc[] = {
557   "Removes entries from the directory stack.  With no arguments,",
558   "removes the top directory from the stack, and cd's to the new",
559   "top directory.",
560   "",
561   "+N   removes the Nth entry counting from the left of the list",
562   "     shown by `dirs', starting with zero.  For example: `popd +0'",
563   "     removes the first directory, `popd +1' the second.",   
564   "",
565   "-N   removes the Nth entry counting from the right of the list",
566   "     shown by `dirs', starting with zero.  For example: `popd -0'",
567   "     removes the last directory, `popd -1' the next to last.",
568   "",
569   "-n   suppress the normal change of directory when removing directories",
570   "     from the stack, so only the stack is manipulated.",
571   "",
572   "You can see the directory stack with the `dirs' command.",
573   (char *)NULL
574 };
575
576 struct builtin pushd_struct = {
577         "pushd",
578         pushd_builtin,
579         BUILTIN_ENABLED,
580         pushd_doc,
581         "pushd [+N | -N] [-n] [dir]",
582         0
583 };
584
585 struct builtin popd_struct = {
586         "popd",
587         popd_builtin,
588         BUILTIN_ENABLED,
589         popd_doc,
590         "popd [+N | -N] [-n]",
591         0
592 };
593
594 struct builtin dirs_struct = {
595         "dirs",
596         dirs_builtin,
597         BUILTIN_ENABLED,
598         dirs_doc,
599         "dirs [-clpv] [+N] [-N]",
600         0
601 };