xref: /csrg-svn/usr.bin/more/prompt.c (revision 35283)
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