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