1 /* pushd.c, created from pushd.def. */
7 #if defined (HAVE_UNISTD_H)
15 #include <tilde/tilde.h>
26 static char *m_badarg = "%s: bad argument";
28 /* The list of remembered directories. */
29 static char **pushd_directory_list = (char **)NULL;
31 /* Number of existing slots in this list. */
32 static int directory_list_size;
34 /* Offset to the end of the list. */
35 static int directory_list_offset;
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 ();
47 #define CLEARSTAK 0x08
53 char *temp, *current_directory, *top;
58 /* If there is no argument list then switch current and
62 if (directory_list_offset == 0)
64 builtin_error ("no other directory");
65 return (EXECUTION_FAILURE);
68 current_directory = get_working_directory ("pushd");
69 if (current_directory == 0)
70 return (EXECUTION_FAILURE);
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);
80 for (flags = 0; list; list = list->next)
82 if (ISOPTION (list->word->word, 'n'))
86 else if (ISOPTION (list->word->word, '-'))
91 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
92 /* Let `pushd -' work like it used to. */
94 else if (((direction = list->word->word[0]) == '+') || direction == '-')
96 if (legal_number (list->word->word + 1, &num) == 0)
98 builtin_error (m_badarg, list->word->word);
100 return (EXECUTION_FAILURE);
103 if (direction == '-')
104 num = directory_list_offset - num;
106 if (num > directory_list_offset || num < 0)
108 pushd_error (directory_list_offset, list->word->word);
109 return (EXECUTION_FAILURE);
113 else if (*list->word->word == '-')
115 bad_option (list->word->word);
117 return (EXECUTION_FAILURE);
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");
131 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
138 top = pushd_directory_list[directory_list_offset - 1];
140 for (j = directory_list_offset - 2; j > -1; j--)
141 pushd_directory_list[j + 1] = pushd_directory_list[j];
143 pushd_directory_list[j + 1] = temp;
150 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
156 return (EXECUTION_SUCCESS);
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);
164 j = ((flags & NOCD) == 0) ? cd_builtin (list) : EXECUTION_SUCCESS;
165 if (j == EXECUTION_SUCCESS)
167 add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
168 dirs_builtin ((WORD_LIST *)NULL);
170 free (current_directory);
171 return (EXECUTION_SUCCESS);
175 free (current_directory);
176 return (EXECUTION_FAILURE);
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. */
193 which_word = (char *)NULL;
194 for (flags = 0, which = 0L, direction = '+'; list; list = list->next)
196 if (ISOPTION (list->word->word, 'n'))
200 else if (ISOPTION (list->word->word, '-'))
205 else if (((direction = list->word->word[0]) == '+') || direction == '-')
207 if (legal_number (list->word->word + 1, &which) == 0)
209 builtin_error (m_badarg, list->word->word);
211 return (EXECUTION_FAILURE);
213 which_word = list->word->word;
215 else if (*list->word->word == '-')
217 bad_option (list->word->word);
219 return (EXECUTION_FAILURE);
225 if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
227 pushd_error (directory_list_offset, which_word ? which_word : "");
228 return (EXECUTION_FAILURE);
231 /* Handle case of no specification, or top of stack specification. */
232 if ((direction == '+' && which == 0) ||
233 (direction == '-' && which == directory_list_offset))
235 i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
237 if (i != EXECUTION_SUCCESS)
239 free (pushd_directory_list[--directory_list_offset]);
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--;
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];
255 dirs_builtin ((WORD_LIST *)NULL);
256 return (EXECUTION_SUCCESS);
259 /* Print the current list of directories on the directory stack. */
264 int flags, desired_index, index_flag, vflag;
268 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
270 if (ISOPTION (list->word->word, 'l'))
274 else if (ISOPTION (list->word->word, 'c'))
278 else if (ISOPTION (list->word->word, 'v'))
282 else if (ISOPTION (list->word->word, 'p'))
286 else if (ISOPTION (list->word->word, '-'))
291 else if (*list->word->word == '+' || *list->word->word == '-')
294 if (legal_number (w = list->word->word + 1, &i) == 0)
296 builtin_error (m_badarg, list->word->word);
298 return (EXECUTION_FAILURE);
300 sign = (*list->word->word == '+') ? 1 : -1;
301 desired_index = get_dirstack_index (i, sign, &index_flag);
305 bad_option (list->word->word);
307 return (EXECUTION_FAILURE);
311 if (flags & CLEARSTAK)
313 clear_directory_stack ();
314 return (EXECUTION_SUCCESS);
317 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
319 pushd_error (directory_list_offset, w);
320 return (EXECUTION_FAILURE);
323 #define DIRSTACK_FORMAT(temp) \
324 (flags & LONGFORM) ? temp : polite_directory_format (temp)
326 /* The first directory printed is always the current working directory. */
327 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
329 temp = get_working_directory ("dirs");
331 temp = savestring ("<no current directory>");
333 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
335 printf ("%s", DIRSTACK_FORMAT (temp));
340 return EXECUTION_SUCCESS;
344 #define DIRSTACK_ENTRY(i) \
345 (flags & LONGFORM) ? pushd_directory_list[i] \
346 : polite_directory_format (pushd_directory_list[i])
348 /* Now print the requested directory stack entries. */
352 printf ("%2d %s", directory_list_offset - desired_index,
353 DIRSTACK_ENTRY (desired_index));
355 printf ("%s", DIRSTACK_ENTRY (desired_index));
358 for (i = directory_list_offset - 1; i >= 0; i--)
360 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
362 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
366 return (EXECUTION_SUCCESS);
370 pushd_error (offset, arg)
375 builtin_error ("directory stack empty");
377 builtin_error ("%s: bad directory stack index", arg);
379 builtin_error ("bad directory stack index");
383 clear_directory_stack ()
387 for (i = 0; i < directory_list_offset; i++)
388 free (pushd_directory_list[i]);
389 directory_list_offset = 0;
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
402 tlist = make_word_list (make_word (name), NULL);
403 result = cd_builtin (tlist);
404 dispose_words (tlist);
409 change_to_temp (temp)
414 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
416 if (tt == EXECUTION_SUCCESS)
417 dirs_builtin ((WORD_LIST *)NULL);
423 add_dirstack_element (dir)
428 if (directory_list_offset == directory_list_size)
430 j = (directory_list_size += 10) * sizeof (char *);
431 pushd_directory_list = (char **)xrealloc (pushd_directory_list, j);
433 pushd_directory_list[directory_list_offset++] = dir;
437 get_dirstack_index (ind, sign, indexp)
438 int ind, sign, *indexp;
441 *indexp = sign > 0 ? 1 : 2;
443 /* dirs +0 prints the current working directory. */
444 /* dirs -0 prints last element in directory stack */
445 if (ind == 0 && sign > 0)
447 else if (ind == directory_list_offset)
450 *indexp = sign > 0 ? 2 : 1;
454 return (sign > 0 ? directory_list_offset - ind : ind);
458 get_dirstack_element (ind, sign)
463 i = get_dirstack_index (ind, sign, (int *)NULL);
464 return (i < 0 || i > directory_list_offset) ? (char *)NULL
465 : pushd_directory_list[i];
469 set_dirstack_element (ind, sign, value)
475 i = get_dirstack_index (ind, sign, (int *)NULL);
476 if (ind == 0 || i < 0 || i > directory_list_offset)
478 free (pushd_directory_list[i]);
479 pushd_directory_list[i] = savestring (value);
483 get_directory_stack ()
489 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
491 d = polite_directory_format (pushd_directory_list[i]);
492 ret = make_word_list (make_word (d), ret);
494 /* Now the current directory. */
495 d = get_working_directory ("dirstack");
496 i = 0; /* sentinel to decide whether or not to free d */
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. */
510 else /* t == d, so d is what we want */
513 ret = make_word_list (make_word (d), ret);
516 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
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.",
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.",
532 "+N displays the Nth entry counting from the left of the list shown by",
533 " dirs when invoked without options, starting with zero.",
535 "-N displays the Nth entry counting from the right of the list shown by",
536 " dirs when invoked without options, starting with zero.",
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.",
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.",
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.",
553 "-n suppress the normal change of directory when adding directories",
554 " to the stack, so only the stack is manipulated.",
556 "dir adds DIR to the directory stack at the top, making it the",
557 " new current working directory.",
559 "You can see the directory stack with the `dirs' command.",
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",
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.",
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.",
576 "-n suppress the normal change of directory when removing directories",
577 " from the stack, so only the stack is manipulated.",
579 "You can see the directory stack with the `dirs' command.",
583 struct builtin pushd_struct = {
588 "pushd [+N | -N] [-n] [dir]",
592 struct builtin popd_struct = {
597 "popd [+N | -N] [-n]",
601 struct builtin dirs_struct = {
606 "dirs [-clpv] [+N] [-N]",