]> git.wh0rd.org - dump.git/blob - restore/interactive.c
Relicensed dump/restore under the revised BSD license, as per ftp://ftp.cs.berkeley...
[dump.git] / restore / interactive.c
1 /*
2 * Ported to Linux's Second Extended File System as part of the
3 * dump and restore backup suit
4 * Remy Card <card@Linux.EU.Org>, 1994-1997
5 * Stelian Pop <stelian@popies.net>, 1999-2000
6 * Stelian Pop <stelian@popies.net> - AlcĂ´ve <www.alcove.com>, 2000-2002
7 */
8
9 /*
10 * Copyright (c) 1985, 1993
11 * The Regents of the University of California. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. 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.26 2003/03/30 15:40:38 stelian Exp $";
41 #endif /* not lint */
42
43 #include <config.h>
44 #include <sys/param.h>
45 #include <sys/stat.h>
46
47 #ifdef __linux__
48 #ifdef HAVE_EXT2FS_EXT2_FS_H
49 #include <ext2fs/ext2_fs.h>
50 #else
51 #include <linux/ext2_fs.h>
52 #endif
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 (const char *text, int start, int end);
83 static char *command_generator(const char *text, int state);
84 static char *filename_generator(const 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 dump_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 struct entry *np;
129 dump_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 = (int (*)(const char *, void *))glob_stat;
145 arglist.glob.gl_stat = (int (*)(const char *, void *))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(oflag ? FORCE : 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 printvolinfo();
316 break;
317 /*
318 * Turn on debugging.
319 */
320 case 'D':
321 if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
322 goto bad;
323 if (dflag) {
324 fprintf(stderr, "debugging mode off\n");
325 dflag = 0;
326 break;
327 }
328 fprintf(stderr, "debugging mode on\n");
329 dflag++;
330 break;
331 /*
332 * Unknown command.
333 */
334 default:
335 bad:
336 fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
337 break;
338 }
339 goto loop;
340 }
341
342 /*
343 * Read and parse an interactive command.
344 * The first word on the line is assigned to "cmd". If
345 * there are no arguments on the command line, then "curdir"
346 * is returned as the argument. If there are arguments
347 * on the line they are returned one at a time on each
348 * successive call to getcmd. Each argument is first assigned
349 * to "name". If it does not start with "/" the pathname in
350 * "curdir" is prepended to it. Finally "canon" is called to
351 * eliminate any embedded ".." components.
352 */
353 static void
354 getcmd(char *curdir, char *cmd, char *name, int size, struct arglist *ap)
355 {
356 char *cp;
357 static char input[BUFSIZ];
358 char output[BUFSIZ];
359 # define rawname input /* save space by reusing input buffer */
360
361 /*
362 * Check to see if still processing arguments.
363 */
364 if (ap->argcnt > 0)
365 goto retnext;
366 if (nextarg != NULL)
367 goto getnext;
368 /*
369 * Read a command line and trim off trailing white space.
370 */
371 #if HAVE_READLINE
372 snprintf(input, BUFSIZ, "%s\n", rl_gets(curdir));
373 #else
374 do {
375 if (pflag)
376 fprintf(stderr, "%s:%s:%s > ",
377 __progname,
378 spcl.c_filesys,
379 curdir[1] ? &curdir[1] : "/");
380 else
381 fprintf(stderr, "%s > ", __progname);
382 (void) fflush(stderr);
383 (void) fgets(input, BUFSIZ, terminal);
384 } while (!feof(terminal) && input[0] == '\n');
385 if (feof(terminal)) {
386 (void) strcpy(cmd, "quit");
387 return;
388 }
389 #endif
390 for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
391 /* trim off trailing white space and newline */;
392 *++cp = '\0';
393 /*
394 * Copy the command into "cmd".
395 */
396 cp = copynext(input, cmd);
397 ap->cmd = cmd;
398 /*
399 * If no argument, use curdir as the default.
400 */
401 if (*cp == '\0') {
402 (void) strncpy(name, curdir, size);
403 name[size - 1] = '\0';
404 return;
405 }
406 nextarg = cp;
407 /*
408 * Find the next argument.
409 */
410 getnext:
411 cp = copynext(nextarg, rawname);
412 if (*cp == '\0')
413 nextarg = NULL;
414 else
415 nextarg = cp;
416 /*
417 * If it is an absolute pathname, canonicalize it and return it.
418 */
419 if (rawname[0] == '/') {
420 canon(rawname, name, size);
421 } else {
422 /*
423 * For relative pathnames, prepend the current directory to
424 * it then canonicalize and return it.
425 */
426 snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
427 canon(output, name, size);
428 }
429 if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
430 fprintf(stderr, "%s: out of memory\n", ap->cmd);
431 if (ap->glob.gl_pathc == 0)
432 return;
433 ap->freeglob = 1;
434 ap->argcnt = ap->glob.gl_pathc;
435
436 retnext:
437 strncpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
438 name[size - 1] = '\0';
439 if (--ap->argcnt == 0) {
440 ap->freeglob = 0;
441 globfree(&ap->glob);
442 }
443 # undef rawname
444 }
445
446 /*
447 * Strip off the next token of the input.
448 */
449 static char *
450 copynext(char *input, char *output)
451 {
452 char *cp, *bp;
453 char quote;
454
455 for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
456 /* skip to argument */;
457 bp = output;
458 while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
459 /*
460 * Handle back slashes.
461 */
462 if (*cp == '\\') {
463 if (*++cp == '\0') {
464 fprintf(stderr,
465 "command lines cannot be continued\n");
466 continue;
467 }
468 *bp++ = *cp++;
469 continue;
470 }
471 /*
472 * The usual unquoted case.
473 */
474 if (*cp != '\'' && *cp != '"') {
475 *bp++ = *cp++;
476 continue;
477 }
478 /*
479 * Handle single and double quotes.
480 */
481 quote = *cp++;
482 while (*cp != quote && *cp != '\0')
483 *bp++ = *cp++;
484 if (*cp++ == '\0') {
485 fprintf(stderr, "missing %c\n", quote);
486 cp--;
487 continue;
488 }
489 }
490 *bp = '\0';
491 return (cp);
492 }
493
494 /*
495 * Canonicalize file names to always start with ``./'' and
496 * remove any embedded "." and ".." components.
497 */
498 void
499 canon(char *rawname, char *canonname, int len)
500 {
501 char *cp, *np;
502
503 if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
504 (void) strcpy(canonname, "");
505 else if (rawname[0] == '/')
506 (void) strcpy(canonname, ".");
507 else
508 (void) strcpy(canonname, "./");
509 if (strlen(canonname) + strlen(rawname) >= (unsigned)len)
510 errx(1, "canonname: not enough buffer space");
511
512 (void) strcat(canonname, rawname);
513 /*
514 * Eliminate multiple and trailing '/'s
515 */
516 for (cp = np = canonname; *np != '\0'; cp++) {
517 *cp = *np++;
518 while (*cp == '/' && *np == '/')
519 np++;
520 }
521 *cp = '\0';
522 if (*--cp == '/')
523 *cp = '\0';
524 /*
525 * Eliminate extraneous "." and ".." from pathnames.
526 */
527 for (np = canonname; *np != '\0'; ) {
528 np++;
529 cp = np;
530 while (*np != '/' && *np != '\0')
531 np++;
532 if (np - cp == 1 && *cp == '.') {
533 cp--;
534 (void) strcpy(cp, np);
535 np = cp;
536 }
537 if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
538 cp--;
539 while (cp > &canonname[1] && *--cp != '/')
540 /* find beginning of name */;
541 (void) strcpy(cp, np);
542 np = cp;
543 }
544 }
545 }
546
547 /*
548 * Do an "ls" style listing of a directory
549 */
550 static void
551 printlist(char *name, char *basename)
552 {
553 struct afile *fp, *list, *listp = NULL;
554 struct direct *dp;
555 struct afile single;
556 RST_DIR *dirp;
557 int entries, len, namelen;
558 char locname[MAXPATHLEN + 1];
559
560 dp = pathsearch(name);
561 if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
562 (!vflag && dp->d_ino == WINO))
563 return;
564 if ((dirp = rst_opendir(name)) == NULL) {
565 entries = 1;
566 list = &single;
567 mkentry(name, dp, list);
568 len = strlen(basename) + 1;
569 if (strlen(name) - len > (unsigned)single.len) {
570 freename(single.fname);
571 single.fname = savename(&name[len]);
572 single.len = strlen(single.fname);
573 }
574 } else {
575 entries = 0;
576 while ((dp = rst_readdir(dirp)))
577 entries++;
578 rst_closedir(dirp);
579 list = (struct afile *)malloc(entries * sizeof(struct afile));
580 if (list == NULL) {
581 fprintf(stderr, "ls: out of memory\n");
582 return;
583 }
584 if ((dirp = rst_opendir(name)) == NULL)
585 panic("directory reopen failed\n");
586 fprintf(stderr, "%s:\n", name);
587 entries = 0;
588 listp = list;
589 namelen = snprintf(locname, sizeof(locname), "%s/", name);
590 if (namelen >= (int)sizeof(locname))
591 namelen = sizeof(locname) - 1;
592 while ((dp = rst_readdir(dirp))) {
593 if (dp == NULL)
594 break;
595 if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
596 continue;
597 if (!vflag && (dp->d_ino == WINO ||
598 strcmp(dp->d_name, ".") == 0 ||
599 strcmp(dp->d_name, "..") == 0))
600 continue;
601 locname[namelen] = '\0';
602 if (namelen + strlen(dp->d_name) >= MAXPATHLEN) {
603 fprintf(stderr, "%s%s: name exceeds %d char\n",
604 locname, dp->d_name, MAXPATHLEN);
605 } else {
606 (void) strncat(locname, dp->d_name,
607 (int)strlen(dp->d_name));
608 mkentry(locname, dp, listp++);
609 entries++;
610 }
611 }
612 rst_closedir(dirp);
613 if (entries == 0) {
614 fprintf(stderr, "\n");
615 free(list);
616 return;
617 }
618 qsort((char *)list, entries, sizeof(struct afile), fcmp);
619 }
620 formatf(list, entries);
621 if (dirp != NULL) {
622 for (fp = listp - 1; fp >= list; fp--)
623 freename(fp->fname);
624 fprintf(stderr, "\n");
625 free(list);
626 }
627 }
628
629 /*
630 * Read the contents of a directory.
631 */
632 static void
633 mkentry(char *name, struct direct *dp, struct afile *fp)
634 {
635 char *cp;
636 struct entry *np;
637
638 fp->fnum = dp->d_ino;
639 fp->fname = savename(dp->d_name);
640 for (cp = fp->fname; *cp; cp++)
641 if (!vflag && (*cp < ' ' || *cp >= 0177))
642 *cp = '?';
643 fp->len = cp - fp->fname;
644 if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
645 fp->prefix = '^';
646 else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
647 fp->prefix = '*';
648 else
649 fp->prefix = ' ';
650 switch(dp->d_type) {
651
652 default:
653 fprintf(stderr, "Warning: undefined file type %d\n",
654 dp->d_type);
655 /* fall through */
656 case DT_REG:
657 fp->postfix = ' ';
658 break;
659
660 case DT_LNK:
661 fp->postfix = '@';
662 break;
663
664 case DT_FIFO:
665 case DT_SOCK:
666 fp->postfix = '=';
667 break;
668
669 case DT_CHR:
670 case DT_BLK:
671 fp->postfix = '#';
672 break;
673
674 #ifndef __linux__
675 /* no need for this */
676 case DT_WHT:
677 fp->postfix = '%';
678 break;
679 #endif
680
681 case DT_UNKNOWN:
682 case DT_DIR:
683 if (inodetype(dp->d_ino) == NODE)
684 fp->postfix = '/';
685 else
686 fp->postfix = ' ';
687 break;
688 }
689 return;
690 }
691
692 /*
693 * Print out a pretty listing of a directory
694 */
695 static void
696 formatf(struct afile *list, int nentry)
697 {
698 struct afile *fp, *endlist;
699 int width, bigino, haveprefix, havepostfix;
700 int i, j, w, precision = 0, columns, lines;
701
702 width = 0;
703 haveprefix = 0;
704 havepostfix = 0;
705 bigino = ROOTINO;
706 endlist = &list[nentry];
707 for (fp = &list[0]; fp < endlist; fp++) {
708 if (bigino < (int)fp->fnum)
709 bigino = fp->fnum;
710 if (width < fp->len)
711 width = fp->len;
712 if (fp->prefix != ' ')
713 haveprefix = 1;
714 if (fp->postfix != ' ')
715 havepostfix = 1;
716 }
717 if (haveprefix)
718 width++;
719 if (havepostfix)
720 width++;
721 if (vflag) {
722 for (precision = 0, i = bigino; i > 0; i /= 10)
723 precision++;
724 width += precision + 1;
725 }
726 width++;
727 columns = 81 / width;
728 if (columns == 0)
729 columns = 1;
730 lines = (nentry + columns - 1) / columns;
731 for (i = 0; i < lines; i++) {
732 for (j = 0; j < columns; j++) {
733 fp = &list[j * lines + i];
734 if (vflag) {
735 fprintf(stderr, "%*ld ", precision, (long)fp->fnum);
736 fp->len += precision + 1;
737 }
738 if (haveprefix) {
739 putc(fp->prefix, stderr);
740 fp->len++;
741 }
742 fprintf(stderr, "%s", fp->fname);
743 if (havepostfix) {
744 putc(fp->postfix, stderr);
745 fp->len++;
746 }
747 if (fp + lines >= endlist) {
748 fprintf(stderr, "\n");
749 break;
750 }
751 for (w = fp->len; w < width; w++)
752 putc(' ', stderr);
753 }
754 }
755 }
756
757 /*
758 * Skip over directory entries that are not on the tape
759 *
760 * First have to get definition of a dirent.
761 *
762 * For Linux the dirent struct is now included from bsdcompat.h
763 */
764 #ifndef __linux__
765 #undef DIRBLKSIZ
766 #include <dirent.h>
767 #undef d_ino
768 #endif /* ! __linux__ */
769
770 struct dirent *
771 glob_readdir(RST_DIR *dirp)
772 {
773 struct direct *dp;
774 static struct dirent adirent;
775
776 while ((dp = rst_readdir(dirp)) != NULL) {
777 if (!vflag && dp->d_ino == WINO)
778 continue;
779 if (dflag || TSTINO(dp->d_ino, dumpmap))
780 break;
781 }
782 if (dp == NULL)
783 return (NULL);
784 adirent.d_fileno = dp->d_ino;
785 memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
786 return (&adirent);
787 }
788
789 /*
790 * Return st_mode information in response to stat or lstat calls
791 */
792 static int
793 glob_stat(const char *name, struct stat *stp)
794 {
795 struct direct *dp;
796 dp = pathsearch(name);
797 if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
798 (!vflag && dp->d_ino == WINO))
799 return (-1);
800 if (inodetype(dp->d_ino) == NODE)
801 stp->st_mode = IFDIR;
802 else
803 stp->st_mode = IFREG;
804 return (0);
805 }
806
807 /*
808 * Comparison routine for qsort.
809 */
810 static int
811 fcmp(const void *f1, const void *f2)
812 {
813 return (strcmp(((struct afile *)f1)->fname,
814 ((struct afile *)f2)->fname));
815 }
816
817 /*
818 * respond to interrupts
819 */
820 void
821 onintr(UNUSED(int signo))
822 {
823 int save_errno = errno;
824
825 if (command == 'i' && runshell)
826 longjmp(reset, 1);
827 if (reply("restore interrupted, continue") == FAIL)
828 exit(1);
829 errno = save_errno;
830 }
831
832
833 #if HAVE_READLINE
834
835 #if !HAVE_READLINE_RLCM
836 #define rl_completion_matches completion_matches
837 #endif
838
839 /* A static variable for holding the line. */
840 static char *line_read = NULL;
841
842 static char completion_curdir[MAXPATHLEN];
843
844 static char *commands[] = {
845 "add ", "cd ", "delete ", "extract ", "help ",
846 "? ", "ls ", "pwd ", "prompt ", "quit ", "xit ",
847 "verbose ", "setmodes ", "what ", "Debug ",
848 NULL };
849
850 static char *files = NULL;
851
852 static char *
853 rl_gets (char *dir)
854 {
855 char *prompt;
856 int sz;
857
858 snprintf(completion_curdir, MAXPATHLEN, "%s", dir);
859 completion_curdir[MAXPATHLEN - 1] = '\0';
860
861 if (pflag) {
862 sz = 6 + strlen(__progname) + strlen(spcl.c_filesys) + strlen((completion_curdir + 1 ? completion_curdir + 1 : "/"));
863 prompt = (char *)malloc(sz);
864 if (!prompt)
865 return NULL;
866 snprintf(prompt, sz, "%s:%s:%s > ",
867 __progname,
868 spcl.c_filesys,
869 (completion_curdir + 1 ? completion_curdir + 1 : "/"));
870 }
871 else {
872 sz = 4 + strlen(__progname);
873 prompt = (char *)malloc(sz);
874 if (!prompt)
875 return NULL;
876 snprintf(prompt, sz, "%s > ", __progname);
877 }
878 prompt[sz - 1] = '\0';
879
880 if (line_read) {
881 free (line_read);
882 line_read = (char *)NULL;
883 }
884
885 do {
886 line_read = readline (prompt);
887 } while (line_read && !*line_read);
888
889 free(prompt);
890
891 if (!line_read) {
892 printf("\n");
893 return strdup("quit");
894 }
895
896 add_history (line_read);
897
898 return (line_read);
899 }
900
901 static char *
902 command_generator(const char *text, int state)
903 {
904 static int list_index, len;
905 char *name;
906
907 if (!state) {
908 list_index = 0;
909 len = strlen(text);
910 }
911
912 while ( (name = commands[list_index]) != NULL) {
913
914 list_index ++;
915
916 if (strncmp(name, text, len) == 0)
917 return strdup(name);
918 }
919
920 return NULL;
921 }
922
923 static char *
924 filename_generator(const char *text, int state)
925 {
926 static int list_index;
927 char *name;
928 RST_DIR *dirp;
929 struct direct *dp;
930 static int entries;
931 char pname[MAXPATHLEN];
932 char fname[MAXPATHLEN];
933 char *slash;
934 char ppname[MAXPATHLEN];
935
936 if (!state) {
937 list_index = 0;
938
939 if (files != NULL) {
940 free(files);
941 entries = 0;
942 files = NULL;
943 }
944 if ((slash = strrchr(text, '/')) != NULL) {
945 int idx = slash - text;
946 if (idx > MAXPATHLEN - 2)
947 idx = MAXPATHLEN - 2;
948 strncpy(ppname, text, MAXPATHLEN);
949 ppname[MAXPATHLEN - 1] = '\0';
950 ppname[idx] = '\0';
951 if (text[0] == '/')
952 snprintf(pname, MAXPATHLEN, ".%s", ppname);
953 else
954 snprintf(pname, MAXPATHLEN, "%s/%s", completion_curdir, ppname);
955 strncpy(fname, ppname + idx + 1, MAXPATHLEN);
956 ppname[idx] = '/';
957 ppname[idx + 1] = '\0';
958 }
959 else {
960 strncpy(pname, completion_curdir, MAXPATHLEN);
961 strncpy(fname, text, MAXPATHLEN);
962 ppname[0] = '\0';
963 }
964 pname[MAXPATHLEN - 1] = '\0';
965 fname[MAXPATHLEN - 1] = '\0';
966 if ((dirp = rst_opendir(pname)) == NULL)
967 return NULL;
968 entries = 0;
969 while ((dp = rst_readdir(dirp)))
970 entries++;
971 rst_closedir(dirp);
972 files = (char *)malloc(entries * MAXPATHLEN);
973 if (files == NULL) {
974 fprintf(stderr, "Out of memory\n");
975 entries = 0;
976 return NULL;
977 }
978 if ((dirp = rst_opendir(pname)) == NULL)
979 panic("directory reopen failed\n");
980 entries = 0;
981 while ((dp = rst_readdir(dirp))) {
982 if (TSTINO(dp->d_ino, dumpmap) == 0)
983 continue;
984 if (strcmp(dp->d_name, ".") == 0 ||
985 strcmp(dp->d_name, "..") == 0)
986 continue;
987 if (strncmp(dp->d_name, fname, strlen(fname)) == 0) {
988 if (inodetype(dp->d_ino) == NODE)
989 snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s/", ppname, dp->d_name);
990 else
991 snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s ", ppname, dp->d_name);
992 *(files + (entries + 1) * MAXPATHLEN - 1) = '\0';
993 ++entries;
994 }
995 }
996 rst_closedir(dirp);
997 }
998
999 if (list_index >= entries)
1000 return NULL;
1001
1002 name = strdup(files + list_index * MAXPATHLEN);
1003 list_index ++;
1004
1005 return name;
1006 }
1007
1008 static char **
1009 restore_completion (const char *text, int start, UNUSED(int end))
1010 {
1011 char **matches;
1012
1013 if (start == 0)
1014 matches = rl_completion_matches (text, command_generator);
1015 else
1016 matches = rl_completion_matches (text, filename_generator);
1017
1018 return (matches);
1019 }
1020
1021 static void
1022 initialize_readline(void)
1023 {
1024 rl_readline_name = "dump";
1025 rl_attempted_completion_function = restore_completion;
1026 rl_completion_entry_function = NULL;
1027 #if HAVE_READLINE_CAC /* compile with readline 2.0 */
1028 rl_completion_append_character = '\0';
1029 #endif
1030 rl_instream = terminal;
1031 }
1032
1033 #endif /* HAVE_READLINE */