1
This file is pushd.def, from which is created pushd.c. It implements the
2
builtins "pushd", "popd", and "dirs" in Bash.
4
Copyright (C) 1987-2013 Free Software Foundation, Inc.
6
This file is part of GNU Bash, the Bourne Again SHell.
8
Bash is free software: you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation, either version 3 of the License, or
11
(at your option) any later version.
13
Bash is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
GNU General Public License for more details.
18
You should have received a copy of the GNU General Public License
19
along with Bash. If not, see <http://www.gnu.org/licenses/>.
24
$FUNCTION pushd_builtin
25
$DEPENDS_ON PUSHD_AND_POPD
26
$SHORT_DOC pushd [-n] [+N | -N | dir]
27
Add directories to stack.
29
Adds a directory to the top of the directory stack, or rotates
30
the stack, making the new top of the stack the current working
31
directory. With no arguments, exchanges the top two directories.
34
-n Suppresses the normal change of directory when adding
35
directories to the stack, so only the stack is manipulated.
38
+N Rotates the stack so that the Nth directory (counting
39
from the left of the list shown by `dirs', starting with
42
-N Rotates the stack so that the Nth directory (counting
43
from the right of the list shown by `dirs', starting with
46
dir Adds DIR to the directory stack at the top, making it the
47
new current working directory.
49
The `dirs' builtin displays the directory stack.
52
Returns success unless an invalid argument is supplied or the directory
57
$FUNCTION popd_builtin
58
$DEPENDS_ON PUSHD_AND_POPD
59
$SHORT_DOC popd [-n] [+N | -N]
60
Remove directories from stack.
62
Removes entries from the directory stack. With no arguments, removes
63
the top directory from the stack, and changes to the new top directory.
66
-n Suppresses the normal change of directory when removing
67
directories from the stack, so only the stack is manipulated.
70
+N Removes the Nth entry counting from the left of the list
71
shown by `dirs', starting with zero. For example: `popd +0'
72
removes the first directory, `popd +1' the second.
74
-N Removes the Nth entry counting from the right of the list
75
shown by `dirs', starting with zero. For example: `popd -0'
76
removes the last directory, `popd -1' the next to last.
78
The `dirs' builtin displays the directory stack.
81
Returns success unless an invalid argument is supplied or the directory
86
$FUNCTION dirs_builtin
87
$DEPENDS_ON PUSHD_AND_POPD
88
$SHORT_DOC dirs [-clpv] [+N] [-N]
89
Display directory stack.
91
Display the list of currently remembered directories. Directories
92
find their way onto the list with the `pushd' command; you can get
93
back up through the list with the `popd' command.
96
-c clear the directory stack by deleting all of the elements
97
-l do not print tilde-prefixed versions of directories relative
98
to your home directory
99
-p print the directory stack with one entry per line
100
-v print the directory stack with one entry per line prefixed
101
with its position in the stack
104
+N Displays the Nth entry counting from the left of the list shown by
105
dirs when invoked without options, starting with zero.
107
-N Displays the Nth entry counting from the right of the list shown by
108
dirs when invoked without options, starting with zero.
111
Returns success unless an invalid option is supplied or an error occurs.
116
#if defined (PUSHD_AND_POPD)
118
#if defined (HAVE_SYS_PARAM_H)
119
# include <sys/param.h>
122
#if defined (HAVE_UNISTD_H)
124
# include <sys/types.h>
129
#include "../bashansi.h"
130
#include "../bashintl.h"
134
#include <tilde/tilde.h>
136
#include "../shell.h"
139
#include "builtext.h"
141
#ifdef LOADABLE_BUILTIN
142
# include "builtins.h"
149
/* The list of remembered directories. */
150
static char **pushd_directory_list = (char **)NULL;
152
/* Number of existing slots in this list. */
153
static int directory_list_size;
155
/* Offset to the end of the list. */
156
static int directory_list_offset;
158
static void pushd_error __P((int, char *));
159
static void clear_directory_stack __P((void));
160
static int cd_to_string __P((char *));
161
static int change_to_temp __P((char *));
162
static void add_dirstack_element __P((char *));
163
static int get_dirstack_index __P((intmax_t, int, int *));
167
#define LONGFORM 0x04
168
#define CLEARSTAK 0x08
174
WORD_LIST *orig_list;
175
char *temp, *current_directory, *top;
176
int j, flags, skipopt;
181
if (list && list->word && ISOPTION (list->word->word, '-'))
189
/* If there is no argument list then switch current and
193
if (directory_list_offset == 0)
195
builtin_error (_("no other directory"));
196
return (EXECUTION_FAILURE);
199
current_directory = get_working_directory ("pushd");
200
if (current_directory == 0)
201
return (EXECUTION_FAILURE);
203
j = directory_list_offset - 1;
204
temp = pushd_directory_list[j];
205
pushd_directory_list[j] = current_directory;
206
j = change_to_temp (temp);
211
for (flags = 0; skipopt == 0 && list; list = list->next)
213
if (ISOPTION (list->word->word, 'n'))
217
else if (ISOPTION (list->word->word, '-'))
222
else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
223
/* Let `pushd -' work like it used to. */
225
else if (((direction = list->word->word[0]) == '+') || direction == '-')
227
if (legal_number (list->word->word + 1, &num) == 0)
229
sh_invalidnum (list->word->word);
234
if (direction == '-')
235
num = directory_list_offset - num;
237
if (num > directory_list_offset || num < 0)
239
pushd_error (directory_list_offset, list->word->word);
240
return (EXECUTION_FAILURE);
244
else if (*list->word->word == '-')
246
sh_invalidopt (list->word->word);
256
/* Rotate the stack num times. Remember, the current
257
directory acts like it is part of the stack. */
258
temp = get_working_directory ("pushd");
262
j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
269
top = pushd_directory_list[directory_list_offset - 1];
271
for (j = directory_list_offset - 2; j > -1; j--)
272
pushd_directory_list[j + 1] = pushd_directory_list[j];
274
pushd_directory_list[j + 1] = temp;
281
j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
287
return (EXECUTION_SUCCESS);
289
/* Change to the directory in list->word->word. Save the current
290
directory on the top of the stack. */
291
current_directory = get_working_directory ("pushd");
292
if (current_directory == 0)
293
return (EXECUTION_FAILURE);
295
j = ((flags & NOCD) == 0) ? cd_builtin (skipopt ? orig_list : list) : EXECUTION_SUCCESS;
296
if (j == EXECUTION_SUCCESS)
298
add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
299
dirs_builtin ((WORD_LIST *)NULL);
301
free (current_directory);
302
return (EXECUTION_SUCCESS);
306
free (current_directory);
307
return (EXECUTION_FAILURE);
311
/* Pop the directory stack, and then change to the new top of the stack.
312
If LIST is non-null it should consist of a word +N or -N, which says
313
what element to delete from the stack. The default is the top one. */
324
which_word = (char *)NULL;
325
for (flags = 0, which = 0, direction = '+'; list; list = list->next)
327
if (ISOPTION (list->word->word, 'n'))
331
else if (ISOPTION (list->word->word, '-'))
336
else if (((direction = list->word->word[0]) == '+') || direction == '-')
338
if (legal_number (list->word->word + 1, &which) == 0)
340
sh_invalidnum (list->word->word);
344
which_word = list->word->word;
346
else if (*list->word->word == '-')
348
sh_invalidopt (list->word->word);
352
else if (*list->word->word)
354
builtin_error (_("%s: invalid argument"), list->word->word);
362
if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
364
pushd_error (directory_list_offset, which_word ? which_word : "");
365
return (EXECUTION_FAILURE);
368
/* Handle case of no specification, or top of stack specification. */
369
if ((direction == '+' && which == 0) ||
370
(direction == '-' && which == directory_list_offset))
372
i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
374
if (i != EXECUTION_SUCCESS)
376
free (pushd_directory_list[--directory_list_offset]);
380
/* Since an offset other than the top directory was specified,
381
remove that directory from the list and shift the remainder
382
of the list into place. */
383
i = (direction == '+') ? directory_list_offset - which : which;
384
free (pushd_directory_list[i]);
385
directory_list_offset--;
387
/* Shift the remainder of the list into place. */
388
for (; i < directory_list_offset; i++)
389
pushd_directory_list[i] = pushd_directory_list[i + 1];
392
dirs_builtin ((WORD_LIST *)NULL);
393
return (EXECUTION_SUCCESS);
396
/* Print the current list of directories on the directory stack. */
401
int flags, desired_index, index_flag, vflag;
405
for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
407
if (ISOPTION (list->word->word, 'l'))
411
else if (ISOPTION (list->word->word, 'c'))
415
else if (ISOPTION (list->word->word, 'v'))
419
else if (ISOPTION (list->word->word, 'p'))
423
else if (ISOPTION (list->word->word, '-'))
428
else if (*list->word->word == '+' || *list->word->word == '-')
431
if (legal_number (w = list->word->word + 1, &i) == 0)
433
sh_invalidnum (list->word->word);
437
sign = (*list->word->word == '+') ? 1 : -1;
438
desired_index = get_dirstack_index (i, sign, &index_flag);
442
sh_invalidopt (list->word->word);
448
if (flags & CLEARSTAK)
450
clear_directory_stack ();
451
return (EXECUTION_SUCCESS);
454
if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
456
pushd_error (directory_list_offset, w);
457
return (EXECUTION_FAILURE);
460
#define DIRSTACK_FORMAT(temp) \
461
(flags & LONGFORM) ? temp : polite_directory_format (temp)
463
/* The first directory printed is always the current working directory. */
464
if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
466
temp = get_working_directory ("dirs");
468
temp = savestring (_("<no current directory>"));
470
printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
472
printf ("%s", DIRSTACK_FORMAT (temp));
477
return (sh_chkwrite (EXECUTION_SUCCESS));
481
#define DIRSTACK_ENTRY(i) \
482
(flags & LONGFORM) ? pushd_directory_list[i] \
483
: polite_directory_format (pushd_directory_list[i])
485
/* Now print the requested directory stack entries. */
489
printf ("%2d %s", directory_list_offset - desired_index,
490
DIRSTACK_ENTRY (desired_index));
492
printf ("%s", DIRSTACK_ENTRY (desired_index));
495
for (i = directory_list_offset - 1; i >= 0; i--)
497
printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
499
printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
503
return (sh_chkwrite (EXECUTION_SUCCESS));
507
pushd_error (offset, arg)
512
builtin_error (_("directory stack empty"));
514
sh_erange (arg, _("directory stack index"));
518
clear_directory_stack ()
522
for (i = 0; i < directory_list_offset; i++)
523
free (pushd_directory_list[i]);
524
directory_list_offset = 0;
527
/* Switch to the directory in NAME. This uses the cd_builtin to do the work,
528
so if the result is EXECUTION_FAILURE then an error message has already
538
dir = make_word_list (make_word (name), NULL);
539
tlist = make_word_list (make_word ("--"), dir);
540
result = cd_builtin (tlist);
541
dispose_words (tlist);
546
change_to_temp (temp)
551
tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
553
if (tt == EXECUTION_SUCCESS)
554
dirs_builtin ((WORD_LIST *)NULL);
560
add_dirstack_element (dir)
563
if (directory_list_offset == directory_list_size)
564
pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10);
565
pushd_directory_list[directory_list_offset++] = dir;
569
get_dirstack_index (ind, sign, indexp)
574
*indexp = sign > 0 ? 1 : 2;
576
/* dirs +0 prints the current working directory. */
577
/* dirs -0 prints last element in directory stack */
578
if (ind == 0 && sign > 0)
580
else if (ind == directory_list_offset)
583
*indexp = sign > 0 ? 2 : 1;
586
else if (ind >= 0 && ind <= directory_list_offset)
587
return (sign > 0 ? directory_list_offset - ind : ind);
592
/* Used by the tilde expansion code. */
594
get_dirstack_from_string (string)
597
int ind, sign, index_flag;
601
if (*string == '-' || *string == '+')
603
sign = (*string == '-') ? -1 : 1;
606
if (legal_number (string, &i) == 0)
607
return ((char *)NULL);
610
ind = get_dirstack_index (i, sign, &index_flag);
611
if (index_flag && (ind < 0 || ind > directory_list_offset))
612
return ((char *)NULL);
613
if (index_flag == 0 || (index_flag == 1 && ind == 0))
614
return (get_string_value ("PWD"));
616
return (pushd_directory_list[ind]);
619
#ifdef INCLUDE_UNUSED
621
get_dirstack_element (ind, sign)
627
i = get_dirstack_index (ind, sign, (int *)NULL);
628
return (i < 0 || i > directory_list_offset) ? (char *)NULL
629
: pushd_directory_list[i];
634
set_dirstack_element (ind, sign, value)
641
i = get_dirstack_index (ind, sign, (int *)NULL);
642
if (ind == 0 || i < 0 || i > directory_list_offset)
644
free (pushd_directory_list[i]);
645
pushd_directory_list[i] = savestring (value);
649
get_directory_stack (flags)
656
for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
658
d = (flags&1) ? polite_directory_format (pushd_directory_list[i])
659
: pushd_directory_list[i];
660
ret = make_word_list (make_word (d), ret);
662
/* Now the current directory. */
663
d = get_working_directory ("dirstack");
664
i = 0; /* sentinel to decide whether or not to free d */
669
t = polite_directory_format (d);
670
/* polite_directory_format sometimes returns its argument unchanged.
671
If it does not, we can free d right away. If it does, we need to
672
mark d to be deleted later. */
678
else /* t == d, so d is what we want */
681
ret = make_word_list (make_word (d), ret);
684
return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
687
#ifdef LOADABLE_BUILTIN
688
char * const dirs_doc[] = {
689
N_("Display the list of currently remembered directories. Directories\n\
690
find their way onto the list with the `pushd' command; you can get\n\
691
back up through the list with the `popd' command.\n\
694
-c clear the directory stack by deleting all of the elements\n\
695
-l do not print tilde-prefixed versions of directories relative\n\
696
to your home directory\n\
697
-p print the directory stack with one entry per line\n\
698
-v print the directory stack with one entry per line prefixed\n\
699
with its position in the stack\n\
702
+N Displays the Nth entry counting from the left of the list shown by\n\
703
dirs when invoked without options, starting with zero.\n\
705
-N Displays the Nth entry counting from the right of the list shown by\n\
706
dirs when invoked without options, starting with zero."),
710
char * const pushd_doc[] = {
711
N_("Adds a directory to the top of the directory stack, or rotates\n\
712
the stack, making the new top of the stack the current working\n\
713
directory. With no arguments, exchanges the top two directories.\n\
716
-n Suppresses the normal change of directory when adding\n\
717
directories to the stack, so only the stack is manipulated.\n\
720
+N Rotates the stack so that the Nth directory (counting\n\
721
from the left of the list shown by `dirs', starting with\n\
722
zero) is at the top.\n\
724
-N Rotates the stack so that the Nth directory (counting\n\
725
from the right of the list shown by `dirs', starting with\n\
726
zero) is at the top.\n\
728
dir Adds DIR to the directory stack at the top, making it the\n\
729
new current working directory.\n\
731
The `dirs' builtin displays the directory stack."),
735
char * const popd_doc[] = {
736
N_("Removes entries from the directory stack. With no arguments, removes\n\
737
the top directory from the stack, and changes to the new top directory.\n\
740
-n Suppresses the normal change of directory when removing\n\
741
directories from the stack, so only the stack is manipulated.\n\
744
+N Removes the Nth entry counting from the left of the list\n\
745
shown by `dirs', starting with zero. For example: `popd +0'\n\
746
removes the first directory, `popd +1' the second.\n\
748
-N Removes the Nth entry counting from the right of the list\n\
749
shown by `dirs', starting with zero. For example: `popd -0'\n\
750
removes the last directory, `popd -1' the next to last.\n\
752
The `dirs' builtin displays the directory stack."),
756
struct builtin pushd_struct = {
761
"pushd [+N | -N] [-n] [dir]",
765
struct builtin popd_struct = {
770
"popd [+N | -N] [-n]",
774
struct builtin dirs_struct = {
779
"dirs [-clpv] [+N] [-N]",
782
#endif /* LOADABLE_BUILTIN */
784
#endif /* PUSHD_AND_POPD */