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