1 /* @(#)p.c	1.75 21/08/20 Copyright 1985-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)p.c	1.75 21/08/20 Copyright 1985-2021 J. Schilling";
6 #endif
7 /*
8  *	Print some files on screen
9  *
10  *	Copyright (c) 1985-2021 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/stdio.h>
27 #include <schily/standard.h>
28 #include <schily/stdlib.h>
29 #include <schily/unistd.h>
30 #include <schily/types.h>
31 #include <schily/utypes.h>
32 #include <schily/fcntl.h>
33 #include <schily/string.h>
34 #include <schily/signal.h>
35 #include <schily/sigset.h>
36 #include <schily/termcap.h>
37 #include <schily/libport.h>
38 #include <schily/errno.h>
39 #define	ungetch	dos_ungetch	/* Avoid DOS/curses ungetch() type clash */
40 #include <schily/termios.h>
41 #undef	ungetch			/* Restore our old value */
42 #include <schily/nlsdefs.h>
43 #include <schily/limits.h>	/* for  MB_LEN_MAX	*/
44 #include <schily/ctype.h>	/* For isprint()	*/
45 #include <schily/wchar.h>	/* wchar_t		*/
46 #include <schily/wctype.h>	/* For iswprint()	*/
47 #include <schily/patmatch.h>
48 #define	GT_COMERR		/* #define comerr gtcomerr */
49 #define	GT_ERROR		/* #define error gterror   */
50 #include <schily/schily.h>
51 
52 #define	SEARCHSIZE	256
53 #define	DEF_PSIZE	24;
54 #define	DEF_LWIDTH	80;
55 
56 #if	MB_LEN_MAX > 1
57 /*
58  * We are on a platform that supports multi byte characters.
59  */
60 #define	nextc()		nextwc()	/* Read next char */
61 #define	peekc()		peekwc()	/* Peek next char */
62 #define	getnextch()	getnextwch()	/* Consume last peeked char */
63 #define	ungetch(c)	ungetwch(c)	/* Unread char back to buffer */
64 #else
65 #define	nextc()	(--len >= 0 ? (int) *bp++ : \
66 			(fill_buf() <= 0 ? (len == 0 ? EOF : -2) : \
67 			(len--, (int) *bp++)))
68 #define	peekc()	(len > 0 ? (int) *bp : \
69 			(fill_buf() <= 0 ? (len == 0 ? EOF : -2) : \
70 			((int) *bp)))
71 #define	getnextch()	(len--, bp++)
72 #define	__peekc()	(ungetch(nextc()))
73 #endif
74 
75 #	ifdef	USE_V7_TTY
76 struct sgttyb old;
77 struct sgttyb new;
78 
79 #	else	/* USE_V7_TTY */
80 
81 #ifdef	USE_TERMIOS
82 struct termios	old;
83 struct termios	new;
84 #endif
85 #	endif	/* USE_V7_TTY */
86 int	tty = -1;
87 
88 FILE	*f;		/* the file being printed */
89 off_t	ofpos;		/* saved filepos for searching */
90 int	lineno;		/* current lineno (used for editing) */
91 int	colcnt;		/* column on screen we are going to print */
92 int	linecnt;	/* # of lines actually printed on screen */
93 int	lwidth;		/* length of a line on screen */
94 int	lines;		/* # of lines until we print a more prompt */
95 int	psize;		/* # of lines on a page */
96 int	supressblank;	/* supress multiple blank lines on output */
97 int	clear;		/* clear screen before displaying page */
98 int	dosmode;	/* wether to supress ^M^J sequences */
99 BOOL	autodos = TRUE;	/* whether to automatically detect DOS mode */
100 int	endline;	/* print a $ at each end of line */
101 int	raw;		/* do not expand control characters */
102 BOOL	raw8;		/* Ausgabe ohne ~ */
103 int	silent;		/* in silent mode there is no more prompt */
104 int	tab;		/* do nut expand tabs to spaces but to ^I */
105 int	visible;	/* _^H sequences are visible */
106 int	underline;	/* do underlining */
107 int	ununderline;	/* remove underlining */
108 int	help;		/* print online help */
109 int	prvers;		/* print version information */
110 BOOL	debug;		/* misc debug flag */
111 BOOL	nobeep;		/* be silent on errors */
112 char	*filename;	/* Filename fuer Ausgabezwecke */
113 int	direction;	/* direction to step through file list */
114 #define	FORWARD	0
115 char	*editor;
116 char	*shell;
117 int	searchnext;
118 #if	MB_LEN_MAX > 1
119 wchar_t	searchbuf[SEARCHSIZE];
120 #else
121 char	searchbuf[SEARCHSIZE];
122 #endif
123 int 	alt;
124 int 	*aux;
125 int 	*state;
126 
127 char	*so;		/* start standout */
128 char	*se;		/* end standout */
129 char	*xso;		/* start abstract bold/standout */
130 char	*xse;		/* end abstract bold/standout */
131 char	*us;		/* start underline */
132 char	*ue;		/* end underline */
133 char	*md;		/* start bold */
134 char	*me;		/* end attributes */
135 char	*ce;		/* clear endline */
136 char	*cl;		/* clear screen */
137 int	li;		/* lines on screen */
138 int	co;		/* columns on screen */
139 BOOL	am = TRUE;	/* automatic margins */
140 BOOL	xn;		/* newline ignored > 80 */
141 BOOL	has_standout;
142 BOOL	has_bold;
143 BOOL	has_ul;
144 BOOL	has_termcap;
145 int	standout;
146 int	underl;
147 
148 /*
149  * As an incomplete multi byte character after a buffer refill
150  * is partially before the begin of the buffer, we need to be
151  * able to store a second character before that. This happens
152  * when peekc() triggered a buffer refill and then ungetch()
153  * is called.
154  */
155 #define	P_BUFSIZE	((8*1024)+(2*MB_LEN_MAX))
156 unsigned char mybuf[P_BUFSIZE];	/* Fuer nextc */
157 unsigned char *bp;		/* ditto */
158 int len = 0;			/* ditto */
159 int clen = 0;			/* # of octects in nextwc() multi byte char */
160 int pclen = 0;			/* # of octects in peekwc() multi byte char */
161 unsigned char *cp;		/* Beginning of multi byte char in buffer   */
162 unsigned char *pcp;		/* Beginning of peeked multi byte char in buf */
163 
164 #ifdef	BUFSIZ
165 char	buffer[BUFSIZ];		/* our buffer for stdout */
166 #endif
167 char	dcl[] = ":::::::::::::::";
168 /* BEGIN CSTYLED */
169 char	options[] =
170 "help,version,debug,nobeep,length#,l#,width#,w#,blank,b,clear,c,dos,nodos%0,end,e,raw,r,raw8,silent,s,tab,t,unul,visible,v";
171 /* END CSTYLED */
172 
173 BOOL nameprint = FALSE;
174 
175 extern	unsigned char	csize[];
176 extern	unsigned char	*ctab[];
177 
178 extern	void	init_charset	__PR((void));
179 
180 LOCAL	void	tstp		__PR((int sig));
181 LOCAL	void	usage		__PR((int exitcode));
182 EXPORT	int	main		__PR((int ac, char **av));
183 LOCAL	void	page		__PR((void));
184 LOCAL	int	outchar		__PR((int c));
185 LOCAL	void	moreprompt	__PR((void));
186 LOCAL	BOOL	more		__PR((void));
187 LOCAL	int	get_action	__PR((void));
188 LOCAL	void	onlinehelp	__PR((void));
189 LOCAL	void	redraw		__PR((void));
190 LOCAL	int	inchar		__PR((void));
191 #if	MB_LEN_MAX > 1
192 LOCAL	int	inwchar		__PR((void));
193 #else
194 #define	inwchar	inchar
195 #endif
196 #ifndef	nextc
197 LOCAL	int	nextc		__PR((void));
198 #endif
199 #if	MB_LEN_MAX > 1
200 LOCAL	int	nextwc		__PR((void));
201 LOCAL	int	peekwc		__PR((void));
202 #endif
203 #ifndef	ungetch
204 LOCAL	int	ungetch		__PR((int c));
205 #endif
206 #if	MB_LEN_MAX > 1
207 LOCAL	int	ungetwch	__PR((int c));
208 LOCAL	void	getnextwch	__PR((void));
209 #endif
210 LOCAL	int	fill_buf	__PR((void));
211 LOCAL	BOOL	read_pattern	__PR((void));
212 LOCAL	int	do_search	__PR((void));
213 LOCAL	int	unul		__PR((Uchar *ob, Uchar *ib, int amt));
214 LOCAL	void	init_termcap	__PR((void));
215 LOCAL	int	oc		__PR((int c));
216 LOCAL	void	start_standout	__PR((void));
217 LOCAL	void	end_standout	__PR((void));
218 #ifdef	__needed__
219 LOCAL	void	start_bold	__PR((void));
220 #endif
221 LOCAL	void	end_attr	__PR((void));
222 LOCAL	void	start_xstandout	__PR((void));
223 LOCAL	void	end_xstandout	__PR((void));
224 LOCAL	void	start_ul	__PR((void));
225 LOCAL	void	end_ul		__PR((void));
226 LOCAL	void	clearline	__PR((void));
227 LOCAL	void	clearscreen	__PR((void));
228 LOCAL	void	init_tty_size	__PR((void));
229 LOCAL	int	get_modes	__PR((void));
230 LOCAL	void	set_modes	__PR((void));
231 LOCAL	void	reset_modes	__PR((void));
232 LOCAL	void	fixtty		__PR((int sig));
233 
234 #ifdef	SIGTSTP
235 LOCAL void
tstp(sig)236 tstp(sig)
237 	int	sig;
238 {
239 	/* ignore SIGTTOU so we don't get stopped if the shell modifies pgrp */
240 	signal(SIGTTOU, SIG_IGN);
241 	end_standout();
242 	end_attr();
243 	end_ul();
244 	clearline();
245 	reset_modes();
246 	signal(SIGTTOU, SIG_DFL);
247 
248 	signal(SIGTSTP, SIG_DFL);
249 #ifdef	OLD
250 #ifdef	HAVE_SIGRELSE
251 	sigrelse(SIGTSTP);
252 #else
253 	(void) sigsetmask(0);
254 #endif
255 #else	/* NEW */
256 	unblock_sig(SIGTSTP);
257 #endif
258 	kill(getpid(), SIGTSTP);
259 
260 	/* Hier stoppt 'p' */
261 
262 	set_modes();
263 	moreprompt();
264 }
265 #endif
266 
267 
268 LOCAL void
usage(exitcode)269 usage(exitcode)
270 	int	exitcode;
271 {
272 	error("Usage:	p [options] [file1...filen]\n");
273 	error("Options:\n");
274 	error("\t-help\t\tprint this online help\n");
275 	error("\t-version\tprint version number\n");
276 	error("\t-debug\t\tprint additional debug output\n");
277 	error("\tlength=#,l=#\tlength of screen (default 24)\n");
278 	error("\twidth=#,w=#\twidth  of screen (default 80)\n");
279 	error("\t-blank,-b\tsupress multiple blank lines on output\n");
280 	error("\t-clear,-c\tclear screen before displaying new page\n");
281 	error("\t-dos\t\tsupress '\\r' in '\\r\\n'\n");
282 	error("\t-nodos\t\tsupress auto detecting the dos mode\n");
283 	error("\t-end,-e\t\tprint a $ at each end of line\n");
284 	error("\t-raw,-r\t\tdo not expand chars\n");
285 	error("\t-raw8\t\tdo not expand 8bit chars\n");
286 	error("\t-silent,-s\tdo not prompt for more stuff\n");
287 	error("\t-tab,-t\t\tdo not expand tabs to spaces but to ^I\n");
288 #ifdef	__needed__
289 	error("\t-unul\t\tremove underlining and bold sequences\n");
290 #endif
291 	error("\t-visible,-v\tunderlining/bold sequences become visible\n\n");
292 	error("	When asked for more:  confirm=page, n=no more, h=half page\n");
293 	error("		q=quarter page, l=single line, 1-9=# lines.\n");
294 	error("	When asked for next file:  confirm=yes, n=skip to next\n");
295 	error("		s=stop (exit), h,q,l,1-9=yes with that # lines.\n");
296 	exit(exitcode);
297 }
298 
299 
300 EXPORT int
main(ac,av)301 main(ac, av)
302 	int	ac;
303 	char	*av[];
304 {
305 	int	i;
306 	int	cac;
307 	char	*const *cav;
308 	int	fac;
309 	char	**fav;
310 
311 	save_args(ac, av);
312 
313 	(void) setlocale(LC_ALL, "");
314 
315 #ifdef  USE_NLS
316 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
317 #define	TEXT_DOMAIN "p"		/* Use this only if it weren't */
318 #endif
319 	{ char	*dir;
320 	dir = searchfileinpath("share/locale", F_OK,
321 					SIP_ANY_FILE|SIP_NO_PATH, NULL);
322 	if (dir)
323 		(void) bindtextdomain(TEXT_DOMAIN, dir);
324 	else
325 #if defined(PROTOTYPES) && defined(INS_BASE)
326 	(void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
327 #else
328 	(void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
329 #endif
330 	(void) textdomain(TEXT_DOMAIN);
331 	}
332 #endif 	/* USE_NLS */
333 
334 
335 	cac = --ac;
336 	cav = ++av;
337 
338 	if (getallargs(&cac, &cav, options, &help, &prvers, &debug, &nobeep,
339 			&psize, &psize,
340 			&lwidth, &lwidth,
341 			&supressblank, &supressblank,
342 			&clear, &clear,
343 			&dosmode, &autodos,
344 			&endline, &endline,
345 			&raw, &raw,
346 			&raw8,
347 			&silent, &silent,
348 			&tab, &tab,
349 			&ununderline,
350 			&visible, &visible) < 0) {
351 		errmsgno(EX_BAD, "Bad flag: %s.\n", cav[0]);
352 		usage(EX_BAD);
353 	}
354 	if (help) usage(0);
355 	if (prvers) {
356 		/* BEGIN CSTYLED */
357 		gtprintf("p %s %s (%s-%s-%s)\n\n", "2.4", "2021/08/20", HOST_CPU, HOST_VENDOR, HOST_OS);
358 		gtprintf("Copyright (C) 1985, 87-92, 95-99, 2000-2021 %s\n", _("J�rg Schilling"));
359 		gtprintf("This is free software; see the source for copying conditions.  There is NO\n");
360 		gtprintf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
361 		/* END CSTYLED */
362 		exit(0);
363 	}
364 
365 	shell = getenv("BEEP");
366 	if (shell != NULL && streql(shell, "off"))
367 		nobeep = TRUE;
368 	if ((editor = getenv("EDITOR")) == NULL)
369 		editor = "vi";
370 	if ((shell = getenv("SHELL")) == NULL)
371 		shell = "sh";
372 
373 	underline = !raw && !visible;
374 
375 	cac = ac;
376 	cav = av;
377 	for (i = 0; getfiles(&cac, &cav, options) > 0; i++, cac--, cav++);
378 
379 	if (0 /*fav = (char **)malloc(i*sizeof(char *))*/) {
380 		cac = ac;
381 		cav = av;
382 		fac = i;
383 		for (i = 0; getfiles(&cac, &cav, options) > 0;
384 		    i++, cac--, cav++) {
385 			fav[i] = *cav;
386 		}
387 	} else {
388 		cac = ac;
389 		cav = av;
390 		getfiles(&cac, &cav, options);
391 	}
392 
393 	if (cac == 0 && isatty(fdown(stdin)))
394 		usage(EX_BAD);
395 
396 	if (get_modes() < 0) {
397 		silent++;
398 		if (!ununderline)
399 			underline = FALSE;
400 	}
401 
402 
403 /*	if (underline || !silent)*/
404 		init_termcap();
405 	init_tty_size();
406 
407 	if (!silent) {
408 		usleep(150000);	/* XXX Hack bis Jobcontrol im bsh geht !!! */
409 		set_modes();
410 	}
411 
412 	init_charset();
413 
414 #ifdef	BUFSIZ			/* XXX #ifdef HAVE_SETBUF ??? */
415 	setbuf(stdout, buffer);
416 #endif
417 
418 	/*
419 	 * If we evaluate "has_termcap" here, we could avoid to reduce the width
420 	 * of the terminal in case that "am" but not "xn" is present. This
421 	 * however would result in missing line breaks in files created by the
422 	 * screen(1) utility.
423 	 */
424 	if (am && !xn && lwidth > 2)
425 		lwidth--;
426 	lines = psize-2;
427 
428 	fac = cac;
429 #ifdef	DEBUG
430 	printf("%d\n", cac);
431 	for (i = 0; i < cac; i++)
432 		printf("%s\n", cav[i]);
433 #endif
434 
435 	if (cac == 0) {
436 		filename = "";
437 		linecnt = 0;
438 		f = stdin;
439 		page();
440 	} else {
441 		if (cac > 1)
442 			nameprint++;
443 		for (;;) {
444 			if ((f = fileopen(*cav, "r")) == (FILE *) NULL) {
445 				errmsg("Can not open '%s'.\n", *cav);
446 			} else {
447 				filename = *cav;
448 				if (nameprint) {
449 					printf("%s\n%s\n%s\n",
450 							dcl, filename, dcl);
451 					linecnt = 3;
452 				} else
453 					linecnt = 0;
454 				page();
455 				fclose(f);
456 				f = (FILE *)0;
457 			}
458 			for (i = 0; ; ) {
459 				if (direction == FORWARD) {
460 					cac--, cav++;
461 					if (cac <= 0)
462 						reset_modes(), exit(0);
463 				} else {
464 					if (cac < fac) {
465 						cac++, cav--;
466 					}
467 				}
468 				if (silent) break;
469 				if (i++ == 0) putchar('\n');
470 				err:
471 
472 				start_standout();
473 				gtprintf("NEXT FILE (%s)?", *cav);
474 				end_standout();
475 				fflush(stdout);
476 				lines = psize-2;
477 
478 				switch (get_action()) {
479 
480 				case -1:
481 					goto err;
482 				case  1:
483 					continue;
484 				}
485 				break;
486 			}
487 			clearline();
488 		}
489 	}
490 	reset_modes();
491 	exit(0);
492 	return (0);	/* Keep lint happy */
493 }
494 
495 LOCAL void
page()496 page()
497 {
498 	register int	c;
499 	register int	cnt;
500 	register unsigned char	*s;
501 	register unsigned char	**rctab;
502 		int	ocolcnt = -1;
503 
504 	rctab = ctab;
505 	ofpos = (off_t)0;
506 	len = 0;			/* fill_buf() on next nextc() */
507 	file_raise(f, FALSE);
508 #ifdef	__nonono__			/* FreeBSD would read single bytes */
509 	setbuf(f, NULL);
510 #endif
511 	lineno = 0;
512 	colcnt = 0;
513 
514 	if (searchnext && do_search() < 0) {
515 		if (!more())
516 			return;
517 	}
518 
519 	while ((c = nextc()) != EOF) {
520 		if (c < 0) {				/* -2 on error */
521 			errmsg("Error reading '%s'.\n", filename);
522 			return;
523 		}
524 		if (c == '_' && underline) {		/* underlining */
525 			if (peekc() != '\b') {
526 				if (outchar('_'))
527 					return;
528 				continue;
529 			}
530 			getnextch();			/* it _is_ a ^H ! */
531 			if (peekc() == '_')
532 				goto bold;
533 			underl++;
534 
535 		} else if (c == '+' && underline && peekc() == '\b') {
536 			getnextch();
537 		} else if (c == '\r' &&
538 			    (autodos || dosmode) && peekc() == '\n') {
539 			/* EMPTY */
540 		} else if (c == '\n') {
541 			if (ocolcnt == 0 && colcnt == 0 && supressblank)
542 				continue;
543 			ocolcnt = colcnt;
544 			lineno++;
545 			if (endline) {
546 				if (outchar('$'))
547 					return;
548 			}
549 			if (outchar('\n'))
550 				return;
551 		} else if (c == '\t' && !tab) {
552 			cnt = 8 - (colcnt&7);
553 			while (--cnt >= 0)
554 				if (outchar(' '))
555 					return;
556 		} else if (peekc() == '\b' && underline) {
557 			getnextch();		/* ^H */
558 		bold:
559 			while (peekc() == c) {
560 				getnextch();
561 				if (peekc() == '\b')
562 					getnextch();
563 				else
564 					break;
565 			}
566 			ungetch(c);
567 			standout++;
568 		} else {
569 			if (raw || iswprint(c)) {
570 				if (outchar(c))
571 					return;
572 			} else {
573 #if	MB_LEN_MAX > 1
574 				int	i = clen;
575 				while (--i >= 0) {
576 					s = rctab[*cp++];
577 					while (*s)
578 						if (outchar(*s++))
579 							return;
580 				}
581 #else
582 				s = rctab[c];
583 				while (*s)
584 					if (outchar(*s++))
585 						return;
586 #endif
587 			}
588 		}
589 	}
590 	fflush(stdout);
591 }
592 
593 LOCAL int
outchar(c)594 outchar(c)
595 	int	c;
596 {
597 #if	MB_LEN_MAX > 1
598 	unsigned char	b[MB_LEN_MAX];
599 	unsigned char	*p = b;
600 	int		l;
601 
602 
603 	l = wctomb((char *)b, c);
604 	if (l < 0) {
605 		l = 1;
606 		b[0] = '?';
607 	}
608 #endif
609 
610 	if (c == '\n') {
611 		if (len <= lwidth)
612 			fflush(stdout);
613 		if (++linecnt >= lines) {
614 			if (!more())
615 				return (1);
616 			clearline();
617 			linecnt = 0;
618 		} else {
619 			putc('\n', stdout);
620 		}
621 		colcnt = 0;
622 		return (0);
623 	} else {
624 		int	w = wcwidth(c);
625 
626 		if (w < 0)
627 			w = 1;
628 		colcnt += w;
629 		if (colcnt > lwidth) {
630 			if (outchar('\n'))
631 				return (1);
632 			colcnt = 1;
633 		}
634 		if (standout) {
635 			standout--;
636 			if (underl) {
637 				underl--;
638 				start_ul();
639 				start_xstandout();
640 #if	MB_LEN_MAX > 1
641 				while (--l >= 0)
642 					putc(*p++, stdout);
643 #else
644 				putc(c, stdout);
645 #endif
646 				end_xstandout();
647 				end_ul();
648 			} else {
649 				start_xstandout();
650 #if	MB_LEN_MAX > 1
651 				while (--l >= 0)
652 					putc(*p++, stdout);
653 #else
654 				putc(c, stdout);
655 #endif
656 				end_xstandout();
657 			}
658 		} else if (underl) {
659 			underl--;
660 			start_ul();
661 #if	MB_LEN_MAX > 1
662 			while (--l >= 0)
663 				putc(*p++, stdout);
664 #else
665 			putc(c, stdout);
666 #endif
667 			end_ul();
668 		} else {
669 #if	MB_LEN_MAX > 1
670 			while (--l >= 0)
671 				putc(*p++, stdout);
672 #else
673 			putc(c, stdout);
674 #endif
675 		}
676 		return (0);
677 	}
678 }
679 
680 LOCAL void
moreprompt()681 moreprompt()
682 {
683 	float	percent;
684 	off_t	size	= -1;
685 	off_t	pos	= -1;
686 
687 	lines = psize-2;
688 	if (f) {
689 		size = filesize(f);
690 		pos = filepos(f) - len;
691 	}
692 	start_standout();
693 	if (!*filename || !f || size < 0 || pos < 0)
694 		gtprintf("%s%c MORE?",
695 				filename,
696 				*filename ? ':' : '-');
697 	else {
698 		percent = (float)pos;
699 		percent /= (float)size;
700 		percent *= 100.0;
701 		gtprintf("%s%c %.1f %% MORE?",
702 				filename,
703 				*filename ? ':' : '-',
704 				percent);
705 	}
706 	end_standout();
707 	fflush(stdout);
708 }
709 
710 LOCAL BOOL
more()711 more()
712 {
713 	if (silent)
714 		return (TRUE);
715 	putchar('\n');
716 	for (;;) {
717 		moreprompt();
718 		switch (get_action()) {
719 
720 		case  1:
721 			return (FALSE);
722 		case -1:
723 			continue;
724 		}
725 		ofpos = filepos(f)-len;
726 		break;
727 	}
728 	return (TRUE);
729 }
730 
731 
732 /*
733  *	Return:
734  *		-1	error
735  *		 0	continue this file
736  *		 1	stop on this file
737  *	EXIT:
738  *		 on demand
739  */
740 LOCAL int
get_action()741 get_action()
742 {
743 	int	c;
744 	char	buf[128];
745 
746 	direction = FORWARD;
747 	searchnext = 0;
748 
749 	switch (c = inwchar()) {
750 
751 	case 'p':
752 	case 'P':
753 	case 'n':
754 	case 'N':
755 		direction = (c == 'P' || c == 'p');
756 		clearline();
757 		return (1);
758 	case 's':
759 	case 'S':
760 	case 003 :				/* ^C  */
761 	case 004 :				/* ^D  */
762 	case 034 :				/* ^\  */
763 	case 0177:				/* DEL */
764 	case EOF :
765 		fixtty(0);
766 		/* NOTREACHED */
767 	case 'Y':
768 	case 'y':
769 	case '\r':
770 	case '\n':
771 	case ' ':
772 		if (clear)
773 			clearscreen();
774 		break;
775 	case 'H':
776 	case 'h':
777 		lines = lines/2;
778 		break;
779 	case 'Q':
780 	case 'q':
781 		lines = lines/4;
782 		break;
783 	case 'L':
784 	case 'l':
785 		lines = 1;
786 		break;
787 	case '1':
788 	case '2':
789 	case '3':
790 	case '4':
791 	case '5':
792 	case '6':
793 	case '7':
794 	case '8':
795 	case '9':
796 		lines = c-'0';
797 		break;
798 	case '/':
799 		clearline();
800 		printf("/");
801 		fflush(stdout);
802 		if (!read_pattern())
803 			return (-1);
804 		/* FALLTHRU */
805 	case 'r':
806 	case 'R':
807 		if (f == (FILE *)0)
808 			searchnext = 1;
809 		else if (do_search() < 0)
810 			return (-1);
811 		break;
812 	case '\f':
813 		redraw();	/* XXX eigentlich nicht ?? */
814 		break;
815 	case '?':
816 		clearline();
817 		onlinehelp();
818 		return (-1);
819 	case '!':
820 		clearline();
821 		sprintf(buf, "%s -t", shell);
822 		(void) system(buf);
823 		return (-1);
824 	case 'v':
825 	case 'V':
826 		if (f != stdin) {
827 			clearline();
828 			sprintf(buf, "%s +%d %s", editor, lineno, filename);
829 			printf("%s", buf);
830 			fflush(stdout);
831 			(void) system(buf);
832 			return (-1);
833 		}
834 		/* FALLTHRU */
835 	default:
836 		if (!nobeep)
837 			putchar('\007');
838 		clearline();
839 		return (-1);
840 	}
841 	return (0);
842 }
843 
844 LOCAL void
onlinehelp()845 onlinehelp()
846 {
847 	/* BEGIN CSTYLED */
848 	gtprintf("\n---------------------------------------------------------------\n");
849 	gtprintf("N, n			Next File\n");
850 	gtprintf("P, p			Previous File\n");
851 	gtprintf("S, s, ^C, ^D, ^\\, DEL	Exit (stop)\n");
852 	gtprintf("Y, y, <return>, <space>	Display next screenfull of text\n");
853 	gtprintf("H, h			Display next half screenfull of text\n");
854 	gtprintf("Q, q			Display next quarter screenfull of text\n");
855 	gtprintf("L, l			Display next line of text\n");
856 	gtprintf("1-9			Display next <n> lines of text\n");
857 	gtprintf("/pattern		Search for <pattern>\n");
858 	gtprintf("R, r			Re-search for <pattern>\n");
859 	gtprintf("^L			Redraw screen\n");
860 	gtprintf("?			Display this message\n");
861 	gtprintf("!			Execute command\n");
862 	gtprintf("V, v			Edit file\n");
863 	gtprintf("---------------------------------------------------------------\n");
864 	/* END CSTYLED */
865 }
866 
867 LOCAL void
redraw()868 redraw()
869 {
870 	if (f && fileseek(f, ofpos) < 0) {
871 		if (!nobeep)
872 			putchar('\007');
873 		clearline();
874 		return;
875 	}
876 	len = 0;
877 	clearscreen();
878 }
879 
880 LOCAL int
inchar()881 inchar()
882 {
883 	char	c;
884 	int	ret = 0;
885 
886 	c = '\004';		/* return ^D on EOF */
887 	do {
888 #	ifdef	USE_GETCH
889 		c = getch();	/* DOS console input	*/
890 #	else
891 		ret = read(tty, &c, 1);
892 #	endif
893 	} while (ret < 0 && geterrno() == EINTR);
894 	if (ret == 0)
895 		return (EOF);
896 	return (c);
897 }
898 
899 #if	MB_LEN_MAX > 1
900 /*
901  * Get next wide character from tty
902  */
903 LOCAL int
inwchar()904 inwchar()
905 {
906 	register int	cur_max = MB_CUR_MAX;
907 	register int	i;
908 	int		mlen;
909 	wchar_t		c = -1;
910 	char		cstr[MB_LEN_MAX];
911 	char		*csp;
912 	int		ic;
913 
914 	(void) mbtowc(NULL, NULL, 0);
915 	for (i = 1, csp = cstr; i <= cur_max; i++) {
916 		ic = inchar();
917 		if (ic == EOF)
918 			break;
919 		*csp++ = (char)ic;
920 		mlen = mbtowc(&c, cstr, i);
921 		if (mlen >= 0)
922 			break;
923 		(void) mbtowc(NULL, NULL, 0);
924 	}
925 #ifdef	_NEXTWC_DEBUG
926 	fprintf(stderr, "C %d %x\n", c, c);
927 #endif
928 	return ((int)c);
929 }
930 #endif
931 
932 
933 #ifndef	nextc
934 LOCAL int
nextc()935 nextc()
936 {
937 	if (--len >= 0) {
938 		return ((int) *bp++);
939 	} else {
940 		if (fill_buf() <= 0)
941 			return (len == 0 ? EOF : -2);
942 	}
943 	len--;
944 	return ((int) *bp++);
945 }
946 #endif
947 
948 #if	MB_LEN_MAX > 1
949 /*
950  * Read the next multi byte character from the buffer.
951  * If the buffer is empty or if less than a multi byte character is inside,
952  * it is refilled. When an illegal byte sequence is discovered, a single
953  * byte is removed from the buffer in hope to be able to resync.
954  */
955 LOCAL int
nextwc()956 nextwc()
957 {
958 	BOOL	eof = FALSE;
959 	int	mlen;
960 	wchar_t	c;
961 
962 	if (len <= 0) {
963 		int	olen;
964 again:
965 		olen = len;
966 		if (fill_buf() <= 0) {
967 			return (len == 0 ? EOF : -2);
968 		} else if (len > 0 && olen == len) {
969 			eof = TRUE;
970 		}
971 	}
972 	mlen = mbtowc(&c, (char *)bp, len);
973 	if (mlen >= 0) {
974 		if (mlen == 0)
975 			mlen = 1;
976 		clen = mlen;
977 		cp = bp;
978 		bp += mlen;
979 		len -= mlen;
980 		return (c);
981 	} else {
982 		mbtowc(NULL, NULL, 0);
983 		if (len < MB_CUR_MAX && !eof) {
984 			seterrno(0);
985 			goto again;
986 		}
987 		clen = 1;
988 		cp = bp;
989 		bp++;
990 		len--;
991 #ifdef	EILSEQ
992 		if (geterrno() == EILSEQ) {
993 			return (*cp);
994 		}
995 #endif
996 	}
997 	return (-2);
998 }
999 
1000 /*
1001  * The same as nextwc(), but the buffer is untouched.
1002  */
1003 LOCAL int
peekwc()1004 peekwc()
1005 {
1006 	BOOL	eof = FALSE;
1007 	int	mlen;
1008 	wchar_t	c;
1009 
1010 	if (len <= 0) {
1011 		int	olen;
1012 again:
1013 		olen = len;
1014 		if (fill_buf() <= 0) {
1015 			return (len == 0 ? EOF : -2);
1016 		} else if (len > 0 && olen == len) {
1017 			eof = TRUE;
1018 		}
1019 	}
1020 	mlen = mbtowc(&c, (char *)bp, len);
1021 	if (mlen >= 0) {
1022 		if (mlen == 0)
1023 			mlen = 1;
1024 		pclen = mlen;
1025 		pcp = bp;
1026 		return (c);
1027 	} else {
1028 		mbtowc(NULL, NULL, 0);
1029 		if (len < MB_CUR_MAX && !eof) {
1030 			seterrno(0);
1031 			goto again;
1032 		}
1033 		pclen = 1;
1034 		pcp = bp;
1035 #ifdef	EILSEQ
1036 		if (geterrno() == EILSEQ) {
1037 			return (*pcp);
1038 		}
1039 #endif
1040 	}
1041 	return (-2);
1042 }
1043 #endif
1044 
1045 #ifndef	ungetch
1046 LOCAL int
ungetch(c)1047 ungetch(c)
1048 	int	c;
1049 {
1050 	if (c == EOF)
1051 		return (c);
1052 	/*
1053 	 * If bp is at the beginning of the buffer, this may go
1054 	 * to one character before the buffer (see below).
1055 	 */
1056 	len++;
1057 	return (*--bp = c);
1058 }
1059 #endif
1060 
1061 #if	MB_LEN_MAX > 1
1062 /*
1063  * Return the character back to the buffer.
1064  */
1065 LOCAL int
ungetwch(c)1066 ungetwch(c)
1067 	int	c;
1068 {
1069 	unsigned char	b[MB_LEN_MAX];
1070 	unsigned char	*p;
1071 	int		l;
1072 
1073 	if (c == EOF)
1074 		return (c);
1075 
1076 	l = wctomb((char *)b, c);
1077 	if (l < 0) {
1078 		l = 1;
1079 		b[0] = '?';
1080 	}
1081 	len += l;
1082 	p = &b[l];
1083 	while (--l >= 0)
1084 		*--bp = *--p;
1085 
1086 	return (c);
1087 }
1088 
1089 /*
1090  * Consume the last character fetched by peekwc()
1091  */
1092 LOCAL void
getnextwch()1093 getnextwch()
1094 {
1095 	len -= pclen;
1096 	bp += pclen;
1097 	cp = pcp;
1098 	pclen = 0;
1099 }
1100 #endif
1101 
1102 LOCAL int
fill_buf()1103 fill_buf()
1104 {
1105 	int		i;
1106 
1107 	/*
1108 	 * Allow ungetch() to always put back one character.
1109 	 * This is done by reserving one char before the normal
1110 	 * space in "mybuf".
1111 	 */
1112 
1113 	if (len > 0) {
1114 		unsigned char	*p = bp;
1115 
1116 		/*
1117 		 * Move a partial character left at the end of the buffer
1118 		 * to the space before the beginning of the buffer.
1119 		 */
1120 		if (len > MB_LEN_MAX)
1121 			len = MB_LEN_MAX;
1122 		p = &mybuf[(2*MB_LEN_MAX) - len];
1123 		for (i = len; --i >= 0; ) {
1124 			*p++ = *bp++;
1125 		}
1126 		bp = &mybuf[(2*MB_LEN_MAX) - len];
1127 	} else {
1128 		len = 0;
1129 		bp = &mybuf[(2*MB_LEN_MAX)];
1130 	}
1131 	i = fileread(f, &mybuf[2*MB_LEN_MAX], sizeof (mybuf) - 2*MB_LEN_MAX);
1132 	if (i < 0)
1133 		return (i);
1134 	return (len += i);
1135 }
1136 
1137 LOCAL BOOL
read_pattern()1138 read_pattern()
1139 {
1140 		int	patlen;
1141 	register int	c;
1142 	register int	count = 0;
1143 #if	MB_LEN_MAX > 1
1144 	register wchar_t *s = searchbuf;
1145 	unsigned char	b[MB_LEN_MAX];
1146 #else
1147 	register char	*s = searchbuf;
1148 #endif
1149 	register unsigned char	*p;
1150 	register int	i;
1151 
1152 	while ((c = inwchar()) != EOF &&
1153 			count++ < (SEARCHSIZE - 1) &&
1154 			c != '\004' && c != '\r' && c != '\n') {
1155 
1156 		if (c == 0177) {		/* DEL */
1157 			if (s == searchbuf) {
1158 				if (!nobeep)
1159 					putchar('\007');
1160 			} else {
1161 				--count;
1162 				--s;
1163 #if	MB_LEN_MAX > 1 && defined(HAVE_WCWIDTH)
1164 				if (iswprint(*s))
1165 					i = wcwidth(*s);
1166 				else
1167 					i = csize[*s & 0xFF];
1168 #else
1169 				i = csize[*s & 0xFF];
1170 #endif
1171 				for (; --i >= 0; )
1172 					printf("\b \b");
1173 			}
1174 			fflush(stdout);
1175 		} else {
1176 #if	MB_LEN_MAX > 1 && defined(HAVE_WCWIDTH)
1177 			int	l;
1178 
1179 			if (iswprint(c)) {
1180 				l = wctomb((char *)b, c);
1181 				if (l < 1) {
1182 					p = ctab[c & 0xFF];
1183 				} else {
1184 					p = b;
1185 					b[l] = '\0';
1186 				}
1187 			} else {
1188 				p = ctab[c & 0xFF];
1189 			}
1190 #else
1191 			p = ctab[c & 0xFF];
1192 #endif
1193 			*s++ = c;
1194 			while (*p)
1195 				putchar(*p++);
1196 			fflush(stdout);
1197 		}
1198 	}
1199 	*s = '\0';
1200 	if (aux)
1201 		free((char *)aux);
1202 #if	MB_LEN_MAX > 1
1203 	patlen = wcslen(searchbuf);
1204 #else
1205 	patlen = strlen(searchbuf);
1206 #endif
1207 	aux = (int *) malloc(patlen * sizeof (int));
1208 	state = (int *) malloc((patlen+1) * sizeof (int));
1209 #if	MB_LEN_MAX > 1
1210 	alt = patwcompile(searchbuf, patlen, aux);
1211 #else
1212 	alt = patcompile((unsigned char *)searchbuf, patlen, aux);
1213 #endif
1214 	if (!alt) {
1215 		gtprintf("Bad Pattern.\r");
1216 		fflush(stdout);
1217 		sleep(1);
1218 		return (FALSE);
1219 	}
1220 	return (TRUE);
1221 }
1222 
1223 LOCAL int
do_search()1224 do_search()
1225 {
1226 	register unsigned char *lp;
1227 	register unsigned char *rbp;
1228 		unsigned char *sp;
1229 	register int	rest = len;
1230 		off_t	curpos;
1231 		BOOL	skipping = FALSE;
1232 		int	newlines = 0;
1233 	unsigned char	sbuf[P_BUFSIZE];
1234 
1235 	if (!alt) {
1236 		if (!nobeep)
1237 			putchar('\007');
1238 		gtprintf("No previous search.\r");
1239 		fflush(stdout);
1240 #ifdef	DEBUG
1241 		sleep(1);
1242 #endif
1243 		return (-1);
1244 	}
1245 	curpos = filepos(f)-len;
1246 
1247 #ifdef	DEBUG
1248 	fprintf(stderr,
1249 		"Efilepos: %lld len: %d bp: 0x%X\n",
1250 		(Llong)filepos(f), len, bp);
1251 #endif
1252 	for (;;) {
1253 		if (rest < MB_LEN_MAX) {
1254 			int	olen;
1255 			/*
1256 			 * We may be operating on a copy and thus
1257 			 * need to set "len" to the remaining characters to
1258 			 * tell fill_buf() that it needs to do a refill.
1259 			 */
1260 			olen = len = rest;
1261 			if (fill_buf() <= 0 || olen == len) {
1262 				if (!nobeep)
1263 					putchar('\007');
1264 				gtprintf("Pattern not found.\r");
1265 				fflush(stdout);
1266 				if (f && fileseek(f, curpos) >= 0)
1267 					len = 0;
1268 				return (-1);
1269 			}
1270 			newlines = 0;
1271 		}
1272 		rbp = lp = sp = bp;
1273 		rest = len;
1274 		if (underline &&
1275 		    findbytes(lp, rest, '\b') != NULL) {
1276 			rest = unul(sbuf, rbp, len);
1277 			rbp = lp = sp = sbuf;
1278 		}
1279 		for (; rest > 0; rest--) {
1280 #if	MB_LEN_MAX > 1
1281 			if (patmbmatch(searchbuf,
1282 #else
1283 			if (patmatch((unsigned char *)searchbuf,
1284 #endif
1285 					aux, rbp, 0, rest, alt, state)) {
1286 #ifdef	DEBUG
1287 			fprintf(stderr,
1288 				"Afilepos: %lld rest: %d rbp: 0x%X lp: 0x%X\n",
1289 				(Llong)filepos(f), rest, rbp, lp);
1290 #endif
1291 				if (skipping) {
1292 					printf("\n");
1293 					if (sp == bp) {
1294 						bp = lp;
1295 						len = rest + (rbp - lp);
1296 					} else {
1297 						lp = bp;
1298 						rbp = &bp[len];
1299 						while (--newlines >= 0) {
1300 							unsigned char *olp = lp;
1301 							if ((lp = (Uchar *)
1302 								findbytes(lp,
1303 								    rbp - lp,
1304 								    '\n')) ==
1305 								    NULL) {
1306 								lp = olp;
1307 								break;
1308 							}
1309 							lp++;
1310 						}
1311 						bp = lp;
1312 						len = rbp - lp;
1313 					}
1314 				}
1315 #ifdef	DEBUG
1316 				fprintf(stderr,
1317 					"Afilepos: %lld len: %d bp: 0x%X\n",
1318 					(Llong)filepos(f), len, bp);
1319 #endif
1320 				return (0);
1321 			}
1322 			if (*rbp++ == '\n') {
1323 				/*
1324 				 * Remember last line start pointer.
1325 				 */
1326 				lp = rbp;
1327 				newlines++;
1328 				if (!skipping) {
1329 					skipping = TRUE;
1330 					gtprintf("skipping...\r");
1331 					fflush(stdout);
1332 				}
1333 #ifdef	DEBUG
1334 				fprintf(stderr, ".");
1335 #endif
1336 			}
1337 		}
1338 	}
1339 }
1340 
1341 /*
1342  * Remove underlining and overstriking sequences.
1343  * Put the result into "ob".
1344  */
1345 LOCAL int
unul(ob,ib,amt)1346 unul(ob, ib, amt)
1347 	register Uchar	*ob;
1348 	register Uchar	*ib;
1349 	register int	amt;
1350 {
1351 	register Uchar	*oob = ob;
1352 		wchar_t	c;
1353 	register ssize_t wclen;
1354 
1355 	mbtowc(NULL, NULL, 0);
1356 	while (amt > 0) {
1357 		wclen = mbtowc(&c, (char *)ib, amt);
1358 		if (wclen < 1) {
1359 			*ob++ = *ib++;
1360 			amt--;
1361 			continue;
1362 		}
1363 		amt -= wclen;
1364 		ib += wclen;
1365 		if (c == '_') {				/* underlining */
1366 			if (ib[0] != '\b') {
1367 				ib -= wclen;
1368 				while (--wclen >= 0)
1369 					*ob++ = *ib++;
1370 				continue;
1371 			}
1372 			ib++;				/* eat ^H */
1373 			amt--;
1374 			wclen = mbtowc(&c, (char *)ib, amt);
1375 			if (wclen > 0 && c == '_') {	/* _ ^H _ */
1376 				goto bold;
1377 			}
1378 		} else if (c == '+' && ib[0] == '\b') {	/* overstriking */
1379 			/* + ^H o */
1380 
1381 			ib++;				/* eat ^H */
1382 			amt--;
1383 		} else if (ib[0] == '\b') {
1384 			wchar_t	c2;
1385 			ssize_t	wclen2;
1386 			Uchar	*oib;
1387 
1388 			/* N ^H N ^H N ^H N */
1389 			ib++;				/* eat ^H */
1390 			amt--;
1391 		bold:
1392 			oib = ib - (wclen +1);
1393 			while (amt > 0) {
1394 				wclen2 = mbtowc(&c2, (char *)ib, amt);
1395 				if (wclen2 < 1)
1396 					break;
1397 				if (c2 != c)
1398 					break;
1399 				ib += wclen2;		/* eat c2 */
1400 				amt -= wclen2;
1401 				if (ib[0] == '\b') {	/* Check for ^H */
1402 					ib++;		/* eat ^H */
1403 					amt--;
1404 				} else
1405 					break;
1406 			}
1407 			while (--wclen >= 0)
1408 				*ob++ = *oib++;
1409 		} else {
1410 			ib -= wclen;
1411 			while (--wclen >= 0)
1412 				*ob++ = *ib++;
1413 		}
1414 	}
1415 	*ob = '\0';
1416 	return (ob - oob);
1417 }
1418 
1419 char	stbuf[1024];	/* Bufer for termcap array (i.e. so and se) */
1420 
1421 LOCAL void
init_termcap()1422 init_termcap()
1423 {
1424 	char	*tname;
1425 	char	*sbp;
1426 
1427 	sbp = stbuf;
1428 
1429 	if ((tname = getenv("TERM")) && tgetent(NULL, tname) == 1) {
1430 		has_termcap = TRUE;
1431 
1432 		so = tgetstr("so", &sbp);	/* start standout */
1433 		se = tgetstr("se", &sbp);	/* end standout */
1434 		us = tgetstr("us", &sbp);	/* start underline */
1435 		ue = tgetstr("ue", &sbp);	/* end underline */
1436 		md = tgetstr("md", &sbp);	/* start bold */
1437 		me = tgetstr("me", &sbp);	/* end attributes */
1438 		ce = tgetstr("ce", &sbp);	/* clear endline */
1439 		cl = tgetstr("cl", &sbp);	/* clear screen */
1440 		li = tgetnum("li");		/* lines on screen */
1441 		co = tgetnum("co");		/* columns on screen */
1442 		am = tgetflag("am");		/* automatic margins */
1443 		xn = tgetflag("xn");		/* newline ignored > 80 */
1444 		if (so != NULL && se != NULL) {
1445 			has_standout = TRUE;
1446 		} else {
1447 			so = se = NULL;
1448 		}
1449 		if (md != NULL && me != NULL) {
1450 			has_bold = TRUE;
1451 		} else {
1452 			md = NULL;
1453 		}
1454 		if (us != NULL && ue != NULL) {
1455 			has_ul = TRUE;
1456 		} else {
1457 			us = ue = NULL;
1458 		}
1459 		if (has_bold) {
1460 			xso = md;
1461 			xse = me;
1462 		} else if (has_standout) {
1463 			xso = so;
1464 			xse = se;
1465 		} else {
1466 			xso = xse = NULL;
1467 		}
1468 		if (debug) {
1469 			printf("so: %d bo: %d ul: %d\n",
1470 				has_standout, has_bold, has_ul);
1471 			sleep(1);
1472 		}
1473 	}
1474 }
1475 
1476 LOCAL int
oc(c)1477 oc(c)
1478 	int	c;
1479 {
1480 	return (putchar(c));
1481 }
1482 
1483 LOCAL void
start_standout()1484 start_standout()
1485 {
1486 	tputs(so, 1, oc);
1487 }
1488 
1489 LOCAL void
end_standout()1490 end_standout()
1491 {
1492 	tputs(se, 1, oc);
1493 }
1494 
1495 #ifdef	__needed__
1496 LOCAL void
start_bold()1497 start_bold()
1498 {
1499 	tputs(md, 1, oc);
1500 }
1501 #endif
1502 
1503 LOCAL void
end_attr()1504 end_attr()
1505 {
1506 	tputs(me, 1, oc);
1507 }
1508 
1509 LOCAL void
start_xstandout()1510 start_xstandout()
1511 {
1512 	tputs(xso, 1, oc);
1513 }
1514 
1515 LOCAL void
end_xstandout()1516 end_xstandout()
1517 {
1518 	tputs(xse, 1, oc);
1519 }
1520 
1521 LOCAL void
start_ul()1522 start_ul()
1523 {
1524 	if (has_ul)
1525 		tputs(us, 1, oc);
1526 }
1527 
1528 LOCAL void
end_ul()1529 end_ul()
1530 {
1531 	if (has_ul)
1532 		tputs(ue, 1, oc);
1533 }
1534 
1535 LOCAL void
clearline()1536 clearline()
1537 {
1538 	int	i;
1539 
1540 	if (silent) {
1541 		putchar('\n');
1542 		return;
1543 	}
1544 	putchar('\r');
1545 	if (ce) {
1546 		tputs(ce, 1, oc);
1547 	} else {
1548 		for (i = 1; i < lwidth; i++)
1549 			putchar(' ');
1550 		putchar('\r');
1551 	}
1552 	fflush(stdout);
1553 }
1554 
1555 LOCAL void
clearscreen()1556 clearscreen()
1557 {
1558 	if (cl)
1559 		tputs(cl, 1, oc);
1560 }
1561 
1562 LOCAL void
init_tty_size()1563 init_tty_size()
1564 {
1565 #ifdef no_TIOCGSIZE
1566 #if	defined(TIOCGSIZE) || defined(TIOCGWINSZ)
1567 #ifdef	TIOCGWINSZ
1568 	struct		winsize ws;
1569 
1570 	ws.ws_rows = 0;
1571 	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) >= 0) {
1572 		if (ws.ws_rows) {
1573 			psize = ws.ws_rows;
1574 			lwidth = ws.ws_cols;
1575 		}
1576 	}
1577 #else
1578 	struct		ttysize	ts;
1579 
1580 	ts.ts_lines = 0;
1581 	if (ioctl(STDOUT_FILENO, TIOCGSIZE, (char *)&ts) >= 0) {
1582 		if (ts.ts_lines) {
1583 			psize = ts.ts_lines;
1584 			lwidth = ts.ts_cols;
1585 		}
1586 	}
1587 #endif
1588 #endif
1589 #else
1590 	if (psize == 0) {
1591 		if (li > 0 && li < 1000)
1592 			psize = li;
1593 		else
1594 			psize = DEF_PSIZE;
1595 	}
1596 	if (lwidth == 0) {
1597 		if (co > 0 && co < 1000)
1598 			lwidth = co;
1599 		else
1600 			lwidth = DEF_LWIDTH;
1601 	}
1602 #endif
1603 }
1604 
1605 LOCAL int
get_modes()1606 get_modes()
1607 {
1608 #	ifdef	USE_V7_TTY
1609 	return (ioctl(STDOUT_FILENO, TIOCGETP, &old));
1610 
1611 #	else	/* USE_V7_TTY */
1612 
1613 #	ifdef	USE_TERMIOS
1614 #	ifdef	TCSANOW
1615 	return (tcgetattr(STDOUT_FILENO, &old));
1616 #	else
1617 	return (ioctl(STDOUT_FILENO, TCGETS, &old));
1618 #	endif
1619 #	else	/* USE_TERMIOS */
1620 	return (0);
1621 #	endif	/* USE_TERMIOS */
1622 #	endif	/* USE_V7_TTY */
1623 }
1624 
1625 LOCAL void
set_modes()1626 set_modes()
1627 {
1628 #ifdef	HAVE__DEV_TTY
1629 	if (tty < 0 && (tty = open("/dev/tty", O_RDONLY)) < 0)
1630 		errmsg("Can't open '/dev/tty'\n");
1631 #endif
1632 	if (tty < 0)
1633 		tty = fileno(stderr);
1634 
1635 	/*
1636 	 * Do signal handling only if they are not already
1637 	 * ignored.
1638 	 */
1639 	if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
1640 		signal(SIGINT, fixtty);
1641 #ifdef	SIGQUIT
1642 		signal(SIGQUIT, fixtty);
1643 #endif
1644 #ifdef	SIGTSTP
1645 		if (signal(SIGTSTP, SIG_IGN) == SIG_DFL)
1646 			signal(SIGTSTP, tstp);
1647 #endif
1648 	}
1649 #	ifdef	USE_V7_TTY
1650 	movebytes(&old, &new, sizeof (old));
1651 #		ifdef	LPASS8
1652 	{
1653 		int	lmode;
1654 
1655 		ioctl(STDOUT_FILENO, TIOCLGET, &lmode);
1656 		if (lmode & LPASS8)
1657 			raw8 = TRUE;
1658 	}
1659 #		else
1660 	if (old.sg_flags & RAW)
1661 		raw8 = TRUE;
1662 #		endif
1663 	new.sg_flags |= CBREAK;
1664 	new.sg_flags &= ~ECHO;
1665 	if (ioctl(STDOUT_FILENO, TIOCSETN, &new) < 0)
1666 		comerr("Can not set new modes.\n");
1667 
1668 #	else	/* USE_V7_TTY */
1669 
1670 #	ifdef	USE_TERMIOS
1671 	movebytes(&old, &new, sizeof (old));
1672 	if (!(old.c_iflag & ISTRIP))
1673 		raw8 = TRUE;
1674 #ifdef	__never__
1675 	new.c_iflag = ICRNL;
1676 	new.c_oflag = (OPOST|ONLCR);
1677 	new.c_lflag = ISIG;
1678 #endif
1679 	new.c_lflag &= ~(ICANON|ECHO);
1680 	new.c_cc[VMIN] = 1;
1681 	new.c_cc[VTIME] = 0;
1682 #	ifdef	TCSANOW
1683 	if (tcsetattr(STDOUT_FILENO, TCSADRAIN, &new) < 0)
1684 #	else
1685 	if (ioctl(STDOUT_FILENO, TCSETSW, &new) < 0)
1686 #	endif
1687 		comerr("Can not set new modes.\n");
1688 #	endif	/* USE_TERMIOS */
1689 
1690 #	endif	/* USE_V7_TTY */
1691 
1692 #ifdef	CATCH_SIGCONT_here
1693 #ifdef	SIGCONT
1694 	signal(SIGONT, redraw); /* XXX ??? */
1695 #endif
1696 #endif
1697 }
1698 
1699 /*
1700  * Reset to previous tty modes.
1701  */
1702 LOCAL void
reset_modes()1703 reset_modes()
1704 {
1705 	if (!silent) {
1706 #	ifdef	USE_V7_TTY
1707 		if (ioctl(STDOUT_FILENO, TIOCSETN, &old) < 0)
1708 
1709 #	else	/* USE_V7_TTY */
1710 
1711 #	ifdef	USE_TERMIOS
1712 #		ifdef	TCSANOW
1713 		if (tcsetattr(STDOUT_FILENO, TCSADRAIN, &old) < 0)
1714 #		else
1715 		if (ioctl(STDOUT_FILENO, TCSETSW, &old) < 0)
1716 #		endif
1717 #	else	/* USE_TERMIOS */
1718 		if (0)
1719 #	endif	/* USE_TERMIOS */
1720 #	endif	/* USE_V7_TTY */
1721 			comerr("Can not reset old modes.\n");
1722 	}
1723 }
1724 
1725 /*
1726  * Fix tty and exit.
1727  */
1728 LOCAL void
fixtty(sig)1729 fixtty(sig)
1730 	int	sig;
1731 {
1732 	end_standout();
1733 	end_attr();
1734 	end_ul();
1735 	clearline();
1736 	reset_modes();
1737 	exit(0);
1738 }
1739