1 /* 2 * Copyright (C) 1984-2021 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 void * 126 ecalloc(count, size) 127 int count; 128 unsigned int size; 129 { 130 void *p; 131 132 p = calloc(count, size); 133 if (p != NULL) 134 return (p); 135 fprintf(stderr, "lesskey: cannot allocate %d bytes of memory\n", count*size); 136 exit(1); 137 } 138 139 static char * 140 mkpathname(dirname, filename) 141 char *dirname; 142 char *filename; 143 { 144 char *pathname; 145 146 pathname = ecalloc(strlen(dirname) + strlen(filename) + 2, sizeof(char)); 147 strcpy(pathname, dirname); 148 strcat(pathname, PATHNAME_SEP); 149 strcat(pathname, filename); 150 return (pathname); 151 } 152 153 /* 154 * Figure out the name of a default file (in the user's HOME directory). 155 */ 156 char * 157 homefile(filename) 158 char *filename; 159 { 160 char *p; 161 char *pathname; 162 163 if ((p = getenv("HOME")) != NULL && *p != '\0') 164 pathname = mkpathname(p, filename); 165 #if OS2 166 else if ((p = getenv("INIT")) != NULL && *p != '\0') 167 pathname = mkpathname(p, filename); 168 #endif 169 else 170 { 171 fprintf(stderr, "cannot find $HOME - using current directory\n"); 172 pathname = mkpathname(".", filename); 173 } 174 return (pathname); 175 } 176 177 /* 178 * Parse command line arguments. 179 */ 180 static void 181 parse_args(argc, argv) 182 int argc; 183 char **argv; 184 { 185 char *arg; 186 187 outfile = NULL; 188 while (--argc > 0) 189 { 190 arg = *++argv; 191 if (arg[0] != '-') 192 /* Arg does not start with "-"; it's not an option. */ 193 break; 194 if (arg[1] == '\0') 195 /* "-" means standard input. */ 196 break; 197 if (arg[1] == '-' && arg[2] == '\0') 198 { 199 /* "--" means end of options. */ 200 argc--; 201 argv++; 202 break; 203 } 204 switch (arg[1]) 205 { 206 case '-': 207 if (strncmp(arg, "--output", 8) == 0) 208 { 209 if (arg[8] == '\0') 210 outfile = &arg[8]; 211 else if (arg[8] == '=') 212 outfile = &arg[9]; 213 else 214 usage(); 215 goto opt_o; 216 } 217 if (strcmp(arg, "--version") == 0) 218 { 219 goto opt_V; 220 } 221 usage(); 222 break; 223 case 'o': 224 outfile = &argv[0][2]; 225 opt_o: 226 if (*outfile == '\0') 227 { 228 if (--argc <= 0) 229 usage(); 230 outfile = *(++argv); 231 } 232 break; 233 case 'V': 234 opt_V: 235 printf("lesskey version %s\n", version); 236 exit(0); 237 default: 238 usage(); 239 } 240 } 241 if (argc > 1) 242 usage(); 243 /* 244 * Open the input file, or use DEF_LESSKEYINFILE if none specified. 245 */ 246 if (argc > 0) 247 infile = *argv; 248 } 249 250 /* 251 * Output some bytes. 252 */ 253 static void 254 fputbytes(fd, buf, len) 255 FILE *fd; 256 char *buf; 257 int len; 258 { 259 while (len-- > 0) 260 { 261 fwrite(buf, sizeof(char), 1, fd); 262 buf++; 263 } 264 } 265 266 /* 267 * Output an integer, in special KRADIX form. 268 */ 269 static void 270 fputint(fd, val) 271 FILE *fd; 272 unsigned int val; 273 { 274 char c; 275 276 if (val >= KRADIX*KRADIX) 277 { 278 fprintf(stderr, "error: cannot write %d, max %d\n", 279 val, KRADIX*KRADIX); 280 exit(1); 281 } 282 c = val % KRADIX; 283 fwrite(&c, sizeof(char), 1, fd); 284 c = val / KRADIX; 285 fwrite(&c, sizeof(char), 1, fd); 286 } 287 288 int 289 main(argc, argv) 290 int argc; 291 char *argv[]; 292 { 293 struct lesskey_tables tables; 294 FILE *out; 295 int errors; 296 297 #ifdef WIN32 298 if (getenv("HOME") == NULL) 299 { 300 /* 301 * If there is no HOME environment variable, 302 * try the concatenation of HOMEDRIVE + HOMEPATH. 303 */ 304 char *drive = getenv("HOMEDRIVE"); 305 char *path = getenv("HOMEPATH"); 306 if (drive != NULL && path != NULL) 307 { 308 char *env = (char *) ecalloc(strlen(drive) + 309 strlen(path) + 6, sizeof(char)); 310 strcpy(env, "HOME="); 311 strcat(env, drive); 312 strcat(env, path); 313 putenv(env); 314 } 315 } 316 #endif /* WIN32 */ 317 318 /* 319 * Process command line arguments. 320 */ 321 parse_args(argc, argv); 322 errors = parse_lesskey(infile, &tables); 323 if (errors) 324 { 325 fprintf(stderr, "%d errors; no output produced\n", errors); 326 return (1); 327 } 328 329 fprintf(stderr, "NOTE: lesskey is deprecated.\n It is no longer necessary to run lesskey,\n when using less version 582 and later.\n"); 330 331 /* 332 * Write the output file. 333 * If no output file was specified, use "$HOME/.less" 334 */ 335 if (outfile == NULL) 336 outfile = getenv("LESSKEY"); 337 if (outfile == NULL) 338 outfile = homefile(LESSKEYFILE); 339 if ((out = fopen(outfile, "wb")) == NULL) 340 { 341 #if HAVE_PERROR 342 perror(outfile); 343 #else 344 fprintf(stderr, "Cannot open %s\n", outfile); 345 #endif 346 return (1); 347 } 348 349 /* File header */ 350 fputbytes(out, fileheader, sizeof(fileheader)); 351 352 /* Command key section */ 353 fputbytes(out, cmdsection, sizeof(cmdsection)); 354 fputint(out, tables.cmdtable.buf.end); 355 fputbytes(out, (char *)tables.cmdtable.buf.data, tables.cmdtable.buf.end); 356 /* Edit key section */ 357 fputbytes(out, editsection, sizeof(editsection)); 358 fputint(out, tables.edittable.buf.end); 359 fputbytes(out, (char *)tables.edittable.buf.data, tables.edittable.buf.end); 360 361 /* Environment variable section */ 362 fputbytes(out, varsection, sizeof(varsection)); 363 fputint(out, tables.vartable.buf.end); 364 fputbytes(out, (char *)tables.vartable.buf.data, tables.vartable.buf.end); 365 366 /* File trailer */ 367 fputbytes(out, endsection, sizeof(endsection)); 368 fputbytes(out, filetrailer, sizeof(filetrailer)); 369 return (0); 370 } 371