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