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 | |
dd4dee41 | 21 | static char sccsid[] = "@(#)decode.c 5.5 (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, | |
bfe13c81 KB |
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 | ||
bfe13c81 KB |
147 | /* |
148 | * Look first for any user-defined commands. | |
149 | */ | |
150 | action = cmd_search(usertable, userendtable); | |
bfe13c81 KB |
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 | { | |
bfe13c81 KB |
233 | char *homedir; |
234 | int f; | |
235 | int n; | |
e60bf546 | 236 | char filename[MAXPATHLEN]; |
bfe13c81 KB |
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; | |
e60bf546 KB |
246 | (void)sprintf(filename, "%s/%s", homedir, ".less"); |
247 | f = open(filename, O_RDONLY); | |
bfe13c81 KB |
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); | |
bfe13c81 | 278 | } |