1 /* 2 * Copyright 1998,2001-2003 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1985 Regents of the University of California. 11 * All rights reserved. The Berkeley software License Agreement 12 * specifies the terms and conditions for redistribution. 13 */ 14 15 #include <setjmp.h> 16 #include <euc.h> 17 #include <widec.h> 18 #include "restore.h" 19 #include <ctype.h> 20 #include <limits.h> 21 #include <sys/wait.h> 22 23 extern eucwidth_t wp; 24 25 #define round(a, b) ((((a) + (b) - 1) / (b)) * (b)) 26 27 /* 28 * Things to handle interruptions. 29 */ 30 static jmp_buf reset; 31 static int reset_OK; 32 static char *nextarg = NULL; 33 34 static int dontexpand; /* co-routine state set in getnext, used in expandarg */ 35 36 #ifdef __STDC__ 37 static void getcmd(char *, char *, size_t, char *, size_t, struct arglist *); 38 static void expandarg(char *, struct arglist *); 39 static void printlist(char *, ino_t, char *, int); 40 static void formatf(struct arglist *); 41 static char *copynext(char *, char *, size_t); 42 static int fcmp(struct afile *, struct afile *); 43 static char *fmtentry(struct afile *); 44 static void setpagercmd(void); 45 static uint_t setpagerargs(char **); 46 #else 47 static void getcmd(); 48 static void expandarg(); 49 static void printlist(); 50 static void formatf(); 51 static char *copynext(); 52 static int fcmp(); 53 static char *fmtentry(); 54 static void setpagercmd(); 55 static uint_t setpagerargs(); 56 #endif 57 58 /* 59 * Read and execute commands from the terminal. 60 */ 61 void 62 #ifdef __STDC__ 63 runcmdshell(void) 64 #else 65 runcmdshell() 66 #endif 67 { 68 struct entry *np; 69 ino_t ino; 70 static struct arglist alist = { 0, 0, 0, 0, 0 }; 71 char curdir[MAXCOMPLEXLEN]; 72 char name[MAXCOMPLEXLEN]; 73 char cmd[BUFSIZ]; 74 75 #ifdef lint 76 curdir[0] = '\0'; 77 #endif /* lint */ 78 79 canon("/", curdir, sizeof (curdir)); 80 loop: 81 if (setjmp(reset) != 0) { 82 for (; alist.head < alist.last; alist.head++) 83 freename(alist.head->fname); 84 nextarg = NULL; 85 volno = 0; 86 goto loop; /* make sure jmpbuf is up-to-date */ 87 } 88 reset_OK = 1; 89 getcmd(curdir, cmd, sizeof (cmd), name, sizeof (name), &alist); 90 91 /* 92 * Using strncmp() to catch unique prefixes. 93 */ 94 switch (cmd[0]) { 95 /* 96 * Add elements to the extraction list. 97 */ 98 case 'a': 99 if (strncmp(cmd, "add", strlen(cmd)) != 0) 100 goto bad; 101 if (name[0] == '\0') 102 break; 103 ino = dirlookup(name); 104 if (ino == 0) 105 break; 106 if (mflag) 107 pathcheck(name); 108 treescan(name, ino, addfile); 109 break; 110 /* 111 * Change working directory. 112 */ 113 case 'c': 114 if (strncmp(cmd, "cd", strlen(cmd)) != 0) 115 goto bad; 116 if (name[0] == '\0') 117 break; 118 ino = dirlookup(name); 119 if (ino == 0) 120 break; 121 if (inodetype(ino) == LEAF) { 122 (void) fprintf(stderr, 123 gettext("%s: not a directory\n"), name); 124 break; 125 } 126 127 /* No need to canon(name), getcmd() did it for us */ 128 (void) strncpy(curdir, name, sizeof (curdir)); 129 curdir[sizeof (curdir) - 1] = '\0'; 130 break; 131 /* 132 * Delete elements from the extraction list. 133 */ 134 case 'd': 135 if (strncmp(cmd, "delete", strlen(cmd)) != 0) 136 goto bad; 137 if (name[0] == '\0') 138 break; 139 np = lookupname(name); 140 if (np == NIL || (np->e_flags & NEW) == 0) { 141 (void) fprintf(stderr, 142 gettext("%s: not on extraction list\n"), name); 143 break; 144 } 145 treescan(name, np->e_ino, deletefile); 146 break; 147 /* 148 * Extract the requested list. 149 */ 150 case 'e': 151 if (strncmp(cmd, "extract", strlen(cmd)) != 0) 152 goto bad; 153 attrscan(0, addfile); 154 createfiles(); 155 createlinks(); 156 setdirmodes(); 157 if (dflag) 158 checkrestore(); 159 volno = 0; 160 break; 161 /* 162 * List available commands. 163 */ 164 case 'h': 165 if (strncmp(cmd, "help", strlen(cmd)) != 0) 166 goto bad; 167 /*FALLTHROUGH*/ 168 case '?': 169 /* ANSI string catenation, to shut cstyle up */ 170 (void) fprintf(stderr, "%s", 171 gettext("Available commands are:\n" 172 "\tls [arg] - list directory\n" 173 "\tmarked [arg] - list items marked for extraction from directory\n" 174 "\tcd arg - change directory\n" 175 "\tpwd - print current directory\n" 176 "\tadd [arg] - add `arg' to list of files to be extracted\n" 177 "\tdelete [arg] - delete `arg' from list of files to be extracted\n" 178 "\textract - extract requested files\n" 179 "\tsetmodes - set modes of requested directories\n" 180 "\tquit - immediately exit program\n" 181 "\twhat - list dump header information\n" 182 "\tverbose - toggle verbose flag (useful with ``ls'')\n" 183 "\tpaginate - toggle pagination flag (affects ``ls'' and ``marked'')\n" 184 "\tsetpager - set pagination command and arguments\n" 185 "\thelp or `?' - print this list\n" 186 "If no `arg' is supplied, the current directory is used\n")); 187 break; 188 /* 189 * List a directory. 190 */ 191 case 'l': 192 case 'm': 193 if ((strncmp(cmd, "ls", strlen(cmd)) != 0) && 194 (strncmp(cmd, "marked", strlen(cmd)) != 0)) 195 goto bad; 196 if (name[0] == '\0') 197 break; 198 ino = dirlookup(name); 199 if (ino == 0) 200 break; 201 printlist(name, ino, curdir, *cmd == 'm'); 202 break; 203 /* 204 * Print current directory or enable pagination. 205 */ 206 case 'p': 207 if (strlen(cmd) < 2) 208 goto ambiguous; 209 if (strncmp(cmd, "pwd", strlen(cmd)) == 0) { 210 if (curdir[1] == '\0') { 211 (void) fprintf(stderr, "/\n"); 212 } else { 213 (void) fprintf(stderr, "%s\n", &curdir[1]); 214 } 215 } else if (strncmp(cmd, "paginate", strlen(cmd)) == 0) { 216 if (paginating) { 217 (void) fprintf(stderr, 218 gettext("paging disabled\n")); 219 paginating = 0; 220 break; 221 } 222 if (vflag) { 223 (void) fprintf(stderr, 224 gettext("paging enabled (%s)\n"), 225 pager_catenated); 226 } else { 227 (void) fprintf(stderr, 228 gettext("paging enabled\n")); 229 } 230 if (dflag) { 231 int index = 0; 232 233 while (index < pager_len) { 234 (void) fprintf(stderr, 235 ">>>pager_vector[%d] = `%s'\n", 236 index, 237 pager_vector[index] ? 238 pager_vector[index] : "(null)"); 239 index += 1; 240 } 241 } 242 paginating = 1; 243 } else { 244 goto bad; 245 } 246 break; 247 /* 248 * Quit. 249 */ 250 case 'q': 251 if (strncmp(cmd, "quit", strlen(cmd)) != 0) 252 goto bad; 253 reset_OK = 0; 254 return; 255 case 'x': 256 if (strncmp(cmd, "xit", strlen(cmd)) != 0) 257 goto bad; 258 reset_OK = 0; 259 return; 260 /* 261 * Toggle verbose mode. 262 */ 263 case 'v': 264 if (strncmp(cmd, "verbose", strlen(cmd)) != 0) 265 goto bad; 266 if (vflag) { 267 (void) fprintf(stderr, gettext("verbose mode off\n")); 268 vflag = 0; 269 break; 270 } 271 (void) fprintf(stderr, gettext("verbose mode on\n")); 272 vflag = 1; 273 break; 274 /* 275 * Just restore requested directory modes, or set pagination command. 276 */ 277 case 's': 278 if (strlen(cmd) < 4) 279 goto ambiguous; 280 if (strncmp(cmd, "setmodes", strlen(cmd)) == 0) { 281 setdirmodes(); 282 } else if (strncmp(cmd, "setpager", strlen(cmd)) == 0) { 283 setpagercmd(); 284 } else { 285 goto bad; 286 } 287 break; 288 /* 289 * Print out dump header information. 290 */ 291 case 'w': 292 if (strncmp(cmd, "what", strlen(cmd)) != 0) 293 goto bad; 294 printdumpinfo(); 295 break; 296 /* 297 * Turn on debugging. 298 */ 299 case 'D': 300 if (strncmp(cmd, "Debug", strlen(cmd)) != 0) 301 goto bad; 302 if (dflag) { 303 (void) fprintf(stderr, gettext("debugging mode off\n")); 304 dflag = 0; 305 break; 306 } 307 (void) fprintf(stderr, gettext("debugging mode on\n")); 308 dflag++; 309 break; 310 /* 311 * Unknown command. 312 */ 313 default: 314 bad: 315 (void) fprintf(stderr, 316 gettext("%s: unknown command; type ? for help\n"), cmd); 317 break; 318 ambiguous: 319 (void) fprintf(stderr, 320 gettext("%s: ambiguous command; type ? for help\n"), cmd); 321 break; 322 } 323 goto loop; 324 } 325 326 static char input[MAXCOMPLEXLEN]; /* shared by getcmd() and setpagercmd() */ 327 #define rawname input /* save space by reusing input buffer */ 328 329 /* 330 * Read and parse an interactive command. 331 * The first word on the line is assigned to "cmd". If 332 * there are no arguments on the command line, then "curdir" 333 * is returned as the argument. If there are arguments 334 * on the line they are returned one at a time on each 335 * successive call to getcmd. Each argument is first assigned 336 * to "name". If it does not start with "/" the pathname in 337 * "curdir" is prepended to it. Finally "canon" is called to 338 * eliminate any embedded ".." components. 339 */ 340 /* ARGSUSED */ 341 static void 342 getcmd(curdir, cmd, cmdsiz, name, namesiz, ap) 343 char *curdir, *cmd, *name; 344 size_t cmdsiz, namesiz; 345 struct arglist *ap; 346 { 347 char *cp; 348 char output[MAXCOMPLEXLEN]; 349 350 /* 351 * Check to see if still processing arguments. 352 */ 353 if (ap->head != ap->last) { 354 (void) strncpy(name, ap->head->fname, namesiz); 355 name[namesiz - 1] = '\0'; 356 /* double null terminate string */ 357 if ((strlen(name) + 2) > namesiz) { 358 fprintf(stderr, gettext("name is too long, ignoring")); 359 memset(name, 0, namesiz); 360 } else { 361 name[strlen(name) + 1] = '\0'; 362 } 363 freename(ap->head->fname); 364 ap->head++; 365 return; 366 } 367 if (nextarg != NULL) 368 goto getnext; 369 /* 370 * Read a command line and trim off trailing white space. 371 */ 372 readagain: 373 do { 374 (void) fprintf(stderr, "%s > ", progname); 375 (void) fflush(stderr); 376 (void) fgets(input, sizeof (input), terminal); 377 } while (!feof(terminal) && input[0] == '\n'); 378 if (feof(terminal)) { 379 (void) strncpy(cmd, "quit", cmdsiz); 380 return; 381 } 382 /* trim off trailing white space and newline */ 383 for (cp = &input[strlen(input) - 2]; 384 cp >= &input[0] && isspace((uchar_t)*cp); 385 cp--) { 386 continue; 387 /*LINTED [empty loop body]*/ 388 } 389 *++cp = '\0'; 390 if ((strlen(input) + 2) > MAXCOMPLEXLEN) { 391 fprintf(stderr, gettext("command is too long\n")); 392 goto readagain; 393 } else { 394 /* double null terminate string */ 395 *(cp + 1) = '\0'; 396 } 397 398 if (cp == &input[0]) 399 goto readagain; 400 401 /* 402 * Copy the command into "cmd". 403 */ 404 cp = copynext(input, cmd, cmdsiz); 405 ap->cmd = cmd; 406 /* 407 * If no argument, use curdir as the default. 408 */ 409 if (*cp == '\0') { 410 (void) strncpy(name, curdir, namesiz); 411 name[namesiz - 1] = '\0'; 412 /* double null terminate string */ 413 if ((strlen(name) + 2) > namesiz) { 414 fprintf(stderr, gettext("name is too long, ignoring")); 415 memset(name, 0, namesiz); 416 } else { 417 name[strlen(name) + 1] = '\0'; 418 } 419 return; 420 } 421 nextarg = cp; 422 /* 423 * Find the next argument. 424 */ 425 getnext: 426 cp = copynext(nextarg, rawname, sizeof (rawname)); 427 if (*cp == '\0') 428 nextarg = NULL; 429 else 430 nextarg = cp; 431 /* 432 * If it an absolute pathname, canonicalize it and return it. 433 */ 434 if (rawname[0] == '/') { 435 canon(rawname, name, namesiz); 436 } else { 437 /* 438 * For relative pathnames, prepend the current directory to 439 * it then canonicalize and return it. 440 */ 441 (void) snprintf(output, sizeof (output), "%s/%s", 442 curdir, rawname); 443 canon(output, name, namesiz); 444 } 445 expandarg(name, ap); 446 /* 447 * ap->head->fname guaranteed to be double null-terminated and 448 * no more than MAXCOMPLEXLEN characters long. 449 */ 450 assert(namesiz >= (MAXCOMPLEXLEN)); 451 (void) strcpy(name, ap->head->fname); 452 /* double null terminate string */ 453 name[strlen(name) + 1] = '\0'; 454 freename(ap->head->fname); 455 ap->head++; 456 #undef rawname 457 } 458 459 /* 460 * Strip off the next token of the input. 461 */ 462 static char * 463 copynext(input, output, outsize) 464 char *input, *output; 465 size_t outsize; 466 { 467 char *cp, *bp, *limit; 468 char quote; 469 470 dontexpand = 0; 471 /* skip to argument */ 472 for (cp = input; *cp != '\0' && isspace((uchar_t)*cp); cp++) { 473 continue; 474 /*LINTED [empty loop body]*/ 475 } 476 bp = output; 477 limit = output + outsize - 1; /* -1 for the trailing \0 */ 478 while (!isspace((uchar_t)*cp) && *cp != '\0' && bp < limit) { 479 /* 480 * Handle back slashes. 481 */ 482 if (*cp == '\\') { 483 if (*++cp == '\0') { 484 (void) fprintf(stderr, gettext( 485 "command lines cannot be continued\n")); 486 continue; 487 } 488 *bp++ = *cp++; 489 continue; 490 } 491 /* 492 * The usual unquoted case. 493 */ 494 if (*cp != '\'' && *cp != '"') { 495 *bp++ = *cp++; 496 continue; 497 } 498 /* 499 * Handle single and double quotes. 500 */ 501 quote = *cp++; 502 dontexpand = 1; 503 while (*cp != quote && *cp != '\0' && bp < limit) 504 *bp++ = *cp++; 505 if (*cp++ == '\0') { 506 (void) fprintf(stderr, 507 gettext("missing %c\n"), (uchar_t)quote); 508 cp--; 509 continue; 510 } 511 } 512 *bp = '\0'; 513 if ((strlen(output) + 2) > outsize) { 514 fprintf(stderr, gettext( 515 "name is too long, ignoring")); 516 memset(output, 0, outsize); 517 } else { 518 /* double null terminate string */ 519 *(bp + 1) = '\0'; 520 } 521 return (cp); 522 } 523 524 /* 525 * Canonicalize file names to always start with ``./'' and 526 * remove any imbedded "." and ".." components. 527 * 528 * The pathname "canonname" is returned double null terminated. 529 */ 530 void 531 canon(rawname, canonname, limit) 532 char *rawname, *canonname; 533 size_t limit; 534 { 535 char *cp, *np, *prefix; 536 uint_t len; 537 538 assert(limit > 3); 539 if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0) 540 prefix = ""; 541 else if (rawname[0] == '/') 542 prefix = "."; 543 else 544 prefix = "./"; 545 (void) snprintf(canonname, limit, "%s%s", prefix, rawname); 546 /* 547 * Eliminate multiple and trailing '/'s 548 */ 549 for (cp = np = canonname; *np != '\0'; cp++) { 550 *cp = *np++; 551 while (*cp == '/' && *np == '/') 552 np++; 553 } 554 *cp = '\0'; 555 if ((strlen(canonname) + 2) > limit) { 556 fprintf(stderr, 557 gettext("canonical name is too long, ignoring name\n")); 558 memset(canonname, 0, limit); 559 } else { 560 /* double null terminate string */ 561 *(cp + 1) = '\0'; 562 } 563 564 if (*--cp == '/') 565 *cp = '\0'; 566 /* 567 * Eliminate extraneous "." and ".." from pathnames. Uses 568 * memmove(), as strcpy() might do the wrong thing for these 569 * small overlaps. 570 */ 571 np = canonname; 572 while (*np != '\0') { 573 np++; 574 cp = np; 575 while (*np != '/' && *np != '\0') 576 np++; 577 if (np - cp == 1 && *cp == '.') { 578 cp--; 579 len = strlen(np); 580 (void) memmove(cp, np, len); 581 *(cp + len) = '\0'; 582 /* double null terminate string */ 583 *(cp + len + 1) = '\0'; 584 np = cp; 585 } 586 if (np - cp == 2 && strncmp(cp, "..", 2) == 0) { 587 cp--; 588 /* find beginning of name */ 589 while (cp > &canonname[1] && *--cp != '/') { 590 continue; 591 /*LINTED [empty loop body]*/ 592 } 593 len = strlen(np); 594 (void) memmove(cp, np, len); 595 *(cp + len) = '\0'; 596 /* double null terminate string */ 597 *(cp + len + 1) = '\0'; 598 np = cp; 599 } 600 } 601 } 602 603 /* 604 * globals (file name generation) 605 * 606 * "*" in params matches r.e ".*" 607 * "?" in params matches r.e. "." 608 * "[...]" in params matches character class 609 * "[...a-z...]" in params matches a through z. 610 */ 611 static void 612 expandarg(arg, ap) 613 char *arg; 614 struct arglist *ap; 615 { 616 static struct afile single; 617 int size; 618 619 ap->head = ap->last = (struct afile *)0; 620 if (dontexpand) 621 size = 0; 622 else 623 size = expand(arg, 0, ap); 624 if (size == 0) { 625 struct entry *ep; 626 627 ep = lookupname(arg); 628 single.fnum = ep ? ep->e_ino : 0; 629 single.fname = savename(arg); 630 ap->head = &single; 631 ap->last = ap->head + 1; 632 return; 633 } 634 if ((ap->last - ap->head) > ULONG_MAX) { 635 (void) fprintf(stderr, 636 gettext("Argument expansion too large to sort\n")); 637 } else { 638 /* LINTED pointer arith just range-checked */ 639 qsort((char *)ap->head, (size_t)(ap->last - ap->head), 640 sizeof (*ap->head), 641 (int (*)(const void *, const void *)) fcmp); 642 } 643 } 644 645 /* 646 * Do an "ls" style listing of a directory 647 */ 648 static void 649 printlist(name, ino, basename, marked_only) 650 char *name; 651 ino_t ino; 652 char *basename; 653 int marked_only; 654 { 655 struct afile *fp; 656 struct direct *dp; 657 static struct arglist alist = { 0, 0, 0, 0, "ls" }; 658 struct afile single; 659 struct entry *np; 660 RST_DIR *dirp; 661 int list_entry; 662 663 if ((dirp = rst_opendir(name)) == NULL) { 664 single.fnum = ino; 665 if (strncmp(name, basename, strlen(basename)) == 0) 666 single.fname = savename(name + strlen(basename) + 1); 667 else 668 single.fname = savename(name); 669 alist.head = &single; 670 alist.last = alist.head + 1; 671 if (alist.base != NULL) { 672 free(alist.base); 673 alist.base = NULL; 674 } 675 } else { 676 alist.head = (struct afile *)0; 677 (void) fprintf(stderr, "%s:\n", name); 678 while (dp = rst_readdir(dirp)) { 679 if (dp == NULL || dp->d_ino == 0) { 680 rst_closedir(dirp); 681 dirp = NULL; 682 break; 683 } 684 if (!dflag && BIT(dp->d_ino, dumpmap) == 0) 685 continue; 686 if (vflag == 0 && 687 (strcmp(dp->d_name, ".") == 0 || 688 strcmp(dp->d_name, "..") == 0)) 689 continue; 690 list_entry = 1; 691 if (marked_only) { 692 np = lookupino(dp->d_ino); 693 if ((np == NIL) || ((np->e_flags & NEW) == 0)) 694 list_entry = 0; 695 } 696 if (list_entry) { 697 if (!mkentry(dp->d_name, dp->d_ino, &alist)) { 698 rst_closedir(dirp); 699 return; 700 } 701 } 702 } 703 } 704 if (alist.head != 0) { 705 if ((alist.last - alist.head) > ULONG_MAX) { 706 (void) fprintf(stderr, 707 gettext("Directory too large to sort\n")); 708 } else { 709 qsort((char *)alist.head, 710 /* LINTED range-checked */ 711 (size_t)(alist.last - alist.head), 712 sizeof (*alist.head), 713 (int (*)(const void *, const void *)) fcmp); 714 } 715 formatf(&alist); 716 for (fp = alist.head; fp < alist.last; fp++) 717 freename(fp->fname); 718 alist.head = NULL; 719 /* 720 * Don't free alist.base, as we'll probably be called 721 * again, and might as well re-use what we've got. 722 */ 723 } 724 if (dirp != NULL) { 725 (void) fprintf(stderr, "\n"); 726 rst_closedir(dirp); 727 } 728 } 729 730 /* 731 * Print out a pretty listing of a directory 732 */ 733 static void 734 formatf(ap) 735 struct arglist *ap; 736 { 737 struct afile *fp; 738 struct entry *np; 739 /* LINTED: result fits into an int */ 740 int nentry = (int)(ap->last - ap->head); 741 int i, j; 742 uint_t len, w, width = 0, columns, lines; 743 char *cp; 744 FILE *output = stderr; 745 746 if (ap->head == ap->last) 747 return; 748 749 if (paginating) { 750 int fds[2]; 751 752 if (pipe(fds) < 0) { 753 perror(gettext("could not create pipe")); 754 goto no_page; 755 } 756 757 switch (fork()) { 758 case -1: 759 perror(gettext("could not fork")); 760 goto no_page; 761 case 0: 762 /* 763 * Make sure final output still ends up in 764 * the same place. 765 */ 766 (void) dup2(fileno(stderr), fileno(stdout)); 767 (void) close(fds[0]); 768 (void) dup2(fds[1], fileno(stdin)); 769 execvp(pager_vector[0], pager_vector); 770 perror(gettext("execvp of pager failed")); 771 exit(1); 772 /*NOTREACHED*/ 773 default: 774 (void) close(fds[1]); 775 output = fdopen(fds[0], "w"); 776 if (output != (FILE *)NULL) { 777 break; 778 } 779 perror(gettext("could not open pipe to pager")); 780 output = stderr; 781 no_page: 782 (void) fprintf(stderr, 783 gettext("pagination disabled\n")); 784 paginating = 0; 785 } 786 } 787 788 for (fp = ap->head; fp < ap->last; fp++) { 789 fp->ftype = inodetype(fp->fnum); 790 np = lookupino(fp->fnum); 791 if (np != NIL) 792 fp->fflags = np->e_flags; 793 else 794 fp->fflags = 0; 795 len = strlen(fmtentry(fp)); 796 if (len > width) 797 width = len; 798 } 799 width += 2; 800 columns = 80 / width; 801 if (columns == 0) 802 columns = 1; 803 lines = (nentry + columns - 1) / columns; 804 for (i = 0; i < lines && !ferror(output); i++) { 805 for (j = 0; j < columns && !ferror(output); j++) { 806 fp = ap->head + j * lines + i; 807 cp = fmtentry(fp); 808 (void) fprintf(output, "%s", cp); 809 if (fp + lines >= ap->last) { 810 (void) fprintf(output, "\n"); 811 break; 812 } 813 w = strlen(cp); 814 while (w < width) { 815 w++; 816 if (fprintf(output, " ") < 0) 817 break; 818 } 819 } 820 } 821 822 if (paginating) { 823 (void) fclose(output); 824 (void) wait((int *)NULL); 825 } 826 } 827 828 /* 829 * Comparison routine for qsort. 830 */ 831 static int 832 fcmp(f1, f2) 833 struct afile *f1, *f2; 834 { 835 836 return (strcoll(f1->fname, f2->fname)); 837 } 838 839 /* 840 * Format a directory entry. 841 */ 842 static char * 843 fmtentry(fp) 844 struct afile *fp; 845 { 846 static char fmtres[MAXCOMPLEXLEN]; 847 static int precision = 0; 848 ino_t i; 849 char *cp, *dp, *limit; 850 851 if (!vflag) { 852 /* MAXCOMPLEXLEN assumed to be >= 1 */ 853 fmtres[0] = '\0'; 854 } else { 855 if (precision == 0) { 856 for (i = maxino; i != 0; i /= 10) 857 precision++; 858 if (sizeof (fmtres) < (unsigned)(precision + 2)) { 859 (void) fprintf(stderr, gettext( 860 "\nInternal check failed, minimum width %d exceeds available size %d\n"), 861 (precision + 2), sizeof (fmtres)); 862 done(1); 863 } 864 } 865 (void) snprintf(fmtres, sizeof (fmtres), "%*ld ", 866 precision, fp->fnum); 867 } 868 dp = &fmtres[strlen(fmtres)]; 869 limit = fmtres + sizeof (fmtres) - 1; 870 if (dflag && BIT(fp->fnum, dumpmap) == 0) 871 *dp++ = '^'; 872 else if ((fp->fflags & NEW) != 0) 873 *dp++ = '*'; 874 else 875 *dp++ = ' '; 876 for (cp = fp->fname; *cp && dp < limit; cp++) 877 /* LINTED: precedence ok, can't fix system macro */ 878 if (!vflag && (!ISPRINT(*cp, wp))) 879 *dp++ = '?'; 880 else 881 *dp++ = *cp; 882 if (fp->ftype == NODE && dp < limit) 883 *dp++ = '/'; 884 *dp++ = 0; 885 return (fmtres); 886 } 887 888 /* 889 * respond to interrupts 890 */ 891 /* ARGSUSED */ 892 void 893 onintr(sig) 894 int sig; 895 { 896 char buf[300]; 897 898 if (command == 'i' && reset_OK) 899 longjmp(reset, 1); 900 901 (void) snprintf(buf, sizeof (buf), 902 gettext("%s interrupted, continue"), progname); 903 if (reply(buf) == FAIL) 904 done(1); 905 } 906 /* 907 * Set up pager_catenated and pager_vector. 908 */ 909 void 910 #ifdef __STDC__ 911 initpagercmd(void) 912 #else 913 initpagercmd() 914 #endif 915 { 916 char *cp; 917 918 cp = getenv("PAGER"); 919 if (cp != NULL) 920 pager_catenated = strdup(cp); 921 if ((pager_catenated == NULL) || (*pager_catenated == '\0')) { 922 if (pager_catenated != NULL) 923 free(pager_catenated); 924 pager_catenated = strdup(DEF_PAGER); 925 } 926 if (pager_catenated == NULL) { 927 (void) fprintf(stderr, gettext("out of memory\n")); 928 done(1); 929 } 930 931 pager_vector = (char **)malloc(sizeof (char *)); 932 if (pager_vector == NULL) { 933 (void) fprintf(stderr, gettext("out of memory\n")); 934 done(1); 935 } 936 937 pager_len = 1; 938 cp = pager_catenated; 939 (void) setpagerargs(&cp); 940 } 941 942 943 /* 944 * Resets pager_catenated and pager_vector from user input. 945 */ 946 void 947 #ifdef __STDC__ 948 setpagercmd(void) 949 #else 950 setpagercmd() 951 #endif 952 { 953 uint_t catenate_length; 954 int index; 955 956 /* 957 * We'll get called immediately after setting a pager, due to 958 * our interaction with getcmd()'s internal state. Don't do 959 * anything when that happens. 960 */ 961 if (*input == '\0') 962 return; 963 964 if (pager_len > 0) { 965 for (index = 0; pager_vector[index] != (char *)NULL; index += 1) 966 free(pager_vector[index]); 967 free(pager_vector); 968 free(pager_catenated); 969 } 970 971 pager_vector = (char **)malloc(2 * sizeof (char *)); 972 if (pager_vector == NULL) { 973 (void) fprintf(stderr, gettext("out of memory\n")); 974 done(1); 975 } 976 977 pager_len = 2; 978 pager_vector[0] = strdup(input); 979 if (pager_vector[0] == NULL) { 980 (void) fprintf(stderr, gettext("out of memory\n")); 981 done(1); 982 } 983 if (dflag) 984 (void) fprintf(stderr, gettext("got command `%s'\n"), input); 985 catenate_length = setpagerargs(&nextarg) + strlen(pager_vector[0]) + 1; 986 pager_catenated = (char *)malloc(catenate_length * 987 (size_t)sizeof (char)); 988 if (pager_catenated == (char *)NULL) { 989 (void) fprintf(stderr, gettext("out of memory\n")); 990 done(1); 991 } 992 for (index = 0; pager_vector[index] != (char *)NULL; index += 1) { 993 if (index > 0) 994 (void) strcat(pager_catenated, " "); 995 (void) strcat(pager_catenated, pager_vector[index]); 996 } 997 } 998 999 1000 /* 1001 * Extract arguments for the pager command from getcmd()'s input buffer. 1002 */ 1003 static uint_t 1004 setpagerargs(source) 1005 char **source; 1006 { 1007 char word[MAXCOMPLEXLEN]; 1008 char *cp = *source; 1009 uint_t length = 0; 1010 1011 while ((cp != (char *)NULL) && (*cp != '\0')) { 1012 cp = copynext(cp, word, sizeof (word)); 1013 if (dflag) 1014 fprintf(stderr, gettext("got word `%s'\n"), word); 1015 pager_vector = (char **)realloc(pager_vector, 1016 (size_t)sizeof (char *) * (pager_len + 1)); 1017 if (pager_vector == (char **)NULL) { 1018 (void) fprintf(stderr, gettext("out of memory\n")); 1019 done(1); 1020 } 1021 pager_vector[pager_len - 1] = strdup(word); 1022 if (pager_vector[pager_len - 1] == (char *)NULL) { 1023 (void) fprintf(stderr, gettext("out of memory\n")); 1024 done(1); 1025 } 1026 length += strlen(word) + 1; 1027 pager_len += 1; 1028 } 1029 pager_vector[pager_len - 1] = (char *)NULL; 1030 *source = cp; 1031 return (length); 1032 } 1033