1 /* 2 * Copyright (C) 1984-2023 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 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 usage(void) 112 { 113 fprintf(stderr, "usage: lesskey [-o output] [input]\n"); 114 exit(1); 115 } 116 117 void lesskey_parse_error(char *s) 118 { 119 fprintf(stderr, "%s\n", s); 120 } 121 122 int lstrtoi(char *buf, char **ebuf, int radix) 123 { 124 return (int) strtol(buf, 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(int count, unsigned int 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(char *dirname, 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(char *filename) 158 { 159 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, char **argv) 180 { 181 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, char *buf, int 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, unsigned int val) 262 { 263 char c; 264 265 if (val >= KRADIX*KRADIX) 266 { 267 fprintf(stderr, "error: cannot write %d, max %d\n", 268 val, KRADIX*KRADIX); 269 exit(1); 270 } 271 c = val % KRADIX; 272 fwrite(&c, sizeof(char), 1, fd); 273 c = val / KRADIX; 274 fwrite(&c, sizeof(char), 1, fd); 275 } 276 277 int main(int argc, char *argv[]) 278 { 279 struct lesskey_tables tables; 280 FILE *out; 281 int errors; 282 283 #ifdef WIN32 284 if (getenv("HOME") == NULL) 285 { 286 /* 287 * If there is no HOME environment variable, 288 * try the concatenation of HOMEDRIVE + HOMEPATH. 289 */ 290 char *drive = getenv("HOMEDRIVE"); 291 char *path = getenv("HOMEPATH"); 292 if (drive != NULL && path != NULL) 293 { 294 char *env = (char *) ecalloc(strlen(drive) + 295 strlen(path) + 6, sizeof(char)); 296 strcpy(env, "HOME="); 297 strcat(env, drive); 298 strcat(env, path); 299 putenv(env); 300 } 301 } 302 #endif /* WIN32 */ 303 304 /* 305 * Process command line arguments. 306 */ 307 parse_args(argc, argv); 308 errors = parse_lesskey(infile, &tables); 309 if (errors) 310 { 311 fprintf(stderr, "%d errors; no output produced\n", errors); 312 return (1); 313 } 314 315 fprintf(stderr, "NOTE: lesskey is deprecated.\n It is no longer necessary to run lesskey,\n when using less version 582 and later.\n"); 316 317 /* 318 * Write the output file. 319 * If no output file was specified, use "$HOME/.less" 320 */ 321 if (outfile == NULL) 322 outfile = getenv("LESSKEY"); 323 if (outfile == NULL) 324 outfile = homefile(LESSKEYFILE); 325 if ((out = fopen(outfile, "wb")) == NULL) 326 { 327 #if HAVE_PERROR 328 perror(outfile); 329 #else 330 fprintf(stderr, "Cannot open %s\n", outfile); 331 #endif 332 return (1); 333 } 334 335 /* File header */ 336 fputbytes(out, fileheader, sizeof(fileheader)); 337 338 /* Command key section */ 339 fputbytes(out, cmdsection, sizeof(cmdsection)); 340 fputint(out, tables.cmdtable.buf.end); 341 fputbytes(out, (char *)tables.cmdtable.buf.data, tables.cmdtable.buf.end); 342 /* Edit key section */ 343 fputbytes(out, editsection, sizeof(editsection)); 344 fputint(out, tables.edittable.buf.end); 345 fputbytes(out, (char *)tables.edittable.buf.data, tables.edittable.buf.end); 346 347 /* Environment variable section */ 348 fputbytes(out, varsection, sizeof(varsection)); 349 fputint(out, tables.vartable.buf.end); 350 fputbytes(out, (char *)tables.vartable.buf.data, tables.vartable.buf.end); 351 352 /* File trailer */ 353 fputbytes(out, endsection, sizeof(endsection)); 354 fputbytes(out, filetrailer, sizeof(filetrailer)); 355 fclose(out); 356 return (0); 357 } 358