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