135209Sbostic /*
235209Sbostic * Copyright (c) 1988 Mark Nudleman
335209Sbostic * Copyright (c) 1988 Regents of the University of California.
435209Sbostic * All rights reserved.
535209Sbostic *
635209Sbostic * Redistribution and use in source and binary forms are permitted
735209Sbostic * provided that the above copyright notice and this paragraph are
835209Sbostic * duplicated in all such forms and that any documentation,
935209Sbostic * advertising materials, and other materials related to such
1035209Sbostic * distribution and use acknowledge that the software was developed
11*35283Sbostic * by Mark Nudleman and the University of California, Berkeley. The
12*35283Sbostic * name of Mark Nudleman or the
1335209Sbostic * University may not be used to endorse or promote products derived
1435209Sbostic * from this software without specific prior written permission.
1535209Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1635209Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1735209Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1835209Sbostic */
1935209Sbostic
2035209Sbostic #ifndef lint
21*35283Sbostic static char sccsid[] = "@(#)prompt.c 5.4 (Berkeley) 07/25/88";
2235209Sbostic #endif /* not lint */
2335209Sbostic
2435209Sbostic /*
2535209Sbostic * Prompting and other messages.
2635209Sbostic * There are three flavors of prompts, SHORT, MEDIUM and LONG,
2735209Sbostic * selected by the -m/-M options.
2835209Sbostic * There is also the "equals message", printed by the = command.
2935209Sbostic * A prompt is a message composed of various pieces, such as the
3035209Sbostic * name of the file being viewed, the percentage into the file, etc.
3135209Sbostic */
3235209Sbostic
3335209Sbostic #include "less.h"
3435209Sbostic #include "position.h"
3535209Sbostic
3635209Sbostic extern int pr_type;
3735209Sbostic extern int ispipe;
3835209Sbostic extern int hit_eof;
3935209Sbostic extern int new_file;
4035209Sbostic extern int sc_width;
4135209Sbostic extern int so_width, se_width;
4235209Sbostic extern char *current_file;
4335209Sbostic extern int ac;
4435209Sbostic extern char **av;
4535209Sbostic extern int curr_ac;
4635209Sbostic extern int linenums;
4735209Sbostic
4835209Sbostic /*
4935209Sbostic * Prototypes for the three flavors of prompts.
5035209Sbostic * These strings are expanded by pr_expand().
5135209Sbostic */
5235209Sbostic static char s_proto[] =
5335209Sbostic "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t";
5435209Sbostic static char m_proto[] =
5535209Sbostic "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
5635209Sbostic static char M_proto[] =
5735209Sbostic "?f%f .?n?m(file %i of %m) ..?ltline %lt :byte %bB?s/%s ..?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
5835209Sbostic static char e_proto[] =
5935209Sbostic "?f%f .?m(file %i of %m) .?ltline %lt .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
6035209Sbostic
6135209Sbostic char *prproto[3];
6235209Sbostic char *eqproto = e_proto;
6335209Sbostic
6435209Sbostic static char message[250];
6535209Sbostic static char *mp;
6635209Sbostic
6735209Sbostic /*
6835209Sbostic * Initialize the prompt prototype strings.
6935209Sbostic */
7035209Sbostic public void
init_prompt()7135209Sbostic init_prompt()
7235209Sbostic {
7335209Sbostic prproto[0] = save(s_proto);
7435209Sbostic prproto[1] = save(m_proto);
7535209Sbostic prproto[2] = save(M_proto);
7635209Sbostic eqproto = save(e_proto);
7735209Sbostic }
7835209Sbostic
7935209Sbostic /*
8035209Sbostic * Set the message pointer to the end of the message string.
8135209Sbostic */
8235209Sbostic static void
setmp()8335209Sbostic setmp()
8435209Sbostic {
8535209Sbostic while (*mp != '\0')
8635209Sbostic mp++;
8735209Sbostic }
8835209Sbostic
8935209Sbostic /*
9035209Sbostic * Append a POSITION (as a decimal integer) to the end of the message.
9135209Sbostic */
9235209Sbostic static void
ap_pos(pos)9335209Sbostic ap_pos(pos)
9435209Sbostic POSITION pos;
9535209Sbostic {
9635241Sbostic (void)sprintf(mp, "%ld", (long)pos);
9735209Sbostic setmp();
9835209Sbostic }
9935209Sbostic
10035209Sbostic /*
10135209Sbostic * Append an integer to the end of the message.
10235209Sbostic */
10335209Sbostic static void
ap_int(n)10435209Sbostic ap_int(n)
10535209Sbostic int n;
10635209Sbostic {
10735241Sbostic (void)sprintf(mp, "%d", n);
10835209Sbostic setmp();
10935209Sbostic }
11035209Sbostic
11135209Sbostic /*
11235209Sbostic * Append a question mark to the end of the message.
11335209Sbostic */
11435209Sbostic static void
ap_quest()11535209Sbostic ap_quest()
11635209Sbostic {
11735209Sbostic *mp++ = '?';
11835209Sbostic }
11935209Sbostic
12035209Sbostic /*
12135209Sbostic * Return the "current" byte offset in the file.
12235209Sbostic */
12335209Sbostic static POSITION
curr_byte(where)12435209Sbostic curr_byte(where)
12535209Sbostic int where;
12635209Sbostic {
12735209Sbostic POSITION pos;
12835209Sbostic
12935209Sbostic pos = position(where);
13035209Sbostic if (pos == NULL_POSITION)
13135209Sbostic pos = ch_length();
13235209Sbostic return (pos);
13335209Sbostic }
13435209Sbostic
13535209Sbostic /*
13635209Sbostic * Return the value of a prototype conditional.
13735209Sbostic * A prototype string may include conditionals which consist of a
13835209Sbostic * question mark followed by a single letter.
13935209Sbostic * Here we decode that letter and return the appropriate boolean value.
14035209Sbostic */
14135209Sbostic static int
cond(c,where)14235209Sbostic cond(c, where)
14335209Sbostic char c;
14435209Sbostic int where;
14535209Sbostic {
14635209Sbostic switch (c)
14735209Sbostic {
14835209Sbostic case 'a': /* Anything in the message yet? */
14935209Sbostic return (mp > message);
15035209Sbostic case 'b': /* Current byte offset known? */
15135209Sbostic return (curr_byte(where) != NULL_POSITION);
15235209Sbostic case 'e': /* At end of file? */
15335209Sbostic return (hit_eof);
15435209Sbostic case 'f': /* Filename known? */
15535209Sbostic return (!ispipe);
15635209Sbostic case 'l': /* Line number known? */
15735209Sbostic return (linenums);
15835209Sbostic case 'm': /* More than one file? */
15935209Sbostic return (ac > 1);
16035209Sbostic case 'n': /* First prompt in a new file? */
16135209Sbostic return (new_file);
16235209Sbostic case 'p': /* Percent into file known? */
16335209Sbostic return (curr_byte(where) != NULL_POSITION &&
16435209Sbostic ch_length() > 0);
16535209Sbostic case 's': /* Size of file known? */
16635209Sbostic return (ch_length() != NULL_POSITION);
16735209Sbostic case 'x': /* Is there a "next" file? */
16835209Sbostic return (curr_ac + 1 < ac);
16935209Sbostic }
17035209Sbostic return (0);
17135209Sbostic }
17235209Sbostic
17335209Sbostic /*
17435209Sbostic * Decode a "percent" prototype character.
17535209Sbostic * A prototype string may include various "percent" escapes;
17635209Sbostic * that is, a percent sign followed by a single letter.
17735209Sbostic * Here we decode that letter and take the appropriate action,
17835209Sbostic * usually by appending something to the message being built.
17935209Sbostic */
18035209Sbostic static void
protochar(c,where)18135209Sbostic protochar(c, where)
18235209Sbostic int c;
18335209Sbostic int where;
18435209Sbostic {
18535209Sbostic POSITION pos;
18635209Sbostic POSITION len;
18735209Sbostic int n;
18835209Sbostic
18935209Sbostic switch (c)
19035209Sbostic {
19135209Sbostic case 'b': /* Current byte offset */
19235209Sbostic pos = curr_byte(where);
19335209Sbostic if (pos != NULL_POSITION)
19435209Sbostic ap_pos(pos);
19535209Sbostic else
19635209Sbostic ap_quest();
19735209Sbostic break;
19835209Sbostic case 'f': /* File name */
19935209Sbostic strtcpy(mp, current_file,
20035209Sbostic (unsigned int)(&message[sizeof(message)] - mp));
20135209Sbostic setmp();
20235209Sbostic break;
20335209Sbostic case 'i': /* Index into list of files */
20435209Sbostic ap_int(curr_ac + 1);
20535209Sbostic break;
20635209Sbostic case 'l': /* Current line number */
20735209Sbostic n = currline(where);
20835209Sbostic if (n != 0)
20935209Sbostic ap_int(n);
21035209Sbostic else
21135209Sbostic ap_quest();
21235209Sbostic break;
21335209Sbostic case 'm': /* Number of files */
21435209Sbostic ap_int(ac);
21535209Sbostic break;
21635209Sbostic case 'p': /* Percent into file */
21735209Sbostic pos = curr_byte(where);
21835209Sbostic len = ch_length();
21935209Sbostic if (pos != NULL_POSITION && len > 0)
22035209Sbostic ap_int((int)(100*pos / len));
22135209Sbostic else
22235209Sbostic ap_quest();
22335209Sbostic break;
22435209Sbostic case 's': /* Size of file */
22535209Sbostic len = ch_length();
22635209Sbostic if (len != NULL_POSITION)
22735209Sbostic ap_pos(len);
22835209Sbostic else
22935209Sbostic ap_quest();
23035209Sbostic break;
23135209Sbostic case 't': /* Truncate trailing spaces in the message */
23235209Sbostic while (mp > message && mp[-1] == ' ')
23335209Sbostic mp--;
23435209Sbostic break;
23535209Sbostic case 'x': /* Name of next file */
23635209Sbostic if (curr_ac + 1 < ac)
23735209Sbostic {
23835209Sbostic strtcpy(mp, av[curr_ac+1],
23935209Sbostic (unsigned int)(&message[sizeof(message)] - mp));
24035209Sbostic setmp();
24135209Sbostic } else
24235209Sbostic ap_quest();
24335209Sbostic break;
24435209Sbostic }
24535209Sbostic }
24635209Sbostic
24735209Sbostic /*
24835209Sbostic * Skip a false conditional.
24935209Sbostic * When a false condition is found (either a false IF or the ELSE part
25035209Sbostic * of a true IF), this routine scans the prototype string to decide
25135209Sbostic * where to resume parsing the string.
25235209Sbostic * We must keep track of nested IFs and skip them properly.
25335209Sbostic */
25435209Sbostic static char *
skipcond(p)25535209Sbostic skipcond(p)
25635209Sbostic register char *p;
25735209Sbostic {
25835209Sbostic register int iflevel = 1;
25935209Sbostic
26035209Sbostic for (;;) switch (*++p)
26135209Sbostic {
26235209Sbostic case '?':
26335209Sbostic /*
26435209Sbostic * Start of a nested IF.
26535209Sbostic */
26635209Sbostic iflevel++;
26735209Sbostic break;
26835209Sbostic case ':':
26935209Sbostic /*
27035209Sbostic * Else.
27135209Sbostic * If this matches the IF we came in here with,
27235209Sbostic * then we're done.
27335209Sbostic */
27435209Sbostic if (iflevel == 1)
27535209Sbostic return (p);
27635209Sbostic break;
27735209Sbostic case '.':
27835209Sbostic /*
27935209Sbostic * Endif.
28035209Sbostic * If this matches the IF we came in here with,
28135209Sbostic * then we're done.
28235209Sbostic */
28335209Sbostic if (--iflevel == 0)
28435209Sbostic return (p);
28535209Sbostic break;
28635209Sbostic case '\\':
28735209Sbostic /*
28835209Sbostic * Backslash escapes the next character.
28935209Sbostic */
29035209Sbostic ++p;
29135209Sbostic break;
29235209Sbostic case '\0':
29335209Sbostic /*
29435209Sbostic * Whoops. Hit end of string.
29535209Sbostic * This is a malformed conditional, but just treat it
29635209Sbostic * as if all active conditionals ends here.
29735209Sbostic */
29835209Sbostic return (p-1);
29935209Sbostic }
30035209Sbostic /*NOTREACHED*/
30135209Sbostic }
30235209Sbostic
30335209Sbostic static char *
wherechar(p,wp)30435209Sbostic wherechar(p, wp)
30535209Sbostic char *p;
30635209Sbostic int *wp;
30735209Sbostic {
30835231Sbostic switch (*p)
30935209Sbostic {
31035209Sbostic case 'b': case 'l': case 'p':
31135209Sbostic switch (*++p)
31235209Sbostic {
31335209Sbostic case 't': *wp = TOP; break;
31435209Sbostic case 'm': *wp = MIDDLE; break;
31535209Sbostic case 'b': *wp = BOTTOM; break;
31635209Sbostic case 'B': *wp = BOTTOM_PLUS_ONE; break;
31735209Sbostic default: *wp = TOP; break;
31835209Sbostic }
31935209Sbostic }
32035209Sbostic return (p);
32135209Sbostic }
32235209Sbostic
32335209Sbostic /*
32435209Sbostic * Construct a message based on a prototype string.
32535209Sbostic */
32635209Sbostic static char *
pr_expand(proto,maxwidth)32735209Sbostic pr_expand(proto, maxwidth)
32835209Sbostic char *proto;
32935209Sbostic int maxwidth;
33035209Sbostic {
33135209Sbostic register char *p;
33235209Sbostic register int c;
33335209Sbostic int where;
33435209Sbostic
33535209Sbostic mp = message;
33635209Sbostic
33735209Sbostic if (*proto == '\0')
33835209Sbostic return ("");
33935209Sbostic
34035209Sbostic for (p = proto; *p != '\0'; p++)
34135209Sbostic {
34235209Sbostic switch (*p)
34335209Sbostic {
34435209Sbostic default: /* Just put the character in the message */
34535209Sbostic *mp++ = *p;
34635209Sbostic break;
34735209Sbostic case '\\': /* Backslash escapes the next character */
34835209Sbostic p++;
34935209Sbostic *mp++ = *p;
35035209Sbostic break;
35135209Sbostic case '?': /* Conditional (IF) */
35235209Sbostic if ((c = *++p) == '\0')
35335209Sbostic --p;
35435209Sbostic else
35535209Sbostic {
35635209Sbostic p = wherechar(p, &where);
35735209Sbostic if (!cond(c, where))
35835209Sbostic p = skipcond(p);
35935209Sbostic }
36035209Sbostic break;
36135209Sbostic case ':': /* ELSE */
36235209Sbostic p = skipcond(p);
36335209Sbostic break;
36435209Sbostic case '.': /* ENDIF */
36535209Sbostic break;
36635209Sbostic case '%': /* Percent escape */
36735209Sbostic if ((c = *++p) == '\0')
36835209Sbostic --p;
36935209Sbostic else
37035209Sbostic {
37135209Sbostic p = wherechar(p, &where);
37235209Sbostic protochar(c, where);
37335209Sbostic }
37435209Sbostic break;
37535209Sbostic }
37635209Sbostic }
37735209Sbostic
37835209Sbostic new_file = 0;
37935209Sbostic if (mp == message)
38035209Sbostic return (NULL);
38135209Sbostic *mp = '\0';
38235209Sbostic if (maxwidth > 0 && mp >= message + maxwidth)
38335209Sbostic {
38435209Sbostic /*
38535209Sbostic * Message is too long.
38635209Sbostic * Return just the final portion of it.
38735209Sbostic */
38835209Sbostic return (mp - maxwidth);
38935209Sbostic }
39035209Sbostic return (message);
39135209Sbostic }
39235209Sbostic
39335209Sbostic /*
39435209Sbostic * Return a message suitable for printing by the "=" command.
39535209Sbostic */
39635209Sbostic public char *
eq_message()39735209Sbostic eq_message()
39835209Sbostic {
39935209Sbostic return (pr_expand(eqproto, 0));
40035209Sbostic }
40135209Sbostic
40235209Sbostic /*
40335209Sbostic * Return a prompt.
40435209Sbostic * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
40535209Sbostic * If we can't come up with an appropriate prompt, return NULL
40635209Sbostic * and the caller will prompt with a colon.
40735209Sbostic */
40835209Sbostic public char *
pr_string()40935209Sbostic pr_string()
41035209Sbostic {
41135209Sbostic return (pr_expand(prproto[pr_type], sc_width-so_width-se_width-2));
41235209Sbostic }
413