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