xref: /csrg-svn/usr.bin/more/lesskey.c (revision 35283)
135209Sbostic /*
235209Sbostic  * Copyright (c) 1988 Mark Nudleman
335209Sbostic  * Copyright (c) 1988 Regents of the University of California.
435209Sbostic  * All rights reserved.
535209Sbostic  *
635209Sbostic  * Redistribution and use in source and binary forms are permitted
735209Sbostic  * provided that the above copyright notice and this paragraph are
835209Sbostic  * duplicated in all such forms and that any documentation,
935209Sbostic  * advertising materials, and other materials related to such
1035209Sbostic  * distribution and use acknowledge that the software was developed
11*35283Sbostic  * by Mark Nudleman and the University of California, Berkeley.  The
12*35283Sbostic  * name of Mark Nudleman or the
1335209Sbostic  * University may not be used to endorse or promote products derived
1435209Sbostic  * from this software without specific prior written permission.
1535209Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1635209Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1735209Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1835209Sbostic  */
1935209Sbostic 
2035209Sbostic #ifndef lint
2135209Sbostic char copyright[] =
2235209Sbostic "@(#) Copyright (c) 1988 Mark Nudleman.\n\
2335209Sbostic @(#) Copyright (c) 1988 Regents of the University of California.\n\
2435209Sbostic  All rights reserved.\n";
2535209Sbostic #endif /* not lint */
2635209Sbostic 
2735209Sbostic #ifndef lint
28*35283Sbostic static char sccsid[] = "@(#)lesskey.c	5.3 (Berkeley) 07/25/88";
2935209Sbostic #endif /* not lint */
3035209Sbostic 
3135209Sbostic /*
3235209Sbostic  *	lesskey [-o output] [input]
3335209Sbostic  *
3435209Sbostic  *	Make a .less file.
3535209Sbostic  *	If no input file is specified, standard input is used.
3635209Sbostic  *	If no output file is specified, $HOME/.less is used.
3735209Sbostic  *
3835209Sbostic  *	The .less file is used to specify (to "less") user-defined
3935209Sbostic  *	key bindings.  Basically any sequence of 1 to MAX_CMDLEN
4035209Sbostic  *	keystrokes may be bound to an existing less function.
4135209Sbostic  *
4235209Sbostic  *	The input file is an ascii file consisting of a
4335209Sbostic  *	sequence of lines of the form:
4435209Sbostic  *		string <whitespace> action <newline>
4535209Sbostic  *
4635209Sbostic  *	"string" is a sequence of command characters which form
4735209Sbostic  *		the new user-defined command.  The command
4835209Sbostic  *		characters may be:
4935209Sbostic  *		1. The actual character itself.
5035209Sbostic  *		2. A character preceeded by ^ to specify a
5135209Sbostic  *		   control character (e.g. ^X means control-X).
5235209Sbostic  *		3. Any character (other than an octal digit) preceeded by
5335209Sbostic  *		   a \ to specify the character itself (characters which
5435209Sbostic  *		   must be preceeded by \ include ^, \, and whitespace.
5535209Sbostic  *		4. A backslash followed by one to three octal digits
5635209Sbostic  *		   to specify a character by its octal value.
5735209Sbostic  *	"action" is the name of a "less" action, from the table below.
5835209Sbostic  *
5935209Sbostic  *	Blank lines and lines which start with # are ignored.
6035209Sbostic  *
6135209Sbostic  *
6235209Sbostic  *	The output file is a non-ascii file, consisting of
6335209Sbostic  *	zero or more byte sequences of the form:
6435209Sbostic  *		string <0> <action>
6535209Sbostic  *
6635209Sbostic  *	"string" is the command string.
6735209Sbostic  *	"<0>" is one null byte.
6835209Sbostic  *	"<action>" is one byte containing the action code (the A_xxx value).
6935209Sbostic  *
7035209Sbostic  *
7135209Sbostic  *		Revision history
7235209Sbostic  *
7335209Sbostic  *	v1: Initial version.					10/13/87  mark
7435209Sbostic  */
7535209Sbostic 
7635209Sbostic #include <stdio.h>
7735209Sbostic #include "less.h"
7835209Sbostic #include "cmd.h"
7935209Sbostic 
8035209Sbostic char usertable[MAX_USERCMD];
8135209Sbostic 
8235209Sbostic struct cmdname
8335209Sbostic {
8435209Sbostic 	char *cn_name;
8535209Sbostic 	int cn_action;
8635209Sbostic } cmdnames[] =
8735209Sbostic {
8835209Sbostic 	"back-line",		A_B_LINE,
8935209Sbostic 	"back-screen",		A_B_SCREEN,
9035209Sbostic 	"back-scroll",		A_B_SCROLL,
9135209Sbostic 	"back-search",		A_B_SEARCH,
9235209Sbostic 	"debug",		A_DEBUG,
9335209Sbostic 	"display-flag",		A_DISP_OPTION,
9435209Sbostic 	"display-option",	A_DISP_OPTION,
9535209Sbostic 	"end",			A_GOEND,
9635209Sbostic 	"examine",		A_EXAMINE,
9735209Sbostic 	"first-cmd",		A_FIRSTCMD,
9835209Sbostic 	"firstcmd",		A_FIRSTCMD,
9935209Sbostic 	"flush-repaint",	A_FREPAINT,
10035209Sbostic 	"forw-line",		A_F_LINE,
10135209Sbostic 	"forw-screen",		A_F_SCREEN,
10235209Sbostic 	"forw-scroll",		A_F_SCROLL,
10335209Sbostic 	"forw-search",		A_F_SEARCH,
10435209Sbostic 	"goto-end",		A_GOEND,
10535209Sbostic 	"goto-line",		A_GOLINE,
10635209Sbostic 	"goto-mark",		A_GOMARK,
10735209Sbostic 	"help",			A_HELP,
10835209Sbostic 	"invalid",		A_NOACTION,
10935209Sbostic 	"next-file",		A_NEXT_FILE,
11035209Sbostic 	"noaction",		A_NOACTION,
11135209Sbostic 	"percent",		A_PERCENT,
11235209Sbostic 	"prev-file",		A_PREV_FILE,
11335209Sbostic 	"quit",			A_QUIT,
11435209Sbostic 	"repaint",		A_REPAINT,
11535209Sbostic 	"repaint-flush",	A_FREPAINT,
11635209Sbostic 	"repeat-search",	A_AGAIN_SEARCH,
11735209Sbostic 	"set-mark",		A_SETMARK,
11835209Sbostic 	"shell",		A_SHELL,
11935209Sbostic 	"status",		A_STAT,
12035209Sbostic 	"toggle-flag",		A_TOGGLE_OPTION,
12135209Sbostic 	"toggle-option",	A_TOGGLE_OPTION,
12235209Sbostic 	"version",		A_VERSION,
12335209Sbostic 	"visual",		A_VISUAL,
12435209Sbostic 	NULL,			0
12535209Sbostic };
12635209Sbostic 
main(argc,argv)12735209Sbostic main(argc, argv)
12835209Sbostic 	int argc;
12935209Sbostic 	char *argv[];
13035209Sbostic {
13135209Sbostic 	char *p;		/* {{ Can't be register since we use &p }} */
13235209Sbostic 	register char *up;	/* Pointer into usertable */
13335209Sbostic 	FILE *desc;		/* Description file (input) */
13435209Sbostic 	FILE *out;		/* Output file */
13535209Sbostic 	int linenum;		/* Line number in input file */
13635209Sbostic 	char *currcmd;		/* Start of current command string */
13735209Sbostic 	int errors;
13835209Sbostic 	int i;
13935209Sbostic 	char line[100];
14035209Sbostic 	char *outfile;
14135209Sbostic 
14235236Sbostic 	extern char *getenv(), *strcat(), *strcpy();
14335209Sbostic 
14435209Sbostic 	/*
14535209Sbostic 	 * Process command line arguments.
14635209Sbostic 	 */
14735209Sbostic 	outfile = NULL;
14835209Sbostic 	while (--argc > 0 && **(++argv) == '-')
14935209Sbostic 	{
15035209Sbostic 		switch (argv[0][1])
15135209Sbostic 		{
15235209Sbostic 		case 'o':
15335209Sbostic 			outfile = &argv[0][2];
15435209Sbostic 			if (*outfile == '\0')
15535209Sbostic 			{
15635209Sbostic 				if (--argc <= 0)
15735209Sbostic 					usage();
15835209Sbostic 				outfile = *(++argv);
15935209Sbostic 			}
16035209Sbostic 			break;
16135209Sbostic 		default:
16235209Sbostic 			usage();
16335209Sbostic 		}
16435209Sbostic 	}
16535209Sbostic 	if (argc > 1)
16635209Sbostic 		usage();
16735209Sbostic 
16835209Sbostic 
16935209Sbostic 	/*
17035209Sbostic 	 * Open the input file, or use standard input if none specified.
17135209Sbostic 	 */
17235209Sbostic 	if (argc > 0)
17335209Sbostic 	{
17435209Sbostic 		if ((desc = fopen(*argv, "r")) == NULL)
17535209Sbostic 		{
17635209Sbostic 			perror(*argv);
17735209Sbostic 			exit(1);
17835209Sbostic 		}
17935209Sbostic 	} else
18035209Sbostic 		desc = stdin;
18135209Sbostic 
18235209Sbostic 	/*
18335209Sbostic 	 * Read the input file, one line at a time.
18435209Sbostic 	 * Each line consists of a command string,
18535209Sbostic 	 * followed by white space, followed by an action name.
18635209Sbostic 	 */
18735209Sbostic 	linenum = 0;
18835209Sbostic 	errors = 0;
18935209Sbostic 	up = usertable;
19035209Sbostic 	while (fgets(line, sizeof(line), desc) != NULL)
19135209Sbostic 	{
19235209Sbostic 		++linenum;
19335209Sbostic 
19435209Sbostic 		/*
19535209Sbostic 		 * Skip leading white space.
19635209Sbostic 		 * Replace the final newline with a null byte.
19735209Sbostic 		 * Ignore blank lines and comment lines.
19835209Sbostic 		 */
19935209Sbostic 		p = line;
20035209Sbostic 		while (*p == ' ' || *p == '\t')
20135209Sbostic 			++p;
20235209Sbostic 		for (i = 0;  p[i] != '\n' && p[i] != '\0';  i++)
20335209Sbostic 			;
20435209Sbostic 		p[i] = '\0';
20535209Sbostic 		if (*p == '#' || *p == '\0')
20635209Sbostic 			continue;
20735209Sbostic 
20835209Sbostic 		/*
20935209Sbostic 		 * Parse the command string and store it in the usertable.
21035209Sbostic 		 */
21135209Sbostic 		currcmd = up;
21235209Sbostic 		do
21335209Sbostic 		{
21435209Sbostic 			if (up >= usertable + MAX_USERCMD)
21535209Sbostic 			{
21635209Sbostic 				fprintf(stderr, "too many commands, line %d\n",
21735209Sbostic 					linenum);
21835209Sbostic 				exit(1);
21935209Sbostic 			}
22035209Sbostic 			if (up >= currcmd + MAX_CMDLEN)
22135209Sbostic 			{
22235209Sbostic 				fprintf(stderr, "command too long on line %d\n",
22335209Sbostic 					linenum);
22435209Sbostic 				errors++;
22535209Sbostic 				break;
22635209Sbostic 			}
22735209Sbostic 
22835209Sbostic 			*up++ = tchar(&p);
22935209Sbostic 
23035209Sbostic 		} while (*p != ' ' && *p != '\t' && *p != '\0');
23135209Sbostic 
23235209Sbostic 		/*
23335209Sbostic 		 * Terminate the command string with a null byte.
23435209Sbostic 		 */
23535209Sbostic 		*up++ = '\0';
23635209Sbostic 
23735209Sbostic 		/*
23835209Sbostic 		 * Skip white space between the command string
23935209Sbostic 		 * and the action name.
24035209Sbostic 		 * Terminate the action name if it is followed
24135209Sbostic 		 * by whitespace or a # comment.
24235209Sbostic 		 */
24335209Sbostic 		if (*p == '\0')
24435209Sbostic 		{
24535209Sbostic 			fprintf(stderr, "missing whitespace on line %d\n",
24635209Sbostic 				linenum);
24735209Sbostic 			errors++;
24835209Sbostic 			continue;
24935209Sbostic 		}
25035209Sbostic 		while (*p == ' ' || *p == '\t')
25135209Sbostic 			++p;
25235209Sbostic 		for (i = 0;  p[i] != ' ' && p[i] != '\t' &&
25335209Sbostic 			     p[i] != '#' && p[i] != '\0';  i++)
25435209Sbostic 			;
25535209Sbostic 		p[i] = '\0';
25635209Sbostic 
25735209Sbostic 		/*
25835209Sbostic 		 * Parse the action name and store it in the usertable.
25935209Sbostic 		 */
26035209Sbostic 		for (i = 0;  cmdnames[i].cn_name != NULL;  i++)
26135209Sbostic 			if (strcmp(cmdnames[i].cn_name, p) == 0)
26235209Sbostic 				break;
26335209Sbostic 		if (cmdnames[i].cn_name == NULL)
26435209Sbostic 		{
26535209Sbostic 			fprintf(stderr, "unknown action <%s> on line %d\n",
26635209Sbostic 				p, linenum);
26735209Sbostic 			errors++;
26835209Sbostic 			continue;
26935209Sbostic 		}
27035209Sbostic 		*up++ = cmdnames[i].cn_action;
27135209Sbostic 	}
27235209Sbostic 
27335209Sbostic 	if (errors > 0)
27435209Sbostic 	{
27535209Sbostic 		fprintf(stderr, "%d errors; no output produced\n", errors);
27635209Sbostic 		exit(1);
27735209Sbostic 	}
27835209Sbostic 
27935209Sbostic 	/*
28035209Sbostic 	 * Write the output file.
28135209Sbostic 	 * If no output file was specified, use "$HOME/.less"
28235209Sbostic 	 */
28335209Sbostic 	if (outfile == NULL)
28435209Sbostic 	{
28535209Sbostic 		p = getenv("HOME");
28635209Sbostic 		if (p == NULL)
28735209Sbostic 		{
28835209Sbostic 			fprintf(stderr, "cannot find $HOME\n");
28935209Sbostic 			exit(1);
29035209Sbostic 		}
29135209Sbostic 		strcpy(line, p);
29235209Sbostic 		strcat(line, "/.less");
29335209Sbostic 		outfile = line;
29435209Sbostic 	}
29535209Sbostic 	if ((out = fopen(outfile, "w")) == NULL)
29635209Sbostic 		perror(outfile);
29735209Sbostic 	else
29835209Sbostic 		fwrite((char *)usertable, 1, up-usertable, out);
29935209Sbostic }
30035209Sbostic 
30135209Sbostic /*
30235209Sbostic  * Parse one character of the command string.
30335209Sbostic  */
tchar(pp)30435209Sbostic tchar(pp)
30535209Sbostic 	char **pp;
30635209Sbostic {
30735209Sbostic 	register char *p;
30835209Sbostic 	register char ch;
30935209Sbostic 	register int i;
31035209Sbostic 
31135209Sbostic 	p = *pp;
31235209Sbostic 	switch (*p)
31335209Sbostic 	{
31435209Sbostic 	case '\\':
31535209Sbostic 		if (*++p >= '0' && *p <= '7')
31635209Sbostic 		{
31735209Sbostic 			/*
31835209Sbostic 			 * Parse an octal number.
31935209Sbostic 			 */
32035209Sbostic 			ch = 0;
32135209Sbostic 			i = 0;
32235209Sbostic 			do
32335209Sbostic 				ch = 8*ch + (*p - '0');
32435209Sbostic 			while (*++p >= '0' && *p <= '7' && ++i < 3);
32535209Sbostic 			*pp = p;
32635209Sbostic 			return (ch);
32735209Sbostic 		}
32835209Sbostic 		/*
32935209Sbostic 		 * Backslash followed by a char just means that char.
33035209Sbostic 		 */
33135209Sbostic 		*pp = p+1;
33235209Sbostic 		return (*p);
33335209Sbostic 	case '^':
33435209Sbostic 		/*
33535209Sbostic 		 * Carat means CONTROL.
33635209Sbostic 		 */
33735209Sbostic 		*pp = p+2;
33835209Sbostic 		return (CONTROL(p[1]));
33935209Sbostic 	}
34035209Sbostic 	*pp = p+1;
34135209Sbostic 	return (*p);
34235209Sbostic }
34335209Sbostic 
usage()34435209Sbostic usage()
34535209Sbostic {
34635209Sbostic 	fprintf(stderr, "usage: lesskey [-o output] [input]\n");
34735209Sbostic 	exit(1);
34835209Sbostic }
349