xref: /csrg-svn/usr.bin/more/decode.c (revision 35209)
1*35209Sbostic /*
2*35209Sbostic  * Copyright (c) 1988 Mark Nudleman
3*35209Sbostic  * Copyright (c) 1988 Regents of the University of California.
4*35209Sbostic  * All rights reserved.
5*35209Sbostic  *
6*35209Sbostic  * This code is derived from software contributed to Berkeley by
7*35209Sbostic  * Mark Nudleman.
8*35209Sbostic  *
9*35209Sbostic  * Redistribution and use in source and binary forms are permitted
10*35209Sbostic  * provided that the above copyright notice and this paragraph are
11*35209Sbostic  * duplicated in all such forms and that any documentation,
12*35209Sbostic  * advertising materials, and other materials related to such
13*35209Sbostic  * distribution and use acknowledge that the software was developed
14*35209Sbostic  * by the University of California, Berkeley.  The name of the
15*35209Sbostic  * University may not be used to endorse or promote products derived
16*35209Sbostic  * from this software without specific prior written permission.
17*35209Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
18*35209Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
19*35209Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20*35209Sbostic  */
21*35209Sbostic 
22*35209Sbostic #ifndef lint
23*35209Sbostic static char sccsid[] = "@(#)decode.c	5.1 (Berkeley) 07/21/88";
24*35209Sbostic #endif /* not lint */
25*35209Sbostic 
26*35209Sbostic /*
27*35209Sbostic  * Routines to decode user commands.
28*35209Sbostic  *
29*35209Sbostic  * This is all table driven.
30*35209Sbostic  * A command table is a sequence of command descriptors.
31*35209Sbostic  * Each command descriptor is a sequence of bytes with the following format:
32*35209Sbostic  *	<c1><c2>...<cN><0><action>
33*35209Sbostic  * The characters c1,c2,...,cN are the command string; that is,
34*35209Sbostic  * the characters which the user must type.
35*35209Sbostic  * It is terminated by a null <0> byte.
36*35209Sbostic  * The byte after the null byte is the action code associated
37*35209Sbostic  * with the command string.
38*35209Sbostic  *
39*35209Sbostic  * The default commands are described by cmdtable.
40*35209Sbostic  * User-defined commands are read into usertable.
41*35209Sbostic  */
42*35209Sbostic 
43*35209Sbostic #include "less.h"
44*35209Sbostic #include "cmd.h"
45*35209Sbostic 
46*35209Sbostic /*
47*35209Sbostic  * Command table is ordered roughly according to expected
48*35209Sbostic  * frequency of use, so the common commands are near the beginning.
49*35209Sbostic  */
50*35209Sbostic static char cmdtable[] =
51*35209Sbostic {
52*35209Sbostic 	'\r',0,				A_F_LINE,
53*35209Sbostic 	'\n',0,				A_F_LINE,
54*35209Sbostic 	'e',0,				A_F_LINE,
55*35209Sbostic 	'j',0,				A_F_LINE,
56*35209Sbostic 	CONTROL('E'),0,			A_F_LINE,
57*35209Sbostic 	CONTROL('N'),0,			A_F_LINE,
58*35209Sbostic 	'k',0,				A_B_LINE,
59*35209Sbostic 	'y',0,				A_B_LINE,
60*35209Sbostic 	CONTROL('Y'),0,			A_B_LINE,
61*35209Sbostic 	CONTROL('K'),0,			A_B_LINE,
62*35209Sbostic 	CONTROL('P'),0,			A_B_LINE,
63*35209Sbostic 	'd',0,				A_F_SCROLL,
64*35209Sbostic 	CONTROL('D'),0,			A_F_SCROLL,
65*35209Sbostic 	'u',0,				A_B_SCROLL,
66*35209Sbostic 	CONTROL('U'),0,			A_B_SCROLL,
67*35209Sbostic 	' ',0,				A_F_SCREEN,
68*35209Sbostic 	'f',0,				A_F_SCREEN,
69*35209Sbostic 	CONTROL('F'),0,			A_F_SCREEN,
70*35209Sbostic 	CONTROL('V'),0,			A_F_SCREEN,
71*35209Sbostic 	'b',0,				A_B_SCREEN,
72*35209Sbostic 	CONTROL('B'),0,			A_B_SCREEN,
73*35209Sbostic 	CONTROL('['),'v',0,		A_B_SCREEN,
74*35209Sbostic 	'R',0,				A_FREPAINT,
75*35209Sbostic 	'r',0,				A_REPAINT,
76*35209Sbostic 	CONTROL('R'),0,			A_REPAINT,
77*35209Sbostic 	CONTROL('L'),0,			A_REPAINT,
78*35209Sbostic 	'g',0,				A_GOLINE,
79*35209Sbostic 	'<',0,				A_GOLINE,
80*35209Sbostic 	CONTROL('['),'<',0,		A_GOLINE,
81*35209Sbostic 	'p',0,				A_PERCENT,
82*35209Sbostic 	'%',0,				A_PERCENT,
83*35209Sbostic 	'G',0,				A_GOEND,
84*35209Sbostic 	CONTROL('['),'>',0,		A_GOEND,
85*35209Sbostic 	'>',0,				A_GOEND,
86*35209Sbostic 
87*35209Sbostic 	'0',0,				A_DIGIT,
88*35209Sbostic 	'1',0,				A_DIGIT,
89*35209Sbostic 	'2',0,				A_DIGIT,
90*35209Sbostic 	'3',0,				A_DIGIT,
91*35209Sbostic 	'4',0,				A_DIGIT,
92*35209Sbostic 	'5',0,				A_DIGIT,
93*35209Sbostic 	'6',0,				A_DIGIT,
94*35209Sbostic 	'7',0,				A_DIGIT,
95*35209Sbostic 	'8',0,				A_DIGIT,
96*35209Sbostic 	'9',0,				A_DIGIT,
97*35209Sbostic 
98*35209Sbostic 	'=',0,				A_STAT,
99*35209Sbostic 	CONTROL('G'),0,			A_STAT,
100*35209Sbostic 	'/',0,				A_F_SEARCH,
101*35209Sbostic 	'?',0,				A_B_SEARCH,
102*35209Sbostic 	'n',0,				A_AGAIN_SEARCH,
103*35209Sbostic 	'm',0,				A_SETMARK,
104*35209Sbostic 	'\'',0,				A_GOMARK,
105*35209Sbostic 	CONTROL('X'),CONTROL('X'),0,	A_GOMARK,
106*35209Sbostic 	'E',0,				A_EXAMINE,
107*35209Sbostic 	':','e',0,			A_EXAMINE,
108*35209Sbostic 	CONTROL('X'),CONTROL('V'),0,	A_EXAMINE,
109*35209Sbostic 	'N',0,				A_NEXT_FILE,
110*35209Sbostic 	'P',0,				A_PREV_FILE,
111*35209Sbostic 	':','n',0,			A_NEXT_FILE,
112*35209Sbostic 	':','p',0,			A_PREV_FILE,
113*35209Sbostic 	'-',0,				A_TOGGLE_OPTION,
114*35209Sbostic 	'_',0,				A_DISP_OPTION,
115*35209Sbostic 	'v',0,				A_VISUAL,
116*35209Sbostic 	'!',0,				A_SHELL,
117*35209Sbostic 	'+',0,				A_FIRSTCMD,
118*35209Sbostic 
119*35209Sbostic 	'H',0,				A_HELP,
120*35209Sbostic 	'h',0,				A_HELP,
121*35209Sbostic 	'V',0,				A_VERSION,
122*35209Sbostic 	'q',0,				A_QUIT,
123*35209Sbostic 	':','q',0,			A_QUIT,
124*35209Sbostic 	'Z','Z',0,			A_QUIT
125*35209Sbostic };
126*35209Sbostic 
127*35209Sbostic char *cmdendtable = cmdtable + sizeof(cmdtable);
128*35209Sbostic 
129*35209Sbostic static char usertable[MAX_USERCMD];
130*35209Sbostic char *userendtable = usertable;
131*35209Sbostic 
132*35209Sbostic static char kbuf[MAX_CMDLEN+1];
133*35209Sbostic static char *kp = kbuf;
134*35209Sbostic 
135*35209Sbostic /*
136*35209Sbostic  * Decode a command character and return the associated action.
137*35209Sbostic  */
138*35209Sbostic 	public int
139*35209Sbostic cmd_decode(c)
140*35209Sbostic 	int c;
141*35209Sbostic {
142*35209Sbostic 	register int action = A_INVALID;
143*35209Sbostic 
144*35209Sbostic 	/*
145*35209Sbostic 	 * Append the new command character to the command string in kbuf.
146*35209Sbostic 	 */
147*35209Sbostic 	*kp++ = c;
148*35209Sbostic 	*kp = '\0';
149*35209Sbostic 
150*35209Sbostic #if USERFILE
151*35209Sbostic 	/*
152*35209Sbostic 	 * Look first for any user-defined commands.
153*35209Sbostic 	 */
154*35209Sbostic 	action = cmd_search(usertable, userendtable);
155*35209Sbostic #endif
156*35209Sbostic 	/*
157*35209Sbostic 	 * If didn't find user-defined command,
158*35209Sbostic 	 * try the normal default commands.
159*35209Sbostic 	 */
160*35209Sbostic 	if (action == A_INVALID)
161*35209Sbostic 		action = cmd_search(cmdtable, cmdendtable);
162*35209Sbostic 
163*35209Sbostic 	if (action != A_PREFIX)
164*35209Sbostic 		/*
165*35209Sbostic 		 * This is not a prefix character.
166*35209Sbostic 		 */
167*35209Sbostic 		noprefix();
168*35209Sbostic 
169*35209Sbostic 	return (action);
170*35209Sbostic }
171*35209Sbostic 
172*35209Sbostic /*
173*35209Sbostic  * Indicate that we're not in a prefix command
174*35209Sbostic  * by resetting the command buffer pointer.
175*35209Sbostic  */
176*35209Sbostic 	public void
177*35209Sbostic noprefix()
178*35209Sbostic {
179*35209Sbostic 	kp = kbuf;
180*35209Sbostic }
181*35209Sbostic 
182*35209Sbostic /*
183*35209Sbostic  * Search a command table for the current command string (in kbuf).
184*35209Sbostic  */
185*35209Sbostic 	static int
186*35209Sbostic cmd_search(table, endtable)
187*35209Sbostic 	char *table;
188*35209Sbostic 	char *endtable;
189*35209Sbostic {
190*35209Sbostic 	register char *p;
191*35209Sbostic 	register char *q;
192*35209Sbostic 
193*35209Sbostic 	for (p = table, q = kbuf;  p < endtable;  p++, q++)
194*35209Sbostic 	{
195*35209Sbostic 		if (*p == *q)
196*35209Sbostic 		{
197*35209Sbostic 			/*
198*35209Sbostic 			 * Current characters match.
199*35209Sbostic 			 * If we're at the end of the string, we've found it.
200*35209Sbostic 			 * Return the action code, which is the character
201*35209Sbostic 			 * after the null at the end of the string
202*35209Sbostic 			 * in the command table.
203*35209Sbostic 			 */
204*35209Sbostic 			if (*p == '\0')
205*35209Sbostic 				return (p[1]);
206*35209Sbostic 		} else if (*q == '\0')
207*35209Sbostic 		{
208*35209Sbostic 			/*
209*35209Sbostic 			 * Hit the end of the user's command,
210*35209Sbostic 			 * but not the end of the string in the command table.
211*35209Sbostic 			 * The user's command is incomplete.
212*35209Sbostic 			 */
213*35209Sbostic 			return (A_PREFIX);
214*35209Sbostic 		} else
215*35209Sbostic 		{
216*35209Sbostic 			/*
217*35209Sbostic 			 * Not a match.
218*35209Sbostic 			 * Skip ahead to the next command in the
219*35209Sbostic 			 * command table, and reset the pointer
220*35209Sbostic 			 * to the user's command.
221*35209Sbostic 			 */
222*35209Sbostic 			while (*p++ != '\0') ;
223*35209Sbostic 			q = kbuf-1;
224*35209Sbostic 		}
225*35209Sbostic 	}
226*35209Sbostic 	/*
227*35209Sbostic 	 * No match found in the entire command table.
228*35209Sbostic 	 */
229*35209Sbostic 	return (A_INVALID);
230*35209Sbostic }
231*35209Sbostic 
232*35209Sbostic /*
233*35209Sbostic  * Initialize the user command table.
234*35209Sbostic  */
235*35209Sbostic 	public void
236*35209Sbostic init_cmd()
237*35209Sbostic {
238*35209Sbostic #if USERFILE
239*35209Sbostic 	char *filename;
240*35209Sbostic 	char *homedir;
241*35209Sbostic 	int f;
242*35209Sbostic 	int n;
243*35209Sbostic 	extern char *getenv();
244*35209Sbostic 
245*35209Sbostic 	/*
246*35209Sbostic 	 * Try to open "$HOME/.less"
247*35209Sbostic 	 * If we can't, return without doing anything.
248*35209Sbostic 	 */
249*35209Sbostic 	homedir = getenv("HOME");
250*35209Sbostic 	if (homedir == NULL)
251*35209Sbostic 		return;
252*35209Sbostic 	filename = calloc(strlen(homedir)+7, sizeof(char));
253*35209Sbostic 	if (filename == NULL)
254*35209Sbostic 		return;
255*35209Sbostic 	sprintf(filename, "%s/%s", homedir, ".less");
256*35209Sbostic 	f = open(filename, 0);
257*35209Sbostic 	free(filename);
258*35209Sbostic 	if (f < 0)
259*35209Sbostic 		return;
260*35209Sbostic 
261*35209Sbostic 	/*
262*35209Sbostic 	 * Read the file into the user table.
263*35209Sbostic 	 * {{ Minimal error checking is done here.
264*35209Sbostic 	 *    A garbage .less file will produce strange results.
265*35209Sbostic 	 *    To avoid a large amount of error checking code here, we
266*35209Sbostic 	 *    rely on the lesskey program to generate a good .less file. }}
267*35209Sbostic 	 */
268*35209Sbostic 	n = read(f, (char *)usertable, MAX_USERCMD);
269*35209Sbostic 	if (n < 3 || usertable[n-2] != '\0')
270*35209Sbostic 	{
271*35209Sbostic 		/*
272*35209Sbostic 		 * Several error cases are lumped together here:
273*35209Sbostic 		 * - Cannot read user file (n < 0).
274*35209Sbostic 		 * - User file is too short (a valid file must
275*35209Sbostic 		 *   have at least 3 chars: one char command string,
276*35209Sbostic 		 *   the terminating null byte, and the action byte).
277*35209Sbostic 		 * - The final entry in the user file is bad (it
278*35209Sbostic 		 *   doesn't have a null byte in the proper place).
279*35209Sbostic 		 * Many other error cases are not caught, such as
280*35209Sbostic 		 * invalid format in any except the last entry,
281*35209Sbostic 		 * invalid action codes, command strings too long, etc.
282*35209Sbostic 		 */
283*35209Sbostic 		error("invalid user key file");
284*35209Sbostic 		n = 0;
285*35209Sbostic 	}
286*35209Sbostic 	userendtable = usertable + n;
287*35209Sbostic 	close(f);
288*35209Sbostic #endif
289*35209Sbostic }
290