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