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