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