blob: aca74ef96bbe52edb5b644b4e28ab2207be8616e [file] [log] [blame]
Jari Aaltoccc6cda1996-12-23 17:02:34 +00001This file is pushd.def, from which is created pushd.c. It implements the
2builtins "pushd", "popd", and "dirs" in Bash.
3
Chet Rameyac50fba2014-02-26 09:36:43 -05004Copyright (C) 1987-2013 Free Software Foundation, Inc.
Jari Aaltoccc6cda1996-12-23 17:02:34 +00005
6This file is part of GNU Bash, the Bourne Again SHell.
7
Jari Aalto31859422009-01-12 13:36:28 +00008Bash is free software: you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 3 of the License, or
11(at your option) any later version.
Jari Aaltoccc6cda1996-12-23 17:02:34 +000012
Jari Aalto31859422009-01-12 13:36:28 +000013Bash is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
Jari Aaltoccc6cda1996-12-23 17:02:34 +000017
Jari Aalto31859422009-01-12 13:36:28 +000018You should have received a copy of the GNU General Public License
19along with Bash. If not, see <http://www.gnu.org/licenses/>.
Jari Aaltoccc6cda1996-12-23 17:02:34 +000020
21$PRODUCES pushd.c
22
23$BUILTIN pushd
24$FUNCTION pushd_builtin
25$DEPENDS_ON PUSHD_AND_POPD
Jari Aalto31859422009-01-12 13:36:28 +000026$SHORT_DOC pushd [-n] [+N | -N | dir]
27Add directories to stack.
28
Jari Aaltoccc6cda1996-12-23 17:02:34 +000029Adds a directory to the top of the directory stack, or rotates
30the stack, making the new top of the stack the current working
31directory. With no arguments, exchanges the top two directories.
32
Jari Aalto31859422009-01-12 13:36:28 +000033Options:
34 -n Suppresses the normal change of directory when adding
35 directories to the stack, so only the stack is manipulated.
36
37Arguments:
38 +N Rotates the stack so that the Nth directory (counting
Jari Aaltod166f041997-06-05 14:59:13 +000039 from the left of the list shown by `dirs', starting with
40 zero) is at the top.
Jari Aaltoccc6cda1996-12-23 17:02:34 +000041
Jari Aalto31859422009-01-12 13:36:28 +000042 -N Rotates the stack so that the Nth directory (counting
Jari Aaltod166f041997-06-05 14:59:13 +000043 from the right of the list shown by `dirs', starting with
44 zero) is at the top.
Jari Aaltoccc6cda1996-12-23 17:02:34 +000045
Jari Aalto31859422009-01-12 13:36:28 +000046 dir Adds DIR to the directory stack at the top, making it the
Jari Aaltoccc6cda1996-12-23 17:02:34 +000047 new current working directory.
48
Jari Aalto31859422009-01-12 13:36:28 +000049The `dirs' builtin displays the directory stack.
50
51Exit Status:
52Returns success unless an invalid argument is supplied or the directory
53change fails.
Jari Aaltoccc6cda1996-12-23 17:02:34 +000054$END
55
56$BUILTIN popd
57$FUNCTION popd_builtin
58$DEPENDS_ON PUSHD_AND_POPD
Jari Aalto31859422009-01-12 13:36:28 +000059$SHORT_DOC popd [-n] [+N | -N]
60Remove directories from stack.
Jari Aaltoccc6cda1996-12-23 17:02:34 +000061
Jari Aalto31859422009-01-12 13:36:28 +000062Removes entries from the directory stack. With no arguments, removes
63the top directory from the stack, and changes to the new top directory.
64
65Options:
66 -n Suppresses the normal change of directory when removing
67 directories from the stack, so only the stack is manipulated.
68
69Arguments:
70 +N Removes the Nth entry counting from the left of the list
Jari Aaltoccc6cda1996-12-23 17:02:34 +000071 shown by `dirs', starting with zero. For example: `popd +0'
72 removes the first directory, `popd +1' the second.
73
Jari Aalto31859422009-01-12 13:36:28 +000074 -N Removes the Nth entry counting from the right of the list
Jari Aaltoccc6cda1996-12-23 17:02:34 +000075 shown by `dirs', starting with zero. For example: `popd -0'
76 removes the last directory, `popd -1' the next to last.
77
Jari Aalto31859422009-01-12 13:36:28 +000078The `dirs' builtin displays the directory stack.
Jari Aaltoccc6cda1996-12-23 17:02:34 +000079
Jari Aalto31859422009-01-12 13:36:28 +000080Exit Status:
81Returns success unless an invalid argument is supplied or the directory
82change fails.
Jari Aaltoccc6cda1996-12-23 17:02:34 +000083$END
84
85$BUILTIN dirs
86$FUNCTION dirs_builtin
87$DEPENDS_ON PUSHD_AND_POPD
88$SHORT_DOC dirs [-clpv] [+N] [-N]
Jari Aalto31859422009-01-12 13:36:28 +000089Display directory stack.
90
Jari Aaltoccc6cda1996-12-23 17:02:34 +000091Display the list of currently remembered directories. Directories
92find their way onto the list with the `pushd' command; you can get
93back up through the list with the `popd' command.
94
Jari Aalto31859422009-01-12 13:36:28 +000095Options:
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
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000102
Jari Aalto31859422009-01-12 13:36:28 +0000103Arguments:
104 +N Displays the Nth entry counting from the left of the list shown by
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000105 dirs when invoked without options, starting with zero.
106
Jari Aalto31859422009-01-12 13:36:28 +0000107 -N Displays the Nth entry counting from the right of the list shown by
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000108 dirs when invoked without options, starting with zero.
Jari Aalto31859422009-01-12 13:36:28 +0000109
110Exit Status:
111Returns success unless an invalid option is supplied or an error occurs.
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000112$END
113
114#include <config.h>
115
116#if defined (PUSHD_AND_POPD)
117#include <stdio.h>
Chet Rameyac50fba2014-02-26 09:36:43 -0500118#if defined (HAVE_SYS_PARAM_H)
Jari Aaltocce855b1998-04-17 19:52:44 +0000119# include <sys/param.h>
120#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000121
122#if defined (HAVE_UNISTD_H)
Jari Aaltocce855b1998-04-17 19:52:44 +0000123# ifdef _MINIX
124# include <sys/types.h>
125# endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000126# include <unistd.h>
127#endif
128
129#include "../bashansi.h"
Jari Aaltob80f6442004-07-27 13:29:18 +0000130#include "../bashintl.h"
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000131
132#include <errno.h>
133
134#include <tilde/tilde.h>
135
136#include "../shell.h"
Jari Aaltobb706242000-03-17 21:46:59 +0000137#include "maxpath.h"
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000138#include "common.h"
139#include "builtext.h"
140
Jari Aaltob72432f1999-02-19 17:11:39 +0000141#ifdef LOADABLE_BUILTIN
142# include "builtins.h"
143#endif
144
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000145#if !defined (errno)
Ricardo Cerqueiraa02fbff2013-07-25 22:35:34 +0100146#include <errno.h>
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000147#endif /* !errno */
148
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000149/* The list of remembered directories. */
150static char **pushd_directory_list = (char **)NULL;
151
152/* Number of existing slots in this list. */
153static int directory_list_size;
154
155/* Offset to the end of the list. */
156static int directory_list_offset;
157
Jari Aaltof73dda02001-11-13 17:56:06 +0000158static void pushd_error __P((int, char *));
159static void clear_directory_stack __P((void));
160static int cd_to_string __P((char *));
161static int change_to_temp __P((char *));
162static void add_dirstack_element __P((char *));
Jari Aalto7117c2d2002-07-17 14:10:11 +0000163static int get_dirstack_index __P((intmax_t, int, int *));
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000164
165#define NOCD 0x01
166#define ROTATE 0x02
167#define LONGFORM 0x04
168#define CLEARSTAK 0x08
169
170int
171pushd_builtin (list)
172 WORD_LIST *list;
173{
Jari Aalto95732b42005-12-07 14:08:12 +0000174 WORD_LIST *orig_list;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000175 char *temp, *current_directory, *top;
Jari Aalto95732b42005-12-07 14:08:12 +0000176 int j, flags, skipopt;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000177 intmax_t num;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000178 char direction;
179
Jari Aalto95732b42005-12-07 14:08:12 +0000180 orig_list = list;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000181 if (list && list->word && ISOPTION (list->word->word, '-'))
Jari Aalto95732b42005-12-07 14:08:12 +0000182 {
183 list = list->next;
184 skipopt = 1;
185 }
186 else
187 skipopt = 0;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000188
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000189 /* If there is no argument list then switch current and
190 top of list. */
191 if (list == 0)
192 {
193 if (directory_list_offset == 0)
194 {
Jari Aaltob80f6442004-07-27 13:29:18 +0000195 builtin_error (_("no other directory"));
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000196 return (EXECUTION_FAILURE);
197 }
198
199 current_directory = get_working_directory ("pushd");
200 if (current_directory == 0)
201 return (EXECUTION_FAILURE);
202
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);
207 free (temp);
208 return j;
209 }
210
Jari Aalto95732b42005-12-07 14:08:12 +0000211 for (flags = 0; skipopt == 0 && list; list = list->next)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000212 {
213 if (ISOPTION (list->word->word, 'n'))
214 {
215 flags |= NOCD;
216 }
217 else if (ISOPTION (list->word->word, '-'))
Jari Aalto28ef6c32001-04-06 19:14:31 +0000218 {
219 list = list->next;
220 break;
221 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000222 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
223 /* Let `pushd -' work like it used to. */
224 break;
225 else if (((direction = list->word->word[0]) == '+') || direction == '-')
226 {
227 if (legal_number (list->word->word + 1, &num) == 0)
228 {
Jari Aalto7117c2d2002-07-17 14:10:11 +0000229 sh_invalidnum (list->word->word);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000230 builtin_usage ();
Chet Rameyac50fba2014-02-26 09:36:43 -0500231 return (EX_USAGE);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000232 }
233
234 if (direction == '-')
235 num = directory_list_offset - num;
236
237 if (num > directory_list_offset || num < 0)
238 {
239 pushd_error (directory_list_offset, list->word->word);
240 return (EXECUTION_FAILURE);
241 }
242 flags |= ROTATE;
243 }
244 else if (*list->word->word == '-')
245 {
Jari Aalto7117c2d2002-07-17 14:10:11 +0000246 sh_invalidopt (list->word->word);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000247 builtin_usage ();
Chet Rameyac50fba2014-02-26 09:36:43 -0500248 return (EX_USAGE);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000249 }
250 else
251 break;
252 }
253
254 if (flags & ROTATE)
255 {
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");
259
260 if (num == 0)
261 {
262 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
263 free (temp);
264 return j;
265 }
266
267 do
268 {
269 top = pushd_directory_list[directory_list_offset - 1];
270
271 for (j = directory_list_offset - 2; j > -1; j--)
272 pushd_directory_list[j + 1] = pushd_directory_list[j];
273
274 pushd_directory_list[j + 1] = temp;
275
276 temp = top;
277 num--;
278 }
279 while (num);
280
281 j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
282 free (temp);
283 return j;
284 }
285
286 if (list == 0)
287 return (EXECUTION_SUCCESS);
288
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);
294
Jari Aalto95732b42005-12-07 14:08:12 +0000295 j = ((flags & NOCD) == 0) ? cd_builtin (skipopt ? orig_list : list) : EXECUTION_SUCCESS;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000296 if (j == EXECUTION_SUCCESS)
297 {
298 add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
299 dirs_builtin ((WORD_LIST *)NULL);
Jari Aaltod166f041997-06-05 14:59:13 +0000300 if (flags & NOCD)
301 free (current_directory);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000302 return (EXECUTION_SUCCESS);
303 }
304 else
305 {
306 free (current_directory);
307 return (EXECUTION_FAILURE);
308 }
309}
310
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. */
314int
315popd_builtin (list)
316 WORD_LIST *list;
317{
318 register int i;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000319 intmax_t which;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000320 int flags;
321 char direction;
Jari Aaltod166f041997-06-05 14:59:13 +0000322 char *which_word;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000323
Jari Aaltod166f041997-06-05 14:59:13 +0000324 which_word = (char *)NULL;
Jari Aaltof73dda02001-11-13 17:56:06 +0000325 for (flags = 0, which = 0, direction = '+'; list; list = list->next)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000326 {
327 if (ISOPTION (list->word->word, 'n'))
Jari Aalto28ef6c32001-04-06 19:14:31 +0000328 {
329 flags |= NOCD;
330 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000331 else if (ISOPTION (list->word->word, '-'))
Jari Aalto28ef6c32001-04-06 19:14:31 +0000332 {
333 list = list->next;
334 break;
335 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000336 else if (((direction = list->word->word[0]) == '+') || direction == '-')
337 {
338 if (legal_number (list->word->word + 1, &which) == 0)
339 {
Jari Aalto7117c2d2002-07-17 14:10:11 +0000340 sh_invalidnum (list->word->word);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000341 builtin_usage ();
Chet Rameyac50fba2014-02-26 09:36:43 -0500342 return (EX_USAGE);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000343 }
Jari Aaltod166f041997-06-05 14:59:13 +0000344 which_word = list->word->word;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000345 }
346 else if (*list->word->word == '-')
347 {
Jari Aalto7117c2d2002-07-17 14:10:11 +0000348 sh_invalidopt (list->word->word);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000349 builtin_usage ();
Chet Rameyac50fba2014-02-26 09:36:43 -0500350 return (EX_USAGE);
351 }
352 else if (*list->word->word)
353 {
354 builtin_error (_("%s: invalid argument"), list->word->word);
355 builtin_usage ();
356 return (EX_USAGE);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000357 }
358 else
359 break;
360 }
361
362 if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
363 {
Jari Aaltod166f041997-06-05 14:59:13 +0000364 pushd_error (directory_list_offset, which_word ? which_word : "");
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000365 return (EXECUTION_FAILURE);
366 }
367
368 /* Handle case of no specification, or top of stack specification. */
369 if ((direction == '+' && which == 0) ||
370 (direction == '-' && which == directory_list_offset))
371 {
372 i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
Jari Aalto28ef6c32001-04-06 19:14:31 +0000373 : EXECUTION_SUCCESS;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000374 if (i != EXECUTION_SUCCESS)
375 return (i);
376 free (pushd_directory_list[--directory_list_offset]);
377 }
378 else
379 {
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--;
386
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];
390 }
391
392 dirs_builtin ((WORD_LIST *)NULL);
393 return (EXECUTION_SUCCESS);
394}
395
396/* Print the current list of directories on the directory stack. */
397int
398dirs_builtin (list)
399 WORD_LIST *list;
400{
401 int flags, desired_index, index_flag, vflag;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000402 intmax_t i;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000403 char *temp, *w;
404
405 for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
406 {
407 if (ISOPTION (list->word->word, 'l'))
408 {
409 flags |= LONGFORM;
410 }
411 else if (ISOPTION (list->word->word, 'c'))
412 {
413 flags |= CLEARSTAK;
414 }
415 else if (ISOPTION (list->word->word, 'v'))
416 {
417 vflag |= 2;
418 }
419 else if (ISOPTION (list->word->word, 'p'))
420 {
421 vflag |= 1;
422 }
423 else if (ISOPTION (list->word->word, '-'))
Jari Aalto28ef6c32001-04-06 19:14:31 +0000424 {
425 list = list->next;
426 break;
427 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000428 else if (*list->word->word == '+' || *list->word->word == '-')
Jari Aalto28ef6c32001-04-06 19:14:31 +0000429 {
430 int sign;
431 if (legal_number (w = list->word->word + 1, &i) == 0)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000432 {
Jari Aalto7117c2d2002-07-17 14:10:11 +0000433 sh_invalidnum (list->word->word);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000434 builtin_usage ();
Chet Rameyac50fba2014-02-26 09:36:43 -0500435 return (EX_USAGE);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000436 }
437 sign = (*list->word->word == '+') ? 1 : -1;
438 desired_index = get_dirstack_index (i, sign, &index_flag);
439 }
440 else
441 {
Jari Aalto7117c2d2002-07-17 14:10:11 +0000442 sh_invalidopt (list->word->word);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000443 builtin_usage ();
Chet Rameyac50fba2014-02-26 09:36:43 -0500444 return (EX_USAGE);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000445 }
446 }
447
448 if (flags & CLEARSTAK)
449 {
450 clear_directory_stack ();
451 return (EXECUTION_SUCCESS);
452 }
453
454 if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
455 {
456 pushd_error (directory_list_offset, w);
457 return (EXECUTION_FAILURE);
458 }
459
460#define DIRSTACK_FORMAT(temp) \
461 (flags & LONGFORM) ? temp : polite_directory_format (temp)
462
463 /* The first directory printed is always the current working directory. */
464 if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
465 {
466 temp = get_working_directory ("dirs");
467 if (temp == 0)
Jari Aaltob80f6442004-07-27 13:29:18 +0000468 temp = savestring (_("<no current directory>"));
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000469 if (vflag & 2)
470 printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
471 else
472 printf ("%s", DIRSTACK_FORMAT (temp));
473 free (temp);
474 if (index_flag)
475 {
476 putchar ('\n');
Jari Aalto31859422009-01-12 13:36:28 +0000477 return (sh_chkwrite (EXECUTION_SUCCESS));
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000478 }
479 }
480
481#define DIRSTACK_ENTRY(i) \
482 (flags & LONGFORM) ? pushd_directory_list[i] \
483 : polite_directory_format (pushd_directory_list[i])
484
485 /* Now print the requested directory stack entries. */
486 if (index_flag)
487 {
488 if (vflag & 2)
489 printf ("%2d %s", directory_list_offset - desired_index,
490 DIRSTACK_ENTRY (desired_index));
491 else
492 printf ("%s", DIRSTACK_ENTRY (desired_index));
493 }
494 else
495 for (i = directory_list_offset - 1; i >= 0; i--)
496 if (vflag >= 2)
497 printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
498 else
499 printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
500
501 putchar ('\n');
Jari Aalto31859422009-01-12 13:36:28 +0000502
503 return (sh_chkwrite (EXECUTION_SUCCESS));
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000504}
505
506static void
507pushd_error (offset, arg)
508 int offset;
509 char *arg;
510{
511 if (offset == 0)
Jari Aalto31859422009-01-12 13:36:28 +0000512 builtin_error (_("directory stack empty"));
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000513 else
Jari Aalto31859422009-01-12 13:36:28 +0000514 sh_erange (arg, _("directory stack index"));
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000515}
516
517static void
518clear_directory_stack ()
519{
520 register int i;
521
522 for (i = 0; i < directory_list_offset; i++)
523 free (pushd_directory_list[i]);
524 directory_list_offset = 0;
525}
526
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
529 been printed. */
530static int
531cd_to_string (name)
532 char *name;
533{
534 WORD_LIST *tlist;
Jari Aalto95732b42005-12-07 14:08:12 +0000535 WORD_LIST *dir;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000536 int result;
537
Jari Aalto95732b42005-12-07 14:08:12 +0000538 dir = make_word_list (make_word (name), NULL);
539 tlist = make_word_list (make_word ("--"), dir);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000540 result = cd_builtin (tlist);
541 dispose_words (tlist);
542 return (result);
543}
544
545static int
546change_to_temp (temp)
547 char *temp;
548{
549 int tt;
550
551 tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
552
553 if (tt == EXECUTION_SUCCESS)
554 dirs_builtin ((WORD_LIST *)NULL);
555
556 return (tt);
557}
558
559static void
560add_dirstack_element (dir)
561 char *dir;
562{
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000563 if (directory_list_offset == directory_list_size)
Jari Aalto7117c2d2002-07-17 14:10:11 +0000564 pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000565 pushd_directory_list[directory_list_offset++] = dir;
566}
567
568static int
569get_dirstack_index (ind, sign, indexp)
Jari Aalto7117c2d2002-07-17 14:10:11 +0000570 intmax_t ind;
Jari Aaltof73dda02001-11-13 17:56:06 +0000571 int sign, *indexp;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000572{
573 if (indexp)
574 *indexp = sign > 0 ? 1 : 2;
575
576 /* dirs +0 prints the current working directory. */
577 /* dirs -0 prints last element in directory stack */
578 if (ind == 0 && sign > 0)
579 return 0;
580 else if (ind == directory_list_offset)
581 {
582 if (indexp)
583 *indexp = sign > 0 ? 2 : 1;
584 return 0;
585 }
Jari Aaltof73dda02001-11-13 17:56:06 +0000586 else if (ind >= 0 && ind <= directory_list_offset)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000587 return (sign > 0 ? directory_list_offset - ind : ind);
Jari Aaltof73dda02001-11-13 17:56:06 +0000588 else
589 return -1;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000590}
591
Jari Aaltocce855b1998-04-17 19:52:44 +0000592/* Used by the tilde expansion code. */
593char *
594get_dirstack_from_string (string)
595 char *string;
596{
597 int ind, sign, index_flag;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000598 intmax_t i;
Jari Aaltocce855b1998-04-17 19:52:44 +0000599
600 sign = 1;
601 if (*string == '-' || *string == '+')
602 {
603 sign = (*string == '-') ? -1 : 1;
604 string++;
605 }
606 if (legal_number (string, &i) == 0)
607 return ((char *)NULL);
608
609 index_flag = 0;
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"));
615 else
616 return (pushd_directory_list[ind]);
617}
618
619#ifdef INCLUDE_UNUSED
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000620char *
621get_dirstack_element (ind, sign)
Jari Aalto7117c2d2002-07-17 14:10:11 +0000622 intmax_t ind;
Jari Aaltof73dda02001-11-13 17:56:06 +0000623 int sign;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000624{
625 int i;
626
627 i = get_dirstack_index (ind, sign, (int *)NULL);
628 return (i < 0 || i > directory_list_offset) ? (char *)NULL
629 : pushd_directory_list[i];
630}
Jari Aaltocce855b1998-04-17 19:52:44 +0000631#endif
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000632
633void
634set_dirstack_element (ind, sign, value)
Jari Aalto7117c2d2002-07-17 14:10:11 +0000635 intmax_t ind;
Jari Aaltof73dda02001-11-13 17:56:06 +0000636 int sign;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000637 char *value;
638{
639 int i;
640
641 i = get_dirstack_index (ind, sign, (int *)NULL);
642 if (ind == 0 || i < 0 || i > directory_list_offset)
643 return;
644 free (pushd_directory_list[i]);
645 pushd_directory_list[i] = savestring (value);
646}
647
648WORD_LIST *
Jari Aalto06285672006-10-10 14:15:34 +0000649get_directory_stack (flags)
650 int flags;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000651{
652 register int i;
653 WORD_LIST *ret;
654 char *d, *t;
655
656 for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
657 {
Jari Aalto06285672006-10-10 14:15:34 +0000658 d = (flags&1) ? polite_directory_format (pushd_directory_list[i])
659 : pushd_directory_list[i];
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000660 ret = make_word_list (make_word (d), ret);
661 }
662 /* Now the current directory. */
663 d = get_working_directory ("dirstack");
664 i = 0; /* sentinel to decide whether or not to free d */
665 if (d == 0)
666 d = ".";
667 else
668 {
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. */
673 if (t != d)
674 {
675 free (d);
676 d = t;
677 }
678 else /* t == d, so d is what we want */
679 i = 1;
680 }
681 ret = make_word_list (make_word (d), ret);
682 if (i)
683 free (d);
684 return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
685}
Jari Aaltob72432f1999-02-19 17:11:39 +0000686
687#ifdef LOADABLE_BUILTIN
Jari Aalto06285672006-10-10 14:15:34 +0000688char * const dirs_doc[] = {
Jari Aalto31859422009-01-12 13:36:28 +0000689N_("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\
692 \n\
693 Options:\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\
700 \n\
701 Arguments:\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\
704 \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."),
Jari Aaltob72432f1999-02-19 17:11:39 +0000707 (char *)NULL
708};
709
Jari Aalto06285672006-10-10 14:15:34 +0000710char * const pushd_doc[] = {
Jari Aalto31859422009-01-12 13:36:28 +0000711N_("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\
714 \n\
715 Options:\n\
716 -n Suppresses the normal change of directory when adding\n\
717 directories to the stack, so only the stack is manipulated.\n\
718 \n\
719 Arguments:\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\
723 \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\
727 \n\
728 dir Adds DIR to the directory stack at the top, making it the\n\
729 new current working directory.\n\
730 \n\
731 The `dirs' builtin displays the directory stack."),
Jari Aaltob72432f1999-02-19 17:11:39 +0000732 (char *)NULL
733};
734
Jari Aalto06285672006-10-10 14:15:34 +0000735char * const popd_doc[] = {
Jari Aalto31859422009-01-12 13:36:28 +0000736N_("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\
738 \n\
739 Options:\n\
740 -n Suppresses the normal change of directory when removing\n\
741 directories from the stack, so only the stack is manipulated.\n\
742 \n\
743 Arguments:\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\
747 \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\
751 \n\
752 The `dirs' builtin displays the directory stack."),
Jari Aaltob72432f1999-02-19 17:11:39 +0000753 (char *)NULL
754};
755
756struct builtin pushd_struct = {
757 "pushd",
758 pushd_builtin,
759 BUILTIN_ENABLED,
760 pushd_doc,
761 "pushd [+N | -N] [-n] [dir]",
762 0
763};
764
765struct builtin popd_struct = {
766 "popd",
767 popd_builtin,
768 BUILTIN_ENABLED,
769 popd_doc,
770 "popd [+N | -N] [-n]",
771 0
772};
773
774struct builtin dirs_struct = {
775 "dirs",
776 dirs_builtin,
777 BUILTIN_ENABLED,
778 dirs_doc,
779 "dirs [-clpv] [+N] [-N]",
780 0
781};
782#endif /* LOADABLE_BUILTIN */
783
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000784#endif /* PUSHD_AND_POPD */