/*1* Copyright (C) 1984-2025 Mark Nudelman2*3* You may distribute under the terms of either the GNU General Public4* License or the Less License, as specified in the README file.5*6* For more information, see the README file.7*/8910/*11* lesskey [-o output] [input]12*13* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *14*15* Make a .less file.16* If no input file is specified, standard input is used.17* If no output file is specified, $HOME/.less is used.18*19* The .less file is used to specify (to "less") user-defined20* key bindings. Basically any sequence of 1 to MAX_CMDLEN21* keystrokes may be bound to an existing less function.22*23* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *24*25* The input file is an ascii file consisting of a26* sequence of lines of the form:27* string <whitespace> action [chars] <newline>28*29* "string" is a sequence of command characters which form30* the new user-defined command. The command31* characters may be:32* 1. The actual character itself.33* 2. A character preceded by ^ to specify a34* control character (e.g. ^X means control-X).35* 3. A backslash followed by one to three octal digits36* to specify a character by its octal value.37* 4. A backslash followed by b, e, n, r or t38* to specify \b, ESC, \n, \r or \t, respectively.39* 5. Any character (other than those mentioned above) preceded40* by a \ to specify the character itself (characters which41* must be preceded by \ include ^, \, and whitespace.42* "action" is the name of a "less" action, from the table below.43* "chars" is an optional sequence of characters which is treated44* as keyboard input after the command is executed.45*46* Blank lines and lines which start with # are ignored,47* except for the special control lines:48* #command Signals the beginning of the command49* keys section.50* #line-edit Signals the beginning of the line-editing51* keys section.52* #env Signals the beginning of the environment53* variable section.54* #stop Stops command parsing in less;55* causes all default keys to be disabled.56*57* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *58*59* The output file is a non-ascii file, consisting of a header,60* one or more sections, and a trailer.61* Each section begins with a section header, a section length word62* and the section data. Normally there are three sections:63* CMD_SECTION Definition of command keys.64* EDIT_SECTION Definition of editing keys.65* END_SECTION A special section header, with no66* length word or section data.67*68* Section data consists of zero or more byte sequences of the form:69* string <0> <action>70* or71* string <0> <action|A_EXTRA> chars <0>72*73* "string" is the command string.74* "<0>" is one null byte.75* "<action>" is one byte containing the action code (the A_xxx value).76* If action is ORed with A_EXTRA, the action byte is followed77* by the null-terminated "chars" string.78*79* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *80*/8182#include "defines.h"83#include <stdio.h>84#include <string.h>85#include <stdlib.h>86#include "lesskey.h"87#include "cmd.h"8889constant char fileheader[] = {90C0_LESSKEY_MAGIC,91C1_LESSKEY_MAGIC,92C2_LESSKEY_MAGIC,93C3_LESSKEY_MAGIC94};95constant char filetrailer[] = {96C0_END_LESSKEY_MAGIC,97C1_END_LESSKEY_MAGIC,98C2_END_LESSKEY_MAGIC99};100constant char cmdsection[1] = { CMD_SECTION };101constant char editsection[1] = { EDIT_SECTION };102constant char varsection[1] = { VAR_SECTION };103constant char endsection[1] = { END_SECTION };104105constant char *infile = NULL;106constant char *outfile = NULL;107108extern char version[];109110static void usage(void)111{112fprintf(stderr, "usage: lesskey [-o output] [input]\n");113exit(1);114}115116void lesskey_parse_error(constant char *s)117{118fprintf(stderr, "%s\n", s);119}120121int lstrtoi(constant char *buf, constant char **ebuf, int radix)122{123return (int) strtol(buf, (char**)ebuf, radix);124}125126void out_of_memory(void)127{128fprintf(stderr, "lesskey: cannot allocate memory\n");129exit(1);130}131132void * ecalloc(size_t count, size_t size)133{134void *p;135136p = calloc(count, size);137if (p == NULL)138out_of_memory();139return (p);140}141142static char * mkpathname(constant char *dirname, constant char *filename)143{144char *pathname;145146pathname = ecalloc(strlen(dirname) + strlen(filename) + 2, sizeof(char));147strcpy(pathname, dirname);148strcat(pathname, PATHNAME_SEP);149strcat(pathname, filename);150return (pathname);151}152153/*154* Figure out the name of a default file (in the user's HOME directory).155*/156char * homefile(constant char *filename)157{158constant char *p;159char *pathname;160161if ((p = getenv("HOME")) != NULL && *p != '\0')162pathname = mkpathname(p, filename);163#if OS2164else if ((p = getenv("INIT")) != NULL && *p != '\0')165pathname = mkpathname(p, filename);166#endif167else168{169fprintf(stderr, "cannot find $HOME - using current directory\n");170pathname = mkpathname(".", filename);171}172return (pathname);173}174175/*176* Parse command line arguments.177*/178static void parse_args(int argc, constant char **argv)179{180constant char *arg;181182outfile = NULL;183while (--argc > 0)184{185arg = *++argv;186if (arg[0] != '-')187/* Arg does not start with "-"; it's not an option. */188break;189if (arg[1] == '\0')190/* "-" means standard input. */191break;192if (arg[1] == '-' && arg[2] == '\0')193{194/* "--" means end of options. */195argc--;196argv++;197break;198}199switch (arg[1])200{201case '-':202if (strncmp(arg, "--output", 8) == 0)203{204if (arg[8] == '\0')205outfile = &arg[8];206else if (arg[8] == '=')207outfile = &arg[9];208else209usage();210goto opt_o;211}212if (strcmp(arg, "--version") == 0)213{214goto opt_V;215}216usage();217break;218case 'o':219outfile = &argv[0][2];220opt_o:221if (*outfile == '\0')222{223if (--argc <= 0)224usage();225outfile = *(++argv);226}227break;228case 'V':229opt_V:230printf("lesskey version %s\n", version);231exit(0);232default:233usage();234}235}236if (argc > 1)237usage();238/*239* Open the input file, or use DEF_LESSKEYINFILE if none specified.240*/241if (argc > 0)242infile = *argv;243}244245/*246* Output some bytes.247*/248static void fputbytes(FILE *fd, constant char *buf, size_t len)249{250while (len-- > 0)251{252fwrite(buf, sizeof(char), 1, fd);253buf++;254}255}256257/*258* Output an integer, in special KRADIX form.259*/260static void fputint(FILE *fd, size_t val)261{262char c1, c2;263264if (val >= KRADIX*KRADIX)265{266fprintf(stderr, "error: cannot write %ld, max %ld\n",267(long) val, (long) (KRADIX*KRADIX));268exit(1);269}270c1 = (char) (val % KRADIX);271val /= KRADIX;272c2 = (char) (val % KRADIX);273val /= KRADIX;274if (val != 0) {275fprintf(stderr, "error: %ld exceeds max integer size (%ld)\n",276(long) val, (long) (KRADIX*KRADIX));277exit(1);278}279fwrite(&c1, sizeof(char), 1, fd);280fwrite(&c2, sizeof(char), 1, fd);281}282283int main(int argc, constant char *argv[])284{285struct lesskey_tables tables;286FILE *out;287int errors;288289#ifdef WIN32290if (getenv("HOME") == NULL)291{292/*293* If there is no HOME environment variable,294* try the concatenation of HOMEDRIVE + HOMEPATH.295*/296constant char *drive = getenv("HOMEDRIVE");297constant char *path = getenv("HOMEPATH");298if (drive != NULL && path != NULL)299{300char *env = (char *) ecalloc(strlen(drive) +301strlen(path) + 6, sizeof(char));302strcpy(env, "HOME=");303strcat(env, drive);304strcat(env, path);305putenv(env);306}307}308#endif /* WIN32 */309310/*311* Process command line arguments.312*/313parse_args(argc, argv);314errors = parse_lesskey(infile, &tables);315if (errors)316{317fprintf(stderr, "%d errors; no output produced\n", errors);318return (1);319}320321fprintf(stderr, "NOTE: lesskey is deprecated.\n It is no longer necessary to run lesskey,\n when using less version 582 and later.\n");322323/*324* Write the output file.325* If no output file was specified, use "$HOME/.less"326*/327if (outfile == NULL)328outfile = getenv("LESSKEY");329if (outfile == NULL)330outfile = homefile(LESSKEYFILE);331if ((out = fopen(outfile, "wb")) == NULL)332{333#if HAVE_PERROR334perror(outfile);335#else336fprintf(stderr, "Cannot open %s\n", outfile);337#endif338return (1);339}340341/* File header */342fputbytes(out, fileheader, sizeof(fileheader));343344/* Command key section */345fputbytes(out, cmdsection, sizeof(cmdsection));346fputint(out, tables.cmdtable.buf.end);347fputbytes(out, xbuf_char_data(&tables.cmdtable.buf), tables.cmdtable.buf.end);348/* Edit key section */349fputbytes(out, editsection, sizeof(editsection));350fputint(out, tables.edittable.buf.end);351fputbytes(out, xbuf_char_data(&tables.edittable.buf), tables.edittable.buf.end);352353/* Environment variable section */354fputbytes(out, varsection, sizeof(varsection));355fputint(out, tables.vartable.buf.end);356fputbytes(out, xbuf_char_data(&tables.vartable.buf), tables.vartable.buf.end);357358/* File trailer */359fputbytes(out, endsection, sizeof(endsection));360fputbytes(out, filetrailer, sizeof(filetrailer));361fclose(out);362return (0);363}364365366