1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2011 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                   Phong Vo <kpv@research.att.com>                    *
20 *                                                                      *
21 ***********************************************************************/
22 #include "sfdchdr.h"
23 
24 #if _PACKAGE_ast
25 #include <ast_tty.h>
26 #include <signal.h>
27 #endif
28 
29 /*
30  * a simple but fast more style pager discipline
31  * if on sfstdout then sfstdin ops reset the page state
32  *
33  * Glenn Fowler
34  * AT&T Research
35  *
36  * @(#)$Id: sfdcmore (AT&T Research) 1998-06-25 $
37  */
38 
39 typedef struct
40 {
41 	Sfdisc_t	disc;		/* sfio discipline		*/
42 	Sfio_t*		input;		/* tied with this input stream	*/
43 	Sfio_t*		error;		/* tied with this error stream	*/
44 	int		rows;		/* max rows			*/
45 	int		cols;		/* max cols			*/
46 	int		row;		/* current row			*/
47 	int		col;		/* current col			*/
48 	int		match;		/* match length, 0 if none	*/
49 	char		pattern[128];	/* match pattern		*/
50 	char		prompt[1];	/* prompt string		*/
51 } More_t;
52 
53 /*
54  * more read
55  * we assume line-at-a-time input
56  */
57 
58 #if __STD_C
moreread(Sfio_t * f,void * buf,size_t n,Sfdisc_t * dp)59 static ssize_t moreread(Sfio_t* f, void* buf, size_t n, Sfdisc_t* dp)
60 #else
61 static ssize_t moreread(f, buf, n, dp)
62 Sfio_t*		f;
63 void*		buf;
64 size_t		n;
65 Sfdisc_t*	dp;
66 #endif
67 {
68 	register More_t*	more = (More_t*)dp;
69 
70 	more->match = 0;
71 	more->row = 2;
72 	more->col = 1;
73 	return sfrd(f, buf, n, dp);
74 }
75 
76 /*
77  * output label on wfd and return next char on rfd with no echo
78  * return < -1 is -(signal + 1)
79  */
80 
81 #if __STD_C
ttyquery(Sfio_t * rp,Sfio_t * wp,const char * label,Sfdisc_t * dp)82 static int ttyquery(Sfio_t* rp, Sfio_t* wp, const char* label, Sfdisc_t* dp)
83 #else
84 static int ttyquery(rp, wp, label, dp)
85 Sfio_t*		rp;
86 Sfio_t*		wp;
87 char*		label;
88 Sfdisc_t*	dp;
89 #endif
90 {
91 	register int	r;
92 	int		n;
93 
94 #ifdef TCSADRAIN
95 	unsigned char	c;
96 	struct termios	old;
97 	struct termios	tty;
98 	int		rfd = sffileno(rp);
99 	int		wfd = sffileno(rp);
100 
101 	if (!label)
102 		n = 0;
103 	else if (n = strlen(label))
104 		write(wfd, label, n);
105 	tcgetattr(rfd, &old);
106 	tty = old;
107 	tty.c_cc[VTIME] = 0;
108 	tty.c_cc[VMIN] = 1;
109 	tty.c_lflag &= ~(ICANON|ECHO|ECHOK|ISIG);
110 	tcsetattr(rfd, TCSADRAIN, &tty);
111 	if ((r = read(rfd, &c, 1)) == 1)
112 	{
113 		if (c == old.c_cc[VEOF])
114 			r = -1;
115 		else if (c == old.c_cc[VINTR])
116 			r = -(SIGINT + 1);
117 		else if (c == old.c_cc[VQUIT])
118 			r = -(SIGQUIT + 1);
119 		else if (c == '\r')
120 			r = '\n';
121 		else
122 			r = c;
123 	}
124 	tcsetattr(rfd, TCSADRAIN, &old);
125 	if (n)
126 	{
127 		write(wfd, "\r", 1);
128 		while (n-- > 0)
129 			write(wfd, " ", 1);
130 		write(wfd, "\r", 1);
131 	}
132 #else
133 	register char*	s;
134 
135 	if (label && (n = strlen(label)))
136 		sfwr(wp, label, n, dp);
137 	r = (s = sfgetr(rp, '\n', 0)) ? *s : -1;
138 #endif
139 	return r;
140 }
141 
142 /*
143  * more write
144  */
145 
146 #if __STD_C
morewrite(Sfio_t * f,const Void_t * buf,register size_t n,Sfdisc_t * dp)147 static ssize_t morewrite(Sfio_t* f, const Void_t* buf, register size_t n, Sfdisc_t* dp)
148 #else
149 static ssize_t morewrite(f, buf, n, dp)
150 Sfio_t* 	f;
151 Void_t*		buf;
152 register size_t	n;
153 Sfdisc_t*	dp;
154 #endif
155 {
156 	register More_t*	more = (More_t*)dp;
157 	register char*		b;
158 	register char*		s;
159 	register char*		e;
160 	register ssize_t	w;
161 	register int		r;
162 
163 	if (!more->row)
164 		return n;
165 	if (!more->col)
166 		return sfwr(f, buf, n, dp);
167 	w = 0;
168 	b = (char*)buf;
169 	s = b;
170 	e = s + n;
171 	if (more->match)
172 	{
173  match:
174 		for (r = more->pattern[0];; s++)
175 		{
176 			if (s >= e)
177 				return n;
178 			if (*s == '\n')
179 				b = s + 1;
180 			else if (*s == r && (e - s) >= more->match && !strncmp(s, more->pattern, more->match))
181 				break;
182 		}
183 		s = b;
184 		w += b - (char*)buf;
185 		more->match = 0;
186 	}
187 	while (s < e)
188 	{
189 		switch (*s++)
190 		{
191 		case '\t':
192 			more->col = ((more->col + 8) & ~7) - 1;
193 			/*FALLTHROUGH*/
194 		default:
195 			if (++more->col <= more->cols || s < e && *s == '\n')
196 				continue;
197 			/*FALLTHROUGH*/
198 		case '\n':
199 			more->col = 1;
200 			if (++more->row < more->rows)
201 				continue;
202 			break;
203 		case '\b':
204 			if (more->col > 1)
205 				more->col--;
206 			continue;
207 		case '\r':
208 			more->col = 1;
209 			continue;
210 		}
211 		w += sfwr(f, b, s - b, dp);
212 		b = s;
213 		r = ttyquery(sfstdin, f, more->prompt, dp);
214 		if (r == '/' || r == 'n')
215 		{
216 			if (r == '/')
217 			{
218 				sfwr(f, "/", 1, dp);
219 				if ((s = sfgetr(sfstdin, '\n', 1)) && (n = sfvalue(sfstdin) - 1) > 0)
220 				{
221 					if (n >= sizeof(more->pattern))
222 						n = sizeof(more->pattern) - 1;
223 					memcpy(more->pattern, s, n);
224 					more->pattern[n] = 0;
225 				}
226 			}
227 			if (more->match = strlen(more->pattern))
228 			{
229 				more->row = 1;
230 				more->col = 1;
231 				goto match;
232 			}
233 		}
234 		switch (r)
235 		{
236 		case '\n':
237 		case '\r':
238 			more->row--;
239 			more->col = 1;
240 			break;
241 		case ' ':
242 			more->row = 2;
243 			more->col = 1;
244 			break;
245 		default:
246 			more->row = 0;
247 			return n;
248 		}
249 	}
250 	if (s > b)
251 		w += sfwr(f, b, s - b, dp);
252 	return w;
253 }
254 
255 /*
256  * remove the discipline on close
257  */
258 
259 #if __STD_C
moreexcept(Sfio_t * f,int type,Void_t * data,Sfdisc_t * dp)260 static int moreexcept(Sfio_t* f, int type, Void_t* data, Sfdisc_t* dp)
261 #else
262 static int moreexcept(f, type, data, dp)
263 Sfio_t*		f;
264 int		type;
265 Void_t*		data;
266 Sfdisc_t*	dp;
267 #endif
268 {
269 	register More_t*	more = (More_t*)dp;
270 
271 	if (type == SF_FINAL || type == SF_DPOP)
272 	{
273 		if (f = more->input)
274 		{
275 			more->input = 0;
276 			sfdisc(f, SF_POPDISC);
277 		}
278 		else if (f = more->error)
279 		{
280 			more->error = 0;
281 			sfdisc(f, SF_POPDISC);
282 		}
283 		else
284 			free(dp);
285 	}
286 	else if (type == SF_SYNC)
287 	{
288 		more->match = 0;
289 		more->row = 1;
290 		more->col = 1;
291 	}
292 	return 0;
293 }
294 
295 /*
296  * push the more discipline on f
297  * if prompt==0 then a default ansi prompt is used
298  * if rows==0 or cols==0 then they are deterimined from the tty
299  * if f==sfstdout then input on sfstdin also resets the state
300  */
301 
302 #if __STD_C
sfdcmore(Sfio_t * f,const char * prompt,int rows,int cols)303 int sfdcmore(Sfio_t* f, const char* prompt, int rows, int cols)
304 #else
305 int sfdcmore(f, prompt, rows, cols)
306 Sfio_t*		f;
307 char*		prompt;
308 int		rows;
309 int		cols;
310 #endif
311 {
312 	register More_t*	more;
313 	size_t			n;
314 
315 	/*
316 	 * this is a writeonly discipline for interactive io
317 	 */
318 
319 	if (!(sfset(f, 0, 0) & SF_WRITE) || !isatty(sffileno(sfstdin)) || !isatty(sffileno(sfstdout)))
320 		return -1;
321 	if (!prompt)
322 		prompt = "\033[7m More\033[m";
323 	n = strlen(prompt) + 1;
324 	if (!(more = (More_t*)malloc(sizeof(More_t) + n)))
325 		return -1;
326 	memset(more, 0, sizeof(*more));
327 
328 	more->disc.readf = moreread;
329 	more->disc.writef = morewrite;
330 	more->disc.exceptf = moreexcept;
331 	memcpy(more->prompt, prompt, n);
332 	if (!rows || !cols)
333 	{
334 #if _PACKAGE_ast
335 		astwinsize(sffileno(sfstdin), &rows, &cols);
336 #endif
337 		if (!rows)
338 			rows = 24;
339 		if (!cols)
340 			cols = 80;
341 	}
342 	more->rows = rows;
343 	more->cols = cols;
344 	more->row = 1;
345 	more->col = 1;
346 
347 	if (sfdisc(f, &more->disc) != &more->disc)
348 	{
349 		free(more);
350 		return -1;
351 	}
352 	if (f == sfstdout)
353 	{
354 		if (sfdisc(sfstdin, &more->disc) != &more->disc)
355 		{
356 			sfdisc(f, SF_POPDISC);
357 			return -1;
358 		}
359 		more->input = sfstdin;
360 		if (sfdisc(sfstderr, &more->disc) != &more->disc)
361 		{
362 			sfdisc(f, SF_POPDISC);
363 			return -1;
364 		}
365 		more->error = sfstdin;
366 	}
367 
368 	return 0;
369 }
370