Commit | Line | Data |
---|---|---|
bfe13c81 KB |
1 | /* |
2 | * Copyright (c) 1988 Mark Nudleman | |
3 | * Copyright (c) 1988 Regents of the University of California. | |
4 | * All rights reserved. | |
5 | * | |
bfe13c81 KB |
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 | |
a942b40b KB |
11 | * by Mark Nudleman and the University of California, Berkeley. The |
12 | * name of Mark Nudleman or the | |
bfe13c81 KB |
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 | |
a942b40b | 21 | static char sccsid[] = "@(#)decode.c 5.4 (Berkeley) %G%"; |
bfe13c81 KB |
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 | 'V',0, A_VERSION, | |
120 | 'q',0, A_QUIT, | |
121 | ':','q',0, A_QUIT, | |
122 | 'Z','Z',0, A_QUIT | |
123 | }; | |
124 | ||
125 | char *cmdendtable = cmdtable + sizeof(cmdtable); | |
126 | ||
127 | static char usertable[MAX_USERCMD]; | |
128 | char *userendtable = usertable; | |
129 | ||
130 | static char kbuf[MAX_CMDLEN+1]; | |
131 | static char *kp = kbuf; | |
132 | ||
133 | /* | |
134 | * Decode a command character and return the associated action. | |
135 | */ | |
136 | public int | |
137 | cmd_decode(c) | |
138 | int c; | |
139 | { | |
140 | register int action = A_INVALID; | |
141 | ||
142 | /* | |
143 | * Append the new command character to the command string in kbuf. | |
144 | */ | |
145 | *kp++ = c; | |
146 | *kp = '\0'; | |
147 | ||
bfe13c81 KB |
148 | /* |
149 | * Look first for any user-defined commands. | |
150 | */ | |
151 | action = cmd_search(usertable, userendtable); | |
bfe13c81 KB |
152 | /* |
153 | * If didn't find user-defined command, | |
154 | * try the normal default commands. | |
155 | */ | |
156 | if (action == A_INVALID) | |
157 | action = cmd_search(cmdtable, cmdendtable); | |
158 | ||
159 | if (action != A_PREFIX) | |
160 | /* | |
161 | * This is not a prefix character. | |
162 | */ | |
163 | noprefix(); | |
164 | ||
165 | return (action); | |
166 | } | |
167 | ||
168 | /* | |
169 | * Indicate that we're not in a prefix command | |
170 | * by resetting the command buffer pointer. | |
171 | */ | |
172 | public void | |
173 | noprefix() | |
174 | { | |
175 | kp = kbuf; | |
176 | } | |
177 | ||
178 | /* | |
179 | * Search a command table for the current command string (in kbuf). | |
180 | */ | |
181 | static int | |
182 | cmd_search(table, endtable) | |
183 | char *table; | |
184 | char *endtable; | |
185 | { | |
186 | register char *p; | |
187 | register char *q; | |
188 | ||
189 | for (p = table, q = kbuf; p < endtable; p++, q++) | |
190 | { | |
191 | if (*p == *q) | |
192 | { | |
193 | /* | |
194 | * Current characters match. | |
195 | * If we're at the end of the string, we've found it. | |
196 | * Return the action code, which is the character | |
197 | * after the null at the end of the string | |
198 | * in the command table. | |
199 | */ | |
200 | if (*p == '\0') | |
201 | return (p[1]); | |
202 | } else if (*q == '\0') | |
203 | { | |
204 | /* | |
205 | * Hit the end of the user's command, | |
206 | * but not the end of the string in the command table. | |
207 | * The user's command is incomplete. | |
208 | */ | |
209 | return (A_PREFIX); | |
210 | } else | |
211 | { | |
212 | /* | |
213 | * Not a match. | |
214 | * Skip ahead to the next command in the | |
215 | * command table, and reset the pointer | |
216 | * to the user's command. | |
217 | */ | |
218 | while (*p++ != '\0') ; | |
219 | q = kbuf-1; | |
220 | } | |
221 | } | |
222 | /* | |
223 | * No match found in the entire command table. | |
224 | */ | |
225 | return (A_INVALID); | |
226 | } | |
227 | ||
228 | /* | |
229 | * Initialize the user command table. | |
230 | */ | |
231 | public void | |
232 | init_cmd() | |
233 | { | |
bfe13c81 KB |
234 | char *homedir; |
235 | int f; | |
236 | int n; | |
e60bf546 | 237 | char filename[MAXPATHLEN]; |
bfe13c81 KB |
238 | extern char *getenv(); |
239 | ||
240 | /* | |
241 | * Try to open "$HOME/.less" | |
242 | * If we can't, return without doing anything. | |
243 | */ | |
244 | homedir = getenv("HOME"); | |
245 | if (homedir == NULL) | |
246 | return; | |
e60bf546 KB |
247 | (void)sprintf(filename, "%s/%s", homedir, ".less"); |
248 | f = open(filename, O_RDONLY); | |
bfe13c81 KB |
249 | if (f < 0) |
250 | return; | |
251 | ||
252 | /* | |
253 | * Read the file into the user table. | |
254 | * {{ Minimal error checking is done here. | |
255 | * A garbage .less file will produce strange results. | |
256 | * To avoid a large amount of error checking code here, we | |
257 | * rely on the lesskey program to generate a good .less file. }} | |
258 | */ | |
259 | n = read(f, (char *)usertable, MAX_USERCMD); | |
260 | if (n < 3 || usertable[n-2] != '\0') | |
261 | { | |
262 | /* | |
263 | * Several error cases are lumped together here: | |
264 | * - Cannot read user file (n < 0). | |
265 | * - User file is too short (a valid file must | |
266 | * have at least 3 chars: one char command string, | |
267 | * the terminating null byte, and the action byte). | |
268 | * - The final entry in the user file is bad (it | |
269 | * doesn't have a null byte in the proper place). | |
270 | * Many other error cases are not caught, such as | |
271 | * invalid format in any except the last entry, | |
272 | * invalid action codes, command strings too long, etc. | |
273 | */ | |
274 | error("invalid user key file"); | |
275 | n = 0; | |
276 | } | |
277 | userendtable = usertable + n; | |
278 | close(f); | |
bfe13c81 | 279 | } |