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