"SfR Fresh" - the SfR Freeware/Shareware Archive 
Member "less-424/prompt.c" of archive less-424.tar.gz:
As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers.
Alternatively you can here view or download the uninterpreted source code file.
That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
1 /*
2 * Copyright (C) 1984-2008 Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information about less, or for information on how to
8 * contact the author, see the README file.
9 */
10
11
12 /*
13 * Prompting and other messages.
14 * There are three flavors of prompts, SHORT, MEDIUM and LONG,
15 * selected by the -m/-M options.
16 * There is also the "equals message", printed by the = command.
17 * A prompt is a message composed of various pieces, such as the
18 * name of the file being viewed, the percentage into the file, etc.
19 */
20
21 #include "less.h"
22 #include "position.h"
23
24 extern int pr_type;
25 extern int new_file;
26 extern int sc_width;
27 extern int so_s_width, so_e_width;
28 extern int linenums;
29 extern int hshift;
30 extern int sc_height;
31 extern int jump_sline;
32 extern int less_is_more;
33 extern IFILE curr_ifile;
34 #if EDITOR
35 extern char *editor;
36 extern char *editproto;
37 #endif
38
39 /*
40 * Prototypes for the three flavors of prompts.
41 * These strings are expanded by pr_expand().
42 */
43 static constant char s_proto[] =
44 "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x..%t";
45 static constant char m_proto[] =
46 "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
47 static constant char M_proto[] =
48 "?f%f .?n?m(%T %i of %m) ..?ltlines %lt-%lb?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
49 static constant char e_proto[] =
50 "?f%f .?m(%T %i of %m) .?ltlines %lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
51 static constant char h_proto[] =
52 "HELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done";
53 static constant char w_proto[] =
54 "Waiting for data";
55 static constant char more_proto[] =
56 "--More--(?eEND ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t)";
57
58 public char *prproto[3];
59 public char constant *eqproto = e_proto;
60 public char constant *hproto = h_proto;
61 public char constant *wproto = w_proto;
62
63 static char message[PROMPT_SIZE];
64 static char *mp;
65
66 /*
67 * Initialize the prompt prototype strings.
68 */
69 public void
70 init_prompt()
71 {
72 prproto[0] = save(s_proto);
73 prproto[1] = save(less_is_more ? more_proto : m_proto);
74 prproto[2] = save(M_proto);
75 eqproto = save(e_proto);
76 hproto = save(h_proto);
77 wproto = save(w_proto);
78 }
79
80 /*
81 * Append a string to the end of the message.
82 */
83 static void
84 ap_str(s)
85 char *s;
86 {
87 int len;
88
89 len = strlen(s);
90 if (mp + len >= message + PROMPT_SIZE)
91 len = message + PROMPT_SIZE - mp - 1;
92 strncpy(mp, s, len);
93 mp += len;
94 *mp = '\0';
95 }
96
97 /*
98 * Append a character to the end of the message.
99 */
100 static void
101 ap_char(c)
102 char c;
103 {
104 char buf[2];
105
106 buf[0] = c;
107 buf[1] = '\0';
108 ap_str(buf);
109 }
110
111 /*
112 * Append a POSITION (as a decimal integer) to the end of the message.
113 */
114 static void
115 ap_pos(pos)
116 POSITION pos;
117 {
118 char buf[INT_STRLEN_BOUND(pos) + 2];
119
120 postoa(pos, buf);
121 ap_str(buf);
122 }
123
124 /*
125 * Append a line number to the end of the message.
126 */
127 static void
128 ap_linenum(linenum)
129 LINENUM linenum;
130 {
131 char buf[INT_STRLEN_BOUND(linenum) + 2];
132
133 linenumtoa(linenum, buf);
134 ap_str(buf);
135 }
136
137 /*
138 * Append an integer to the end of the message.
139 */
140 static void
141 ap_int(num)
142 int num;
143 {
144 char buf[INT_STRLEN_BOUND(num) + 2];
145
146 inttoa(num, buf);
147 ap_str(buf);
148 }
149
150 /*
151 * Append a question mark to the end of the message.
152 */
153 static void
154 ap_quest()
155 {
156 ap_str("?");
157 }
158
159 /*
160 * Return the "current" byte offset in the file.
161 */
162 static POSITION
163 curr_byte(where)
164 int where;
165 {
166 POSITION pos;
167
168 pos = position(where);
169 while (pos == NULL_POSITION && where >= 0 && where < sc_height-1)
170 pos = position(++where);
171 if (pos == NULL_POSITION)
172 pos = ch_length();
173 return (pos);
174 }
175
176 /*
177 * Return the value of a prototype conditional.
178 * A prototype string may include conditionals which consist of a
179 * question mark followed by a single letter.
180 * Here we decode that letter and return the appropriate boolean value.
181 */
182 static int
183 cond(c, where)
184 char c;
185 int where;
186 {
187 POSITION len;
188
189 switch (c)
190 {
191 case 'a': /* Anything in the message yet? */
192 return (mp > message);
193 case 'b': /* Current byte offset known? */
194 return (curr_byte(where) != NULL_POSITION);
195 case 'c':
196 return (hshift != 0);
197 case 'e': /* At end of file? */
198 return (eof_displayed());
199 case 'f': /* Filename known? */
200 return (strcmp(get_filename(curr_ifile), "-") != 0);
201 case 'l': /* Line number known? */
202 case 'd': /* Same as l */
203 return (linenums);
204 case 'L': /* Final line number known? */
205 case 'D': /* Final page number known? */
206 return (linenums && ch_length() != NULL_POSITION);
207 case 'm': /* More than one file? */
208 #if TAGS
209 return (ntags() ? (ntags() > 1) : (nifile() > 1));
210 #else
211 return (nifile() > 1);
212 #endif
213 case 'n': /* First prompt in a new file? */
214 #if TAGS
215 return (ntags() ? 1 : new_file);
216 #else
217 return (new_file);
218 #endif
219 case 'p': /* Percent into file (bytes) known? */
220 return (curr_byte(where) != NULL_POSITION &&
221 ch_length() > 0);
222 case 'P': /* Percent into file (lines) known? */
223 return (currline(where) != 0 &&
224 (len = ch_length()) > 0 &&
225 find_linenum(len) != 0);
226 case 's': /* Size of file known? */
227 case 'B':
228 return (ch_length() != NULL_POSITION);
229 case 'x': /* Is there a "next" file? */
230 #if TAGS
231 if (ntags())
232 return (0);
233 #endif
234 return (next_ifile(curr_ifile) != NULL_IFILE);
235 }
236 return (0);
237 }
238
239 /*
240 * Decode a "percent" prototype character.
241 * A prototype string may include various "percent" escapes;
242 * that is, a percent sign followed by a single letter.
243 * Here we decode that letter and take the appropriate action,
244 * usually by appending something to the message being built.
245 */
246 static void
247 protochar(c, where, iseditproto)
248 int c;
249 int where;
250 int iseditproto;
251 {
252 POSITION pos;
253 POSITION len;
254 int n;
255 LINENUM linenum;
256 LINENUM last_linenum;
257 IFILE h;
258
259 #undef PAGE_NUM
260 #define PAGE_NUM(linenum) ((((linenum) - 1) / (sc_height - 1)) + 1)
261
262 switch (c)
263 {
264 case 'b': /* Current byte offset */
265 pos = curr_byte(where);
266 if (pos != NULL_POSITION)
267 ap_pos(pos);
268 else
269 ap_quest();
270 break;
271 case 'c':
272 ap_int(hshift);
273 break;
274 case 'd': /* Current page number */
275 linenum = currline(where);
276 if (linenum > 0 && sc_height > 1)
277 ap_linenum(PAGE_NUM(linenum));
278 else
279 ap_quest();
280 break;
281 case 'D': /* Final page number */
282 /* Find the page number of the last byte in the file (len-1). */
283 len = ch_length();
284 if (len == NULL_POSITION)
285 ap_quest();
286 else if (len == 0)
287 /* An empty file has no pages. */
288 ap_linenum(0);
289 else
290 {
291 linenum = find_linenum(len - 1);
292 if (linenum <= 0)
293 ap_quest();
294 else
295 ap_linenum(PAGE_NUM(linenum));
296 }
297 break;
298 #if EDITOR
299 case 'E': /* Editor name */
300 ap_str(editor);
301 break;
302 #endif
303 case 'f': /* File name */
304 ap_str(get_filename(curr_ifile));
305 break;
306 case 'i': /* Index into list of files */
307 #if TAGS
308 if (ntags())
309 ap_int(curr_tag());
310 else
311 #endif
312 ap_int(get_index(curr_ifile));
313 break;
314 case 'l': /* Current line number */
315 linenum = currline(where);
316 if (linenum != 0)
317 ap_linenum(linenum);
318 else
319 ap_quest();
320 break;
321 case 'L': /* Final line number */
322 len = ch_length();
323 if (len == NULL_POSITION || len == ch_zero() ||
324 (linenum = find_linenum(len)) <= 0)
325 ap_quest();
326 else
327 ap_linenum(linenum-1);
328 break;
329 case 'm': /* Number of files */
330 #if TAGS
331 n = ntags();
332 if (n)
333 ap_int(n);
334 else
335 #endif
336 ap_int(nifile());
337 break;
338 case 'p': /* Percent into file (bytes) */
339 pos = curr_byte(where);
340 len = ch_length();
341 if (pos != NULL_POSITION && len > 0)
342 ap_int(percentage(pos,len));
343 else
344 ap_quest();
345 break;
346 case 'P': /* Percent into file (lines) */
347 linenum = currline(where);
348 if (linenum == 0 ||
349 (len = ch_length()) == NULL_POSITION || len == ch_zero() ||
350 (last_linenum = find_linenum(len)) <= 0)
351 ap_quest();
352 else
353 ap_int(percentage(linenum, last_linenum));
354 break;
355 case 's': /* Size of file */
356 case 'B':
357 len = ch_length();
358 if (len != NULL_POSITION)
359 ap_pos(len);
360 else
361 ap_quest();
362 break;
363 case 't': /* Truncate trailing spaces in the message */
364 while (mp > message && mp[-1] == ' ')
365 mp--;
366 break;
367 case 'T': /* Type of list */
368 #if TAGS
369 if (ntags())
370 ap_str("tag");
371 else
372 #endif
373 ap_str("file");
374 break;
375 case 'x': /* Name of next file */
376 h = next_ifile(curr_ifile);
377 if (h != NULL_IFILE)
378 ap_str(get_filename(h));
379 else
380 ap_quest();
381 break;
382 }
383 }
384
385 /*
386 * Skip a false conditional.
387 * When a false condition is found (either a false IF or the ELSE part
388 * of a true IF), this routine scans the prototype string to decide
389 * where to resume parsing the string.
390 * We must keep track of nested IFs and skip them properly.
391 */
392 static char *
393 skipcond(p)
394 register char *p;
395 {
396 register int iflevel;
397
398 /*
399 * We came in here after processing a ? or :,
400 * so we start nested one level deep.
401 */
402 iflevel = 1;
403
404 for (;;) switch (*++p)
405 {
406 case '?':
407 /*
408 * Start of a nested IF.
409 */
410 iflevel++;
411 break;
412 case ':':
413 /*
414 * Else.
415 * If this matches the IF we came in here with,
416 * then we're done.
417 */
418 if (iflevel == 1)
419 return (p);
420 break;
421 case '.':
422 /*
423 * Endif.
424 * If this matches the IF we came in here with,
425 * then we're done.
426 */
427 if (--iflevel == 0)
428 return (p);
429 break;
430 case '\\':
431 /*
432 * Backslash escapes the next character.
433 */
434 ++p;
435 break;
436 case '\0':
437 /*
438 * Whoops. Hit end of string.
439 * This is a malformed conditional, but just treat it
440 * as if all active conditionals ends here.
441 */
442 return (p-1);
443 }
444 /*NOTREACHED*/
445 }
446
447 /*
448 * Decode a char that represents a position on the screen.
449 */
450 static char *
451 wherechar(p, wp)
452 char *p;
453 int *wp;
454 {
455 switch (*p)
456 {
457 case 'b': case 'd': case 'l': case 'p': case 'P':
458 switch (*++p)
459 {
460 case 't': *wp = TOP; break;
461 case 'm': *wp = MIDDLE; break;
462 case 'b': *wp = BOTTOM; break;
463 case 'B': *wp = BOTTOM_PLUS_ONE; break;
464 case 'j': *wp = adjsline(jump_sline); break;
465 default: *wp = TOP; p--; break;
466 }
467 }
468 return (p);
469 }
470
471 /*
472 * Construct a message based on a prototype string.
473 */
474 public char *
475 pr_expand(proto, maxwidth)
476 char *proto;
477 int maxwidth;
478 {
479 register char *p;
480 register int c;
481 int where;
482
483 mp = message;
484
485 if (*proto == '\0')
486 return ("");
487
488 for (p = proto; *p != '\0'; p++)
489 {
490 switch (*p)
491 {
492 default: /* Just put the character in the message */
493 ap_char(*p);
494 break;
495 case '\\': /* Backslash escapes the next character */
496 p++;
497 ap_char(*p);
498 break;
499 case '?': /* Conditional (IF) */
500 if ((c = *++p) == '\0')
501 --p;
502 else
503 {
504 where = 0;
505 p = wherechar(p, &where);
506 if (!cond(c, where))
507 p = skipcond(p);
508 }
509 break;
510 case ':': /* ELSE */
511 p = skipcond(p);
512 break;
513 case '.': /* ENDIF */
514 break;
515 case '%': /* Percent escape */
516 if ((c = *++p) == '\0')
517 --p;
518 else
519 {
520 where = 0;
521 p = wherechar(p, &where);
522 protochar(c, where,
523 #if EDITOR
524 (proto == editproto));
525 #else
526 0);
527 #endif
528
529 }
530 break;
531 }
532 }
533
534 if (mp == message)
535 return ("");
536 if (maxwidth > 0 && mp >= message + maxwidth)
537 {
538 /*
539 * Message is too long.
540 * Return just the final portion of it.
541 */
542 return (mp - maxwidth);
543 }
544 return (message);
545 }
546
547 /*
548 * Return a message suitable for printing by the "=" command.
549 */
550 public char *
551 eq_message()
552 {
553 return (pr_expand(eqproto, 0));
554 }
555
556 /*
557 * Return a prompt.
558 * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
559 * If we can't come up with an appropriate prompt, return NULL
560 * and the caller will prompt with a colon.
561 */
562 public char *
563 pr_string()
564 {
565 char *prompt;
566 int type;
567
568 type = (!less_is_more) ? pr_type : pr_type ? 0 : 1;
569 prompt = pr_expand((ch_getflags() & CH_HELPFILE) ?
570 hproto : prproto[type],
571 sc_width-so_s_width-so_e_width-2);
572 new_file = 0;
573 return (prompt);
574 }
575
576 /*
577 * Return a message suitable for printing while waiting in the F command.
578 */
579 public char *
580 wait_message()
581 {
582 return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2));
583 }