]> git.ipfire.org Git - thirdparty/bash.git/blob - examples/loadables/pushd.c
Imported from ../bash-2.01.tar.gz.
[thirdparty/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 };