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