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