]> git.wh0rd.org - dump.git/blob - restore/interactive.c
Prepare for release 0.4b37
[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.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 */