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