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