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 char copyright[] = 22 "@(#) Copyright (c) 1988 Mark Nudleman.\n\ 23 @(#) Copyright (c) 1988 Regents of the University of California.\n\ 24 All rights reserved.\n"; 25 #endif /* not lint */ 26 27 #ifndef lint 28 static char sccsid[] = "@(#)lesskey.c 5.3 (Berkeley) 07/25/88"; 29 #endif /* not lint */ 30 31 /* 32 * lesskey [-o output] [input] 33 * 34 * Make a .less file. 35 * If no input file is specified, standard input is used. 36 * If no output file is specified, $HOME/.less is used. 37 * 38 * The .less file is used to specify (to "less") user-defined 39 * key bindings. Basically any sequence of 1 to MAX_CMDLEN 40 * keystrokes may be bound to an existing less function. 41 * 42 * The input file is an ascii file consisting of a 43 * sequence of lines of the form: 44 * string <whitespace> action <newline> 45 * 46 * "string" is a sequence of command characters which form 47 * the new user-defined command. The command 48 * characters may be: 49 * 1. The actual character itself. 50 * 2. A character preceeded by ^ to specify a 51 * control character (e.g. ^X means control-X). 52 * 3. Any character (other than an octal digit) preceeded by 53 * a \ to specify the character itself (characters which 54 * must be preceeded by \ include ^, \, and whitespace. 55 * 4. A backslash followed by one to three octal digits 56 * to specify a character by its octal value. 57 * "action" is the name of a "less" action, from the table below. 58 * 59 * Blank lines and lines which start with # are ignored. 60 * 61 * 62 * The output file is a non-ascii file, consisting of 63 * zero or more byte sequences of the form: 64 * string <0> <action> 65 * 66 * "string" is the command string. 67 * "<0>" is one null byte. 68 * "<action>" is one byte containing the action code (the A_xxx value). 69 * 70 * 71 * Revision history 72 * 73 * v1: Initial version. 10/13/87 mark 74 */ 75 76 #include <stdio.h> 77 #include "less.h" 78 #include "cmd.h" 79 80 char usertable[MAX_USERCMD]; 81 82 struct cmdname 83 { 84 char *cn_name; 85 int cn_action; 86 } cmdnames[] = 87 { 88 "back-line", A_B_LINE, 89 "back-screen", A_B_SCREEN, 90 "back-scroll", A_B_SCROLL, 91 "back-search", A_B_SEARCH, 92 "debug", A_DEBUG, 93 "display-flag", A_DISP_OPTION, 94 "display-option", A_DISP_OPTION, 95 "end", A_GOEND, 96 "examine", A_EXAMINE, 97 "first-cmd", A_FIRSTCMD, 98 "firstcmd", A_FIRSTCMD, 99 "flush-repaint", A_FREPAINT, 100 "forw-line", A_F_LINE, 101 "forw-screen", A_F_SCREEN, 102 "forw-scroll", A_F_SCROLL, 103 "forw-search", A_F_SEARCH, 104 "goto-end", A_GOEND, 105 "goto-line", A_GOLINE, 106 "goto-mark", A_GOMARK, 107 "help", A_HELP, 108 "invalid", A_NOACTION, 109 "next-file", A_NEXT_FILE, 110 "noaction", A_NOACTION, 111 "percent", A_PERCENT, 112 "prev-file", A_PREV_FILE, 113 "quit", A_QUIT, 114 "repaint", A_REPAINT, 115 "repaint-flush", A_FREPAINT, 116 "repeat-search", A_AGAIN_SEARCH, 117 "set-mark", A_SETMARK, 118 "shell", A_SHELL, 119 "status", A_STAT, 120 "toggle-flag", A_TOGGLE_OPTION, 121 "toggle-option", A_TOGGLE_OPTION, 122 "version", A_VERSION, 123 "visual", A_VISUAL, 124 NULL, 0 125 }; 126 127 main(argc, argv) 128 int argc; 129 char *argv[]; 130 { 131 char *p; /* {{ Can't be register since we use &p }} */ 132 register char *up; /* Pointer into usertable */ 133 FILE *desc; /* Description file (input) */ 134 FILE *out; /* Output file */ 135 int linenum; /* Line number in input file */ 136 char *currcmd; /* Start of current command string */ 137 int errors; 138 int i; 139 char line[100]; 140 char *outfile; 141 142 extern char *getenv(), *strcat(), *strcpy(); 143 144 /* 145 * Process command line arguments. 146 */ 147 outfile = NULL; 148 while (--argc > 0 && **(++argv) == '-') 149 { 150 switch (argv[0][1]) 151 { 152 case 'o': 153 outfile = &argv[0][2]; 154 if (*outfile == '\0') 155 { 156 if (--argc <= 0) 157 usage(); 158 outfile = *(++argv); 159 } 160 break; 161 default: 162 usage(); 163 } 164 } 165 if (argc > 1) 166 usage(); 167 168 169 /* 170 * Open the input file, or use standard input if none specified. 171 */ 172 if (argc > 0) 173 { 174 if ((desc = fopen(*argv, "r")) == NULL) 175 { 176 perror(*argv); 177 exit(1); 178 } 179 } else 180 desc = stdin; 181 182 /* 183 * Read the input file, one line at a time. 184 * Each line consists of a command string, 185 * followed by white space, followed by an action name. 186 */ 187 linenum = 0; 188 errors = 0; 189 up = usertable; 190 while (fgets(line, sizeof(line), desc) != NULL) 191 { 192 ++linenum; 193 194 /* 195 * Skip leading white space. 196 * Replace the final newline with a null byte. 197 * Ignore blank lines and comment lines. 198 */ 199 p = line; 200 while (*p == ' ' || *p == '\t') 201 ++p; 202 for (i = 0; p[i] != '\n' && p[i] != '\0'; i++) 203 ; 204 p[i] = '\0'; 205 if (*p == '#' || *p == '\0') 206 continue; 207 208 /* 209 * Parse the command string and store it in the usertable. 210 */ 211 currcmd = up; 212 do 213 { 214 if (up >= usertable + MAX_USERCMD) 215 { 216 fprintf(stderr, "too many commands, line %d\n", 217 linenum); 218 exit(1); 219 } 220 if (up >= currcmd + MAX_CMDLEN) 221 { 222 fprintf(stderr, "command too long on line %d\n", 223 linenum); 224 errors++; 225 break; 226 } 227 228 *up++ = tchar(&p); 229 230 } while (*p != ' ' && *p != '\t' && *p != '\0'); 231 232 /* 233 * Terminate the command string with a null byte. 234 */ 235 *up++ = '\0'; 236 237 /* 238 * Skip white space between the command string 239 * and the action name. 240 * Terminate the action name if it is followed 241 * by whitespace or a # comment. 242 */ 243 if (*p == '\0') 244 { 245 fprintf(stderr, "missing whitespace on line %d\n", 246 linenum); 247 errors++; 248 continue; 249 } 250 while (*p == ' ' || *p == '\t') 251 ++p; 252 for (i = 0; p[i] != ' ' && p[i] != '\t' && 253 p[i] != '#' && p[i] != '\0'; i++) 254 ; 255 p[i] = '\0'; 256 257 /* 258 * Parse the action name and store it in the usertable. 259 */ 260 for (i = 0; cmdnames[i].cn_name != NULL; i++) 261 if (strcmp(cmdnames[i].cn_name, p) == 0) 262 break; 263 if (cmdnames[i].cn_name == NULL) 264 { 265 fprintf(stderr, "unknown action <%s> on line %d\n", 266 p, linenum); 267 errors++; 268 continue; 269 } 270 *up++ = cmdnames[i].cn_action; 271 } 272 273 if (errors > 0) 274 { 275 fprintf(stderr, "%d errors; no output produced\n", errors); 276 exit(1); 277 } 278 279 /* 280 * Write the output file. 281 * If no output file was specified, use "$HOME/.less" 282 */ 283 if (outfile == NULL) 284 { 285 p = getenv("HOME"); 286 if (p == NULL) 287 { 288 fprintf(stderr, "cannot find $HOME\n"); 289 exit(1); 290 } 291 strcpy(line, p); 292 strcat(line, "/.less"); 293 outfile = line; 294 } 295 if ((out = fopen(outfile, "w")) == NULL) 296 perror(outfile); 297 else 298 fwrite((char *)usertable, 1, up-usertable, out); 299 } 300 301 /* 302 * Parse one character of the command string. 303 */ 304 tchar(pp) 305 char **pp; 306 { 307 register char *p; 308 register char ch; 309 register int i; 310 311 p = *pp; 312 switch (*p) 313 { 314 case '\\': 315 if (*++p >= '0' && *p <= '7') 316 { 317 /* 318 * Parse an octal number. 319 */ 320 ch = 0; 321 i = 0; 322 do 323 ch = 8*ch + (*p - '0'); 324 while (*++p >= '0' && *p <= '7' && ++i < 3); 325 *pp = p; 326 return (ch); 327 } 328 /* 329 * Backslash followed by a char just means that char. 330 */ 331 *pp = p+1; 332 return (*p); 333 case '^': 334 /* 335 * Carat means CONTROL. 336 */ 337 *pp = p+2; 338 return (CONTROL(p[1])); 339 } 340 *pp = p+1; 341 return (*p); 342 } 343 344 usage() 345 { 346 fprintf(stderr, "usage: lesskey [-o output] [input]\n"); 347 exit(1); 348 } 349