2ecbcbbcb1cfd934aad4fc37907bdb0936d0e9f2
[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       if (flags & NOCD)
170         free (current_directory);
171       return (EXECUTION_SUCCESS);
172     }
173   else
174     {
175       free (current_directory);
176       return (EXECUTION_FAILURE);
177     }
178 }
179
180 /* Pop the directory stack, and then change to the new top of the stack.
181    If LIST is non-null it should consist of a word +N or -N, which says
182    what element to delete from the stack.  The default is the top one. */
183 int
184 popd_builtin (list)
185      WORD_LIST *list;
186 {
187   register int i;
188   long which;
189   int flags;
190   char direction;
191   char *which_word;
192
193   which_word = (char *)NULL;
194   for (flags = 0, which = 0L, direction = '+'; list; list = list->next)
195     {
196       if (ISOPTION (list->word->word, 'n'))
197         {
198           flags |= NOCD;
199         }
200       else if (ISOPTION (list->word->word, '-'))
201         {
202           list = list->next;
203           break;
204         }
205       else if (((direction = list->word->word[0]) == '+') || direction == '-')
206         {
207           if (legal_number (list->word->word + 1, &which) == 0)
208             {
209               builtin_error (m_badarg, list->word->word);
210               builtin_usage ();
211               return (EXECUTION_FAILURE);
212             }
213           which_word = list->word->word;
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 (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
226     {
227       pushd_error (directory_list_offset, which_word ? which_word : "");
228       return (EXECUTION_FAILURE);
229     }
230
231   /* Handle case of no specification, or top of stack specification. */
232   if ((direction == '+' && which == 0) ||
233       (direction == '-' && which == directory_list_offset))
234     {
235       i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
236                                 : EXECUTION_SUCCESS;
237       if (i != EXECUTION_SUCCESS)
238         return (i);
239       free (pushd_directory_list[--directory_list_offset]);
240     }
241   else
242     {
243       /* Since an offset other than the top directory was specified,
244          remove that directory from the list and shift the remainder
245          of the list into place. */
246       i = (direction == '+') ? directory_list_offset - which : which;
247       free (pushd_directory_list[i]);
248       directory_list_offset--;
249
250       /* Shift the remainder of the list into place. */
251       for (; i < directory_list_offset; i++)
252         pushd_directory_list[i] = pushd_directory_list[i + 1];
253     }
254
255   dirs_builtin ((WORD_LIST *)NULL);
256   return (EXECUTION_SUCCESS);
257 }
258
259 /* Print the current list of directories on the directory stack. */
260 int
261 dirs_builtin (list)
262      WORD_LIST *list;
263 {
264   int flags, desired_index, index_flag, vflag;
265   long i;
266   char *temp, *w;
267
268   for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
269     {
270       if (ISOPTION (list->word->word, 'l'))
271         {
272           flags |= LONGFORM;
273         }
274       else if (ISOPTION (list->word->word, 'c'))
275         {
276           flags |= CLEARSTAK;
277         }
278       else if (ISOPTION (list->word->word, 'v'))
279         {
280           vflag |= 2;
281         }
282       else if (ISOPTION (list->word->word, 'p'))
283         {
284           vflag |= 1;
285         }
286       else if (ISOPTION (list->word->word, '-'))
287         {
288           list = list->next;
289           break;
290         }
291       else if (*list->word->word == '+' || *list->word->word == '-')
292         {
293           int sign;
294           if (legal_number (w = list->word->word + 1, &i) == 0)
295             {
296               builtin_error (m_badarg, list->word->word);
297               builtin_usage ();
298               return (EXECUTION_FAILURE);
299             }
300           sign = (*list->word->word == '+') ? 1 : -1;
301           desired_index = get_dirstack_index (i, sign, &index_flag);
302         }
303       else
304         {
305           bad_option (list->word->word);
306           builtin_usage ();
307           return (EXECUTION_FAILURE);
308         }
309     }
310
311   if (flags & CLEARSTAK)
312     {
313       clear_directory_stack ();
314       return (EXECUTION_SUCCESS);
315     }
316
317   if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
318     {
319       pushd_error (directory_list_offset, w);
320       return (EXECUTION_FAILURE);
321     }
322
323 #define DIRSTACK_FORMAT(temp) \
324   (flags & LONGFORM) ? temp : polite_directory_format (temp)
325
326   /* The first directory printed is always the current working directory. */
327   if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
328     {
329       temp = get_working_directory ("dirs");
330       if (temp == 0)
331         temp = savestring ("<no current directory>");
332       if (vflag & 2)
333         printf ("%2d  %s", 0, DIRSTACK_FORMAT (temp));
334       else
335         printf ("%s", DIRSTACK_FORMAT (temp));
336       free (temp);
337       if (index_flag)
338         {
339           putchar ('\n');
340           return EXECUTION_SUCCESS;
341         }
342     }
343
344 #define DIRSTACK_ENTRY(i) \
345   (flags & LONGFORM) ? pushd_directory_list[i] \
346                      : polite_directory_format (pushd_directory_list[i])
347
348   /* Now print the requested directory stack entries. */
349   if (index_flag)
350     {
351       if (vflag & 2)
352         printf ("%2d  %s", directory_list_offset - desired_index,
353                            DIRSTACK_ENTRY (desired_index));
354       else
355         printf ("%s", DIRSTACK_ENTRY (desired_index));
356     }
357   else
358     for (i = directory_list_offset - 1; i >= 0; i--)
359       if (vflag >= 2)
360         printf ("\n%2d  %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
361       else
362         printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
363
364   putchar ('\n');
365   fflush (stdout);
366   return (EXECUTION_SUCCESS);
367 }
368
369 static void
370 pushd_error (offset, arg)
371      int offset;
372      char *arg;
373 {
374   if (offset == 0)
375     builtin_error ("directory stack empty");
376   else if (arg)
377     builtin_error ("%s: bad directory stack index", arg);
378   else
379     builtin_error ("bad directory stack index");
380 }
381
382 static void
383 clear_directory_stack ()
384 {
385   register int i;
386
387   for (i = 0; i < directory_list_offset; i++)
388     free (pushd_directory_list[i]);
389   directory_list_offset = 0;
390 }
391
392 /* Switch to the directory in NAME.  This uses the cd_builtin to do the work,
393    so if the result is EXECUTION_FAILURE then an error message has already
394    been printed. */
395 static int
396 cd_to_string (name)
397      char *name;
398 {
399   WORD_LIST *tlist;
400   int result;
401
402   tlist = make_word_list (make_word (name), NULL);
403   result = cd_builtin (tlist);
404   dispose_words (tlist);
405   return (result);
406 }
407
408 static int
409 change_to_temp (temp)
410      char *temp;
411 {
412   int tt;
413
414   tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
415
416   if (tt == EXECUTION_SUCCESS)
417     dirs_builtin ((WORD_LIST *)NULL);
418
419   return (tt);
420 }
421
422 static void
423 add_dirstack_element (dir)
424      char *dir;
425 {
426   int j;
427
428   if (directory_list_offset == directory_list_size)
429     {
430       j = (directory_list_size += 10) * sizeof (char *);
431       pushd_directory_list = (char **)xrealloc (pushd_directory_list, j);
432     }
433   pushd_directory_list[directory_list_offset++] = dir;
434 }
435
436 static int
437 get_dirstack_index (ind, sign, indexp)
438      int ind, sign, *indexp;
439 {
440   if (indexp)
441     *indexp = sign > 0 ? 1 : 2;
442
443   /* dirs +0 prints the current working directory. */
444   /* dirs -0 prints last element in directory stack */
445   if (ind == 0 && sign > 0)
446     return 0;
447   else if (ind == directory_list_offset)
448     {
449       if (indexp)
450         *indexp = sign > 0 ? 2 : 1;
451       return 0;
452     }
453   else
454     return (sign > 0 ? directory_list_offset - ind : ind);
455 }
456
457 char *
458 get_dirstack_element (ind, sign)
459      int ind, sign;
460 {
461   int i;
462
463   i = get_dirstack_index (ind, sign, (int *)NULL);
464   return (i < 0 || i > directory_list_offset) ? (char *)NULL
465                                               : pushd_directory_list[i];
466 }
467
468 void
469 set_dirstack_element (ind, sign, value)
470      int ind, sign;
471      char *value;
472 {
473   int i;
474
475   i = get_dirstack_index (ind, sign, (int *)NULL);
476   if (ind == 0 || i < 0 || i > directory_list_offset)
477     return;
478   free (pushd_directory_list[i]);
479   pushd_directory_list[i] = savestring (value);
480 }
481
482 WORD_LIST *
483 get_directory_stack ()
484 {
485   register int i;
486   WORD_LIST *ret;
487   char *d, *t;
488
489   for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
490     {
491       d = polite_directory_format (pushd_directory_list[i]);
492       ret = make_word_list (make_word (d), ret);
493     }
494   /* Now the current directory. */
495   d = get_working_directory ("dirstack");
496   i = 0;        /* sentinel to decide whether or not to free d */
497   if (d == 0)
498     d = ".";
499   else
500     {
501       t = polite_directory_format (d);
502       /* polite_directory_format sometimes returns its argument unchanged.
503          If it does not, we can free d right away.  If it does, we need to
504          mark d to be deleted later. */
505       if (t != d)
506         {
507           free (d);
508           d = t;
509         }
510       else /* t == d, so d is what we want */
511         i = 1;
512     }
513   ret = make_word_list (make_word (d), ret);
514   if (i)
515     free (d);
516   return ret;   /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
517 }
518
519 static char *dirs_doc[] = {
520   "Display the list of currently remembered directories.  Directories",
521   "find their way onto the list with the `pushd' command; you can get",
522   "back up through the list with the `popd' command.",
523   "",
524   "The -l flag specifies that `dirs' should not print shorthand versions",
525   "of directories which are relative to your home directory.  This means",
526   "that `~/bin' might be displayed as `/homes/bfox/bin'.  The -v flag",
527   "causes `dirs' to print the directory stack with one entry per line,",
528   "prepending the directory name with its position in the stack.  The -p",
529   "flag does the same thing, but the stack position is not prepended.",
530   "The -c flag clears the directory stack by deleting all of the elements.",
531   "",
532   "+N   displays the Nth entry counting from the left of the list shown by",
533   "     dirs when invoked without options, starting with zero.",
534   "",
535   "-N   displays the Nth entry counting from the right of the list shown by",
536   "     dirs when invoked without options, starting with zero.",
537   (char *)NULL
538 };
539
540 static char *pushd_doc[] = {
541   "Adds a directory to the top of the directory stack, or rotates",
542   "the stack, making the new top of the stack the current working",
543   "directory.  With no arguments, exchanges the top two directories.",
544   "",
545   "+N   Rotates the stack so that the Nth directory (counting",
546   "     from the left of the list shown by `dirs', starting with"
547   "     zero) is at the top.",
548   "",
549   "-N   Rotates the stack so that the Nth directory (counting",
550   "     from the right of the list shown by `dirs', starting with" 
551   "     zero) is at the top.",
552   "",
553   "-n   suppress the normal change of directory when adding directories",
554   "     to the stack, so only the stack is manipulated.",
555   "",
556   "dir  adds DIR to the directory stack at the top, making it the",
557   "     new current working directory.",
558   "",
559   "You can see the directory stack with the `dirs' command.",
560   (char *)NULL
561 };
562
563 static char *popd_doc[] = {
564   "Removes entries from the directory stack.  With no arguments,",
565   "removes the top directory from the stack, and cd's to the new",
566   "top directory.",
567   "",
568   "+N   removes the Nth entry counting from the left of the list",
569   "     shown by `dirs', starting with zero.  For example: `popd +0'",
570   "     removes the first directory, `popd +1' the second.",   
571   "",
572   "-N   removes the Nth entry counting from the right of the list",
573   "     shown by `dirs', starting with zero.  For example: `popd -0'",
574   "     removes the last directory, `popd -1' the next to last.",
575   "",
576   "-n   suppress the normal change of directory when removing directories",
577   "     from the stack, so only the stack is manipulated.",
578   "",
579   "You can see the directory stack with the `dirs' command.",
580   (char *)NULL
581 };
582
583 struct builtin pushd_struct = {
584         "pushd",
585         pushd_builtin,
586         BUILTIN_ENABLED,
587         pushd_doc,
588         "pushd [+N | -N] [-n] [dir]",
589         0
590 };
591
592 struct builtin popd_struct = {
593         "popd",
594         popd_builtin,
595         BUILTIN_ENABLED,
596         popd_doc,
597         "popd [+N | -N] [-n]",
598         0
599 };
600
601 struct builtin dirs_struct = {
602         "dirs",
603         dirs_builtin,
604         BUILTIN_ENABLED,
605         dirs_doc,
606         "dirs [-clpv] [+N] [-N]",
607         0
608 };