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