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