1 /* 2 * Copyright (c) 1985 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)interactive.c 5.16 (Berkeley) 10/06/92"; 10 #endif /* not lint */ 11 12 #include "restore.h" 13 #include <protocols/dumprestore.h> 14 #include <setjmp.h> 15 #include <ufs/ufs/dir.h> 16 17 #define round(a, b) (((a) + (b) - 1) / (b) * (b)) 18 19 /* 20 * Things to handle interruptions. 21 */ 22 static int runshell; 23 static jmp_buf reset; 24 static char *nextarg = NULL; 25 26 /* 27 * Structure and routines associated with listing directories. 28 */ 29 struct afile { 30 ino_t fnum; /* inode number of file */ 31 char *fname; /* file name */ 32 short fflags; /* extraction flags, if any */ 33 char ftype; /* file type, e.g. LEAF or NODE */ 34 char finotype; /* file type specified in directory entry */ 35 }; 36 struct arglist { 37 struct afile *head; /* start of argument list */ 38 struct afile *last; /* end of argument list */ 39 struct afile *base; /* current list arena */ 40 int nent; /* maximum size of list */ 41 char *cmd; /* the current command */ 42 }; 43 extern int fcmp(); 44 extern char *fmtentry(); 45 char *copynext(); 46 47 /* 48 * Read and execute commands from the terminal. 49 */ 50 runcmdshell() 51 { 52 register struct entry *np; 53 ino_t ino; 54 static struct arglist alist = { 0, 0, 0, 0, 0 }; 55 char curdir[MAXPATHLEN]; 56 char name[MAXPATHLEN]; 57 char cmd[BUFSIZ]; 58 59 canon("/", curdir); 60 loop: 61 if (setjmp(reset) != 0) { 62 for (; alist.head < alist.last; alist.head++) 63 freename(alist.head->fname); 64 nextarg = NULL; 65 volno = 0; 66 } 67 runshell = 1; 68 getcmd(curdir, cmd, name, &alist); 69 switch (cmd[0]) { 70 /* 71 * Add elements to the extraction list. 72 */ 73 case 'a': 74 if (strncmp(cmd, "add", strlen(cmd)) != 0) 75 goto bad; 76 ino = dirlookup(name); 77 if (ino == 0) 78 break; 79 if (mflag) 80 pathcheck(name); 81 treescan(name, ino, addfile); 82 break; 83 /* 84 * Change working directory. 85 */ 86 case 'c': 87 if (strncmp(cmd, "cd", strlen(cmd)) != 0) 88 goto bad; 89 ino = dirlookup(name); 90 if (ino == 0) 91 break; 92 if (inodetype(ino) == LEAF) { 93 fprintf(stderr, "%s: not a directory\n", name); 94 break; 95 } 96 (void) strcpy(curdir, name); 97 break; 98 /* 99 * Delete elements from the extraction list. 100 */ 101 case 'd': 102 if (strncmp(cmd, "delete", strlen(cmd)) != 0) 103 goto bad; 104 np = lookupname(name); 105 if (np == NIL || (np->e_flags & NEW) == 0) { 106 fprintf(stderr, "%s: not on extraction list\n", name); 107 break; 108 } 109 treescan(name, np->e_ino, deletefile); 110 break; 111 /* 112 * Extract the requested list. 113 */ 114 case 'e': 115 if (strncmp(cmd, "extract", strlen(cmd)) != 0) 116 goto bad; 117 createfiles(); 118 createlinks(); 119 setdirmodes(0); 120 if (dflag) 121 checkrestore(); 122 volno = 0; 123 break; 124 /* 125 * List available commands. 126 */ 127 case 'h': 128 if (strncmp(cmd, "help", strlen(cmd)) != 0) 129 goto bad; 130 case '?': 131 fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 132 "Available commands are:\n", 133 "\tls [arg] - list directory\n", 134 "\tcd arg - change directory\n", 135 "\tpwd - print current directory\n", 136 "\tadd [arg] - add `arg' to list of", 137 " files to be extracted\n", 138 "\tdelete [arg] - delete `arg' from", 139 " list of files to be extracted\n", 140 "\textract - extract requested files\n", 141 "\tsetmodes - set modes of requested directories\n", 142 "\tquit - immediately exit program\n", 143 "\twhat - list dump header information\n", 144 "\tverbose - toggle verbose flag", 145 " (useful with ``ls'')\n", 146 "\thelp or `?' - print this list\n", 147 "If no `arg' is supplied, the current", 148 " directory is used\n"); 149 break; 150 /* 151 * List a directory. 152 */ 153 case 'l': 154 if (strncmp(cmd, "ls", strlen(cmd)) != 0) 155 goto bad; 156 ino = dirlookup(name); 157 if (ino == 0) 158 break; 159 printlist(name, ino, curdir); 160 break; 161 /* 162 * Print current directory. 163 */ 164 case 'p': 165 if (strncmp(cmd, "pwd", strlen(cmd)) != 0) 166 goto bad; 167 if (curdir[1] == '\0') 168 fprintf(stderr, "/\n"); 169 else 170 fprintf(stderr, "%s\n", &curdir[1]); 171 break; 172 /* 173 * Quit. 174 */ 175 case 'q': 176 if (strncmp(cmd, "quit", strlen(cmd)) != 0) 177 goto bad; 178 return; 179 case 'x': 180 if (strncmp(cmd, "xit", strlen(cmd)) != 0) 181 goto bad; 182 return; 183 /* 184 * Toggle verbose mode. 185 */ 186 case 'v': 187 if (strncmp(cmd, "verbose", strlen(cmd)) != 0) 188 goto bad; 189 if (vflag) { 190 fprintf(stderr, "verbose mode off\n"); 191 vflag = 0; 192 break; 193 } 194 fprintf(stderr, "verbose mode on\n"); 195 vflag++; 196 break; 197 /* 198 * Just restore requested directory modes. 199 */ 200 case 's': 201 if (strncmp(cmd, "setmodes", strlen(cmd)) != 0) 202 goto bad; 203 setdirmodes(FORCE); 204 break; 205 /* 206 * Print out dump header information. 207 */ 208 case 'w': 209 if (strncmp(cmd, "what", strlen(cmd)) != 0) 210 goto bad; 211 printdumpinfo(); 212 break; 213 /* 214 * Turn on debugging. 215 */ 216 case 'D': 217 if (strncmp(cmd, "Debug", strlen(cmd)) != 0) 218 goto bad; 219 if (dflag) { 220 fprintf(stderr, "debugging mode off\n"); 221 dflag = 0; 222 break; 223 } 224 fprintf(stderr, "debugging mode on\n"); 225 dflag++; 226 break; 227 /* 228 * Unknown command. 229 */ 230 default: 231 bad: 232 fprintf(stderr, "%s: unknown command; type ? for help\n", cmd); 233 break; 234 } 235 goto loop; 236 } 237 238 /* 239 * Read and parse an interactive command. 240 * The first word on the line is assigned to "cmd". If 241 * there are no arguments on the command line, then "curdir" 242 * is returned as the argument. If there are arguments 243 * on the line they are returned one at a time on each 244 * successive call to getcmd. Each argument is first assigned 245 * to "name". If it does not start with "/" the pathname in 246 * "curdir" is prepended to it. Finally "canon" is called to 247 * eliminate any embedded ".." components. 248 */ 249 getcmd(curdir, cmd, name, ap) 250 char *curdir, *cmd, *name; 251 struct arglist *ap; 252 { 253 register char *cp; 254 static char input[BUFSIZ]; 255 char output[BUFSIZ]; 256 # define rawname input /* save space by reusing input buffer */ 257 258 /* 259 * Check to see if still processing arguments. 260 */ 261 if (ap->head != ap->last) { 262 strcpy(name, ap->head->fname); 263 freename(ap->head->fname); 264 ap->head++; 265 return; 266 } 267 if (nextarg != NULL) 268 goto getnext; 269 /* 270 * Read a command line and trim off trailing white space. 271 */ 272 do { 273 fprintf(stderr, "restore > "); 274 (void) fflush(stderr); 275 (void) fgets(input, BUFSIZ, terminal); 276 } while (!feof(terminal) && input[0] == '\n'); 277 if (feof(terminal)) { 278 (void) strcpy(cmd, "quit"); 279 return; 280 } 281 for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--) 282 /* trim off trailing white space and newline */; 283 *++cp = '\0'; 284 /* 285 * Copy the command into "cmd". 286 */ 287 cp = copynext(input, cmd); 288 ap->cmd = cmd; 289 /* 290 * If no argument, use curdir as the default. 291 */ 292 if (*cp == '\0') { 293 (void) strcpy(name, curdir); 294 return; 295 } 296 nextarg = cp; 297 /* 298 * Find the next argument. 299 */ 300 getnext: 301 cp = copynext(nextarg, rawname); 302 if (*cp == '\0') 303 nextarg = NULL; 304 else 305 nextarg = cp; 306 /* 307 * If it an absolute pathname, canonicalize it and return it. 308 */ 309 if (rawname[0] == '/') { 310 canon(rawname, name); 311 } else { 312 /* 313 * For relative pathnames, prepend the current directory to 314 * it then canonicalize and return it. 315 */ 316 (void) strcpy(output, curdir); 317 (void) strcat(output, "/"); 318 (void) strcat(output, rawname); 319 canon(output, name); 320 } 321 expandarg(name, ap); 322 strcpy(name, ap->head->fname); 323 freename(ap->head->fname); 324 ap->head++; 325 # undef rawname 326 } 327 328 /* 329 * Strip off the next token of the input. 330 */ 331 char * 332 copynext(input, output) 333 char *input, *output; 334 { 335 register char *cp, *bp; 336 char quote; 337 338 for (cp = input; *cp == ' ' || *cp == '\t'; cp++) 339 /* skip to argument */; 340 bp = output; 341 while (*cp != ' ' && *cp != '\t' && *cp != '\0') { 342 /* 343 * Handle back slashes. 344 */ 345 if (*cp == '\\') { 346 if (*++cp == '\0') { 347 fprintf(stderr, 348 "command lines cannot be continued\n"); 349 continue; 350 } 351 *bp++ = *cp++; 352 continue; 353 } 354 /* 355 * The usual unquoted case. 356 */ 357 if (*cp != '\'' && *cp != '"') { 358 *bp++ = *cp++; 359 continue; 360 } 361 /* 362 * Handle single and double quotes. 363 */ 364 quote = *cp++; 365 while (*cp != quote && *cp != '\0') 366 *bp++ = *cp++ | 0200; 367 if (*cp++ == '\0') { 368 fprintf(stderr, "missing %c\n", quote); 369 cp--; 370 continue; 371 } 372 } 373 *bp = '\0'; 374 return (cp); 375 } 376 377 /* 378 * Canonicalize file names to always start with ``./'' and 379 * remove any imbedded "." and ".." components. 380 */ 381 canon(rawname, canonname) 382 char *rawname, *canonname; 383 { 384 register char *cp, *np; 385 int len; 386 387 if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0) 388 (void) strcpy(canonname, ""); 389 else if (rawname[0] == '/') 390 (void) strcpy(canonname, "."); 391 else 392 (void) strcpy(canonname, "./"); 393 (void) strcat(canonname, rawname); 394 /* 395 * Eliminate multiple and trailing '/'s 396 */ 397 for (cp = np = canonname; *np != '\0'; cp++) { 398 *cp = *np++; 399 while (*cp == '/' && *np == '/') 400 np++; 401 } 402 *cp = '\0'; 403 if (*--cp == '/') 404 *cp = '\0'; 405 /* 406 * Eliminate extraneous "." and ".." from pathnames. 407 */ 408 for (np = canonname; *np != '\0'; ) { 409 np++; 410 cp = np; 411 while (*np != '/' && *np != '\0') 412 np++; 413 if (np - cp == 1 && *cp == '.') { 414 cp--; 415 (void) strcpy(cp, np); 416 np = cp; 417 } 418 if (np - cp == 2 && strncmp(cp, "..", 2) == 0) { 419 cp--; 420 while (cp > &canonname[1] && *--cp != '/') 421 /* find beginning of name */; 422 (void) strcpy(cp, np); 423 np = cp; 424 } 425 } 426 } 427 428 /* 429 * globals (file name generation) 430 * 431 * "*" in params matches r.e ".*" 432 * "?" in params matches r.e. "." 433 * "[...]" in params matches character class 434 * "[...a-z...]" in params matches a through z. 435 */ 436 expandarg(arg, ap) 437 char *arg; 438 register struct arglist *ap; 439 { 440 static struct afile single; 441 struct entry *ep; 442 int size; 443 444 ap->head = ap->last = (struct afile *)0; 445 size = expand(arg, 0, ap); 446 if (size == 0) { 447 ep = lookupname(arg); 448 single.fnum = ep ? ep->e_ino : 0; 449 single.fname = savename(arg); 450 ap->head = &single; 451 ap->last = ap->head + 1; 452 return; 453 } 454 qsort((char *)ap->head, ap->last - ap->head, sizeof *ap->head, fcmp); 455 } 456 457 /* 458 * Expand a file name 459 */ 460 expand(as, rflg, ap) 461 char *as; 462 int rflg; 463 register struct arglist *ap; 464 { 465 int count, size; 466 char dir = 0; 467 char *rescan = 0; 468 RST_DIR *dirp; 469 register char *s, *cs; 470 int sindex, rindex, lindex; 471 struct direct *dp; 472 register char slash; 473 register char *rs; 474 register char c; 475 476 /* 477 * check for meta chars 478 */ 479 s = cs = as; 480 slash = 0; 481 while (*cs != '*' && *cs != '?' && *cs != '[') { 482 if (*cs++ == 0) { 483 if (rflg && slash) 484 break; 485 else 486 return (0) ; 487 } else if (*cs == '/') { 488 slash++; 489 } 490 } 491 for (;;) { 492 if (cs == s) { 493 s = ""; 494 break; 495 } else if (*--cs == '/') { 496 *cs = 0; 497 if (s == cs) 498 s = "/"; 499 break; 500 } 501 } 502 if ((dirp = rst_opendir(s)) != NULL) 503 dir++; 504 count = 0; 505 if (*cs == 0) 506 *cs++ = 0200; 507 if (dir) { 508 /* 509 * check for rescan 510 */ 511 rs = cs; 512 do { 513 if (*rs == '/') { 514 rescan = rs; 515 *rs = 0; 516 } 517 } while (*rs++); 518 sindex = ap->last - ap->head; 519 while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) { 520 if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) 521 continue; 522 if ((*dp->d_name == '.' && *cs != '.')) 523 continue; 524 if (gmatch(dp->d_name, cs)) { 525 if (addg(dp, s, rescan, ap) < 0) 526 return (-1); 527 count++; 528 } 529 } 530 if (rescan) { 531 rindex = sindex; 532 lindex = ap->last - ap->head; 533 if (count) { 534 count = 0; 535 while (rindex < lindex) { 536 size = expand(ap->head[rindex].fname, 537 1, ap); 538 if (size < 0) 539 return (size); 540 count += size; 541 rindex++; 542 } 543 } 544 bcopy((char *)&ap->head[lindex], 545 (char *)&ap->head[sindex], 546 (ap->last - &ap->head[rindex]) * sizeof *ap->head); 547 ap->last -= lindex - sindex; 548 *rescan = '/'; 549 } 550 } 551 s = as; 552 while (c = *s) 553 *s++ = (c&0177 ? c : '/'); 554 return (count); 555 } 556 557 /* 558 * Check for a name match 559 */ 560 gmatch(s, p) 561 register char *s, *p; 562 { 563 register int scc; 564 char c; 565 char ok; 566 int lc; 567 568 if (scc = *s++) 569 if ((scc &= 0177) == 0) 570 scc = 0200; 571 switch (c = *p++) { 572 573 case '[': 574 ok = 0; 575 lc = 077777; 576 while (c = *p++) { 577 if (c == ']') { 578 return (ok ? gmatch(s, p) : 0); 579 } else if (c == '-') { 580 if (lc <= scc && scc <= (*p++)) 581 ok++ ; 582 } else { 583 if (scc == (lc = (c&0177))) 584 ok++ ; 585 } 586 } 587 return (0); 588 589 default: 590 if ((c&0177) != scc) 591 return (0) ; 592 /* falls through */ 593 594 case '?': 595 return (scc ? gmatch(s, p) : 0); 596 597 case '*': 598 if (*p == 0) 599 return (1) ; 600 s--; 601 while (*s) { 602 if (gmatch(s++, p)) 603 return (1); 604 } 605 return (0); 606 607 case 0: 608 return (scc == 0); 609 } 610 } 611 612 /* 613 * Construct a matched name. 614 */ 615 addg(dp, as1, as3, ap) 616 struct direct *dp; 617 char *as1, *as3; 618 struct arglist *ap; 619 { 620 register char *s1, *s2; 621 register int c; 622 char buf[BUFSIZ]; 623 624 s2 = buf; 625 s1 = as1; 626 while (c = *s1++) { 627 if ((c &= 0177) == 0) { 628 *s2++ = '/'; 629 break; 630 } 631 *s2++ = c; 632 } 633 s1 = dp->d_name; 634 while (*s2 = *s1++) 635 s2++; 636 if (s1 = as3) { 637 *s2++ = '/'; 638 while (*s2++ = *++s1) 639 /* void */; 640 } 641 if (mkentry(buf, dp, ap) == FAIL) 642 return (-1); 643 } 644 645 /* 646 * Do an "ls" style listing of a directory 647 */ 648 printlist(name, ino, basename) 649 char *name; 650 ino_t ino; 651 char *basename; 652 { 653 register struct afile *fp; 654 register struct direct *dp; 655 static struct arglist alist = { 0, 0, 0, 0, "ls" }; 656 struct afile single; 657 RST_DIR *dirp; 658 659 if ((dirp = rst_opendir(name)) == NULL) { 660 single.fnum = ino; 661 single.finotype = DT_UNKNOWN; 662 single.fname = savename(name + strlen(basename) + 1); 663 alist.head = &single; 664 alist.last = alist.head + 1; 665 } else { 666 alist.head = (struct afile *)0; 667 fprintf(stderr, "%s:\n", name); 668 while (dp = rst_readdir(dirp)) { 669 if (dp == NULL || dp->d_ino == 0) 670 break; 671 if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) 672 continue; 673 if (vflag == 0 && 674 (strcmp(dp->d_name, ".") == 0 || 675 strcmp(dp->d_name, "..") == 0)) 676 continue; 677 if (!mkentry(dp->d_name, dp, &alist)) 678 return; 679 } 680 } 681 if (alist.head != 0) { 682 qsort((char *)alist.head, alist.last - alist.head, 683 sizeof *alist.head, fcmp); 684 formatf(&alist); 685 for (fp = alist.head; fp < alist.last; fp++) 686 freename(fp->fname); 687 } 688 if (dirp != NULL) 689 fprintf(stderr, "\n"); 690 } 691 692 /* 693 * Read the contents of a directory. 694 */ 695 mkentry(name, dp, ap) 696 char *name; 697 struct direct *dp; 698 register struct arglist *ap; 699 { 700 register struct afile *fp; 701 702 if (ap->base == NULL) { 703 ap->nent = 20; 704 ap->base = (struct afile *)calloc((unsigned)ap->nent, 705 sizeof (struct afile)); 706 if (ap->base == NULL) { 707 fprintf(stderr, "%s: out of memory\n", ap->cmd); 708 return (FAIL); 709 } 710 } 711 if (ap->head == 0) 712 ap->head = ap->last = ap->base; 713 fp = ap->last; 714 fp->fnum = dp->d_ino; 715 if (oldinofmt) 716 fp->finotype = DT_UNKNOWN; 717 else 718 fp->finotype = dp->d_type; 719 fp->fname = savename(name); 720 fp++; 721 if (fp == ap->head + ap->nent) { 722 ap->base = (struct afile *)realloc((char *)ap->base, 723 (unsigned)(2 * ap->nent * sizeof (struct afile))); 724 if (ap->base == 0) { 725 fprintf(stderr, "%s: out of memory\n", ap->cmd); 726 return (FAIL); 727 } 728 ap->head = ap->base; 729 fp = ap->head + ap->nent; 730 ap->nent *= 2; 731 } 732 ap->last = fp; 733 return (GOOD); 734 } 735 736 /* 737 * Print out a pretty listing of a directory 738 */ 739 formatf(ap) 740 register struct arglist *ap; 741 { 742 register struct afile *fp; 743 struct entry *np; 744 int width = 0, w, nentry = ap->last - ap->head; 745 int i, j, len, columns, lines; 746 char *cp; 747 748 if (ap->head == ap->last) 749 return; 750 for (fp = ap->head; fp < ap->last; fp++) { 751 fp->ftype = inodetype(fp->fnum); 752 np = lookupino(fp->fnum); 753 if (np != NIL) 754 fp->fflags = np->e_flags; 755 else 756 fp->fflags = 0; 757 len = strlen(fmtentry(fp)); 758 if (len > width) 759 width = len; 760 } 761 width += 2; 762 columns = 80 / width; 763 if (columns == 0) 764 columns = 1; 765 lines = (nentry + columns - 1) / columns; 766 for (i = 0; i < lines; i++) { 767 for (j = 0; j < columns; j++) { 768 fp = ap->head + j * lines + i; 769 cp = fmtentry(fp); 770 fprintf(stderr, "%s", cp); 771 if (fp + lines >= ap->last) { 772 fprintf(stderr, "\n"); 773 break; 774 } 775 w = strlen(cp); 776 while (w < width) { 777 w++; 778 fprintf(stderr, " "); 779 } 780 } 781 } 782 } 783 784 /* 785 * Comparison routine for qsort. 786 */ 787 fcmp(f1, f2) 788 register struct afile *f1, *f2; 789 { 790 791 return (strcmp(f1->fname, f2->fname)); 792 } 793 794 /* 795 * Format a directory entry. 796 */ 797 char * 798 fmtentry(fp) 799 register struct afile *fp; 800 { 801 static char fmtres[BUFSIZ]; 802 static int precision = 0; 803 int i; 804 register char *cp, *dp; 805 806 if (!vflag) { 807 fmtres[0] = '\0'; 808 } else { 809 if (precision == 0) 810 for (i = maxino; i > 0; i /= 10) 811 precision++; 812 (void) sprintf(fmtres, "%*d ", precision, fp->fnum); 813 } 814 dp = &fmtres[strlen(fmtres)]; 815 if (dflag && TSTINO(fp->fnum, dumpmap) == 0) 816 *dp++ = '^'; 817 else if ((fp->fflags & NEW) != 0) 818 *dp++ = '*'; 819 else 820 *dp++ = ' '; 821 for (cp = fp->fname; *cp; cp++) 822 if (!vflag && (*cp < ' ' || *cp >= 0177)) 823 *dp++ = '?'; 824 else 825 *dp++ = *cp; 826 switch(fp->finotype) { 827 828 case DT_LNK: 829 *dp++ = '@'; 830 break; 831 832 case DT_FIFO: 833 case DT_SOCK: 834 *dp++ = '='; 835 break; 836 837 case DT_CHR: 838 case DT_BLK: 839 *dp++ = '#'; 840 break; 841 842 case DT_UNKNOWN: 843 case DT_DIR: 844 if (fp->ftype == NODE) 845 *dp++ = '/'; 846 break; 847 848 case DT_REG: 849 /* nothing */ 850 break; 851 852 default: 853 fprintf(stderr, "Warning: undefined file type %d\n", 854 fp->finotype); 855 } 856 *dp++ = 0; 857 return (fmtres); 858 } 859 860 /* 861 * respond to interrupts 862 */ 863 void 864 onintr() 865 { 866 if (command == 'i' && runshell) 867 longjmp(reset, 1); 868 if (reply("restore interrupted, continue") == FAIL) 869 done(1); 870 } 871