1*3d8a4f7fSsimonb /* $NetBSD: lesskey.c,v 1.6 2023/10/06 07:26:47 simonb Exp $ */
220006a0bStron
320006a0bStron /*
4838f5788Ssimonb * Copyright (C) 1984-2023 Mark Nudelman
520006a0bStron *
620006a0bStron * You may distribute under the terms of either the GNU General Public
720006a0bStron * License or the Less License, as specified in the README file.
820006a0bStron *
9ec18bca0Stron * For more information, see the README file.
1020006a0bStron */
1120006a0bStron
1220006a0bStron
1320006a0bStron /*
1420006a0bStron * lesskey [-o output] [input]
1520006a0bStron *
1620006a0bStron * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1720006a0bStron *
1820006a0bStron * Make a .less file.
1920006a0bStron * If no input file is specified, standard input is used.
2020006a0bStron * If no output file is specified, $HOME/.less is used.
2120006a0bStron *
2220006a0bStron * The .less file is used to specify (to "less") user-defined
2320006a0bStron * key bindings. Basically any sequence of 1 to MAX_CMDLEN
2420006a0bStron * keystrokes may be bound to an existing less function.
2520006a0bStron *
2620006a0bStron * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2720006a0bStron *
2820006a0bStron * The input file is an ascii file consisting of a
2920006a0bStron * sequence of lines of the form:
3020006a0bStron * string <whitespace> action [chars] <newline>
3120006a0bStron *
3220006a0bStron * "string" is a sequence of command characters which form
3320006a0bStron * the new user-defined command. The command
3420006a0bStron * characters may be:
3520006a0bStron * 1. The actual character itself.
3620006a0bStron * 2. A character preceded by ^ to specify a
3720006a0bStron * control character (e.g. ^X means control-X).
3820006a0bStron * 3. A backslash followed by one to three octal digits
3920006a0bStron * to specify a character by its octal value.
4020006a0bStron * 4. A backslash followed by b, e, n, r or t
4120006a0bStron * to specify \b, ESC, \n, \r or \t, respectively.
4220006a0bStron * 5. Any character (other than those mentioned above) preceded
4320006a0bStron * by a \ to specify the character itself (characters which
4420006a0bStron * must be preceded by \ include ^, \, and whitespace.
4520006a0bStron * "action" is the name of a "less" action, from the table below.
4620006a0bStron * "chars" is an optional sequence of characters which is treated
4720006a0bStron * as keyboard input after the command is executed.
4820006a0bStron *
4920006a0bStron * Blank lines and lines which start with # are ignored,
5020006a0bStron * except for the special control lines:
5120006a0bStron * #command Signals the beginning of the command
5220006a0bStron * keys section.
5320006a0bStron * #line-edit Signals the beginning of the line-editing
5420006a0bStron * keys section.
5520006a0bStron * #env Signals the beginning of the environment
5620006a0bStron * variable section.
5720006a0bStron * #stop Stops command parsing in less;
5820006a0bStron * causes all default keys to be disabled.
5920006a0bStron *
6020006a0bStron * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6120006a0bStron *
6220006a0bStron * The output file is a non-ascii file, consisting of a header,
6320006a0bStron * one or more sections, and a trailer.
6420006a0bStron * Each section begins with a section header, a section length word
6520006a0bStron * and the section data. Normally there are three sections:
6620006a0bStron * CMD_SECTION Definition of command keys.
6720006a0bStron * EDIT_SECTION Definition of editing keys.
6820006a0bStron * END_SECTION A special section header, with no
6920006a0bStron * length word or section data.
7020006a0bStron *
7120006a0bStron * Section data consists of zero or more byte sequences of the form:
7220006a0bStron * string <0> <action>
7320006a0bStron * or
7420006a0bStron * string <0> <action|A_EXTRA> chars <0>
7520006a0bStron *
7620006a0bStron * "string" is the command string.
7720006a0bStron * "<0>" is one null byte.
7820006a0bStron * "<action>" is one byte containing the action code (the A_xxx value).
7920006a0bStron * If action is ORed with A_EXTRA, the action byte is followed
8020006a0bStron * by the null-terminated "chars" string.
8120006a0bStron *
8220006a0bStron * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8320006a0bStron */
8420006a0bStron
85838f5788Ssimonb #include "defines.h"
86838f5788Ssimonb #include <stdio.h>
87838f5788Ssimonb #include <string.h>
88838f5788Ssimonb #include <stdlib.h>
8920006a0bStron #include "lesskey.h"
9020006a0bStron #include "cmd.h"
9120006a0bStron
9220006a0bStron char fileheader[] = {
9320006a0bStron C0_LESSKEY_MAGIC,
9420006a0bStron C1_LESSKEY_MAGIC,
9520006a0bStron C2_LESSKEY_MAGIC,
9620006a0bStron C3_LESSKEY_MAGIC
9720006a0bStron };
9820006a0bStron char filetrailer[] = {
9920006a0bStron C0_END_LESSKEY_MAGIC,
10020006a0bStron C1_END_LESSKEY_MAGIC,
10120006a0bStron C2_END_LESSKEY_MAGIC
10220006a0bStron };
10320006a0bStron char cmdsection[1] = { CMD_SECTION };
10420006a0bStron char editsection[1] = { EDIT_SECTION };
10520006a0bStron char varsection[1] = { VAR_SECTION };
10620006a0bStron char endsection[1] = { END_SECTION };
10720006a0bStron
10820006a0bStron char *infile = NULL;
10920006a0bStron char *outfile = NULL ;
11020006a0bStron
11120006a0bStron extern char version[];
11220006a0bStron
usage(void)113838f5788Ssimonb static void usage(void)
11420006a0bStron {
11520006a0bStron fprintf(stderr, "usage: lesskey [-o output] [input]\n");
11620006a0bStron exit(1);
11720006a0bStron }
11820006a0bStron
lesskey_parse_error(char * s)119838f5788Ssimonb void lesskey_parse_error(char *s)
120838f5788Ssimonb {
121838f5788Ssimonb fprintf(stderr, "%s\n", s);
122838f5788Ssimonb }
123838f5788Ssimonb
lstrtoi(char * buf,char ** ebuf,int radix)124838f5788Ssimonb int lstrtoi(char *buf, char **ebuf, int radix)
125838f5788Ssimonb {
126838f5788Ssimonb return (int) strtol(buf, ebuf, radix);
127838f5788Ssimonb }
128838f5788Ssimonb
out_of_memory(void)129838f5788Ssimonb void out_of_memory(void)
130838f5788Ssimonb {
131838f5788Ssimonb fprintf(stderr, "lesskey: cannot allocate memory\n");
132838f5788Ssimonb exit(1);
133838f5788Ssimonb }
134838f5788Ssimonb
ecalloc(int count,unsigned int size)135838f5788Ssimonb void * ecalloc(int count, unsigned int size)
136838f5788Ssimonb {
137838f5788Ssimonb void *p;
138838f5788Ssimonb
139838f5788Ssimonb p = calloc(count, size);
140838f5788Ssimonb if (p == NULL)
141838f5788Ssimonb out_of_memory();
142838f5788Ssimonb return (p);
143838f5788Ssimonb }
144838f5788Ssimonb
mkpathname(char * dirname,char * filename)145838f5788Ssimonb static char * mkpathname(char *dirname, char *filename)
14620006a0bStron {
14720006a0bStron char *pathname;
14820006a0bStron
149838f5788Ssimonb pathname = ecalloc(strlen(dirname) + strlen(filename) + 2, sizeof(char));
15020006a0bStron strcpy(pathname, dirname);
15120006a0bStron strcat(pathname, PATHNAME_SEP);
15220006a0bStron strcat(pathname, filename);
15320006a0bStron return (pathname);
15420006a0bStron }
15520006a0bStron
15620006a0bStron /*
15720006a0bStron * Figure out the name of a default file (in the user's HOME directory).
15820006a0bStron */
homefile(char * filename)159838f5788Ssimonb char * homefile(char *filename)
16020006a0bStron {
16120006a0bStron char *p;
16220006a0bStron char *pathname;
16320006a0bStron
16420006a0bStron if ((p = getenv("HOME")) != NULL && *p != '\0')
16520006a0bStron pathname = mkpathname(p, filename);
16620006a0bStron #if OS2
16720006a0bStron else if ((p = getenv("INIT")) != NULL && *p != '\0')
16820006a0bStron pathname = mkpathname(p, filename);
16920006a0bStron #endif
17020006a0bStron else
17120006a0bStron {
17220006a0bStron fprintf(stderr, "cannot find $HOME - using current directory\n");
17320006a0bStron pathname = mkpathname(".", filename);
17420006a0bStron }
17520006a0bStron return (pathname);
17620006a0bStron }
17720006a0bStron
17820006a0bStron /*
17920006a0bStron * Parse command line arguments.
18020006a0bStron */
parse_args(int argc,char ** argv)181838f5788Ssimonb static void parse_args(int argc, char **argv)
18220006a0bStron {
18320006a0bStron char *arg;
18420006a0bStron
18520006a0bStron outfile = NULL;
18620006a0bStron while (--argc > 0)
18720006a0bStron {
18820006a0bStron arg = *++argv;
18920006a0bStron if (arg[0] != '-')
19020006a0bStron /* Arg does not start with "-"; it's not an option. */
19120006a0bStron break;
19220006a0bStron if (arg[1] == '\0')
19320006a0bStron /* "-" means standard input. */
19420006a0bStron break;
19520006a0bStron if (arg[1] == '-' && arg[2] == '\0')
19620006a0bStron {
19720006a0bStron /* "--" means end of options. */
19820006a0bStron argc--;
19920006a0bStron argv++;
20020006a0bStron break;
20120006a0bStron }
20220006a0bStron switch (arg[1])
20320006a0bStron {
20420006a0bStron case '-':
20520006a0bStron if (strncmp(arg, "--output", 8) == 0)
20620006a0bStron {
20720006a0bStron if (arg[8] == '\0')
20820006a0bStron outfile = &arg[8];
20920006a0bStron else if (arg[8] == '=')
21020006a0bStron outfile = &arg[9];
21120006a0bStron else
21220006a0bStron usage();
21320006a0bStron goto opt_o;
21420006a0bStron }
21520006a0bStron if (strcmp(arg, "--version") == 0)
21620006a0bStron {
21720006a0bStron goto opt_V;
21820006a0bStron }
21920006a0bStron usage();
22020006a0bStron break;
22120006a0bStron case 'o':
22220006a0bStron outfile = &argv[0][2];
22320006a0bStron opt_o:
22420006a0bStron if (*outfile == '\0')
22520006a0bStron {
22620006a0bStron if (--argc <= 0)
22720006a0bStron usage();
22820006a0bStron outfile = *(++argv);
22920006a0bStron }
23020006a0bStron break;
23120006a0bStron case 'V':
23220006a0bStron opt_V:
23320006a0bStron printf("lesskey version %s\n", version);
23420006a0bStron exit(0);
23520006a0bStron default:
23620006a0bStron usage();
23720006a0bStron }
23820006a0bStron }
23920006a0bStron if (argc > 1)
24020006a0bStron usage();
24120006a0bStron /*
24220006a0bStron * Open the input file, or use DEF_LESSKEYINFILE if none specified.
24320006a0bStron */
24420006a0bStron if (argc > 0)
24520006a0bStron infile = *argv;
24620006a0bStron }
24720006a0bStron
24820006a0bStron /*
24920006a0bStron * Output some bytes.
25020006a0bStron */
fputbytes(FILE * fd,char * buf,int len)251838f5788Ssimonb static void fputbytes(FILE *fd, char *buf, int len)
25220006a0bStron {
25320006a0bStron while (len-- > 0)
25420006a0bStron {
25520006a0bStron fwrite(buf, sizeof(char), 1, fd);
25620006a0bStron buf++;
25720006a0bStron }
25820006a0bStron }
25920006a0bStron
26020006a0bStron /*
26120006a0bStron * Output an integer, in special KRADIX form.
26220006a0bStron */
fputint(FILE * fd,unsigned int val)263838f5788Ssimonb static void fputint(FILE *fd, unsigned int val)
26420006a0bStron {
26520006a0bStron char c;
26620006a0bStron
26720006a0bStron if (val >= KRADIX*KRADIX)
26820006a0bStron {
269838f5788Ssimonb fprintf(stderr, "error: cannot write %d, max %d\n",
27020006a0bStron val, KRADIX*KRADIX);
27120006a0bStron exit(1);
27220006a0bStron }
27320006a0bStron c = val % KRADIX;
27420006a0bStron fwrite(&c, sizeof(char), 1, fd);
27520006a0bStron c = val / KRADIX;
27620006a0bStron fwrite(&c, sizeof(char), 1, fd);
27720006a0bStron }
27820006a0bStron
main(int argc,char * argv[])279838f5788Ssimonb int main(int argc, char *argv[])
28020006a0bStron {
281838f5788Ssimonb struct lesskey_tables tables;
28220006a0bStron FILE *out;
283838f5788Ssimonb int errors;
28420006a0bStron
28520006a0bStron #ifdef WIN32
28620006a0bStron if (getenv("HOME") == NULL)
28720006a0bStron {
28820006a0bStron /*
28920006a0bStron * If there is no HOME environment variable,
29020006a0bStron * try the concatenation of HOMEDRIVE + HOMEPATH.
29120006a0bStron */
29220006a0bStron char *drive = getenv("HOMEDRIVE");
29320006a0bStron char *path = getenv("HOMEPATH");
29420006a0bStron if (drive != NULL && path != NULL)
29520006a0bStron {
296838f5788Ssimonb char *env = (char *) ecalloc(strlen(drive) +
29720006a0bStron strlen(path) + 6, sizeof(char));
29820006a0bStron strcpy(env, "HOME=");
29920006a0bStron strcat(env, drive);
30020006a0bStron strcat(env, path);
30120006a0bStron putenv(env);
30220006a0bStron }
30320006a0bStron }
30420006a0bStron #endif /* WIN32 */
30520006a0bStron
30620006a0bStron /*
30720006a0bStron * Process command line arguments.
30820006a0bStron */
30920006a0bStron parse_args(argc, argv);
310838f5788Ssimonb errors = parse_lesskey(infile, &tables);
311838f5788Ssimonb if (errors)
31220006a0bStron {
313838f5788Ssimonb fprintf(stderr, "%d errors; no output produced\n", errors);
314838f5788Ssimonb return (1);
31520006a0bStron }
31620006a0bStron
317838f5788Ssimonb fprintf(stderr, "NOTE: lesskey is deprecated.\n It is no longer necessary to run lesskey,\n when using less version 582 and later.\n");
31820006a0bStron
31920006a0bStron /*
32020006a0bStron * Write the output file.
32120006a0bStron * If no output file was specified, use "$HOME/.less"
32220006a0bStron */
32320006a0bStron if (outfile == NULL)
32420006a0bStron outfile = getenv("LESSKEY");
32520006a0bStron if (outfile == NULL)
32620006a0bStron outfile = homefile(LESSKEYFILE);
32720006a0bStron if ((out = fopen(outfile, "wb")) == NULL)
32820006a0bStron {
32920006a0bStron #if HAVE_PERROR
33020006a0bStron perror(outfile);
33120006a0bStron #else
33220006a0bStron fprintf(stderr, "Cannot open %s\n", outfile);
33320006a0bStron #endif
334838f5788Ssimonb return (1);
33520006a0bStron }
33620006a0bStron
33720006a0bStron /* File header */
33820006a0bStron fputbytes(out, fileheader, sizeof(fileheader));
33920006a0bStron
34020006a0bStron /* Command key section */
34120006a0bStron fputbytes(out, cmdsection, sizeof(cmdsection));
342838f5788Ssimonb fputint(out, tables.cmdtable.buf.end);
343838f5788Ssimonb fputbytes(out, (char *)tables.cmdtable.buf.data, tables.cmdtable.buf.end);
34420006a0bStron /* Edit key section */
34520006a0bStron fputbytes(out, editsection, sizeof(editsection));
346838f5788Ssimonb fputint(out, tables.edittable.buf.end);
347838f5788Ssimonb fputbytes(out, (char *)tables.edittable.buf.data, tables.edittable.buf.end);
34820006a0bStron
34920006a0bStron /* Environment variable section */
35020006a0bStron fputbytes(out, varsection, sizeof(varsection));
351838f5788Ssimonb fputint(out, tables.vartable.buf.end);
352838f5788Ssimonb fputbytes(out, (char *)tables.vartable.buf.data, tables.vartable.buf.end);
35320006a0bStron
35420006a0bStron /* File trailer */
35520006a0bStron fputbytes(out, endsection, sizeof(endsection));
35620006a0bStron fputbytes(out, filetrailer, sizeof(filetrailer));
357838f5788Ssimonb fclose(out);
35820006a0bStron return (0);
35920006a0bStron }
360