update copyright notice
[unix-history] / usr / src / usr.bin / more / prompt.c
CommitLineData
bfe13c81
KB
1/*
2 * Copyright (c) 1988 Mark Nudleman
3 * Copyright (c) 1988 Regents of the University of California.
4 * All rights reserved.
5 *
bfe13c81
KB
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
a942b40b
KB
11 * by Mark Nudleman and the University of California, Berkeley. The
12 * name of Mark Nudleman or the
bfe13c81
KB
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
a942b40b 21static char sccsid[] = "@(#)prompt.c 5.4 (Berkeley) %G%";
bfe13c81
KB
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
36extern int pr_type;
37extern int ispipe;
38extern int hit_eof;
39extern int new_file;
40extern int sc_width;
41extern int so_width, se_width;
42extern char *current_file;
43extern int ac;
44extern char **av;
45extern int curr_ac;
46extern int linenums;
47
48/*
49 * Prototypes for the three flavors of prompts.
50 * These strings are expanded by pr_expand().
51 */
52static char s_proto[] =
53 "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t";
54static char m_proto[] =
55 "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
56static 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";
58static char e_proto[] =
59 "?f%f .?m(file %i of %m) .?ltline %lt .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
60
61char *prproto[3];
62char *eqproto = e_proto;
63
64static char message[250];
65static char *mp;
66
67/*
68 * Initialize the prompt prototype strings.
69 */
70 public void
71init_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
83setmp()
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
93ap_pos(pos)
94 POSITION pos;
95{
8ef60375 96 (void)sprintf(mp, "%ld", (long)pos);
bfe13c81
KB
97 setmp();
98}
99
100/*
101 * Append an integer to the end of the message.
102 */
103 static void
104ap_int(n)
105 int n;
106{
8ef60375 107 (void)sprintf(mp, "%d", n);
bfe13c81
KB
108 setmp();
109}
110
111/*
112 * Append a question mark to the end of the message.
113 */
114 static void
115ap_quest()
116{
117 *mp++ = '?';
118}
119
120/*
121 * Return the "current" byte offset in the file.
122 */
123 static POSITION
124curr_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
142cond(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
181protochar(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 *
255skipcond(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 *
304wherechar(p, wp)
305 char *p;
306 int *wp;
307{
557a82ec 308 switch (*p)
bfe13c81
KB
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 *
327pr_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 *
397eq_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 *
409pr_string()
410{
411 return (pr_expand(prproto[pr_type], sc_width-so_width-se_width-2));
412}