]> git.wh0rd.org - dump.git/blob - restore/interactive.c
bc94ed45c339cdff5c5bd02eae8598e1d83ee1b9
[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.21 2002/01/25 15:08:59 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 (char *text, int start, int end);
87 static char *command_generator(char *text, int state);
88 static char *filename_generator(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(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 /* A static variable for holding the line. */
840 static char *line_read = NULL;
841
842 static char completion_curdir[MAXPATHLEN];
843
844 static char *commands[] = {
845 "add ", "cd ", "delete ", "extract ", "help ",
846 "? ", "ls ", "pwd ", "prompt ", "quit ", "xit ",
847 "verbose ", "setmodes ", "what ", "Debug ",
848 NULL };
849
850 static char *files = NULL;
851
852 static char *
853 rl_gets (char *dir)
854 {
855 char *prompt;
856 int sz;
857
858 snprintf(completion_curdir, MAXPATHLEN, "%s", dir);
859 completion_curdir[MAXPATHLEN - 1] = '\0';
860
861 if (pflag) {
862 sz = 6 + strlen(__progname) + strlen(spcl.c_filesys) + strlen((completion_curdir + 1 ? completion_curdir + 1 : "/"));
863 prompt = (char *)malloc(sz);
864 if (!prompt)
865 return NULL;
866 snprintf(prompt, sz, "%s:%s:%s > ",
867 __progname,
868 spcl.c_filesys,
869 (completion_curdir + 1 ? completion_curdir + 1 : "/"));
870 }
871 else {
872 sz = 4 + strlen(__progname);
873 prompt = (char *)malloc(sz);
874 if (!prompt)
875 return NULL;
876 snprintf(prompt, sz, "%s > ", __progname);
877 }
878 prompt[sz - 1] = '\0';
879
880 if (line_read) {
881 free (line_read);
882 line_read = (char *)NULL;
883 }
884
885 do {
886 line_read = readline (prompt);
887 } while (line_read && !*line_read);
888
889 free(prompt);
890
891 if (!line_read) {
892 printf("\n");
893 return strdup("quit");
894 }
895
896 add_history (line_read);
897
898 return (line_read);
899 }
900
901 static void
902 initialize_readline(void)
903 {
904 rl_attempted_completion_function = restore_completion;
905 rl_completion_entry_function = (Function *)NULL;
906 rl_completion_append_character = '\0';
907 rl_instream = terminal;
908 }
909
910 static char **
911 restore_completion (char *text, int start, int end)
912 {
913 char **matches;
914
915 if (start == 0)
916 matches = completion_matches (text, command_generator);
917 else
918 matches = completion_matches (text, filename_generator);
919
920 return (matches);
921 }
922
923 static char *
924 command_generator(char *text, int state)
925 {
926 static int list_index, len;
927 char *name;
928
929 if (!state) {
930 list_index = 0;
931 len = strlen(text);
932 }
933
934 while ( (name = commands[list_index]) != NULL) {
935
936 list_index ++;
937
938 if (strncmp(name, text, len) == 0)
939 return strdup(name);
940 }
941
942 return NULL;
943 }
944
945 static char *
946 filename_generator(char *text, int state)
947 {
948 static int list_index;
949 char *name;
950 RST_DIR *dirp;
951 struct direct *dp;
952 static int entries;
953 char pname[MAXPATHLEN];
954 char fname[MAXPATHLEN];
955 char *slash;
956 char ppname[MAXPATHLEN];
957
958 if (!state) {
959 list_index = 0;
960
961 if (files != NULL) {
962 free(files);
963 entries = 0;
964 files = NULL;
965 }
966 if ((slash = strrchr(text, '/')) != NULL) {
967 int idx = slash - text;
968 if (idx > MAXPATHLEN - 2)
969 idx = MAXPATHLEN - 2;
970 strncpy(ppname, text, MAXPATHLEN);
971 ppname[MAXPATHLEN - 1] = '\0';
972 ppname[idx] = '\0';
973 if (text[0] == '/')
974 snprintf(pname, MAXPATHLEN, ".%s", ppname);
975 else
976 snprintf(pname, MAXPATHLEN, "%s/%s", completion_curdir, ppname);
977 strncpy(fname, ppname + idx + 1, MAXPATHLEN);
978 ppname[idx] = '/';
979 ppname[idx + 1] = '\0';
980 }
981 else {
982 strncpy(pname, completion_curdir, MAXPATHLEN);
983 strncpy(fname, text, MAXPATHLEN);
984 ppname[0] = '\0';
985 }
986 pname[MAXPATHLEN - 1] = '\0';
987 fname[MAXPATHLEN - 1] = '\0';
988 if ((dirp = rst_opendir(pname)) == NULL)
989 return NULL;
990 entries = 0;
991 while ((dp = rst_readdir(dirp)))
992 entries++;
993 rst_closedir(dirp);
994 files = (char *)malloc(entries * MAXPATHLEN);
995 if (files == NULL) {
996 fprintf(stderr, "Out of memory\n");
997 entries = 0;
998 return NULL;
999 }
1000 if ((dirp = rst_opendir(pname)) == NULL)
1001 panic("directory reopen failed\n");
1002 entries = 0;
1003 while ((dp = rst_readdir(dirp))) {
1004 if (TSTINO(dp->d_ino, dumpmap) == 0)
1005 continue;
1006 if (strcmp(dp->d_name, ".") == 0 ||
1007 strcmp(dp->d_name, "..") == 0)
1008 continue;
1009 if (strncmp(dp->d_name, fname, strlen(fname)) == 0) {
1010 if (inodetype(dp->d_ino) == NODE)
1011 snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s/", ppname, dp->d_name);
1012 else
1013 snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s ", ppname, dp->d_name);
1014 *(files + (entries + 1) * MAXPATHLEN - 1) = '\0';
1015 ++entries;
1016 }
1017 }
1018 rst_closedir(dirp);
1019 }
1020
1021 if (list_index >= entries)
1022 return NULL;
1023
1024 name = strdup(files + list_index * MAXPATHLEN);
1025 list_index ++;
1026
1027 return name;
1028 }
1029 #endif /* HAVE_READLINE */