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