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