1 /* 2 * Copyright (C) 1984-2022 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information, see the README file. 8 */ 9 10 11 /* 12 * lesskey [-o output] [input] 13 * 14 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 15 * 16 * Make a .less file. 17 * If no input file is specified, standard input is used. 18 * If no output file is specified, $HOME/.less is used. 19 * 20 * The .less file is used to specify (to "less") user-defined 21 * key bindings. Basically any sequence of 1 to MAX_CMDLEN 22 * keystrokes may be bound to an existing less function. 23 * 24 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 25 * 26 * The input file is an ascii file consisting of a 27 * sequence of lines of the form: 28 * string <whitespace> action [chars] <newline> 29 * 30 * "string" is a sequence of command characters which form 31 * the new user-defined command. The command 32 * characters may be: 33 * 1. The actual character itself. 34 * 2. A character preceded by ^ to specify a 35 * control character (e.g. ^X means control-X). 36 * 3. A backslash followed by one to three octal digits 37 * to specify a character by its octal value. 38 * 4. A backslash followed by b, e, n, r or t 39 * to specify \b, ESC, \n, \r or \t, respectively. 40 * 5. Any character (other than those mentioned above) preceded 41 * by a \ to specify the character itself (characters which 42 * must be preceded by \ include ^, \, and whitespace. 43 * "action" is the name of a "less" action, from the table below. 44 * "chars" is an optional sequence of characters which is treated 45 * as keyboard input after the command is executed. 46 * 47 * Blank lines and lines which start with # are ignored, 48 * except for the special control lines: 49 * #command Signals the beginning of the command 50 * keys section. 51 * #line-edit Signals the beginning of the line-editing 52 * keys section. 53 * #env Signals the beginning of the environment 54 * variable section. 55 * #stop Stops command parsing in less; 56 * causes all default keys to be disabled. 57 * 58 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 59 * 60 * The output file is a non-ascii file, consisting of a header, 61 * one or more sections, and a trailer. 62 * Each section begins with a section header, a section length word 63 * and the section data. Normally there are three sections: 64 * CMD_SECTION Definition of command keys. 65 * EDIT_SECTION Definition of editing keys. 66 * END_SECTION A special section header, with no 67 * length word or section data. 68 * 69 * Section data consists of zero or more byte sequences of the form: 70 * string <0> <action> 71 * or 72 * string <0> <action|A_EXTRA> chars <0> 73 * 74 * "string" is the command string. 75 * "<0>" is one null byte. 76 * "<action>" is one byte containing the action code (the A_xxx value). 77 * If action is ORed with A_EXTRA, the action byte is followed 78 * by the null-terminated "chars" string. 79 * 80 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 81 */ 82 83 #include <stdio.h> 84 #include <string.h> 85 #include <stdlib.h> 86 #include "lesskey.h" 87 #include "cmd.h" 88 #include "defines.h" 89 90 char fileheader[] = { 91 C0_LESSKEY_MAGIC, 92 C1_LESSKEY_MAGIC, 93 C2_LESSKEY_MAGIC, 94 C3_LESSKEY_MAGIC 95 }; 96 char filetrailer[] = { 97 C0_END_LESSKEY_MAGIC, 98 C1_END_LESSKEY_MAGIC, 99 C2_END_LESSKEY_MAGIC 100 }; 101 char cmdsection[1] = { CMD_SECTION }; 102 char editsection[1] = { EDIT_SECTION }; 103 char varsection[1] = { VAR_SECTION }; 104 char endsection[1] = { END_SECTION }; 105 106 char *infile = NULL; 107 char *outfile = NULL ; 108 109 extern char version[]; 110 111 static void 112 usage(void) 113 { 114 fprintf(stderr, "usage: lesskey [-o output] [input]\n"); 115 exit(1); 116 } 117 118 void 119 lesskey_parse_error(s) 120 char *s; 121 { 122 fprintf(stderr, "%s\n", s); 123 } 124 125 int 126 lstrtoi(buf, ebuf) 127 char *buf; 128 char **ebuf; 129 { 130 return (int) strtol(buf, ebuf, 10); 131 } 132 133 void * 134 ecalloc(count, size) 135 int count; 136 unsigned int size; 137 { 138 void *p; 139 140 p = calloc(count, size); 141 if (p != NULL) 142 return (p); 143 fprintf(stderr, "lesskey: cannot allocate %d bytes of memory\n", count*size); 144 exit(1); 145 } 146 147 static char * 148 mkpathname(dirname, filename) 149 char *dirname; 150 char *filename; 151 { 152 char *pathname; 153 154 pathname = ecalloc(strlen(dirname) + strlen(filename) + 2, sizeof(char)); 155 strcpy(pathname, dirname); 156 strcat(pathname, PATHNAME_SEP); 157 strcat(pathname, filename); 158 return (pathname); 159 } 160 161 /* 162 * Figure out the name of a default file (in the user's HOME directory). 163 */ 164 char * 165 homefile(filename) 166 char *filename; 167 { 168 char *p; 169 char *pathname; 170 171 if ((p = getenv("HOME")) != NULL && *p != '\0') 172 pathname = mkpathname(p, filename); 173 #if OS2 174 else if ((p = getenv("INIT")) != NULL && *p != '\0') 175 pathname = mkpathname(p, filename); 176 #endif 177 else 178 { 179 fprintf(stderr, "cannot find $HOME - using current directory\n"); 180 pathname = mkpathname(".", filename); 181 } 182 return (pathname); 183 } 184 185 /* 186 * Parse command line arguments. 187 */ 188 static void 189 parse_args(argc, argv) 190 int argc; 191 char **argv; 192 { 193 char *arg; 194 195 outfile = NULL; 196 while (--argc > 0) 197 { 198 arg = *++argv; 199 if (arg[0] != '-') 200 /* Arg does not start with "-"; it's not an option. */ 201 break; 202 if (arg[1] == '\0') 203 /* "-" means standard input. */ 204 break; 205 if (arg[1] == '-' && arg[2] == '\0') 206 { 207 /* "--" means end of options. */ 208 argc--; 209 argv++; 210 break; 211 } 212 switch (arg[1]) 213 { 214 case '-': 215 if (strncmp(arg, "--output", 8) == 0) 216 { 217 if (arg[8] == '\0') 218 outfile = &arg[8]; 219 else if (arg[8] == '=') 220 outfile = &arg[9]; 221 else 222 usage(); 223 goto opt_o; 224 } 225 if (strcmp(arg, "--version") == 0) 226 { 227 goto opt_V; 228 } 229 usage(); 230 break; 231 case 'o': 232 outfile = &argv[0][2]; 233 opt_o: 234 if (*outfile == '\0') 235 { 236 if (--argc <= 0) 237 usage(); 238 outfile = *(++argv); 239 } 240 break; 241 case 'V': 242 opt_V: 243 printf("lesskey version %s\n", version); 244 exit(0); 245 default: 246 usage(); 247 } 248 } 249 if (argc > 1) 250 usage(); 251 /* 252 * Open the input file, or use DEF_LESSKEYINFILE if none specified. 253 */ 254 if (argc > 0) 255 infile = *argv; 256 } 257 258 /* 259 * Output some bytes. 260 */ 261 static void 262 fputbytes(fd, buf, len) 263 FILE *fd; 264 char *buf; 265 int len; 266 { 267 while (len-- > 0) 268 { 269 fwrite(buf, sizeof(char), 1, fd); 270 buf++; 271 } 272 } 273 274 /* 275 * Output an integer, in special KRADIX form. 276 */ 277 static void 278 fputint(fd, val) 279 FILE *fd; 280 unsigned int val; 281 { 282 char c; 283 284 if (val >= KRADIX*KRADIX) 285 { 286 fprintf(stderr, "error: cannot write %d, max %d\n", 287 val, KRADIX*KRADIX); 288 exit(1); 289 } 290 c = val % KRADIX; 291 fwrite(&c, sizeof(char), 1, fd); 292 c = val / KRADIX; 293 fwrite(&c, sizeof(char), 1, fd); 294 } 295 296 int 297 main(argc, argv) 298 int argc; 299 char *argv[]; 300 { 301 struct lesskey_tables tables; 302 FILE *out; 303 int errors; 304 305 #ifdef WIN32 306 if (getenv("HOME") == NULL) 307 { 308 /* 309 * If there is no HOME environment variable, 310 * try the concatenation of HOMEDRIVE + HOMEPATH. 311 */ 312 char *drive = getenv("HOMEDRIVE"); 313 char *path = getenv("HOMEPATH"); 314 if (drive != NULL && path != NULL) 315 { 316 char *env = (char *) ecalloc(strlen(drive) + 317 strlen(path) + 6, sizeof(char)); 318 strcpy(env, "HOME="); 319 strcat(env, drive); 320 strcat(env, path); 321 putenv(env); 322 } 323 } 324 #endif /* WIN32 */ 325 326 /* 327 * Process command line arguments. 328 */ 329 parse_args(argc, argv); 330 errors = parse_lesskey(infile, &tables); 331 if (errors) 332 { 333 fprintf(stderr, "%d errors; no output produced\n", errors); 334 return (1); 335 } 336 337 fprintf(stderr, "NOTE: lesskey is deprecated.\n It is no longer necessary to run lesskey,\n when using less version 582 and later.\n"); 338 339 /* 340 * Write the output file. 341 * If no output file was specified, use "$HOME/.less" 342 */ 343 if (outfile == NULL) 344 outfile = getenv("LESSKEY"); 345 if (outfile == NULL) 346 outfile = homefile(LESSKEYFILE); 347 if ((out = fopen(outfile, "wb")) == NULL) 348 { 349 #if HAVE_PERROR 350 perror(outfile); 351 #else 352 fprintf(stderr, "Cannot open %s\n", outfile); 353 #endif 354 return (1); 355 } 356 357 /* File header */ 358 fputbytes(out, fileheader, sizeof(fileheader)); 359 360 /* Command key section */ 361 fputbytes(out, cmdsection, sizeof(cmdsection)); 362 fputint(out, tables.cmdtable.buf.end); 363 fputbytes(out, (char *)tables.cmdtable.buf.data, tables.cmdtable.buf.end); 364 /* Edit key section */ 365 fputbytes(out, editsection, sizeof(editsection)); 366 fputint(out, tables.edittable.buf.end); 367 fputbytes(out, (char *)tables.edittable.buf.data, tables.edittable.buf.end); 368 369 /* Environment variable section */ 370 fputbytes(out, varsection, sizeof(varsection)); 371 fputint(out, tables.vartable.buf.end); 372 fputbytes(out, (char *)tables.vartable.buf.data, tables.vartable.buf.end); 373 374 /* File trailer */ 375 fputbytes(out, endsection, sizeof(endsection)); 376 fputbytes(out, filetrailer, sizeof(filetrailer)); 377 fclose(out); 378 return (0); 379 } 380