9402 restore: this statement may fall through
[unleashed.git] / usr / src / cmd / backup / restore / interactive.c
blob3d2c2c7bb0878709f88ccf69ab1c0029ae400ed7
1 /*
2 * Copyright 1998,2001-2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
9 /*
10 * Copyright (c) 1985 Regents of the University of California.
11 * All rights reserved. The Berkeley software License Agreement
12 * specifies the terms and conditions for redistribution.
15 #include <setjmp.h>
16 #include <euc.h>
17 #include <widec.h>
18 #include "restore.h"
19 #include <ctype.h>
20 #include <limits.h>
21 #include <sys/wait.h>
23 extern eucwidth_t wp;
25 #define round(a, b) ((((a) + (b) - 1) / (b)) * (b))
28 * Things to handle interruptions.
30 static jmp_buf reset;
31 static int reset_OK;
32 static char *nextarg = NULL;
34 static int dontexpand; /* co-routine state set in getnext, used in expandarg */
36 #ifdef __STDC__
37 static void getcmd(char *, char *, size_t, char *, size_t, struct arglist *);
38 static void expandarg(char *, struct arglist *);
39 static void printlist(char *, ino_t, char *, int);
40 static void formatf(struct arglist *);
41 static char *copynext(char *, char *, size_t);
42 static int fcmp(struct afile *, struct afile *);
43 static char *fmtentry(struct afile *);
44 static void setpagercmd(void);
45 static uint_t setpagerargs(char **);
46 #else
47 static void getcmd();
48 static void expandarg();
49 static void printlist();
50 static void formatf();
51 static char *copynext();
52 static int fcmp();
53 static char *fmtentry();
54 static void setpagercmd();
55 static uint_t setpagerargs();
56 #endif
59 * Read and execute commands from the terminal.
61 void
62 #ifdef __STDC__
63 runcmdshell(void)
64 #else
65 runcmdshell()
66 #endif
68 struct entry *np;
69 ino_t ino;
70 static struct arglist alist = { 0, 0, 0, 0, 0 };
71 char curdir[MAXCOMPLEXLEN];
72 char name[MAXCOMPLEXLEN];
73 char cmd[BUFSIZ];
75 #ifdef lint
76 curdir[0] = '\0';
77 #endif /* lint */
79 canon("/", curdir, sizeof (curdir));
80 loop:
81 if (setjmp(reset) != 0) {
82 for (; alist.head < alist.last; alist.head++)
83 freename(alist.head->fname);
84 nextarg = NULL;
85 volno = 0;
86 goto loop; /* make sure jmpbuf is up-to-date */
88 reset_OK = 1;
89 getcmd(curdir, cmd, sizeof (cmd), name, sizeof (name), &alist);
92 * Using strncmp() to catch unique prefixes.
94 switch (cmd[0]) {
96 * Add elements to the extraction list.
98 case 'a':
99 if (strncmp(cmd, "add", strlen(cmd)) != 0)
100 goto bad;
101 if (name[0] == '\0')
102 break;
103 ino = dirlookup(name);
104 if (ino == 0)
105 break;
106 if (mflag)
107 pathcheck(name);
108 treescan(name, ino, addfile);
109 break;
111 * Change working directory.
113 case 'c':
114 if (strncmp(cmd, "cd", strlen(cmd)) != 0)
115 goto bad;
116 if (name[0] == '\0')
117 break;
118 ino = dirlookup(name);
119 if (ino == 0)
120 break;
121 if (inodetype(ino) == LEAF) {
122 (void) fprintf(stderr,
123 gettext("%s: not a directory\n"), name);
124 break;
127 /* No need to canon(name), getcmd() did it for us */
128 (void) strncpy(curdir, name, sizeof (curdir));
129 curdir[sizeof (curdir) - 1] = '\0';
130 break;
132 * Delete elements from the extraction list.
134 case 'd':
135 if (strncmp(cmd, "delete", strlen(cmd)) != 0)
136 goto bad;
137 if (name[0] == '\0')
138 break;
139 np = lookupname(name);
140 if (np == NIL || (np->e_flags & NEW) == 0) {
141 (void) fprintf(stderr,
142 gettext("%s: not on extraction list\n"), name);
143 break;
145 treescan(name, np->e_ino, deletefile);
146 break;
148 * Extract the requested list.
150 case 'e':
151 if (strncmp(cmd, "extract", strlen(cmd)) != 0)
152 goto bad;
153 attrscan(0, addfile);
154 createfiles();
155 createlinks();
156 setdirmodes();
157 if (dflag)
158 checkrestore();
159 volno = 0;
160 break;
162 * List available commands.
164 case 'h':
165 if (strncmp(cmd, "help", strlen(cmd)) != 0)
166 goto bad;
167 /*FALLTHROUGH*/
168 case '?':
169 /* ANSI string catenation, to shut cstyle up */
170 (void) fprintf(stderr, "%s",
171 gettext("Available commands are:\n"
172 "\tls [arg] - list directory\n"
173 "\tmarked [arg] - list items marked for extraction from directory\n"
174 "\tcd arg - change directory\n"
175 "\tpwd - print current directory\n"
176 "\tadd [arg] - add `arg' to list of files to be extracted\n"
177 "\tdelete [arg] - delete `arg' from list of files to be extracted\n"
178 "\textract - extract requested files\n"
179 "\tsetmodes - set modes of requested directories\n"
180 "\tquit - immediately exit program\n"
181 "\twhat - list dump header information\n"
182 "\tverbose - toggle verbose flag (useful with ``ls'')\n"
183 "\tpaginate - toggle pagination flag (affects ``ls'' and ``marked'')\n"
184 "\tsetpager - set pagination command and arguments\n"
185 "\thelp or `?' - print this list\n"
186 "If no `arg' is supplied, the current directory is used\n"));
187 break;
189 * List a directory.
191 case 'l':
192 case 'm':
193 if ((strncmp(cmd, "ls", strlen(cmd)) != 0) &&
194 (strncmp(cmd, "marked", strlen(cmd)) != 0))
195 goto bad;
196 if (name[0] == '\0')
197 break;
198 ino = dirlookup(name);
199 if (ino == 0)
200 break;
201 printlist(name, ino, curdir, *cmd == 'm');
202 break;
204 * Print current directory or enable pagination.
206 case 'p':
207 if (strlen(cmd) < 2)
208 goto ambiguous;
209 if (strncmp(cmd, "pwd", strlen(cmd)) == 0) {
210 if (curdir[1] == '\0') {
211 (void) fprintf(stderr, "/\n");
212 } else {
213 (void) fprintf(stderr, "%s\n", &curdir[1]);
215 } else if (strncmp(cmd, "paginate", strlen(cmd)) == 0) {
216 if (paginating) {
217 (void) fprintf(stderr,
218 gettext("paging disabled\n"));
219 paginating = 0;
220 break;
222 if (vflag) {
223 (void) fprintf(stderr,
224 gettext("paging enabled (%s)\n"),
225 pager_catenated);
226 } else {
227 (void) fprintf(stderr,
228 gettext("paging enabled\n"));
230 if (dflag) {
231 int index = 0;
233 while (index < pager_len) {
234 (void) fprintf(stderr,
235 ">>>pager_vector[%d] = `%s'\n",
236 index,
237 pager_vector[index] ?
238 pager_vector[index] : "(null)");
239 index += 1;
242 paginating = 1;
243 } else {
244 goto bad;
246 break;
248 * Quit.
250 case 'q':
251 if (strncmp(cmd, "quit", strlen(cmd)) != 0)
252 goto bad;
253 reset_OK = 0;
254 return;
255 case 'x':
256 if (strncmp(cmd, "xit", strlen(cmd)) != 0)
257 goto bad;
258 reset_OK = 0;
259 return;
261 * Toggle verbose mode.
263 case 'v':
264 if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
265 goto bad;
266 if (vflag) {
267 (void) fprintf(stderr, gettext("verbose mode off\n"));
268 vflag = 0;
269 break;
271 (void) fprintf(stderr, gettext("verbose mode on\n"));
272 vflag = 1;
273 break;
275 * Just restore requested directory modes, or set pagination command.
277 case 's':
278 if (strlen(cmd) < 4)
279 goto ambiguous;
280 if (strncmp(cmd, "setmodes", strlen(cmd)) == 0) {
281 setdirmodes();
282 } else if (strncmp(cmd, "setpager", strlen(cmd)) == 0) {
283 setpagercmd();
284 } else {
285 goto bad;
287 break;
289 * Print out dump header information.
291 case 'w':
292 if (strncmp(cmd, "what", strlen(cmd)) != 0)
293 goto bad;
294 printdumpinfo();
295 break;
297 * Turn on debugging.
299 case 'D':
300 if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
301 goto bad;
302 if (dflag) {
303 (void) fprintf(stderr, gettext("debugging mode off\n"));
304 dflag = 0;
305 break;
307 (void) fprintf(stderr, gettext("debugging mode on\n"));
308 dflag++;
309 break;
311 * Unknown command.
313 default:
314 bad:
315 (void) fprintf(stderr,
316 gettext("%s: unknown command; type ? for help\n"), cmd);
317 break;
318 ambiguous:
319 (void) fprintf(stderr,
320 gettext("%s: ambiguous command; type ? for help\n"), cmd);
321 break;
323 goto loop;
326 static char input[MAXCOMPLEXLEN]; /* shared by getcmd() and setpagercmd() */
327 #define rawname input /* save space by reusing input buffer */
330 * Read and parse an interactive command.
331 * The first word on the line is assigned to "cmd". If
332 * there are no arguments on the command line, then "curdir"
333 * is returned as the argument. If there are arguments
334 * on the line they are returned one at a time on each
335 * successive call to getcmd. Each argument is first assigned
336 * to "name". If it does not start with "/" the pathname in
337 * "curdir" is prepended to it. Finally "canon" is called to
338 * eliminate any embedded ".." components.
340 /* ARGSUSED */
341 static void
342 getcmd(curdir, cmd, cmdsiz, name, namesiz, ap)
343 char *curdir, *cmd, *name;
344 size_t cmdsiz, namesiz;
345 struct arglist *ap;
347 char *cp;
348 char output[MAXCOMPLEXLEN];
351 * Check to see if still processing arguments.
353 if (ap->head != ap->last) {
354 (void) strncpy(name, ap->head->fname, namesiz);
355 name[namesiz - 1] = '\0';
356 /* double null terminate string */
357 if ((strlen(name) + 2) > namesiz) {
358 fprintf(stderr, gettext("name is too long, ignoring"));
359 memset(name, 0, namesiz);
360 } else {
361 name[strlen(name) + 1] = '\0';
363 freename(ap->head->fname);
364 ap->head++;
365 return;
367 if (nextarg != NULL)
368 goto getnext;
370 * Read a command line and trim off trailing white space.
372 readagain:
373 do {
374 (void) fprintf(stderr, "%s > ", progname);
375 (void) fflush(stderr);
376 (void) fgets(input, sizeof (input), terminal);
377 } while (!feof(terminal) && input[0] == '\n');
378 if (feof(terminal)) {
379 (void) strncpy(cmd, "quit", cmdsiz);
380 return;
382 /* trim off trailing white space and newline */
383 for (cp = &input[strlen(input) - 2];
384 cp >= &input[0] && isspace((uchar_t)*cp);
385 cp--) {
386 continue;
387 /*LINTED [empty loop body]*/
389 *++cp = '\0';
390 if ((strlen(input) + 2) > MAXCOMPLEXLEN) {
391 fprintf(stderr, gettext("command is too long\n"));
392 goto readagain;
393 } else {
394 /* double null terminate string */
395 *(cp + 1) = '\0';
398 if (cp == &input[0])
399 goto readagain;
402 * Copy the command into "cmd".
404 cp = copynext(input, cmd, cmdsiz);
405 ap->cmd = cmd;
407 * If no argument, use curdir as the default.
409 if (*cp == '\0') {
410 (void) strncpy(name, curdir, namesiz);
411 name[namesiz - 1] = '\0';
412 /* double null terminate string */
413 if ((strlen(name) + 2) > namesiz) {
414 fprintf(stderr, gettext("name is too long, ignoring"));
415 memset(name, 0, namesiz);
416 } else {
417 name[strlen(name) + 1] = '\0';
419 return;
421 nextarg = cp;
423 * Find the next argument.
425 getnext:
426 cp = copynext(nextarg, rawname, sizeof (rawname));
427 if (*cp == '\0')
428 nextarg = NULL;
429 else
430 nextarg = cp;
432 * If it an absolute pathname, canonicalize it and return it.
434 if (rawname[0] == '/') {
435 canon(rawname, name, namesiz);
436 } else {
438 * For relative pathnames, prepend the current directory to
439 * it then canonicalize and return it.
441 (void) snprintf(output, sizeof (output), "%s/%s",
442 curdir, rawname);
443 canon(output, name, namesiz);
445 expandarg(name, ap);
447 * ap->head->fname guaranteed to be double null-terminated and
448 * no more than MAXCOMPLEXLEN characters long.
450 assert(namesiz >= (MAXCOMPLEXLEN));
451 (void) strcpy(name, ap->head->fname);
452 /* double null terminate string */
453 name[strlen(name) + 1] = '\0';
454 freename(ap->head->fname);
455 ap->head++;
456 #undef rawname
460 * Strip off the next token of the input.
462 static char *
463 copynext(input, output, outsize)
464 char *input, *output;
465 size_t outsize;
467 char *cp, *bp, *limit;
468 char quote;
470 dontexpand = 0;
471 /* skip to argument */
472 for (cp = input; *cp != '\0' && isspace((uchar_t)*cp); cp++) {
473 continue;
474 /*LINTED [empty loop body]*/
476 bp = output;
477 limit = output + outsize - 1; /* -1 for the trailing \0 */
478 while (!isspace((uchar_t)*cp) && *cp != '\0' && bp < limit) {
480 * Handle back slashes.
482 if (*cp == '\\') {
483 if (*++cp == '\0') {
484 (void) fprintf(stderr, gettext(
485 "command lines cannot be continued\n"));
486 continue;
488 *bp++ = *cp++;
489 continue;
492 * The usual unquoted case.
494 if (*cp != '\'' && *cp != '"') {
495 *bp++ = *cp++;
496 continue;
499 * Handle single and double quotes.
501 quote = *cp++;
502 dontexpand = 1;
503 while (*cp != quote && *cp != '\0' && bp < limit)
504 *bp++ = *cp++;
505 if (*cp++ == '\0') {
506 (void) fprintf(stderr,
507 gettext("missing %c\n"), (uchar_t)quote);
508 cp--;
509 continue;
512 *bp = '\0';
513 if ((strlen(output) + 2) > outsize) {
514 fprintf(stderr, gettext(
515 "name is too long, ignoring"));
516 memset(output, 0, outsize);
517 } else {
518 /* double null terminate string */
519 *(bp + 1) = '\0';
521 return (cp);
525 * Canonicalize file names to always start with ``./'' and
526 * remove any imbedded "." and ".." components.
528 * The pathname "canonname" is returned double null terminated.
530 void
531 canon(rawname, canonname, limit)
532 char *rawname, *canonname;
533 size_t limit;
535 char *cp, *np, *prefix;
536 uint_t len;
538 assert(limit > 3);
539 if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
540 prefix = "";
541 else if (rawname[0] == '/')
542 prefix = ".";
543 else
544 prefix = "./";
545 (void) snprintf(canonname, limit, "%s%s", prefix, rawname);
547 * Eliminate multiple and trailing '/'s
549 for (cp = np = canonname; *np != '\0'; cp++) {
550 *cp = *np++;
551 while (*cp == '/' && *np == '/')
552 np++;
554 *cp = '\0';
555 if ((strlen(canonname) + 2) > limit) {
556 fprintf(stderr,
557 gettext("canonical name is too long, ignoring name\n"));
558 memset(canonname, 0, limit);
559 } else {
560 /* double null terminate string */
561 *(cp + 1) = '\0';
564 if (*--cp == '/')
565 *cp = '\0';
567 * Eliminate extraneous "." and ".." from pathnames. Uses
568 * memmove(), as strcpy() might do the wrong thing for these
569 * small overlaps.
571 np = canonname;
572 while (*np != '\0') {
573 np++;
574 cp = np;
575 while (*np != '/' && *np != '\0')
576 np++;
577 if (np - cp == 1 && *cp == '.') {
578 cp--;
579 len = strlen(np);
580 (void) memmove(cp, np, len);
581 *(cp + len) = '\0';
582 /* double null terminate string */
583 *(cp + len + 1) = '\0';
584 np = cp;
586 if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
587 cp--;
588 /* find beginning of name */
589 while (cp > &canonname[1] && *--cp != '/') {
590 continue;
591 /*LINTED [empty loop body]*/
593 len = strlen(np);
594 (void) memmove(cp, np, len);
595 *(cp + len) = '\0';
596 /* double null terminate string */
597 *(cp + len + 1) = '\0';
598 np = cp;
604 * globals (file name generation)
606 * "*" in params matches r.e ".*"
607 * "?" in params matches r.e. "."
608 * "[...]" in params matches character class
609 * "[...a-z...]" in params matches a through z.
611 static void
612 expandarg(arg, ap)
613 char *arg;
614 struct arglist *ap;
616 static struct afile single;
617 int size;
619 ap->head = ap->last = (struct afile *)0;
620 if (dontexpand)
621 size = 0;
622 else
623 size = expand(arg, 0, ap);
624 if (size == 0) {
625 struct entry *ep;
627 ep = lookupname(arg);
628 single.fnum = ep ? ep->e_ino : 0;
629 single.fname = savename(arg);
630 ap->head = &single;
631 ap->last = ap->head + 1;
632 return;
634 if ((ap->last - ap->head) > ULONG_MAX) {
635 (void) fprintf(stderr,
636 gettext("Argument expansion too large to sort\n"));
637 } else {
638 /* LINTED pointer arith just range-checked */
639 qsort((char *)ap->head, (size_t)(ap->last - ap->head),
640 sizeof (*ap->head),
641 (int (*)(const void *, const void *)) fcmp);
646 * Do an "ls" style listing of a directory
648 static void
649 printlist(name, ino, basename, marked_only)
650 char *name;
651 ino_t ino;
652 char *basename;
653 int marked_only;
655 struct afile *fp;
656 struct direct *dp;
657 static struct arglist alist = { 0, 0, 0, 0, "ls" };
658 struct afile single;
659 struct entry *np;
660 RST_DIR *dirp;
661 int list_entry;
663 if ((dirp = rst_opendir(name)) == NULL) {
664 single.fnum = ino;
665 if (strncmp(name, basename, strlen(basename)) == 0)
666 single.fname = savename(name + strlen(basename) + 1);
667 else
668 single.fname = savename(name);
669 alist.head = &single;
670 alist.last = alist.head + 1;
671 if (alist.base != NULL) {
672 free(alist.base);
673 alist.base = NULL;
675 } else {
676 alist.head = (struct afile *)0;
677 (void) fprintf(stderr, "%s:\n", name);
678 while (dp = rst_readdir(dirp)) {
679 if (dp == NULL || dp->d_ino == 0) {
680 rst_closedir(dirp);
681 dirp = NULL;
682 break;
684 if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
685 continue;
686 if (vflag == 0 &&
687 (strcmp(dp->d_name, ".") == 0 ||
688 strcmp(dp->d_name, "..") == 0))
689 continue;
690 list_entry = 1;
691 if (marked_only) {
692 np = lookupino(dp->d_ino);
693 if ((np == NIL) || ((np->e_flags & NEW) == 0))
694 list_entry = 0;
696 if (list_entry) {
697 if (!mkentry(dp->d_name, dp->d_ino, &alist)) {
698 rst_closedir(dirp);
699 return;
704 if (alist.head != 0) {
705 if ((alist.last - alist.head) > ULONG_MAX) {
706 (void) fprintf(stderr,
707 gettext("Directory too large to sort\n"));
708 } else {
709 qsort((char *)alist.head,
710 /* LINTED range-checked */
711 (size_t)(alist.last - alist.head),
712 sizeof (*alist.head),
713 (int (*)(const void *, const void *)) fcmp);
715 formatf(&alist);
716 for (fp = alist.head; fp < alist.last; fp++)
717 freename(fp->fname);
718 alist.head = NULL;
720 * Don't free alist.base, as we'll probably be called
721 * again, and might as well re-use what we've got.
724 if (dirp != NULL) {
725 (void) fprintf(stderr, "\n");
726 rst_closedir(dirp);
731 * Print out a pretty listing of a directory
733 static void
734 formatf(ap)
735 struct arglist *ap;
737 struct afile *fp;
738 struct entry *np;
739 /* LINTED: result fits into an int */
740 int nentry = (int)(ap->last - ap->head);
741 int i, j;
742 uint_t len, w, width = 0, columns, lines;
743 char *cp;
744 FILE *output = stderr;
746 if (ap->head == ap->last)
747 return;
749 if (paginating) {
750 int fds[2];
752 if (pipe(fds) < 0) {
753 perror(gettext("could not create pipe"));
754 goto no_page;
757 switch (fork()) {
758 case -1:
759 perror(gettext("could not fork"));
760 goto no_page;
761 case 0:
763 * Make sure final output still ends up in
764 * the same place.
766 (void) dup2(fileno(stderr), fileno(stdout));
767 (void) close(fds[0]);
768 (void) dup2(fds[1], fileno(stdin));
769 execvp(pager_vector[0], pager_vector);
770 perror(gettext("execvp of pager failed"));
771 exit(1);
772 /*NOTREACHED*/
773 default:
774 (void) close(fds[1]);
775 output = fdopen(fds[0], "w");
776 if (output != (FILE *)NULL) {
777 break;
779 perror(gettext("could not open pipe to pager"));
780 output = stderr;
781 no_page:
782 (void) fprintf(stderr,
783 gettext("pagination disabled\n"));
784 paginating = 0;
788 for (fp = ap->head; fp < ap->last; fp++) {
789 fp->ftype = inodetype(fp->fnum);
790 np = lookupino(fp->fnum);
791 if (np != NIL)
792 fp->fflags = np->e_flags;
793 else
794 fp->fflags = 0;
795 len = strlen(fmtentry(fp));
796 if (len > width)
797 width = len;
799 width += 2;
800 columns = 80 / width;
801 if (columns == 0)
802 columns = 1;
803 lines = (nentry + columns - 1) / columns;
804 for (i = 0; i < lines && !ferror(output); i++) {
805 for (j = 0; j < columns && !ferror(output); j++) {
806 fp = ap->head + j * lines + i;
807 cp = fmtentry(fp);
808 (void) fprintf(output, "%s", cp);
809 if (fp + lines >= ap->last) {
810 (void) fprintf(output, "\n");
811 break;
813 w = strlen(cp);
814 while (w < width) {
815 w++;
816 if (fprintf(output, " ") < 0)
817 break;
822 if (paginating) {
823 (void) fclose(output);
824 (void) wait((int *)NULL);
829 * Comparison routine for qsort.
831 static int
832 fcmp(f1, f2)
833 struct afile *f1, *f2;
836 return (strcoll(f1->fname, f2->fname));
840 * Format a directory entry.
842 static char *
843 fmtentry(fp)
844 struct afile *fp;
846 static char fmtres[MAXCOMPLEXLEN];
847 static int precision = 0;
848 ino_t i;
849 char *cp, *dp, *limit;
851 if (!vflag) {
852 /* MAXCOMPLEXLEN assumed to be >= 1 */
853 fmtres[0] = '\0';
854 } else {
855 if (precision == 0) {
856 for (i = maxino; i != 0; i /= 10)
857 precision++;
858 if (sizeof (fmtres) < (unsigned)(precision + 2)) {
859 (void) fprintf(stderr, gettext(
860 "\nInternal check failed, minimum width %d exceeds available size %d\n"),
861 (precision + 2), sizeof (fmtres));
862 done(1);
865 (void) snprintf(fmtres, sizeof (fmtres), "%*ld ",
866 precision, fp->fnum);
868 dp = &fmtres[strlen(fmtres)];
869 limit = fmtres + sizeof (fmtres) - 1;
870 if (dflag && BIT(fp->fnum, dumpmap) == 0)
871 *dp++ = '^';
872 else if ((fp->fflags & NEW) != 0)
873 *dp++ = '*';
874 else
875 *dp++ = ' ';
876 for (cp = fp->fname; *cp && dp < limit; cp++)
877 /* LINTED: precedence ok, can't fix system macro */
878 if (!vflag && (!ISPRINT(*cp, wp)))
879 *dp++ = '?';
880 else
881 *dp++ = *cp;
882 if (fp->ftype == NODE && dp < limit)
883 *dp++ = '/';
884 *dp++ = 0;
885 return (fmtres);
889 * respond to interrupts
891 /* ARGSUSED */
892 void
893 onintr(sig)
894 int sig;
896 char buf[300];
898 if (command == 'i' && reset_OK)
899 longjmp(reset, 1);
901 (void) snprintf(buf, sizeof (buf),
902 gettext("%s interrupted, continue"), progname);
903 if (reply(buf) == FAIL)
904 done(1);
907 * Set up pager_catenated and pager_vector.
909 void
910 #ifdef __STDC__
911 initpagercmd(void)
912 #else
913 initpagercmd()
914 #endif
916 char *cp;
918 cp = getenv("PAGER");
919 if (cp != NULL)
920 pager_catenated = strdup(cp);
921 if ((pager_catenated == NULL) || (*pager_catenated == '\0')) {
922 if (pager_catenated != NULL)
923 free(pager_catenated);
924 pager_catenated = strdup(DEF_PAGER);
926 if (pager_catenated == NULL) {
927 (void) fprintf(stderr, gettext("out of memory\n"));
928 done(1);
931 pager_vector = (char **)malloc(sizeof (char *));
932 if (pager_vector == NULL) {
933 (void) fprintf(stderr, gettext("out of memory\n"));
934 done(1);
937 pager_len = 1;
938 cp = pager_catenated;
939 (void) setpagerargs(&cp);
944 * Resets pager_catenated and pager_vector from user input.
946 void
947 #ifdef __STDC__
948 setpagercmd(void)
949 #else
950 setpagercmd()
951 #endif
953 uint_t catenate_length;
954 int index;
957 * We'll get called immediately after setting a pager, due to
958 * our interaction with getcmd()'s internal state. Don't do
959 * anything when that happens.
961 if (*input == '\0')
962 return;
964 if (pager_len > 0) {
965 for (index = 0; pager_vector[index] != (char *)NULL; index += 1)
966 free(pager_vector[index]);
967 free(pager_vector);
968 free(pager_catenated);
971 pager_vector = (char **)malloc(2 * sizeof (char *));
972 if (pager_vector == NULL) {
973 (void) fprintf(stderr, gettext("out of memory\n"));
974 done(1);
977 pager_len = 2;
978 pager_vector[0] = strdup(input);
979 if (pager_vector[0] == NULL) {
980 (void) fprintf(stderr, gettext("out of memory\n"));
981 done(1);
983 if (dflag)
984 (void) fprintf(stderr, gettext("got command `%s'\n"), input);
985 catenate_length = setpagerargs(&nextarg) + strlen(pager_vector[0]) + 1;
986 pager_catenated = (char *)malloc(catenate_length *
987 (size_t)sizeof (char));
988 if (pager_catenated == (char *)NULL) {
989 (void) fprintf(stderr, gettext("out of memory\n"));
990 done(1);
992 for (index = 0; pager_vector[index] != (char *)NULL; index += 1) {
993 if (index > 0)
994 (void) strcat(pager_catenated, " ");
995 (void) strcat(pager_catenated, pager_vector[index]);
1001 * Extract arguments for the pager command from getcmd()'s input buffer.
1003 static uint_t
1004 setpagerargs(source)
1005 char **source;
1007 char word[MAXCOMPLEXLEN];
1008 char *cp = *source;
1009 uint_t length = 0;
1011 while ((cp != (char *)NULL) && (*cp != '\0')) {
1012 cp = copynext(cp, word, sizeof (word));
1013 if (dflag)
1014 fprintf(stderr, gettext("got word `%s'\n"), word);
1015 pager_vector = (char **)realloc(pager_vector,
1016 (size_t)sizeof (char *) * (pager_len + 1));
1017 if (pager_vector == (char **)NULL) {
1018 (void) fprintf(stderr, gettext("out of memory\n"));
1019 done(1);
1021 pager_vector[pager_len - 1] = strdup(word);
1022 if (pager_vector[pager_len - 1] == (char *)NULL) {
1023 (void) fprintf(stderr, gettext("out of memory\n"));
1024 done(1);
1026 length += strlen(word) + 1;
1027 pager_len += 1;
1029 pager_vector[pager_len - 1] = (char *)NULL;
1030 *source = cp;
1031 return (length);