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