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