/* * Copyright (C) 1984-2024 Mark Nudelman * * You may distribute under the terms of either the GNU General Public * License or the Less License, as specified in the README file. * * For more information, see the README file. */ /* * lesskey [-o output] [input] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Make a .less file. * If no input file is specified, standard input is used. * If no output file is specified, $HOME/.less is used. * * The .less file is used to specify (to "less") user-defined * key bindings. Basically any sequence of 1 to MAX_CMDLEN * keystrokes may be bound to an existing less function. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * The input file is an ascii file consisting of a * sequence of lines of the form: * string action [chars] * * "string" is a sequence of command characters which form * the new user-defined command. The command * characters may be: * 1. The actual character itself. * 2. A character preceded by ^ to specify a * control character (e.g. ^X means control-X). * 3. A backslash followed by one to three octal digits * to specify a character by its octal value. * 4. A backslash followed by b, e, n, r or t * to specify \b, ESC, \n, \r or \t, respectively. * 5. Any character (other than those mentioned above) preceded * by a \ to specify the character itself (characters which * must be preceded by \ include ^, \, and whitespace. * "action" is the name of a "less" action, from the table below. * "chars" is an optional sequence of characters which is treated * as keyboard input after the command is executed. * * Blank lines and lines which start with # are ignored, * except for the special control lines: * #command Signals the beginning of the command * keys section. * #line-edit Signals the beginning of the line-editing * keys section. * #env Signals the beginning of the environment * variable section. * #stop Stops command parsing in less; * causes all default keys to be disabled. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * The output file is a non-ascii file, consisting of a header, * one or more sections, and a trailer. * Each section begins with a section header, a section length word * and the section data. Normally there are three sections: * CMD_SECTION Definition of command keys. * EDIT_SECTION Definition of editing keys. * END_SECTION A special section header, with no * length word or section data. * * Section data consists of zero or more byte sequences of the form: * string <0> * or * string <0> chars <0> * * "string" is the command string. * "<0>" is one null byte. * "" is one byte containing the action code (the A_xxx value). * If action is ORed with A_EXTRA, the action byte is followed * by the null-terminated "chars" string. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "defines.h" #include #include #include #include "lesskey.h" #include "cmd.h" constant char fileheader[] = { C0_LESSKEY_MAGIC, C1_LESSKEY_MAGIC, C2_LESSKEY_MAGIC, C3_LESSKEY_MAGIC }; constant char filetrailer[] = { C0_END_LESSKEY_MAGIC, C1_END_LESSKEY_MAGIC, C2_END_LESSKEY_MAGIC }; constant char cmdsection[1] = { CMD_SECTION }; constant char editsection[1] = { EDIT_SECTION }; constant char varsection[1] = { VAR_SECTION }; constant char endsection[1] = { END_SECTION }; constant char *infile = NULL; constant char *outfile = NULL; extern char version[]; static void usage(void) { fprintf(stderr, "usage: lesskey [-o output] [input]\n"); exit(1); } void lesskey_parse_error(constant char *s) { fprintf(stderr, "%s\n", s); } int lstrtoi(constant char *buf, constant char **ebuf, int radix) { return (int) strtol(buf, (char**)ebuf, radix); } void out_of_memory(void) { fprintf(stderr, "lesskey: cannot allocate memory\n"); exit(1); } void * ecalloc(size_t count, size_t size) { void *p; p = calloc(count, size); if (p == NULL) out_of_memory(); return (p); } static char * mkpathname(constant char *dirname, constant char *filename) { char *pathname; pathname = ecalloc(strlen(dirname) + strlen(filename) + 2, sizeof(char)); strcpy(pathname, dirname); strcat(pathname, PATHNAME_SEP); strcat(pathname, filename); return (pathname); } /* * Figure out the name of a default file (in the user's HOME directory). */ char * homefile(constant char *filename) { constant char *p; char *pathname; if ((p = getenv("HOME")) != NULL && *p != '\0') pathname = mkpathname(p, filename); #if OS2 else if ((p = getenv("INIT")) != NULL && *p != '\0') pathname = mkpathname(p, filename); #endif else { fprintf(stderr, "cannot find $HOME - using current directory\n"); pathname = mkpathname(".", filename); } return (pathname); } /* * Parse command line arguments. */ static void parse_args(int argc, constant char **argv) { constant char *arg; outfile = NULL; while (--argc > 0) { arg = *++argv; if (arg[0] != '-') /* Arg does not start with "-"; it's not an option. */ break; if (arg[1] == '\0') /* "-" means standard input. */ break; if (arg[1] == '-' && arg[2] == '\0') { /* "--" means end of options. */ argc--; argv++; break; } switch (arg[1]) { case '-': if (strncmp(arg, "--output", 8) == 0) { if (arg[8] == '\0') outfile = &arg[8]; else if (arg[8] == '=') outfile = &arg[9]; else usage(); goto opt_o; } if (strcmp(arg, "--version") == 0) { goto opt_V; } usage(); break; case 'o': outfile = &argv[0][2]; opt_o: if (*outfile == '\0') { if (--argc <= 0) usage(); outfile = *(++argv); } break; case 'V': opt_V: printf("lesskey version %s\n", version); exit(0); default: usage(); } } if (argc > 1) usage(); /* * Open the input file, or use DEF_LESSKEYINFILE if none specified. */ if (argc > 0) infile = *argv; } /* * Output some bytes. */ static void fputbytes(FILE *fd, constant char *buf, size_t len) { while (len-- > 0) { fwrite(buf, sizeof(char), 1, fd); buf++; } } /* * Output an integer, in special KRADIX form. */ static void fputint(FILE *fd, size_t val) { char c1, c2; if (val >= KRADIX*KRADIX) { fprintf(stderr, "error: cannot write %ld, max %ld\n", (long) val, (long) (KRADIX*KRADIX)); exit(1); } c1 = (char) (val % KRADIX); val /= KRADIX; c2 = (char) (val % KRADIX); val /= KRADIX; if (val != 0) { fprintf(stderr, "error: %ld exceeds max integer size (%ld)\n", (long) val, (long) (KRADIX*KRADIX)); exit(1); } fwrite(&c1, sizeof(char), 1, fd); fwrite(&c2, sizeof(char), 1, fd); } int main(int argc, constant char *argv[]) { struct lesskey_tables tables; FILE *out; int errors; #ifdef WIN32 if (getenv("HOME") == NULL) { /* * If there is no HOME environment variable, * try the concatenation of HOMEDRIVE + HOMEPATH. */ constant char *drive = getenv("HOMEDRIVE"); constant char *path = getenv("HOMEPATH"); if (drive != NULL && path != NULL) { char *env = (char *) ecalloc(strlen(drive) + strlen(path) + 6, sizeof(char)); strcpy(env, "HOME="); strcat(env, drive); strcat(env, path); putenv(env); } } #endif /* WIN32 */ /* * Process command line arguments. */ parse_args(argc, argv); errors = parse_lesskey(infile, &tables); if (errors) { fprintf(stderr, "%d errors; no output produced\n", errors); return (1); } fprintf(stderr, "NOTE: lesskey is deprecated.\n It is no longer necessary to run lesskey,\n when using less version 582 and later.\n"); /* * Write the output file. * If no output file was specified, use "$HOME/.less" */ if (outfile == NULL) outfile = getenv("LESSKEY"); if (outfile == NULL) outfile = homefile(LESSKEYFILE); if ((out = fopen(outfile, "wb")) == NULL) { #if HAVE_PERROR perror(outfile); #else fprintf(stderr, "Cannot open %s\n", outfile); #endif return (1); } /* File header */ fputbytes(out, fileheader, sizeof(fileheader)); /* Command key section */ fputbytes(out, cmdsection, sizeof(cmdsection)); fputint(out, tables.cmdtable.buf.end); fputbytes(out, xbuf_char_data(&tables.cmdtable.buf), tables.cmdtable.buf.end); /* Edit key section */ fputbytes(out, editsection, sizeof(editsection)); fputint(out, tables.edittable.buf.end); fputbytes(out, xbuf_char_data(&tables.edittable.buf), tables.edittable.buf.end); /* Environment variable section */ fputbytes(out, varsection, sizeof(varsection)); fputint(out, tables.vartable.buf.end); fputbytes(out, xbuf_char_data(&tables.vartable.buf), tables.vartable.buf.end); /* File trailer */ fputbytes(out, endsection, sizeof(endsection)); fputbytes(out, filetrailer, sizeof(filetrailer)); fclose(out); return (0); }