GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* |
||
2 |
* Copyright (C) 1984-2012 Mark Nudelman |
||
3 |
* Modified for use with illumos by Garrett D'Amore. |
||
4 |
* Copyright 2014 Garrett D'Amore <garrett@damore.org> |
||
5 |
* |
||
6 |
* You may distribute under the terms of either the GNU General Public |
||
7 |
* License or the Less License, as specified in the README file. |
||
8 |
* |
||
9 |
* For more information, see the README file. |
||
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 |
extern char *editor; |
||
35 |
extern char *editproto; |
||
36 |
|||
37 |
/* |
||
38 |
* Prototypes for the three flavors of prompts. |
||
39 |
* These strings are expanded by pr_expand(). |
||
40 |
*/ |
||
41 |
static const char s_proto[] = |
||
42 |
"?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x..%t"; |
||
43 |
static const char m_proto[] = |
||
44 |
"?n?f%f .?m(%T %i of %m) ..?e(END) " |
||
45 |
"?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t"; |
||
46 |
static const char M_proto[] = |
||
47 |
"?f%f .?n?m(%T %i of %m) ..?" |
||
48 |
"ltlines %lt-%lb?L/%L. :byte %bB?s/%s. .?e(END)" |
||
49 |
" ?x- Next\\: %x.:?pB%pB\\%..%t"; |
||
50 |
static const char e_proto[] = |
||
51 |
"?f%f .?m(%T %i of %m) .?ltlines " |
||
52 |
"%lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t"; |
||
53 |
static const char h_proto[] = |
||
54 |
"HELP -- ?eEND -- Press g to see it again:" |
||
55 |
"Press RETURN for more., or q when done"; |
||
56 |
static const char w_proto[] = |
||
57 |
"Waiting for data"; |
||
58 |
static const char more_proto[] = |
||
59 |
"%f (?eEND ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t)"; |
||
60 |
static const char more_M_proto[] = |
||
61 |
"%f (?eEND ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t)" |
||
62 |
"[Press space to continue, q to quit, h for help]"; |
||
63 |
|||
64 |
char *prproto[3]; |
||
65 |
char const *eqproto = e_proto; |
||
66 |
char const *hproto = h_proto; |
||
67 |
char const *wproto = w_proto; |
||
68 |
|||
69 |
static char message[PROMPT_SIZE]; |
||
70 |
static char *mp; |
||
71 |
|||
72 |
/* |
||
73 |
* Initialize the prompt prototype strings. |
||
74 |
*/ |
||
75 |
void |
||
76 |
init_prompt(void) |
||
77 |
{ |
||
78 |
78 |
prproto[0] = estrdup(s_proto); |
|
79 |
39 |
prproto[1] = estrdup(less_is_more ? more_proto : m_proto); |
|
80 |
39 |
prproto[2] = estrdup(less_is_more ? more_M_proto : M_proto); |
|
81 |
39 |
eqproto = estrdup(e_proto); |
|
82 |
39 |
hproto = estrdup(h_proto); |
|
83 |
39 |
wproto = estrdup(w_proto); |
|
84 |
39 |
} |
|
85 |
|||
86 |
/* |
||
87 |
* Append a string to the end of the message. |
||
88 |
*/ |
||
89 |
static void |
||
90 |
ap_str(char *s) |
||
91 |
{ |
||
92 |
int len; |
||
93 |
|||
94 |
1332 |
len = strlen(s); |
|
95 |
✗✓ | 666 |
if (mp + len >= message + PROMPT_SIZE) |
96 |
len = message + PROMPT_SIZE - mp - 1; |
||
97 |
666 |
(void) strncpy(mp, s, len); |
|
98 |
666 |
mp += len; |
|
99 |
666 |
*mp = '\0'; |
|
100 |
666 |
} |
|
101 |
|||
102 |
/* |
||
103 |
* Append a character to the end of the message. |
||
104 |
*/ |
||
105 |
static void |
||
106 |
ap_char(char c) |
||
107 |
{ |
||
108 |
1028 |
char buf[2]; |
|
109 |
|||
110 |
514 |
buf[0] = c; |
|
111 |
514 |
buf[1] = '\0'; |
|
112 |
514 |
ap_str(buf); |
|
113 |
514 |
} |
|
114 |
|||
115 |
/* |
||
116 |
* Append a off_t (as a decimal integer) to the end of the message. |
||
117 |
*/ |
||
118 |
static void |
||
119 |
ap_pos(off_t pos) |
||
120 |
{ |
||
121 |
char buf[23]; |
||
122 |
|||
123 |
postoa(pos, buf, sizeof(buf)); |
||
124 |
ap_str(buf); |
||
125 |
} |
||
126 |
|||
127 |
/* |
||
128 |
* Append an integer to the end of the message. |
||
129 |
*/ |
||
130 |
static void |
||
131 |
ap_int(int num) |
||
132 |
{ |
||
133 |
128 |
char buf[13]; |
|
134 |
|||
135 |
64 |
inttoa(num, buf, sizeof buf); |
|
136 |
64 |
ap_str(buf); |
|
137 |
64 |
} |
|
138 |
|||
139 |
/* |
||
140 |
* Append a question mark to the end of the message. |
||
141 |
*/ |
||
142 |
static void |
||
143 |
ap_quest(void) |
||
144 |
{ |
||
145 |
ap_str("?"); |
||
146 |
} |
||
147 |
|||
148 |
/* |
||
149 |
* Return the "current" byte offset in the file. |
||
150 |
*/ |
||
151 |
static off_t |
||
152 |
curr_byte(int where) |
||
153 |
{ |
||
154 |
off_t pos; |
||
155 |
|||
156 |
256 |
pos = position(where); |
|
157 |
✗✓✗✗ |
256 |
while (pos == -1 && where >= 0 && where < sc_height-1) |
158 |
pos = position(++where); |
||
159 |
✗✓ | 128 |
if (pos == -1) |
160 |
pos = ch_length(); |
||
161 |
128 |
return (pos); |
|
162 |
} |
||
163 |
|||
164 |
/* |
||
165 |
* Return the value of a prototype conditional. |
||
166 |
* A prototype string may include conditionals which consist of a |
||
167 |
* question mark followed by a single letter. |
||
168 |
* Here we decode that letter and return the appropriate boolean value. |
||
169 |
*/ |
||
170 |
static int |
||
171 |
cond(char c, int where) |
||
172 |
{ |
||
173 |
off_t len; |
||
174 |
|||
175 |
✗✗✗✓ ✓✗✗✗ ✗✓✓✓ ✗✗✗✓ ✗ |
3862 |
switch (c) { |
176 |
case 'a': /* Anything in the message yet? */ |
||
177 |
return (*message != '\0'); |
||
178 |
case 'b': /* Current byte offset known? */ |
||
179 |
return (curr_byte(where) != -1); |
||
180 |
case 'c': |
||
181 |
return (hshift != 0); |
||
182 |
case 'e': /* At end of file? */ |
||
183 |
916 |
return (eof_displayed()); |
|
184 |
case 'f': /* Filename known? */ |
||
185 |
32 |
return (strcmp(get_filename(curr_ifile), "-") != 0); |
|
186 |
case 'l': /* Line number known? */ |
||
187 |
case 'd': /* Same as l */ |
||
188 |
return (linenums); |
||
189 |
case 'L': /* Final line number known? */ |
||
190 |
case 'D': /* Final page number known? */ |
||
191 |
return (linenums && ch_length() != -1); |
||
192 |
case 'm': /* More than one file? */ |
||
193 |
✗✓ | 96 |
return (ntags() ? (ntags() > 1) : (nifile() > 1)); |
194 |
case 'n': /* First prompt in a new file? */ |
||
195 |
848 |
return (ntags() ? 1 : new_file); |
|
196 |
case 'p': /* Percent into file (bytes) known? */ |
||
197 |
✓✗ | 192 |
return (curr_byte(where) != -1 && ch_length() > 0); |
198 |
case 'P': /* Percent into file (lines) known? */ |
||
199 |
return (currline(where) != 0 && |
||
200 |
(len = ch_length()) > 0 && find_linenum(len) != 0); |
||
201 |
case 's': /* Size of file known? */ |
||
202 |
case 'B': |
||
203 |
return (ch_length() != -1); |
||
204 |
case 'x': /* Is there a "next" file? */ |
||
205 |
✗✓ | 39 |
if (ntags()) |
206 |
return (0); |
||
207 |
39 |
return (next_ifile(curr_ifile) != NULL); |
|
208 |
} |
||
209 |
return (0); |
||
210 |
1931 |
} |
|
211 |
|||
212 |
/* |
||
213 |
* Decode a "percent" prototype character. |
||
214 |
* A prototype string may include various "percent" escapes; |
||
215 |
* that is, a percent sign followed by a single letter. |
||
216 |
* Here we decode that letter and take the appropriate action, |
||
217 |
* usually by appending something to the message being built. |
||
218 |
*/ |
||
219 |
static void |
||
220 |
protochar(int c, int where) |
||
221 |
{ |
||
222 |
off_t pos; |
||
223 |
off_t len; |
||
224 |
int n; |
||
225 |
off_t linenum; |
||
226 |
off_t last_linenum; |
||
227 |
IFILE h; |
||
228 |
|||
229 |
#undef PAGE_NUM |
||
230 |
#define PAGE_NUM(linenum) ((((linenum) - 1) / (sc_height - 1)) + 1) |
||
231 |
|||
232 |
✗✗✗✗ ✗✓✗✗ ✗✗✗✓ ✗✗✗✓ ✗✗✓ |
3252 |
switch (c) { |
233 |
case 'b': /* Current byte offset */ |
||
234 |
pos = curr_byte(where); |
||
235 |
if (pos != -1) |
||
236 |
ap_pos(pos); |
||
237 |
else |
||
238 |
ap_quest(); |
||
239 |
break; |
||
240 |
case 'c': |
||
241 |
ap_int(hshift); |
||
242 |
break; |
||
243 |
case 'd': /* Current page number */ |
||
244 |
linenum = currline(where); |
||
245 |
if (linenum > 0 && sc_height > 1) |
||
246 |
ap_pos(PAGE_NUM(linenum)); |
||
247 |
else |
||
248 |
ap_quest(); |
||
249 |
break; |
||
250 |
case 'D': /* Final page number */ |
||
251 |
/* Find the page number of the last byte in the file (len-1). */ |
||
252 |
len = ch_length(); |
||
253 |
if (len == -1) { |
||
254 |
ap_quest(); |
||
255 |
} else if (len == 0) { |
||
256 |
/* An empty file has no pages. */ |
||
257 |
ap_pos(0); |
||
258 |
} else { |
||
259 |
linenum = find_linenum(len - 1); |
||
260 |
if (linenum <= 0) |
||
261 |
ap_quest(); |
||
262 |
else |
||
263 |
ap_pos(PAGE_NUM(linenum)); |
||
264 |
} |
||
265 |
break; |
||
266 |
case 'E': /* Editor name */ |
||
267 |
ap_str(editor); |
||
268 |
break; |
||
269 |
case 'f': /* File name */ |
||
270 |
88 |
ap_str(get_filename(curr_ifile)); |
|
271 |
88 |
break; |
|
272 |
case 'F': /* Last component of file name */ |
||
273 |
ap_str(last_component(get_filename(curr_ifile))); |
||
274 |
break; |
||
275 |
case 'i': /* Index into list of files */ |
||
276 |
if (ntags()) |
||
277 |
ap_int(curr_tag()); |
||
278 |
else |
||
279 |
ap_int(get_index(curr_ifile)); |
||
280 |
break; |
||
281 |
case 'l': /* Current line number */ |
||
282 |
linenum = currline(where); |
||
283 |
if (linenum != 0) |
||
284 |
ap_pos(linenum); |
||
285 |
else |
||
286 |
ap_quest(); |
||
287 |
break; |
||
288 |
case 'L': /* Final line number */ |
||
289 |
len = ch_length(); |
||
290 |
if (len == -1 || len == ch_zero() || |
||
291 |
(linenum = find_linenum(len)) <= 0) |
||
292 |
ap_quest(); |
||
293 |
else |
||
294 |
ap_pos(linenum-1); |
||
295 |
break; |
||
296 |
case 'm': /* Number of files */ |
||
297 |
n = ntags(); |
||
298 |
if (n) |
||
299 |
ap_int(n); |
||
300 |
else |
||
301 |
ap_int(nifile()); |
||
302 |
break; |
||
303 |
case 'p': /* Percent into file (bytes) */ |
||
304 |
64 |
pos = curr_byte(where); |
|
305 |
64 |
len = ch_length(); |
|
306 |
✓✗ | 64 |
if (pos != -1 && len > 0) |
307 |
64 |
ap_int(percentage(pos, len)); |
|
308 |
else |
||
309 |
ap_quest(); |
||
310 |
break; |
||
311 |
case 'P': /* Percent into file (lines) */ |
||
312 |
linenum = currline(where); |
||
313 |
if (linenum == 0 || |
||
314 |
(len = ch_length()) == -1 || len == ch_zero() || |
||
315 |
(last_linenum = find_linenum(len)) <= 0) |
||
316 |
ap_quest(); |
||
317 |
else |
||
318 |
ap_int(percentage(linenum, last_linenum)); |
||
319 |
break; |
||
320 |
case 's': /* Size of file */ |
||
321 |
case 'B': |
||
322 |
len = ch_length(); |
||
323 |
if (len != -1) |
||
324 |
ap_pos(len); |
||
325 |
else |
||
326 |
ap_quest(); |
||
327 |
break; |
||
328 |
case 't': /* Truncate trailing spaces in the message */ |
||
329 |
✓✓✓✓ |
2136 |
while (mp > message && mp[-1] == ' ') |
330 |
48 |
mp--; |
|
331 |
916 |
*mp = '\0'; |
|
332 |
916 |
break; |
|
333 |
case 'T': /* Type of list */ |
||
334 |
if (ntags()) |
||
335 |
ap_str("tag"); |
||
336 |
else |
||
337 |
ap_str("file"); |
||
338 |
break; |
||
339 |
case 'x': /* Name of next file */ |
||
340 |
h = next_ifile(curr_ifile); |
||
341 |
if (h != NULL) |
||
342 |
ap_str(get_filename(h)); |
||
343 |
else |
||
344 |
ap_quest(); |
||
345 |
break; |
||
346 |
} |
||
347 |
1068 |
} |
|
348 |
|||
349 |
/* |
||
350 |
* Skip a false conditional. |
||
351 |
* When a false condition is found (either a false IF or the ELSE part |
||
352 |
* of a true IF), this routine scans the prototype string to decide |
||
353 |
* where to resume parsing the string. |
||
354 |
* We must keep track of nested IFs and skip them properly. |
||
355 |
*/ |
||
356 |
static const char * |
||
357 |
skipcond(const char *p) |
||
358 |
{ |
||
359 |
int iflevel; |
||
360 |
|||
361 |
/* |
||
362 |
* We came in here after processing a ? or :, |
||
363 |
* so we start nested one level deep. |
||
364 |
*/ |
||
365 |
iflevel = 1; |
||
366 |
|||
367 |
3688 |
for (;;) { |
|
368 |
✓✓✓✓ ✓✗ |
46902 |
switch (*++p) { |
369 |
case '?': |
||
370 |
/* |
||
371 |
* Start of a nested IF. |
||
372 |
*/ |
||
373 |
2581 |
iflevel++; |
|
374 |
2581 |
break; |
|
375 |
case ':': |
||
376 |
/* |
||
377 |
* Else. |
||
378 |
* If this matches the IF we came in here with, |
||
379 |
* then we're done. |
||
380 |
*/ |
||
381 |
✓✓ | 68 |
if (iflevel == 1) |
382 |
64 |
return (p); |
|
383 |
break; |
||
384 |
case '.': |
||
385 |
/* |
||
386 |
* Endif. |
||
387 |
* If this matches the IF we came in here with, |
||
388 |
* then we're done. |
||
389 |
*/ |
||
390 |
✓✓ | 4361 |
if (--iflevel == 0) |
391 |
1780 |
return (p); |
|
392 |
break; |
||
393 |
case '\\': |
||
394 |
/* |
||
395 |
* Backslash escapes the next character. |
||
396 |
*/ |
||
397 |
920 |
++p; |
|
398 |
920 |
break; |
|
399 |
case '\0': |
||
400 |
/* |
||
401 |
* Whoops. Hit end of string. |
||
402 |
* This is a malformed conditional, but just treat it |
||
403 |
* as if all active conditionals ends here. |
||
404 |
*/ |
||
405 |
return (p-1); |
||
406 |
} |
||
407 |
} |
||
408 |
1844 |
} |
|
409 |
|||
410 |
/* |
||
411 |
* Decode a char that represents a position on the screen. |
||
412 |
*/ |
||
413 |
static const char * |
||
414 |
wherechar(const char *p, int *wp) |
||
415 |
{ |
||
416 |
✗✗✗✗ ✓✓ |
5998 |
switch (*p) { |
417 |
case 'b': case 'd': case 'l': case 'p': case 'P': |
||
418 |
✗✗✗✓ ✗✗ |
128 |
switch (*++p) { |
419 |
case 't': *wp = TOP; break; |
||
420 |
case 'm': *wp = MIDDLE; break; |
||
421 |
case 'b': *wp = BOTTOM; break; |
||
422 |
128 |
case 'B': *wp = BOTTOM_PLUS_ONE; break; |
|
423 |
case 'j': *wp = adjsline(jump_sline); break; |
||
424 |
default: *wp = TOP; p--; break; |
||
425 |
} |
||
426 |
} |
||
427 |
2999 |
return (p); |
|
428 |
} |
||
429 |
|||
430 |
/* |
||
431 |
* Construct a message based on a prototype string. |
||
432 |
*/ |
||
433 |
char * |
||
434 |
pr_expand(const char *proto, int maxwidth) |
||
435 |
{ |
||
436 |
const char *p; |
||
437 |
int c; |
||
438 |
1832 |
int where; |
|
439 |
|||
440 |
916 |
mp = message; |
|
441 |
|||
442 |
✗✓ | 916 |
if (*proto == '\0') |
443 |
return (""); |
||
444 |
|||
445 |
✓✓ | 9296 |
for (p = proto; *p != '\0'; p++) { |
446 |
✓✓✓✓ ✓✓ |
7313 |
switch (*p) { |
447 |
default: /* Just put the character in the message */ |
||
448 |
450 |
ap_char(*p); |
|
449 |
450 |
break; |
|
450 |
case '\\': /* Backslash escapes the next character */ |
||
451 |
64 |
p++; |
|
452 |
64 |
ap_char(*p); |
|
453 |
64 |
break; |
|
454 |
case '?': /* Conditional (IF) */ |
||
455 |
✗✓ | 1931 |
if ((c = *++p) == '\0') { |
456 |
--p; |
||
457 |
} else { |
||
458 |
1931 |
where = 0; |
|
459 |
1931 |
p = wherechar(p, &where); |
|
460 |
✓✓ | 1931 |
if (!cond(c, where)) |
461 |
1776 |
p = skipcond(p); |
|
462 |
} |
||
463 |
break; |
||
464 |
case ':': /* ELSE */ |
||
465 |
68 |
p = skipcond(p); |
|
466 |
68 |
break; |
|
467 |
case '.': /* ENDIF */ |
||
468 |
break; |
||
469 |
case '%': /* Percent escape */ |
||
470 |
✗✓ | 1068 |
if ((c = *++p) == '\0') { |
471 |
--p; |
||
472 |
} else { |
||
473 |
1068 |
where = 0; |
|
474 |
1068 |
p = wherechar(p, &where); |
|
475 |
1068 |
protochar(c, where); |
|
476 |
} |
||
477 |
break; |
||
478 |
} |
||
479 |
} |
||
480 |
|||
481 |
✓✓ | 916 |
if (*message == '\0') |
482 |
804 |
return (""); |
|
483 |
✓✗✗✓ |
224 |
if (maxwidth > 0 && mp >= message + maxwidth) { |
484 |
/* |
||
485 |
* Message is too long. |
||
486 |
* Return just the final portion of it. |
||
487 |
*/ |
||
488 |
return (mp - maxwidth); |
||
489 |
} |
||
490 |
112 |
return (message); |
|
491 |
916 |
} |
|
492 |
|||
493 |
/* |
||
494 |
* Return a message suitable for printing by the "=" command. |
||
495 |
*/ |
||
496 |
char * |
||
497 |
eq_message(void) |
||
498 |
{ |
||
499 |
return (pr_expand(eqproto, 0)); |
||
500 |
} |
||
501 |
|||
502 |
/* |
||
503 |
* Return a prompt. |
||
504 |
* This depends on the prompt type (SHORT, MEDIUM, LONG), etc. |
||
505 |
* If we can't come up with an appropriate prompt, return NULL |
||
506 |
* and the caller will prompt with a colon. |
||
507 |
*/ |
||
508 |
char * |
||
509 |
prompt_string(void) |
||
510 |
{ |
||
511 |
char *prompt; |
||
512 |
int type; |
||
513 |
|||
514 |
1832 |
type = pr_type; |
|
515 |
✓✗ | 2748 |
prompt = pr_expand((ch_getflags() & CH_HELPFILE) ? |
516 |
1832 |
hproto : prproto[type], sc_width-so_s_width-so_e_width-2); |
|
517 |
916 |
new_file = 0; |
|
518 |
916 |
return (prompt); |
|
519 |
} |
||
520 |
|||
521 |
/* |
||
522 |
* Return a message suitable for printing while waiting in the F command. |
||
523 |
*/ |
||
524 |
char * |
||
525 |
wait_message(void) |
||
526 |
{ |
||
527 |
return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2)); |
||
528 |
} |
Generated by: GCOVR (Version 3.3) |