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