1*17752Smckusick /* Copyright (c) 1985 Regents of the University of California */ 2*17752Smckusick 3*17752Smckusick #ifndef lint 4*17752Smckusick static char sccsid[] = "@(#)interactive.c 3.1 (Berkeley) 01/18/85"; 5*17752Smckusick #endif not lint 6*17752Smckusick 7*17752Smckusick #include "restore.h" 8*17752Smckusick #include <dumprestor.h> 9*17752Smckusick #include <setjmp.h> 10*17752Smckusick 11*17752Smckusick /* 12*17752Smckusick * Things to handle interruptions. 13*17752Smckusick */ 14*17752Smckusick static jmp_buf reset; 15*17752Smckusick static char *nextarg = NULL; 16*17752Smckusick 17*17752Smckusick /* 18*17752Smckusick * Structure and routines associated with listing directories. 19*17752Smckusick */ 20*17752Smckusick struct afile { 21*17752Smckusick ino_t fnum; /* inode number of file */ 22*17752Smckusick char *fname; /* file name */ 23*17752Smckusick short fflags; /* extraction flags, if any */ 24*17752Smckusick char ftype; /* file type, e.g. LEAF or NODE */ 25*17752Smckusick }; 26*17752Smckusick extern int fcmp(); 27*17752Smckusick extern char *fmtentry(); 28*17752Smckusick char *copynext(); 29*17752Smckusick 30*17752Smckusick /* 31*17752Smckusick * Read and execute commands from the terminal. 32*17752Smckusick */ 33*17752Smckusick runcmdshell() 34*17752Smckusick { 35*17752Smckusick register struct entry *np; 36*17752Smckusick ino_t ino; 37*17752Smckusick char curdir[MAXPATHLEN]; 38*17752Smckusick char name[MAXPATHLEN]; 39*17752Smckusick char cmd[BUFSIZ]; 40*17752Smckusick 41*17752Smckusick canon("/", curdir); 42*17752Smckusick loop: 43*17752Smckusick if (setjmp(reset) != 0) { 44*17752Smckusick nextarg = NULL; 45*17752Smckusick volno = 0; 46*17752Smckusick } 47*17752Smckusick getcmd(curdir, cmd, name); 48*17752Smckusick switch (cmd[0]) { 49*17752Smckusick /* 50*17752Smckusick * Add elements to the extraction list. 51*17752Smckusick */ 52*17752Smckusick case 'a': 53*17752Smckusick ino = dirlookup(name); 54*17752Smckusick if (ino == 0) 55*17752Smckusick break; 56*17752Smckusick if (mflag) 57*17752Smckusick pathcheck(name); 58*17752Smckusick treescan(name, ino, addfile); 59*17752Smckusick break; 60*17752Smckusick /* 61*17752Smckusick * Change working directory. 62*17752Smckusick */ 63*17752Smckusick case 'c': 64*17752Smckusick ino = dirlookup(name); 65*17752Smckusick if (ino == 0) 66*17752Smckusick break; 67*17752Smckusick if (inodetype(ino) == LEAF) { 68*17752Smckusick fprintf(stderr, "%s: not a directory\n", name); 69*17752Smckusick break; 70*17752Smckusick } 71*17752Smckusick (void) strcpy(curdir, name); 72*17752Smckusick break; 73*17752Smckusick /* 74*17752Smckusick * Delete elements from the extraction list. 75*17752Smckusick */ 76*17752Smckusick case 'd': 77*17752Smckusick np = lookupname(name); 78*17752Smckusick if (np == NIL || (np->e_flags & NEW) == 0) { 79*17752Smckusick fprintf(stderr, "%s: not on extraction list\n", name); 80*17752Smckusick break; 81*17752Smckusick } 82*17752Smckusick treescan(name, np->e_ino, deletefile); 83*17752Smckusick break; 84*17752Smckusick /* 85*17752Smckusick * Extract the requested list. 86*17752Smckusick */ 87*17752Smckusick case 'e': 88*17752Smckusick createfiles(); 89*17752Smckusick createlinks(); 90*17752Smckusick setdirmodes(); 91*17752Smckusick if (dflag) 92*17752Smckusick checkrestore(); 93*17752Smckusick volno = 0; 94*17752Smckusick break; 95*17752Smckusick /* 96*17752Smckusick * List available commands. 97*17752Smckusick */ 98*17752Smckusick case 'h': 99*17752Smckusick case '?': 100*17752Smckusick fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 101*17752Smckusick "Available commands are:\n", 102*17752Smckusick "\tls [arg] - list directory\n", 103*17752Smckusick "\tcd arg - change directory\n", 104*17752Smckusick "\tpwd - print current directory\n", 105*17752Smckusick "\tadd [arg] - add `arg' to list of", 106*17752Smckusick " files to be extracted\n", 107*17752Smckusick "\tdelete [arg] - delete `arg' from", 108*17752Smckusick " list of files to be extracted\n", 109*17752Smckusick "\textract - extract requested files\n", 110*17752Smckusick "\tquit - immediately exit program\n", 111*17752Smckusick "\tverbose - toggle verbose flag", 112*17752Smckusick " (useful with ``ls'')\n", 113*17752Smckusick "\thelp or `?' - print this list\n", 114*17752Smckusick "If no `arg' is supplied, the current", 115*17752Smckusick " directory is used\n"); 116*17752Smckusick break; 117*17752Smckusick /* 118*17752Smckusick * List a directory. 119*17752Smckusick */ 120*17752Smckusick case 'l': 121*17752Smckusick ino = dirlookup(name); 122*17752Smckusick if (ino == 0) 123*17752Smckusick break; 124*17752Smckusick printlist(name, ino, curdir); 125*17752Smckusick break; 126*17752Smckusick /* 127*17752Smckusick * Print current directory. 128*17752Smckusick */ 129*17752Smckusick case 'p': 130*17752Smckusick if (curdir[1] == '\0') 131*17752Smckusick fprintf(stderr, "/\n"); 132*17752Smckusick else 133*17752Smckusick fprintf(stderr, "%s\n", &curdir[1]); 134*17752Smckusick break; 135*17752Smckusick /* 136*17752Smckusick * Quit. 137*17752Smckusick */ 138*17752Smckusick case 'q': 139*17752Smckusick case 'x': 140*17752Smckusick return; 141*17752Smckusick /* 142*17752Smckusick * Toggle verbose mode. 143*17752Smckusick */ 144*17752Smckusick case 'v': 145*17752Smckusick if (vflag) { 146*17752Smckusick fprintf(stderr, "verbose mode off\n"); 147*17752Smckusick vflag = 0; 148*17752Smckusick break; 149*17752Smckusick } 150*17752Smckusick fprintf(stderr, "verbose mode on\n"); 151*17752Smckusick vflag++; 152*17752Smckusick break; 153*17752Smckusick /* 154*17752Smckusick * Just restore requested directory modes. 155*17752Smckusick */ 156*17752Smckusick case 'R': 157*17752Smckusick setdirmodes(); 158*17752Smckusick break; 159*17752Smckusick /* 160*17752Smckusick * Turn on debugging. 161*17752Smckusick */ 162*17752Smckusick case 'D': 163*17752Smckusick if (dflag) { 164*17752Smckusick fprintf(stderr, "debugging mode off\n"); 165*17752Smckusick dflag = 0; 166*17752Smckusick break; 167*17752Smckusick } 168*17752Smckusick fprintf(stderr, "debugging mode on\n"); 169*17752Smckusick dflag++; 170*17752Smckusick break; 171*17752Smckusick /* 172*17752Smckusick * Unknown command. 173*17752Smckusick */ 174*17752Smckusick default: 175*17752Smckusick fprintf(stderr, "%s: unknown command; type ? for help\n", cmd); 176*17752Smckusick break; 177*17752Smckusick } 178*17752Smckusick goto loop; 179*17752Smckusick } 180*17752Smckusick 181*17752Smckusick /* 182*17752Smckusick * Read and parse an interactive command. 183*17752Smckusick * The first word on the line is assigned to "cmd". If 184*17752Smckusick * there are no arguments on the command line, then "curdir" 185*17752Smckusick * is returned as the argument. If there are arguments 186*17752Smckusick * on the line they are returned one at a time on each 187*17752Smckusick * successive call to getcmd. Each argument is first assigned 188*17752Smckusick * to "name". If it does not start with "/" the pathname in 189*17752Smckusick * "curdir" is prepended to it. Finally "canon" is called to 190*17752Smckusick * eliminate any embedded ".." components. 191*17752Smckusick */ 192*17752Smckusick getcmd(curdir, cmd, name) 193*17752Smckusick char *curdir, *cmd, *name; 194*17752Smckusick { 195*17752Smckusick register char *cp; 196*17752Smckusick static char input[BUFSIZ]; 197*17752Smckusick char output[BUFSIZ]; 198*17752Smckusick # define rawname input /* save space by reusing input buffer */ 199*17752Smckusick 200*17752Smckusick /* 201*17752Smckusick * Check to see if still processing arguments. 202*17752Smckusick */ 203*17752Smckusick if (nextarg != NULL) 204*17752Smckusick goto getnext; 205*17752Smckusick /* 206*17752Smckusick * Read a command line and trim off trailing white space. 207*17752Smckusick */ 208*17752Smckusick do { 209*17752Smckusick fprintf(stderr, "restore > "); 210*17752Smckusick (void) fflush(stderr); 211*17752Smckusick (void) fgets(input, BUFSIZ, terminal); 212*17752Smckusick } while (!feof(terminal) && input[0] == '\n'); 213*17752Smckusick if (feof(terminal)) { 214*17752Smckusick (void) strcpy(cmd, "quit"); 215*17752Smckusick return; 216*17752Smckusick } 217*17752Smckusick for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--) 218*17752Smckusick /* trim off trailing white space and newline */; 219*17752Smckusick *++cp = '\0'; 220*17752Smckusick /* 221*17752Smckusick * Copy the command into "cmd". 222*17752Smckusick */ 223*17752Smckusick cp = copynext(input, cmd); 224*17752Smckusick /* 225*17752Smckusick * If no argument, use curdir as the default. 226*17752Smckusick */ 227*17752Smckusick if (*cp == '\0') { 228*17752Smckusick (void) strcpy(name, curdir); 229*17752Smckusick return; 230*17752Smckusick } 231*17752Smckusick nextarg = cp; 232*17752Smckusick /* 233*17752Smckusick * Find the next argument. 234*17752Smckusick */ 235*17752Smckusick getnext: 236*17752Smckusick cp = copynext(nextarg, rawname); 237*17752Smckusick if (*cp == '\0') 238*17752Smckusick nextarg = NULL; 239*17752Smckusick else 240*17752Smckusick nextarg = cp; 241*17752Smckusick /* 242*17752Smckusick * If it an absolute pathname, canonicalize it and return it. 243*17752Smckusick */ 244*17752Smckusick if (rawname[0] == '/') { 245*17752Smckusick canon(rawname, name); 246*17752Smckusick } else { 247*17752Smckusick /* 248*17752Smckusick * For relative pathnames, prepend the current directory to 249*17752Smckusick * it then canonicalize and return it. 250*17752Smckusick */ 251*17752Smckusick (void) strcpy(output, curdir); 252*17752Smckusick (void) strcat(output, "/"); 253*17752Smckusick (void) strcat(output, rawname); 254*17752Smckusick canon(output, name); 255*17752Smckusick } 256*17752Smckusick # undef rawname 257*17752Smckusick } 258*17752Smckusick 259*17752Smckusick /* 260*17752Smckusick * Strip off the next token of the input. 261*17752Smckusick */ 262*17752Smckusick char * 263*17752Smckusick copynext(input, output) 264*17752Smckusick char *input, *output; 265*17752Smckusick { 266*17752Smckusick register char *cp, *bp; 267*17752Smckusick char quote; 268*17752Smckusick 269*17752Smckusick for (cp = input; *cp == ' ' || *cp == '\t'; cp++) 270*17752Smckusick /* skip to argument */; 271*17752Smckusick bp = output; 272*17752Smckusick while (*cp != ' ' && *cp != '\t' && *cp != '\0') { 273*17752Smckusick /* 274*17752Smckusick * Handle back slashes. 275*17752Smckusick */ 276*17752Smckusick if (*cp == '\\') { 277*17752Smckusick if (*++cp == '\0') { 278*17752Smckusick fprintf(stderr, 279*17752Smckusick "command lines cannot be continued\n"); 280*17752Smckusick continue; 281*17752Smckusick } 282*17752Smckusick *bp++ = *cp++; 283*17752Smckusick continue; 284*17752Smckusick } 285*17752Smckusick /* 286*17752Smckusick * The usual unquoted case. 287*17752Smckusick */ 288*17752Smckusick if (*cp != '\'' && *cp != '"') { 289*17752Smckusick *bp++ = *cp++; 290*17752Smckusick continue; 291*17752Smckusick } 292*17752Smckusick /* 293*17752Smckusick * Handle single and double quotes. 294*17752Smckusick */ 295*17752Smckusick quote = *cp++; 296*17752Smckusick while (*cp != quote && *cp != '\0') 297*17752Smckusick *bp++ = *cp++ | 0200; 298*17752Smckusick if (*cp++ == '\0') { 299*17752Smckusick fprintf(stderr, "missing %c\n", quote); 300*17752Smckusick cp--; 301*17752Smckusick continue; 302*17752Smckusick } 303*17752Smckusick } 304*17752Smckusick *bp = '\0'; 305*17752Smckusick return (cp); 306*17752Smckusick } 307*17752Smckusick 308*17752Smckusick /* 309*17752Smckusick * Canonicalize file names to always start with ``./'' and 310*17752Smckusick * remove any imbedded ".." components. 311*17752Smckusick */ 312*17752Smckusick canon(rawname, canonname) 313*17752Smckusick char *rawname, *canonname; 314*17752Smckusick { 315*17752Smckusick register char *cp, *np; 316*17752Smckusick int len; 317*17752Smckusick 318*17752Smckusick if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0) 319*17752Smckusick (void) strcpy(canonname, ""); 320*17752Smckusick else if (rawname[0] == '/') 321*17752Smckusick (void) strcpy(canonname, "."); 322*17752Smckusick else 323*17752Smckusick (void) strcpy(canonname, "./"); 324*17752Smckusick (void) strcat(canonname, rawname); 325*17752Smckusick len = strlen(canonname) - 1; 326*17752Smckusick if (canonname[len] == '/') 327*17752Smckusick canonname[len] = '\0'; 328*17752Smckusick /* 329*17752Smckusick * Eliminate extraneous ".." from pathnames. 330*17752Smckusick */ 331*17752Smckusick for (np = canonname; *np != '\0'; ) { 332*17752Smckusick np++; 333*17752Smckusick cp = np; 334*17752Smckusick while (*np != '/' && *np != '\0') 335*17752Smckusick np++; 336*17752Smckusick if (np - cp == 2 && strncmp(cp, "..", 2) == 0) { 337*17752Smckusick cp--; 338*17752Smckusick while (cp > &canonname[1] && *--cp != '/') 339*17752Smckusick /* find beginning of name */; 340*17752Smckusick (void) strcpy(cp, np); 341*17752Smckusick np = cp; 342*17752Smckusick } 343*17752Smckusick } 344*17752Smckusick } 345*17752Smckusick 346*17752Smckusick /* 347*17752Smckusick * Do an "ls" style listing of a directory 348*17752Smckusick */ 349*17752Smckusick printlist(name, ino, basename) 350*17752Smckusick char *name; 351*17752Smckusick ino_t ino; 352*17752Smckusick char *basename; 353*17752Smckusick { 354*17752Smckusick register struct afile *fp; 355*17752Smckusick struct afile *dfp0, *dfplast; 356*17752Smckusick struct afile single; 357*17752Smckusick DIR *dirp; 358*17752Smckusick 359*17752Smckusick if ((dirp = rst_opendir(name)) == NULL) { 360*17752Smckusick single.fnum = ino; 361*17752Smckusick single.fname = savename(name + strlen(basename)); 362*17752Smckusick dfp0 = &single; 363*17752Smckusick dfplast = dfp0 + 1; 364*17752Smckusick } else { 365*17752Smckusick if (getdir(dirp, &dfp0, &dfplast) == FAIL) 366*17752Smckusick return; 367*17752Smckusick } 368*17752Smckusick qsort((char *)dfp0, dfplast - dfp0, sizeof (struct afile), fcmp); 369*17752Smckusick formatf(dfp0, dfplast); 370*17752Smckusick for (fp = dfp0; fp < dfplast; fp++) 371*17752Smckusick freename(fp->fname); 372*17752Smckusick } 373*17752Smckusick 374*17752Smckusick /* 375*17752Smckusick * Read the contents of a directory. 376*17752Smckusick */ 377*17752Smckusick getdir(dirp, pfp0, pfplast) 378*17752Smckusick DIR *dirp; 379*17752Smckusick struct afile **pfp0, **pfplast; 380*17752Smckusick { 381*17752Smckusick register struct afile *fp; 382*17752Smckusick register struct direct *dp; 383*17752Smckusick static struct afile *basefp = NULL; 384*17752Smckusick static long nent = 20; 385*17752Smckusick 386*17752Smckusick if (basefp == NULL) { 387*17752Smckusick basefp = (struct afile *)calloc((unsigned)nent, 388*17752Smckusick sizeof (struct afile)); 389*17752Smckusick if (basefp == NULL) { 390*17752Smckusick fprintf(stderr, "ls: out of memory\n"); 391*17752Smckusick return (FAIL); 392*17752Smckusick } 393*17752Smckusick } 394*17752Smckusick fp = *pfp0 = basefp; 395*17752Smckusick *pfplast = *pfp0 + nent; 396*17752Smckusick while (dp = rst_readdir(dirp)) { 397*17752Smckusick if (dp == NULL || dp->d_ino == 0) 398*17752Smckusick break; 399*17752Smckusick if (!dflag && BIT(dp->d_ino, dumpmap) == 0) 400*17752Smckusick continue; 401*17752Smckusick if (vflag == 0 && 402*17752Smckusick (strcmp(dp->d_name, ".") == 0 || 403*17752Smckusick strcmp(dp->d_name, "..") == 0)) 404*17752Smckusick continue; 405*17752Smckusick fp->fnum = dp->d_ino; 406*17752Smckusick fp->fname = savename(dp->d_name); 407*17752Smckusick fp++; 408*17752Smckusick if (fp == *pfplast) { 409*17752Smckusick basefp = (struct afile *)realloc((char *)basefp, 410*17752Smckusick (unsigned)(2 * nent * sizeof (struct afile))); 411*17752Smckusick if (basefp == 0) { 412*17752Smckusick fprintf(stderr, "ls: out of memory\n"); 413*17752Smckusick return (FAIL); 414*17752Smckusick } 415*17752Smckusick *pfp0 = basefp; 416*17752Smckusick fp = *pfp0 + nent; 417*17752Smckusick *pfplast = fp + nent; 418*17752Smckusick nent *= 2; 419*17752Smckusick } 420*17752Smckusick } 421*17752Smckusick *pfplast = fp; 422*17752Smckusick return (GOOD); 423*17752Smckusick } 424*17752Smckusick 425*17752Smckusick /* 426*17752Smckusick * Print out a pretty listing of a directory 427*17752Smckusick */ 428*17752Smckusick formatf(fp0, fplast) 429*17752Smckusick struct afile *fp0, *fplast; 430*17752Smckusick { 431*17752Smckusick register struct afile *fp; 432*17752Smckusick struct entry *np; 433*17752Smckusick int width = 0, w, nentry = fplast - fp0; 434*17752Smckusick int i, j, len, columns, lines; 435*17752Smckusick char *cp; 436*17752Smckusick 437*17752Smckusick if (fp0 == fplast) 438*17752Smckusick return; 439*17752Smckusick for (fp = fp0; fp < fplast; fp++) { 440*17752Smckusick fp->ftype = inodetype(fp->fnum); 441*17752Smckusick np = lookupino(fp->fnum); 442*17752Smckusick if (np != NIL) 443*17752Smckusick fp->fflags = np->e_flags; 444*17752Smckusick else 445*17752Smckusick fp->fflags = 0; 446*17752Smckusick len = strlen(fmtentry(fp)); 447*17752Smckusick if (len > width) 448*17752Smckusick width = len; 449*17752Smckusick } 450*17752Smckusick width += 2; 451*17752Smckusick columns = 80 / width; 452*17752Smckusick if (columns == 0) 453*17752Smckusick columns = 1; 454*17752Smckusick lines = (nentry + columns - 1) / columns; 455*17752Smckusick for (i = 0; i < lines; i++) { 456*17752Smckusick for (j = 0; j < columns; j++) { 457*17752Smckusick fp = fp0 + j * lines + i; 458*17752Smckusick cp = fmtentry(fp); 459*17752Smckusick fprintf(stderr, "%s", cp); 460*17752Smckusick if (fp + lines >= fplast) { 461*17752Smckusick fprintf(stderr, "\n"); 462*17752Smckusick break; 463*17752Smckusick } 464*17752Smckusick w = strlen(cp); 465*17752Smckusick while (w < width) { 466*17752Smckusick w++; 467*17752Smckusick fprintf(stderr, " "); 468*17752Smckusick } 469*17752Smckusick } 470*17752Smckusick } 471*17752Smckusick } 472*17752Smckusick 473*17752Smckusick /* 474*17752Smckusick * Comparison routine for qsort. 475*17752Smckusick */ 476*17752Smckusick fcmp(f1, f2) 477*17752Smckusick register struct afile *f1, *f2; 478*17752Smckusick { 479*17752Smckusick 480*17752Smckusick return (strcmp(f1->fname, f2->fname)); 481*17752Smckusick } 482*17752Smckusick 483*17752Smckusick /* 484*17752Smckusick * Format a directory entry. 485*17752Smckusick */ 486*17752Smckusick char * 487*17752Smckusick fmtentry(fp) 488*17752Smckusick register struct afile *fp; 489*17752Smckusick { 490*17752Smckusick static char fmtres[BUFSIZ]; 491*17752Smckusick register char *cp, *dp; 492*17752Smckusick 493*17752Smckusick if (vflag) 494*17752Smckusick (void) sprintf(fmtres, "%5d ", fp->fnum); 495*17752Smckusick else 496*17752Smckusick fmtres[0] = '\0'; 497*17752Smckusick dp = &fmtres[strlen(fmtres)]; 498*17752Smckusick if (dflag && BIT(fp->fnum, dumpmap) == 0) 499*17752Smckusick *dp++ = '^'; 500*17752Smckusick else if ((fp->fflags & NEW) != 0) 501*17752Smckusick *dp++ = '*'; 502*17752Smckusick else 503*17752Smckusick *dp++ = ' '; 504*17752Smckusick for (cp = fp->fname; *cp; cp++) 505*17752Smckusick if (!vflag && (*cp < ' ' || *cp >= 0177)) 506*17752Smckusick *dp++ = '?'; 507*17752Smckusick else 508*17752Smckusick *dp++ = *cp; 509*17752Smckusick if (fp->ftype == NODE) 510*17752Smckusick *dp++ = '/'; 511*17752Smckusick *dp++ = 0; 512*17752Smckusick return (fmtres); 513*17752Smckusick } 514*17752Smckusick 515*17752Smckusick /* 516*17752Smckusick * respond to interrupts 517*17752Smckusick */ 518*17752Smckusick onintr() 519*17752Smckusick { 520*17752Smckusick if (command == 'i') 521*17752Smckusick longjmp(reset, 1); 522*17752Smckusick if (reply("restore interrupted, continue") == FAIL) 523*17752Smckusick done(1); 524*17752Smckusick } 525