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