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