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