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