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