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