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