Home | History | Annotate | Line # | Download | only in dist
      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