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