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
23extern eucwidth_t wp;
24
25#define	round(a, b) ((((a) + (b) - 1) / (b)) * (b))
26
27/*
28 * Things to handle interruptions.
29 */
30static jmp_buf reset;
31static int reset_OK;
32static char *nextarg = NULL;
33
34static int dontexpand;	/* co-routine state set in getnext, used in expandarg */
35
36#ifdef __STDC__
37static void getcmd(char *, char *, size_t, char *, size_t, struct arglist *);
38static void expandarg(char *, struct arglist *);
39static void printlist(char *, ino_t, char *, int);
40static void formatf(struct arglist *);
41static char *copynext(char *, char *, size_t);
42static int fcmp(struct afile *, struct afile *);
43static char *fmtentry(struct afile *);
44static void setpagercmd(void);
45static uint_t setpagerargs(char **);
46#else
47static void getcmd();
48static void expandarg();
49static void printlist();
50static void formatf();
51static char *copynext();
52static int fcmp();
53static char *fmtentry();
54static void setpagercmd();
55static uint_t setpagerargs();
56#endif
57
58/*
59 * Read and execute commands from the terminal.
60 */
61void
62#ifdef __STDC__
63runcmdshell(void)
64#else
65runcmdshell()
66#endif
67{
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];
74
75#ifdef	lint
76	curdir[0] = '\0';
77#endif	/* lint */
78
79	canon("/", curdir, sizeof (curdir));
80loop:
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 */
87	}
88	reset_OK = 1;
89	getcmd(curdir, cmd, sizeof (cmd), name, sizeof (name), &alist);
90
91	/*
92	 * Using strncmp() to catch unique prefixes.
93	 */
94	switch (cmd[0]) {
95	/*
96	 * Add elements to the extraction list.
97	 */
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;
110	/*
111	 * Change working directory.
112	 */
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;
125		}
126
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;
131	/*
132	 * Delete elements from the extraction list.
133	 */
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;
144		}
145		treescan(name, np->e_ino, deletefile);
146		break;
147	/*
148	 * Extract the requested list.
149	 */
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;
161	/*
162	 * List available commands.
163	 */
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;
188	/*
189	 * List a directory.
190	 */
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;
203	/*
204	 * Print current directory or enable pagination.
205	 */
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]);
214			}
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;
221			}
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"));
229			}
230			if (dflag) {
231				int index = 0;
232
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;
240				}
241			}
242			paginating = 1;
243		} else {
244			goto bad;
245		}
246		break;
247	/*
248	 * Quit.
249	 */
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;
260	/*
261	 * Toggle verbose mode.
262	 */
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;
270		}
271		(void) fprintf(stderr, gettext("verbose mode on\n"));
272		vflag = 1;
273		break;
274	/*
275	 * Just restore requested directory modes, or set pagination command.
276	 */
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;
286		}
287		break;
288	/*
289	 * Print out dump header information.
290	 */
291	case 'w':
292		if (strncmp(cmd, "what", strlen(cmd)) != 0)
293			goto bad;
294		printdumpinfo();
295		break;
296	/*
297	 * Turn on debugging.
298	 */
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;
306		}
307		(void) fprintf(stderr, gettext("debugging mode on\n"));
308		dflag++;
309		break;
310	/*
311	 * Unknown command.
312	 */
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;
322	}
323	goto loop;
324}
325
326static char input[MAXCOMPLEXLEN]; /* shared by getcmd() and setpagercmd() */
327#define	rawname input	/* save space by reusing input buffer */
328
329/*
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.
339 */
340/* ARGSUSED */
341static void
342getcmd(curdir, cmd, cmdsiz, name, namesiz, ap)
343	char *curdir, *cmd, *name;
344	size_t cmdsiz, namesiz;
345	struct arglist *ap;
346{
347	char *cp;
348	char output[MAXCOMPLEXLEN];
349
350	/*
351	 * Check to see if still processing arguments.
352	 */
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';
362		}
363		freename(ap->head->fname);
364		ap->head++;
365		return;
366	}
367	if (nextarg != NULL)
368		goto getnext;
369	/*
370	 * Read a command line and trim off trailing white space.
371	 */
372readagain:
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;
381	}
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]*/
388	}
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';
396	}
397
398	if (cp == &input[0])
399		goto readagain;
400
401	/*
402	 * Copy the command into "cmd".
403	 */
404	cp = copynext(input, cmd, cmdsiz);
405	ap->cmd = cmd;
406	/*
407	 * If no argument, use curdir as the default.
408	 */
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';
418		}
419		return;
420	}
421	nextarg = cp;
422	/*
423	 * Find the next argument.
424	 */
425getnext:
426	cp = copynext(nextarg, rawname, sizeof (rawname));
427	if (*cp == '\0')
428		nextarg = NULL;
429	else
430		nextarg = cp;
431	/*
432	 * If it an absolute pathname, canonicalize it and return it.
433	 */
434	if (rawname[0] == '/') {
435		canon(rawname, name, namesiz);
436	} else {
437		/*
438		 * For relative pathnames, prepend the current directory to
439		 * it then canonicalize and return it.
440		 */
441		(void) snprintf(output, sizeof (output), "%s/%s",
442		    curdir, rawname);
443		canon(output, name, namesiz);
444	}
445	expandarg(name, ap);
446	/*
447	 * ap->head->fname guaranteed to be double null-terminated and
448	 * no more than MAXCOMPLEXLEN characters long.
449	 */
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
457}
458
459/*
460 * Strip off the next token of the input.
461 */
462static char *
463copynext(input, output, outsize)
464	char *input, *output;
465	size_t outsize;
466{
467	char *cp, *bp, *limit;
468	char quote;
469
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]*/
475	}
476	bp = output;
477	limit = output + outsize - 1; /* -1 for the trailing \0 */
478	while (!isspace((uchar_t)*cp) && *cp != '\0' && bp < limit) {
479		/*
480		 * Handle back slashes.
481		 */
482		if (*cp == '\\') {
483			if (*++cp == '\0') {
484				(void) fprintf(stderr, gettext(
485				    "command lines cannot be continued\n"));
486				continue;
487			}
488			*bp++ = *cp++;
489			continue;
490		}
491		/*
492		 * The usual unquoted case.
493		 */
494		if (*cp != '\'' && *cp != '"') {
495			*bp++ = *cp++;
496			continue;
497		}
498		/*
499		 * Handle single and double quotes.
500		 */
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;
510		}
511	}
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';
520	}
521	return (cp);
522}
523
524/*
525 * Canonicalize file names to always start with ``./'' and
526 * remove any imbedded "." and ".." components.
527 *
528 * The pathname "canonname" is returned double null terminated.
529 */
530void
531canon(rawname, canonname, limit)
532	char *rawname, *canonname;
533	size_t limit;
534{
535	char *cp, *np, *prefix;
536	uint_t len;
537
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);
546	/*
547	 * Eliminate multiple and trailing '/'s
548	 */
549	for (cp = np = canonname; *np != '\0'; cp++) {
550		*cp = *np++;
551		while (*cp == '/' && *np == '/')
552			np++;
553	}
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';
562	}
563
564	if (*--cp == '/')
565		*cp = '\0';
566	/*
567	 * Eliminate extraneous "." and ".." from pathnames.  Uses
568	 * memmove(), as strcpy() might do the wrong thing for these
569	 * small overlaps.
570	 */
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;
585		}
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]*/
592			}
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;
599		}
600	}
601}
602
603/*
604 * globals (file name generation)
605 *
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.
610 */
611static void
612expandarg(arg, ap)
613	char *arg;
614	struct arglist *ap;
615{
616	static struct afile single;
617	int size;
618
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;
626
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;
633	}
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);
642	}
643}
644
645/*
646 * Do an "ls" style listing of a directory
647 */
648static void
649printlist(name, ino, basename, marked_only)
650	char *name;
651	ino_t ino;
652	char *basename;
653	int marked_only;
654{
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;
662
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;
674		}
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;
683			}
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;
695			}
696			if (list_entry) {
697				if (!mkentry(dp->d_name, dp->d_ino, &alist)) {
698					rst_closedir(dirp);
699					return;
700				}
701			}
702		}
703	}
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);
714		}
715		formatf(&alist);
716		for (fp = alist.head; fp < alist.last; fp++)
717			freename(fp->fname);
718		alist.head = NULL;
719		/*
720		 * Don't free alist.base, as we'll probably be called
721		 * again, and might as well re-use what we've got.
722		 */
723	}
724	if (dirp != NULL) {
725		(void) fprintf(stderr, "\n");
726		rst_closedir(dirp);
727	}
728}
729
730/*
731 * Print out a pretty listing of a directory
732 */
733static void
734formatf(ap)
735	struct arglist *ap;
736{
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;
745
746	if (ap->head == ap->last)
747		return;
748
749	if (paginating) {
750		int fds[2];
751
752		if (pipe(fds) < 0) {
753			perror(gettext("could not create pipe"));
754			goto no_page;
755		}
756
757		switch (fork()) {
758		case -1:
759			perror(gettext("could not fork"));
760			goto no_page;
761		case 0:
762			/*
763			 * Make sure final output still ends up in
764			 * the same place.
765			 */
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;
778			}
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;
785		}
786	}
787
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;
798	}
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;
812			}
813			w = strlen(cp);
814			while (w < width) {
815				w++;
816				if (fprintf(output, " ") < 0)
817					break;
818			}
819		}
820	}
821
822	if (paginating) {
823		(void) fclose(output);
824		(void) wait((int *)NULL);
825	}
826}
827
828/*
829 * Comparison routine for qsort.
830 */
831static int
832fcmp(f1, f2)
833	struct afile *f1, *f2;
834{
835
836	return (strcoll(f1->fname, f2->fname));
837}
838
839/*
840 * Format a directory entry.
841 */
842static char *
843fmtentry(fp)
844	struct afile *fp;
845{
846	static char fmtres[MAXCOMPLEXLEN];
847	static int precision = 0;
848	ino_t i;
849	char *cp, *dp, *limit;
850
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);
863			}
864		}
865		(void) snprintf(fmtres, sizeof (fmtres), "%*ld ",
866		    precision, fp->fnum);
867	}
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);
886}
887
888/*
889 * respond to interrupts
890 */
891/* ARGSUSED */
892void
893onintr(sig)
894	int	sig;
895{
896	char	buf[300];
897
898	if (command == 'i' && reset_OK)
899		longjmp(reset, 1);
900
901	(void) snprintf(buf, sizeof (buf),
902	    gettext("%s interrupted, continue"), progname);
903	if (reply(buf) == FAIL)
904		done(1);
905}
906/*
907 * Set up pager_catenated and pager_vector.
908 */
909void
910#ifdef __STDC__
911initpagercmd(void)
912#else
913initpagercmd()
914#endif
915{
916	char *cp;
917
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);
925	}
926	if (pager_catenated == NULL) {
927		(void) fprintf(stderr, gettext("out of memory\n"));
928		done(1);
929	}
930
931	pager_vector = (char **)malloc(sizeof (char *));
932	if (pager_vector == NULL) {
933		(void) fprintf(stderr, gettext("out of memory\n"));
934		done(1);
935	}
936
937	pager_len = 1;
938	cp = pager_catenated;
939	(void) setpagerargs(&cp);
940}
941
942
943/*
944 * Resets pager_catenated and pager_vector from user input.
945 */
946void
947#ifdef __STDC__
948setpagercmd(void)
949#else
950setpagercmd()
951#endif
952{
953	uint_t catenate_length;
954	int index;
955
956	/*
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.
960	 */
961	if (*input == '\0')
962		return;
963
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);
969	}
970
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);
975	}
976
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);
982	}
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);
991	}
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]);
996	}
997}
998
999
1000/*
1001 * Extract arguments for the pager command from getcmd()'s input buffer.
1002 */
1003static uint_t
1004setpagerargs(source)
1005	char	**source;
1006{
1007	char	word[MAXCOMPLEXLEN];
1008	char	*cp = *source;
1009	uint_t	length = 0;
1010
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);
1020		}
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);
1025		}
1026		length += strlen(word) + 1;
1027		pager_len += 1;
1028	}
1029	pager_vector[pager_len - 1] = (char *)NULL;
1030	*source = cp;
1031	return (length);
1032}
1033