xref: /original-bsd/usr.bin/more/prompt.c (revision 94cb6cb2)
1 /*
2  * Copyright (c) 1988 Mark Nudleman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted
7  * provided that the above copyright notice and this paragraph are
8  * duplicated in all such forms and that any documentation,
9  * advertising materials, and other materials related to such
10  * distribution and use acknowledge that the software was developed
11  * by Mark Nudleman and the University of California, Berkeley.  The
12  * name of Mark Nudleman or the
13  * University may not be used to endorse or promote products derived
14  * from this software without specific prior written permission.
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18  */
19 
20 #ifndef lint
21 static char sccsid[] = "@(#)prompt.c	5.4 (Berkeley) 07/25/88";
22 #endif /* not lint */
23 
24 /*
25  * Prompting and other messages.
26  * There are three flavors of prompts, SHORT, MEDIUM and LONG,
27  * selected by the -m/-M options.
28  * There is also the "equals message", printed by the = command.
29  * A prompt is a message composed of various pieces, such as the
30  * name of the file being viewed, the percentage into the file, etc.
31  */
32 
33 #include "less.h"
34 #include "position.h"
35 
36 extern int pr_type;
37 extern int ispipe;
38 extern int hit_eof;
39 extern int new_file;
40 extern int sc_width;
41 extern int so_width, se_width;
42 extern char *current_file;
43 extern int ac;
44 extern char **av;
45 extern int curr_ac;
46 extern int linenums;
47 
48 /*
49  * Prototypes for the three flavors of prompts.
50  * These strings are expanded by pr_expand().
51  */
52 static char s_proto[] =
53   "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t";
54 static char m_proto[] =
55   "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
56 static char M_proto[] =
57   "?f%f .?n?m(file %i of %m) ..?ltline %lt :byte %bB?s/%s ..?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
58 static char e_proto[] =
59   "?f%f .?m(file %i of %m) .?ltline %lt .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
60 
61 char *prproto[3];
62 char *eqproto = e_proto;
63 
64 static char message[250];
65 static char *mp;
66 
67 /*
68  * Initialize the prompt prototype strings.
69  */
70 	public void
init_prompt()71 init_prompt()
72 {
73 	prproto[0] = save(s_proto);
74 	prproto[1] = save(m_proto);
75 	prproto[2] = save(M_proto);
76 	eqproto = save(e_proto);
77 }
78 
79 /*
80  * Set the message pointer to the end of the message string.
81  */
82 	static void
setmp()83 setmp()
84 {
85 	while (*mp != '\0')
86 		mp++;
87 }
88 
89 /*
90  * Append a POSITION (as a decimal integer) to the end of the message.
91  */
92 	static void
ap_pos(pos)93 ap_pos(pos)
94 	POSITION pos;
95 {
96 	(void)sprintf(mp, "%ld", (long)pos);
97 	setmp();
98 }
99 
100 /*
101  * Append an integer to the end of the message.
102  */
103 	static void
ap_int(n)104 ap_int(n)
105 	int n;
106 {
107 	(void)sprintf(mp, "%d", n);
108 	setmp();
109 }
110 
111 /*
112  * Append a question mark to the end of the message.
113  */
114 	static void
ap_quest()115 ap_quest()
116 {
117 	*mp++ = '?';
118 }
119 
120 /*
121  * Return the "current" byte offset in the file.
122  */
123 	static POSITION
curr_byte(where)124 curr_byte(where)
125 	int where;
126 {
127 	POSITION pos;
128 
129 	pos = position(where);
130 	if (pos == NULL_POSITION)
131 		pos = ch_length();
132 	return (pos);
133 }
134 
135 /*
136  * Return the value of a prototype conditional.
137  * A prototype string may include conditionals which consist of a
138  * question mark followed by a single letter.
139  * Here we decode that letter and return the appropriate boolean value.
140  */
141 	static int
cond(c,where)142 cond(c, where)
143 	char c;
144 	int where;
145 {
146 	switch (c)
147 	{
148 	case 'a':	/* Anything in the message yet? */
149 		return (mp > message);
150 	case 'b':	/* Current byte offset known? */
151 		return (curr_byte(where) != NULL_POSITION);
152 	case 'e':	/* At end of file? */
153 		return (hit_eof);
154 	case 'f':	/* Filename known? */
155 		return (!ispipe);
156 	case 'l':	/* Line number known? */
157 		return (linenums);
158 	case 'm':	/* More than one file? */
159 		return (ac > 1);
160 	case 'n':	/* First prompt in a new file? */
161 		return (new_file);
162 	case 'p':	/* Percent into file known? */
163 		return (curr_byte(where) != NULL_POSITION &&
164 				ch_length() > 0);
165 	case 's':	/* Size of file known? */
166 		return (ch_length() != NULL_POSITION);
167 	case 'x':	/* Is there a "next" file? */
168 		return (curr_ac + 1 < ac);
169 	}
170 	return (0);
171 }
172 
173 /*
174  * Decode a "percent" prototype character.
175  * A prototype string may include various "percent" escapes;
176  * that is, a percent sign followed by a single letter.
177  * Here we decode that letter and take the appropriate action,
178  * usually by appending something to the message being built.
179  */
180 	static void
protochar(c,where)181 protochar(c, where)
182 	int c;
183 	int where;
184 {
185 	POSITION pos;
186 	POSITION len;
187 	int n;
188 
189 	switch (c)
190 	{
191 	case 'b':	/* Current byte offset */
192 		pos = curr_byte(where);
193 		if (pos != NULL_POSITION)
194 			ap_pos(pos);
195 		else
196 			ap_quest();
197 		break;
198 	case 'f':	/* File name */
199 		strtcpy(mp, current_file,
200 			(unsigned int)(&message[sizeof(message)] - mp));
201 		setmp();
202 		break;
203 	case 'i':	/* Index into list of files */
204 		ap_int(curr_ac + 1);
205 		break;
206 	case 'l':	/* Current line number */
207 		n = currline(where);
208 		if (n != 0)
209 			ap_int(n);
210 		else
211 			ap_quest();
212 		break;
213 	case 'm':	/* Number of files */
214 		ap_int(ac);
215 		break;
216 	case 'p':	/* Percent into file */
217 		pos = curr_byte(where);
218 		len = ch_length();
219 		if (pos != NULL_POSITION && len > 0)
220 			ap_int((int)(100*pos / len));
221 		else
222 			ap_quest();
223 		break;
224 	case 's':	/* Size of file */
225 		len = ch_length();
226 		if (len != NULL_POSITION)
227 			ap_pos(len);
228 		else
229 			ap_quest();
230 		break;
231 	case 't':	/* Truncate trailing spaces in the message */
232 		while (mp > message && mp[-1] == ' ')
233 			mp--;
234 		break;
235 	case 'x':	/* Name of next file */
236 		if (curr_ac + 1 < ac)
237 		{
238 			strtcpy(mp, av[curr_ac+1],
239 				(unsigned int)(&message[sizeof(message)] - mp));
240 			setmp();
241 		} else
242 			ap_quest();
243 		break;
244 	}
245 }
246 
247 /*
248  * Skip a false conditional.
249  * When a false condition is found (either a false IF or the ELSE part
250  * of a true IF), this routine scans the prototype string to decide
251  * where to resume parsing the string.
252  * We must keep track of nested IFs and skip them properly.
253  */
254 	static char *
skipcond(p)255 skipcond(p)
256 	register char *p;
257 {
258 	register int iflevel = 1;
259 
260 	for (;;) switch (*++p)
261 	{
262 	case '?':
263 		/*
264 		 * Start of a nested IF.
265 		 */
266 		iflevel++;
267 		break;
268 	case ':':
269 		/*
270 		 * Else.
271 		 * If this matches the IF we came in here with,
272 		 * then we're done.
273 		 */
274 		if (iflevel == 1)
275 			return (p);
276 		break;
277 	case '.':
278 		/*
279 		 * Endif.
280 		 * If this matches the IF we came in here with,
281 		 * then we're done.
282 		 */
283 		if (--iflevel == 0)
284 			return (p);
285 		break;
286 	case '\\':
287 		/*
288 		 * Backslash escapes the next character.
289 		 */
290 		++p;
291 		break;
292 	case '\0':
293 		/*
294 		 * Whoops.  Hit end of string.
295 		 * This is a malformed conditional, but just treat it
296 		 * as if all active conditionals ends here.
297 		 */
298 		return (p-1);
299 	}
300 	/*NOTREACHED*/
301 }
302 
303 	static char *
wherechar(p,wp)304 wherechar(p, wp)
305 	char *p;
306 	int *wp;
307 {
308 	switch (*p)
309 	{
310 	case 'b': case 'l': case 'p':
311 		switch (*++p)
312 		{
313 		case 't':   *wp = TOP;			break;
314 		case 'm':   *wp = MIDDLE;		break;
315 		case 'b':   *wp = BOTTOM;		break;
316 		case 'B':   *wp = BOTTOM_PLUS_ONE;	break;
317 		default:    *wp = TOP;			break;
318 		}
319 	}
320 	return (p);
321 }
322 
323 /*
324  * Construct a message based on a prototype string.
325  */
326 	static char *
pr_expand(proto,maxwidth)327 pr_expand(proto, maxwidth)
328 	char *proto;
329 	int maxwidth;
330 {
331 	register char *p;
332 	register int c;
333 	int where;
334 
335 	mp = message;
336 
337 	if (*proto == '\0')
338 		return ("");
339 
340 	for (p = proto;  *p != '\0';  p++)
341 	{
342 		switch (*p)
343 		{
344 		default:	/* Just put the character in the message */
345 			*mp++ = *p;
346 			break;
347 		case '\\':	/* Backslash escapes the next character */
348 			p++;
349 			*mp++ = *p;
350 			break;
351 		case '?':	/* Conditional (IF) */
352 			if ((c = *++p) == '\0')
353 				--p;
354 			else
355 			{
356 				p = wherechar(p, &where);
357 				if (!cond(c, where))
358 					p = skipcond(p);
359 			}
360 			break;
361 		case ':':	/* ELSE */
362 			p = skipcond(p);
363 			break;
364 		case '.':	/* ENDIF */
365 			break;
366 		case '%':	/* Percent escape */
367 			if ((c = *++p) == '\0')
368 				--p;
369 			else
370 			{
371 				p = wherechar(p, &where);
372 				protochar(c, where);
373 			}
374 			break;
375 		}
376 	}
377 
378 	new_file = 0;
379 	if (mp == message)
380 		return (NULL);
381 	*mp = '\0';
382 	if (maxwidth > 0 && mp >= message + maxwidth)
383 	{
384 		/*
385 		 * Message is too long.
386 		 * Return just the final portion of it.
387 		 */
388 		return (mp - maxwidth);
389 	}
390 	return (message);
391 }
392 
393 /*
394  * Return a message suitable for printing by the "=" command.
395  */
396 	public char *
eq_message()397 eq_message()
398 {
399 	return (pr_expand(eqproto, 0));
400 }
401 
402 /*
403  * Return a prompt.
404  * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
405  * If we can't come up with an appropriate prompt, return NULL
406  * and the caller will prompt with a colon.
407  */
408 	public char *
pr_string()409 pr_string()
410 {
411 	return (pr_expand(prproto[pr_type], sc_width-so_width-se_width-2));
412 }
413