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