]> git.wh0rd.org Git - dump.git/blob - restore/interactive.c
noos -> popies.net
[dump.git] / restore / interactive.c
1 /*
2  *      Ported to Linux's Second Extended File System as part of the
3  *      dump and restore backup suit
4  *      Remy Card <card@Linux.EU.Org>, 1994-1997
5  *      Stelian Pop <stelian@popies.net>, 1999-2000
6  *      Stelian Pop <stelian@popies.net> - AlcĂ´ve <www.alcove.com>, 2000-2002
7  */
8
9 /*
10  * Copyright (c) 1985, 1993
11  *      The Regents of the University of California.  All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *      This product includes software developed by the University of
24  *      California, Berkeley and its contributors.
25  * 4. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  */
41
42 #ifndef lint
43 static const char rcsid[] =
44         "$Id: interactive.c,v 1.18 2002/01/16 09:32:14 stelian Exp $";
45 #endif /* not lint */
46
47 #include <config.h>
48 #include <sys/param.h>
49 #include <sys/stat.h>
50
51 #ifdef  __linux__
52 #ifdef HAVE_EXT2FS_EXT2_FS_H
53 #include <ext2fs/ext2_fs.h>
54 #else
55 #include <linux/ext2_fs.h>
56 #endif
57 #include <bsdcompat.h>
58 #else   /* __linux__ */
59 #include <ufs/ufs/dinode.h>
60 #include <ufs/ufs/dir.h>
61 #endif  /* __linux__ */
62 #include <protocols/dumprestore.h>
63
64 #include <setjmp.h>
65 #include <compaterr.h>
66 #include <errno.h>
67 #include <compatglob.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71
72 #ifdef  __linux__
73 #include <ext2fs/ext2fs.h>
74 extern char * __progname;
75 #endif
76
77 #include "restore.h"
78 #include "extern.h"
79
80 #if HAVE_READLINE
81 #include <readline/readline.h>
82 #include <readline/history.h>
83
84 static char *rl_gets (char *prompt);
85 static void initialize_readline(void);
86 static char **restore_completion (char *text, int start, int end);
87 static char *command_generator(char *text, int state);
88 static char *filename_generator(char *text, int state);
89 #endif
90
91 #define round(a, b) (((a) + (b) - 1) / (b) * (b))
92
93 /*
94  * Things to handle interruptions.
95  */
96 static int runshell;
97 static jmp_buf reset;
98 static char *nextarg = NULL;
99 static int pflag = 0;           /* prompt mode */
100 /*
101  * Structure and routines associated with listing directories.
102  */
103 struct afile {
104         dump_ino_t fnum;        /* inode number of file */
105         char    *fname;         /* file name */
106         short   len;            /* name length */
107         char    prefix;         /* prefix character */
108         char    postfix;        /* postfix character */
109 };
110 struct arglist {
111         int     freeglob;       /* glob structure needs to be freed */
112         int     argcnt;         /* next globbed argument to return */
113         glob_t  glob;           /* globbing information */
114         char    *cmd;           /* the current command */
115 };
116
117 static char     *copynext __P((char *, char *));
118 static int       fcmp __P((const void *, const void *));
119 static void      formatf __P((struct afile *, int));
120 static void      getcmd __P((char *, char *, char *, int, struct arglist *));
121 struct dirent   *glob_readdir __P((RST_DIR *dirp));
122 static int       glob_stat __P((const char *, struct stat *));
123 static void      mkentry __P((char *, struct direct *, struct afile *));
124 static void      printlist __P((char *, char *));
125
126 /*
127  * Read and execute commands from the terminal.
128  */
129 void
130 runcmdshell(void)
131 {
132         register struct entry *np;
133         dump_ino_t ino;
134         struct arglist arglist;
135         char curdir[MAXPATHLEN];
136         char name[MAXPATHLEN];
137         char cmd[BUFSIZ];
138
139 #if HAVE_READLINE
140         initialize_readline();
141 #endif
142         arglist.freeglob = 0;
143         arglist.argcnt = 0;
144         arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
145         arglist.glob.gl_opendir = (void *)rst_opendir;
146         arglist.glob.gl_readdir = (void *)glob_readdir;
147         arglist.glob.gl_closedir = (void *)rst_closedir;
148         arglist.glob.gl_lstat = glob_stat;
149         arglist.glob.gl_stat = glob_stat;
150         canon("/", curdir, sizeof(curdir));
151 loop:
152         if (setjmp(reset) != 0) {
153                 if (arglist.freeglob != 0) {
154                         arglist.freeglob = 0;
155                         arglist.argcnt = 0;
156                         globfree(&arglist.glob);
157                 }
158                 nextarg = NULL;
159                 volno = 0;
160         }
161         runshell = 1;
162         getcmd(curdir, cmd, name, sizeof(name), &arglist);
163         switch (cmd[0]) {
164         /*
165          * Add elements to the extraction list.
166          */
167         case 'a':
168                 if (strncmp(cmd, "add", strlen(cmd)) != 0)
169                         goto bad;
170                 ino = dirlookup(name);
171                 if (ino == 0)
172                         break;
173                 if (mflag)
174                         pathcheck(name);
175                 treescan(name, ino, addfile);
176                 break;
177         /*
178          * Change working directory.
179          */
180         case 'c':
181                 if (strncmp(cmd, "cd", strlen(cmd)) != 0)
182                         goto bad;
183                 ino = dirlookup(name);
184                 if (ino == 0)
185                         break;
186                 if (inodetype(ino) == LEAF) {
187                         fprintf(stderr, "%s: not a directory\n", name);
188                         break;
189                 }
190                 (void) strncpy(curdir, name, sizeof(curdir));
191                 curdir[sizeof(curdir) - 1] = '\0';
192                 break;
193         /*
194          * Delete elements from the extraction list.
195          */
196         case 'd':
197                 if (strncmp(cmd, "delete", strlen(cmd)) != 0)
198                         goto bad;
199                 np = lookupname(name);
200                 if (np == NULL || (np->e_flags & NEW) == 0) {
201                         fprintf(stderr, "%s: not on extraction list\n", name);
202                         break;
203                 }
204                 treescan(name, np->e_ino, deletefile);
205                 break;
206         /*
207          * Extract the requested list.
208          */
209         case 'e':
210                 if (strncmp(cmd, "extract", strlen(cmd)) != 0)
211                         goto bad;
212                 createfiles();
213                 createlinks();
214                 setdirmodes(0);
215                 if (dflag)
216                         checkrestore();
217                 volno = 0;
218                 break;
219         /*
220          * List available commands.
221          */
222         case 'h':
223                 if (strncmp(cmd, "help", strlen(cmd)) != 0)
224                         goto bad;
225         case '?':
226                 fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
227                         "Available commands are:\n",
228                         "\tls [arg] - list directory\n",
229                         "\tcd arg - change directory\n",
230                         "\tpwd - print current directory\n",
231                         "\tadd [arg] - add `arg' to list of",
232                         " files to be extracted\n",
233                         "\tdelete [arg] - delete `arg' from",
234                         " list of files to be extracted\n",
235                         "\textract - extract requested files\n",
236                         "\tsetmodes - set modes of requested directories\n",
237                         "\tquit - immediately exit program\n",
238                         "\twhat - list dump header information\n",
239                         "\tverbose - toggle verbose flag",
240                         " (useful with ``ls'')\n",
241                         "\tprompt - toggle the prompt display\n",
242                         "\thelp or `?' - print this list\n",
243                         "If no `arg' is supplied, the current",
244                         " directory is used\n");
245                 break;
246         /*
247          * List a directory.
248          */
249         case 'l':
250                 if (strncmp(cmd, "ls", strlen(cmd)) != 0)
251                         goto bad;
252                 printlist(name, curdir);
253                 break;
254         /*
255          * Print current directory.
256          */
257         case 'p':
258                 if (strncmp(cmd, "pwd", strlen(cmd)) == 0) {
259                         if (curdir[1] == '\0')
260                                 fprintf(stderr, "/\n");
261                         else
262                                 fprintf(stderr, "%s\n", &curdir[1]);
263                 }
264         /*
265          * Toggle prompt mode.
266          */
267                 else if (strncmp(cmd, "prompt", strlen(cmd)) == 0) {
268                         if (pflag) {
269                                 fprintf(stderr, "prompt mode off\n");
270                                 pflag = 0;
271                                 break;
272                         }
273                         fprintf(stderr, "prompt mode on\n");
274                         pflag++;
275                         break;
276                 }
277                 else goto bad;
278                 break;
279         /*
280          * Quit.
281          */
282         case 'q':
283                 if (strncmp(cmd, "quit", strlen(cmd)) != 0)
284                         goto bad;
285                 return;
286         case 'x':
287                 if (strncmp(cmd, "xit", strlen(cmd)) != 0)
288                         goto bad;
289                 return;
290         /*
291          * Toggle verbose mode.
292          */
293         case 'v':
294                 if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
295                         goto bad;
296                 if (vflag) {
297                         fprintf(stderr, "verbose mode off\n");
298                         vflag = 0;
299                         break;
300                 }
301                 fprintf(stderr, "verbose mode on\n");
302                 vflag++;
303                 break;
304         /*
305          * Just restore requested directory modes.
306          */
307         case 's':
308                 if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
309                         goto bad;
310                 setdirmodes(FORCE);
311                 break;
312         /*
313          * Print out dump header information.
314          */
315         case 'w':
316                 if (strncmp(cmd, "what", strlen(cmd)) != 0)
317                         goto bad;
318                 printdumpinfo();
319                 break;
320         /*
321          * Turn on debugging.
322          */
323         case 'D':
324                 if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
325                         goto bad;
326                 if (dflag) {
327                         fprintf(stderr, "debugging mode off\n");
328                         dflag = 0;
329                         break;
330                 }
331                 fprintf(stderr, "debugging mode on\n");
332                 dflag++;
333                 break;
334         /*
335          * Unknown command.
336          */
337         default:
338         bad:
339                 fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
340                 break;
341         }
342         goto loop;
343 }
344
345 /*
346  * Read and parse an interactive command.
347  * The first word on the line is assigned to "cmd". If
348  * there are no arguments on the command line, then "curdir"
349  * is returned as the argument. If there are arguments
350  * on the line they are returned one at a time on each
351  * successive call to getcmd. Each argument is first assigned
352  * to "name". If it does not start with "/" the pathname in
353  * "curdir" is prepended to it. Finally "canon" is called to
354  * eliminate any embedded ".." components.
355  */
356 static void
357 getcmd(char *curdir, char *cmd, char *name, int size, struct arglist *ap)
358 {
359         register char *cp;
360         static char input[BUFSIZ];
361         char output[BUFSIZ];
362 #       define rawname input    /* save space by reusing input buffer */
363
364         /*
365          * Check to see if still processing arguments.
366          */
367         if (ap->argcnt > 0)
368                 goto retnext;
369         if (nextarg != NULL)
370                 goto getnext;
371         /*
372          * Read a command line and trim off trailing white space.
373          */
374 #if HAVE_READLINE
375         snprintf(input, BUFSIZ, "%s\n", rl_gets(curdir));
376 #else
377         do      {
378                 if (pflag)
379                         fprintf(stderr, "%s:%s:%s > ", 
380                                 __progname,
381                                 spcl.c_filesys, 
382                                 curdir[1] ? &curdir[1] : "/");
383                 else
384                         fprintf(stderr, "%s > ", __progname);
385                 (void) fflush(stderr);
386                 (void) fgets(input, BUFSIZ, terminal);
387         } while (!feof(terminal) && input[0] == '\n');
388         if (feof(terminal)) {
389                 (void) strcpy(cmd, "quit");
390                 return;
391         }
392 #endif
393         for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
394                 /* trim off trailing white space and newline */;
395         *++cp = '\0';
396         /*
397          * Copy the command into "cmd".
398          */
399         cp = copynext(input, cmd);
400         ap->cmd = cmd;
401         /*
402          * If no argument, use curdir as the default.
403          */
404         if (*cp == '\0') {
405                 (void) strncpy(name, curdir, size);
406                 name[size - 1] = '\0';
407                 return;
408         }
409         nextarg = cp;
410         /*
411          * Find the next argument.
412          */
413 getnext:
414         cp = copynext(nextarg, rawname);
415         if (*cp == '\0')
416                 nextarg = NULL;
417         else
418                 nextarg = cp;
419         /*
420          * If it is an absolute pathname, canonicalize it and return it.
421          */
422         if (rawname[0] == '/') {
423                 canon(rawname, name, size);
424         } else {
425                 /*
426                  * For relative pathnames, prepend the current directory to
427                  * it then canonicalize and return it.
428                  */
429                 snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
430                 canon(output, name, size);
431         }
432         if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
433                 fprintf(stderr, "%s: out of memory\n", ap->cmd);
434         if (ap->glob.gl_pathc == 0)
435                 return;
436         ap->freeglob = 1;
437         ap->argcnt = ap->glob.gl_pathc;
438
439 retnext:
440         strncpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
441         name[size - 1] = '\0';
442         if (--ap->argcnt == 0) {
443                 ap->freeglob = 0;
444                 globfree(&ap->glob);
445         }
446 #       undef rawname
447 }
448
449 /*
450  * Strip off the next token of the input.
451  */
452 static char *
453 copynext(char *input, char *output)
454 {
455         register char *cp, *bp;
456         char quote;
457
458         for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
459                 /* skip to argument */;
460         bp = output;
461         while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
462                 /*
463                  * Handle back slashes.
464                  */
465                 if (*cp == '\\') {
466                         if (*++cp == '\0') {
467                                 fprintf(stderr,
468                                         "command lines cannot be continued\n");
469                                 continue;
470                         }
471                         *bp++ = *cp++;
472                         continue;
473                 }
474                 /*
475                  * The usual unquoted case.
476                  */
477                 if (*cp != '\'' && *cp != '"') {
478                         *bp++ = *cp++;
479                         continue;
480                 }
481                 /*
482                  * Handle single and double quotes.
483                  */
484                 quote = *cp++;
485                 while (*cp != quote && *cp != '\0')
486                         *bp++ = *cp++;
487                 if (*cp++ == '\0') {
488                         fprintf(stderr, "missing %c\n", quote);
489                         cp--;
490                         continue;
491                 }
492         }
493         *bp = '\0';
494         return (cp);
495 }
496
497 /*
498  * Canonicalize file names to always start with ``./'' and
499  * remove any embedded "." and ".." components.
500  */
501 void
502 canon(char *rawname, char *canonname, int len)
503 {
504         register char *cp, *np;
505
506         if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
507                 (void) strcpy(canonname, "");
508         else if (rawname[0] == '/')
509                 (void) strcpy(canonname, ".");
510         else
511                 (void) strcpy(canonname, "./");
512         if (strlen(canonname) + strlen(rawname) >= len)
513                 errx(1, "canonname: not enough buffer space");
514                 
515         (void) strcat(canonname, rawname);
516         /*
517          * Eliminate multiple and trailing '/'s
518          */
519         for (cp = np = canonname; *np != '\0'; cp++) {
520                 *cp = *np++;
521                 while (*cp == '/' && *np == '/')
522                         np++;
523         }
524         *cp = '\0';
525         if (*--cp == '/')
526                 *cp = '\0';
527         /*
528          * Eliminate extraneous "." and ".." from pathnames.
529          */
530         for (np = canonname; *np != '\0'; ) {
531                 np++;
532                 cp = np;
533                 while (*np != '/' && *np != '\0')
534                         np++;
535                 if (np - cp == 1 && *cp == '.') {
536                         cp--;
537                         (void) strcpy(cp, np);
538                         np = cp;
539                 }
540                 if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
541                         cp--;
542                         while (cp > &canonname[1] && *--cp != '/')
543                                 /* find beginning of name */;
544                         (void) strcpy(cp, np);
545                         np = cp;
546                 }
547         }
548 }
549
550 /*
551  * Do an "ls" style listing of a directory
552  */
553 static void
554 printlist(char *name, char *basename)
555 {
556         register struct afile *fp, *list, *listp = NULL;
557         register struct direct *dp;
558         struct afile single;
559         RST_DIR *dirp;
560         int entries, len, namelen;
561         char locname[MAXPATHLEN + 1];
562
563         dp = pathsearch(name);
564         if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
565             (!vflag && dp->d_ino == WINO))
566                 return;
567         if ((dirp = rst_opendir(name)) == NULL) {
568                 entries = 1;
569                 list = &single;
570                 mkentry(name, dp, list);
571                 len = strlen(basename) + 1;
572                 if (strlen(name) - len > single.len) {
573                         freename(single.fname);
574                         single.fname = savename(&name[len]);
575                         single.len = strlen(single.fname);
576                 }
577         } else {
578                 entries = 0;
579                 while ((dp = rst_readdir(dirp)))
580                         entries++;
581                 rst_closedir(dirp);
582                 list = (struct afile *)malloc(entries * sizeof(struct afile));
583                 if (list == NULL) {
584                         fprintf(stderr, "ls: out of memory\n");
585                         return;
586                 }
587                 if ((dirp = rst_opendir(name)) == NULL)
588                         panic("directory reopen failed\n");
589                 fprintf(stderr, "%s:\n", name);
590                 entries = 0;
591                 listp = list;
592                 namelen = snprintf(locname, sizeof(locname), "%s/", name);
593                 if (namelen >= sizeof(locname))
594                         namelen = sizeof(locname) - 1;
595                 while ((dp = rst_readdir(dirp))) {
596                         if (dp == NULL)
597                                 break;
598                         if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
599                                 continue;
600                         if (!vflag && (dp->d_ino == WINO ||
601                              strcmp(dp->d_name, ".") == 0 ||
602                              strcmp(dp->d_name, "..") == 0))
603                                 continue;
604                         locname[namelen] = '\0';
605                         if (namelen + strlen(dp->d_name) >= MAXPATHLEN) {
606                                 fprintf(stderr, "%s%s: name exceeds %d char\n",
607                                         locname, dp->d_name, MAXPATHLEN);
608                         } else {
609                                 (void) strncat(locname, dp->d_name,
610                                     (int)strlen(dp->d_name));
611                                 mkentry(locname, dp, listp++);
612                                 entries++;
613                         }
614                 }
615                 rst_closedir(dirp);
616                 if (entries == 0) {
617                         fprintf(stderr, "\n");
618                         free(list);
619                         return;
620                 }
621                 qsort((char *)list, entries, sizeof(struct afile), fcmp);
622         }
623         formatf(list, entries);
624         if (dirp != NULL) {
625                 for (fp = listp - 1; fp >= list; fp--)
626                         freename(fp->fname);
627                 fprintf(stderr, "\n");
628                 free(list);
629         }
630 }
631
632 /*
633  * Read the contents of a directory.
634  */
635 static void
636 mkentry(char *name, struct direct *dp, struct afile *fp)
637 {
638         char *cp;
639         struct entry *np;
640
641         fp->fnum = dp->d_ino;
642         fp->fname = savename(dp->d_name);
643         for (cp = fp->fname; *cp; cp++)
644                 if (!vflag && (*cp < ' ' || *cp >= 0177))
645                         *cp = '?';
646         fp->len = cp - fp->fname;
647         if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
648                 fp->prefix = '^';
649         else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
650                 fp->prefix = '*';
651         else
652                 fp->prefix = ' ';
653         switch(dp->d_type) {
654
655         default:
656                 fprintf(stderr, "Warning: undefined file type %d\n",
657                     dp->d_type);
658                 /* fall through */
659         case DT_REG:
660                 fp->postfix = ' ';
661                 break;
662
663         case DT_LNK:
664                 fp->postfix = '@';
665                 break;
666
667         case DT_FIFO:
668         case DT_SOCK:
669                 fp->postfix = '=';
670                 break;
671
672         case DT_CHR:
673         case DT_BLK:
674                 fp->postfix = '#';
675                 break;
676
677 #ifndef __linux__
678         /* no need for this */
679         case DT_WHT:
680                 fp->postfix = '%';
681                 break;
682 #endif
683
684         case DT_UNKNOWN:
685         case DT_DIR:
686                 if (inodetype(dp->d_ino) == NODE)
687                         fp->postfix = '/';
688                 else
689                         fp->postfix = ' ';
690                 break;
691         }
692         return;
693 }
694
695 /*
696  * Print out a pretty listing of a directory
697  */
698 static void
699 formatf(struct afile *list, int nentry)
700 {
701         register struct afile *fp, *endlist;
702         int width, bigino, haveprefix, havepostfix;
703         int i, j, w, precision = 0, columns, lines;
704
705         width = 0;
706         haveprefix = 0;
707         havepostfix = 0;
708         bigino = ROOTINO;
709         endlist = &list[nentry];
710         for (fp = &list[0]; fp < endlist; fp++) {
711                 if (bigino < fp->fnum)
712                         bigino = fp->fnum;
713                 if (width < fp->len)
714                         width = fp->len;
715                 if (fp->prefix != ' ')
716                         haveprefix = 1;
717                 if (fp->postfix != ' ')
718                         havepostfix = 1;
719         }
720         if (haveprefix)
721                 width++;
722         if (havepostfix)
723                 width++;
724         if (vflag) {
725                 for (precision = 0, i = bigino; i > 0; i /= 10)
726                         precision++;
727                 width += precision + 1;
728         }
729         width++;
730         columns = 81 / width;
731         if (columns == 0)
732                 columns = 1;
733         lines = (nentry + columns - 1) / columns;
734         for (i = 0; i < lines; i++) {
735                 for (j = 0; j < columns; j++) {
736                         fp = &list[j * lines + i];
737                         if (vflag) {
738                                 fprintf(stderr, "%*ld ", precision, (long)fp->fnum);
739                                 fp->len += precision + 1;
740                         }
741                         if (haveprefix) {
742                                 putc(fp->prefix, stderr);
743                                 fp->len++;
744                         }
745                         fprintf(stderr, "%s", fp->fname);
746                         if (havepostfix) {
747                                 putc(fp->postfix, stderr);
748                                 fp->len++;
749                         }
750                         if (fp + lines >= endlist) {
751                                 fprintf(stderr, "\n");
752                                 break;
753                         }
754                         for (w = fp->len; w < width; w++)
755                                 putc(' ', stderr);
756                 }
757         }
758 }
759
760 /*
761  * Skip over directory entries that are not on the tape
762  *
763  * First have to get definition of a dirent.
764  *
765  * For Linux the dirent struct is now included from bsdcompat.h
766  */
767 #ifndef __linux__
768 #undef DIRBLKSIZ
769 #include <dirent.h>
770 #undef d_ino
771 #endif  /* ! __linux__ */
772
773 struct dirent *
774 glob_readdir(RST_DIR *dirp)
775 {
776         struct direct *dp;
777         static struct dirent adirent; 
778
779         while ((dp = rst_readdir(dirp)) != NULL) {
780                 if (!vflag && dp->d_ino == WINO)
781                         continue;
782                 if (dflag || TSTINO(dp->d_ino, dumpmap))
783                         break;
784         }
785         if (dp == NULL)
786                 return (NULL);
787         adirent.d_fileno = dp->d_ino;
788         memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
789         return (&adirent);
790 }
791
792 /*
793  * Return st_mode information in response to stat or lstat calls
794  */
795 static int
796 glob_stat(const char *name, struct stat *stp)
797 {
798         register struct direct *dp;
799         dp = pathsearch(name);
800         if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
801             (!vflag && dp->d_ino == WINO))
802                 return (-1);
803         if (inodetype(dp->d_ino) == NODE)
804                 stp->st_mode = IFDIR;
805         else
806                 stp->st_mode = IFREG;
807         return (0);
808 }
809
810 /*
811  * Comparison routine for qsort.
812  */
813 static int
814 fcmp(const void *f1, const void *f2)
815 {
816         return (strcmp(((struct afile *)f1)->fname,
817             ((struct afile *)f2)->fname));
818 }
819
820 /*
821  * respond to interrupts
822  */
823 void
824 onintr(int signo)
825 {
826         int save_errno = errno;
827
828         if (command == 'i' && runshell)
829                 longjmp(reset, 1);
830         if (reply("restore interrupted, continue") == FAIL)
831                 exit(1);
832         errno = save_errno;
833 }
834
835
836 #if HAVE_READLINE
837
838 /* A static variable for holding the line. */
839 static char *line_read = NULL;
840
841 static char completion_curdir[MAXPATHLEN];
842
843 static char *commands[] = { 
844         "add ", "cd ", "delete ", "extract ", "help ", 
845         "? ", "ls ", "pwd ", "prompt ", "quit ", "xit ", 
846         "verbose ", "setmodes ", "what ", "Debug ",
847         NULL };
848
849 static char *files = NULL;
850
851 static char *
852 rl_gets (char *dir)
853 {
854         char *prompt;
855         int sz;
856
857         snprintf(completion_curdir, MAXPATHLEN, "%s", dir);
858         completion_curdir[MAXPATHLEN - 1] = '\0';
859
860         if (pflag) {
861                 sz = 6 + strlen(__progname) + strlen(spcl.c_filesys) + strlen((completion_curdir + 1 ? completion_curdir + 1 : "/"));
862                 prompt = (char *)malloc(sz);
863                 if (!prompt)
864                         return NULL;
865                 snprintf(prompt, sz, "%s:%s:%s > ", 
866                         __progname,
867                         spcl.c_filesys, 
868                         (completion_curdir + 1 ? completion_curdir + 1 : "/"));
869         }
870         else {
871                 sz = 4 + strlen(__progname);
872                 prompt = (char *)malloc(sz);
873                 if (!prompt)
874                         return NULL;
875                 snprintf(prompt, sz, "%s > ", __progname);
876         }
877         prompt[sz - 1] = '\0';
878
879         if (line_read) {
880                 free (line_read);
881                 line_read = (char *)NULL;
882         }
883
884         do {
885                 line_read = readline (prompt);
886         } while (line_read && !*line_read);
887
888         free(prompt);
889
890         if (!line_read) {
891                 printf("\n");
892                 return strdup("quit");
893         }
894
895         add_history (line_read);
896
897         return (line_read);
898 }
899
900 static void 
901 initialize_readline(void) 
902 {
903         rl_attempted_completion_function = restore_completion;
904         rl_completion_entry_function = (Function *)NULL;
905         rl_completion_append_character = '\0';
906         rl_instream = terminal;
907 }
908
909 static char **
910 restore_completion (char *text, int start, int end)
911 {
912         char **matches;
913
914         if (start == 0)
915                 matches = completion_matches (text, command_generator);
916         else
917                 matches = completion_matches (text, filename_generator);
918
919         return (matches);
920 }
921
922 static char *
923 command_generator(char *text, int state)
924 {
925         static int list_index, len;
926         char *name;
927
928         if (!state) {
929                 list_index = 0;
930                 len = strlen(text);
931         }
932
933         while ( (name = commands[list_index]) != NULL) {
934
935                 list_index ++;
936
937                 if (strncmp(name, text, len) == 0)
938                         return strdup(name);
939         }
940
941         return NULL;
942 }
943
944 static char *
945 filename_generator(char *text, int state)
946 {
947         static int list_index;
948         char *name;
949         RST_DIR *dirp;
950         struct direct *dp;
951         static int entries;
952         char pname[MAXPATHLEN];
953         char fname[MAXPATHLEN];
954         char *slash;
955         char ppname[MAXPATHLEN];
956
957         if (!state) {
958                 list_index = 0;
959
960                 if (files != NULL) {
961                         free(files);
962                         entries = 0;
963                         files = NULL;
964                 }
965                 if ((slash = strrchr(text, '/')) != NULL) {
966                         int idx = slash - text;
967                         if (idx > MAXPATHLEN - 2)
968                                 idx = MAXPATHLEN - 2;
969                         strncpy(ppname, text, MAXPATHLEN);
970                         ppname[MAXPATHLEN - 1] = '\0';
971                         ppname[idx] = '\0';
972                         if (text[0] == '/')
973                                 snprintf(pname, MAXPATHLEN, ".%s", ppname);
974                         else
975                                 snprintf(pname, MAXPATHLEN, "%s/%s", completion_curdir, ppname);
976                         strncpy(fname, ppname + idx + 1, MAXPATHLEN);
977                         ppname[idx] = '/';
978                         ppname[idx + 1] = '\0';
979                 }
980                 else {
981                         strncpy(pname, completion_curdir, MAXPATHLEN);
982                         strncpy(fname, text, MAXPATHLEN);
983                         ppname[0] = '\0';
984                 }
985                 pname[MAXPATHLEN - 1] = '\0';
986                 fname[MAXPATHLEN - 1] = '\0';
987                 if ((dirp = rst_opendir(pname)) == NULL)
988                         return NULL;
989                 entries = 0;
990                 while ((dp = rst_readdir(dirp)))
991                         entries++;
992                 rst_closedir(dirp);
993                 files = (char *)malloc(entries * MAXPATHLEN);
994                 if (files == NULL) {
995                         fprintf(stderr, "Out of memory\n");
996                         entries = 0;
997                         return NULL;
998                 }
999                 if ((dirp = rst_opendir(pname)) == NULL)
1000                         panic("directory reopen failed\n");
1001                 entries = 0;
1002                 while ((dp = rst_readdir(dirp))) {
1003                         if (TSTINO(dp->d_ino, dumpmap) == 0)
1004                                 continue;
1005                         if (strcmp(dp->d_name, ".") == 0 ||
1006                             strcmp(dp->d_name, "..") == 0)
1007                                 continue;
1008                         if (strncmp(dp->d_name, fname, strlen(fname)) == 0) {
1009                                 if (inodetype(dp->d_ino) == NODE)
1010                                         snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s/", ppname, dp->d_name);
1011                                 else
1012                                         snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s ", ppname, dp->d_name);
1013                                 *(files + (entries + 1) * MAXPATHLEN - 1) = '\0';
1014                                 ++entries;
1015                         }
1016                  }
1017                  rst_closedir(dirp);
1018         }
1019
1020         if (list_index >= entries)
1021                 return NULL;
1022
1023         name = strdup(files + list_index * MAXPATHLEN);
1024         list_index ++;
1025
1026         return name;
1027 }
1028 #endif /* HAVE_READLINE */