1238730Sdelphij/* 2330571Sdelphij * Copyright (C) 1984-2017 Mark Nudelman 3238730Sdelphij * 4238730Sdelphij * You may distribute under the terms of either the GNU General Public 5238730Sdelphij * License or the Less License, as specified in the README file. 6238730Sdelphij * 7238730Sdelphij * For more information, see the README file. 8238730Sdelphij */ 960786Sps 1060786Sps 1160786Sps/* 1260786Sps * lesskey [-o output] [input] 1360786Sps * 1460786Sps * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1560786Sps * 1660786Sps * Make a .less file. 1760786Sps * If no input file is specified, standard input is used. 1860786Sps * If no output file is specified, $HOME/.less is used. 1960786Sps * 2060786Sps * The .less file is used to specify (to "less") user-defined 2160786Sps * key bindings. Basically any sequence of 1 to MAX_CMDLEN 2260786Sps * keystrokes may be bound to an existing less function. 2360786Sps * 2460786Sps * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2560786Sps * 2660786Sps * The input file is an ascii file consisting of a 2760786Sps * sequence of lines of the form: 2860786Sps * string <whitespace> action [chars] <newline> 2960786Sps * 3060786Sps * "string" is a sequence of command characters which form 3160786Sps * the new user-defined command. The command 3260786Sps * characters may be: 3360786Sps * 1. The actual character itself. 3460786Sps * 2. A character preceded by ^ to specify a 3560786Sps * control character (e.g. ^X means control-X). 3660786Sps * 3. A backslash followed by one to three octal digits 3760786Sps * to specify a character by its octal value. 3860786Sps * 4. A backslash followed by b, e, n, r or t 3960786Sps * to specify \b, ESC, \n, \r or \t, respectively. 4060786Sps * 5. Any character (other than those mentioned above) preceded 4160786Sps * by a \ to specify the character itself (characters which 4260786Sps * must be preceded by \ include ^, \, and whitespace. 4360786Sps * "action" is the name of a "less" action, from the table below. 4460786Sps * "chars" is an optional sequence of characters which is treated 4560786Sps * as keyboard input after the command is executed. 4660786Sps * 4760786Sps * Blank lines and lines which start with # are ignored, 4860786Sps * except for the special control lines: 4960786Sps * #command Signals the beginning of the command 5060786Sps * keys section. 5160786Sps * #line-edit Signals the beginning of the line-editing 5260786Sps * keys section. 5360786Sps * #env Signals the beginning of the environment 5460786Sps * variable section. 5560786Sps * #stop Stops command parsing in less; 5660786Sps * causes all default keys to be disabled. 5760786Sps * 5860786Sps * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5960786Sps * 6060786Sps * The output file is a non-ascii file, consisting of a header, 6160786Sps * one or more sections, and a trailer. 6260786Sps * Each section begins with a section header, a section length word 6360786Sps * and the section data. Normally there are three sections: 6460786Sps * CMD_SECTION Definition of command keys. 6560786Sps * EDIT_SECTION Definition of editing keys. 6660786Sps * END_SECTION A special section header, with no 6760786Sps * length word or section data. 6860786Sps * 6960786Sps * Section data consists of zero or more byte sequences of the form: 7060786Sps * string <0> <action> 7160786Sps * or 7260786Sps * string <0> <action|A_EXTRA> chars <0> 7360786Sps * 7460786Sps * "string" is the command string. 7560786Sps * "<0>" is one null byte. 7660786Sps * "<action>" is one byte containing the action code (the A_xxx value). 7760786Sps * If action is ORed with A_EXTRA, the action byte is followed 7860786Sps * by the null-terminated "chars" string. 7960786Sps * 8060786Sps * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 8160786Sps */ 8260786Sps 8360786Sps#include "less.h" 8460786Sps#include "lesskey.h" 8560786Sps#include "cmd.h" 8660786Sps 8760786Spsstruct cmdname 8860786Sps{ 8960786Sps char *cn_name; 9060786Sps int cn_action; 9160786Sps}; 9260786Sps 9360786Spsstruct cmdname cmdnames[] = 9460786Sps{ 95191930Sdelphij { "back-bracket", A_B_BRACKET }, 96191930Sdelphij { "back-line", A_B_LINE }, 97191930Sdelphij { "back-line-force", A_BF_LINE }, 98191930Sdelphij { "back-screen", A_B_SCREEN }, 99191930Sdelphij { "back-scroll", A_B_SCROLL }, 100191930Sdelphij { "back-search", A_B_SEARCH }, 101191930Sdelphij { "back-window", A_B_WINDOW }, 102330571Sdelphij { "clear-mark", A_CLRMARK }, 103191930Sdelphij { "debug", A_DEBUG }, 104191930Sdelphij { "digit", A_DIGIT }, 105191930Sdelphij { "display-flag", A_DISP_OPTION }, 106191930Sdelphij { "display-option", A_DISP_OPTION }, 107191930Sdelphij { "end", A_GOEND }, 108330571Sdelphij { "end-scroll", A_RRSHIFT }, 109191930Sdelphij { "examine", A_EXAMINE }, 110191930Sdelphij { "filter", A_FILTER }, 111191930Sdelphij { "first-cmd", A_FIRSTCMD }, 112191930Sdelphij { "firstcmd", A_FIRSTCMD }, 113191930Sdelphij { "flush-repaint", A_FREPAINT }, 114191930Sdelphij { "forw-bracket", A_F_BRACKET }, 115191930Sdelphij { "forw-forever", A_F_FOREVER }, 116237613Sdelphij { "forw-until-hilite", A_F_UNTIL_HILITE }, 117191930Sdelphij { "forw-line", A_F_LINE }, 118191930Sdelphij { "forw-line-force", A_FF_LINE }, 119191930Sdelphij { "forw-screen", A_F_SCREEN }, 120191930Sdelphij { "forw-screen-force", A_FF_SCREEN }, 121191930Sdelphij { "forw-scroll", A_F_SCROLL }, 122191930Sdelphij { "forw-search", A_F_SEARCH }, 123191930Sdelphij { "forw-window", A_F_WINDOW }, 124191930Sdelphij { "goto-end", A_GOEND }, 125294286Sdelphij { "goto-end-buffered", A_GOEND_BUF }, 126191930Sdelphij { "goto-line", A_GOLINE }, 127191930Sdelphij { "goto-mark", A_GOMARK }, 128191930Sdelphij { "help", A_HELP }, 129191930Sdelphij { "index-file", A_INDEX_FILE }, 130191930Sdelphij { "invalid", A_UINVALID }, 131191930Sdelphij { "left-scroll", A_LSHIFT }, 132191930Sdelphij { "next-file", A_NEXT_FILE }, 133191930Sdelphij { "next-tag", A_NEXT_TAG }, 134191930Sdelphij { "noaction", A_NOACTION }, 135330571Sdelphij { "no-scroll", A_LLSHIFT }, 136191930Sdelphij { "percent", A_PERCENT }, 137191930Sdelphij { "pipe", A_PIPE }, 138191930Sdelphij { "prev-file", A_PREV_FILE }, 139191930Sdelphij { "prev-tag", A_PREV_TAG }, 140191930Sdelphij { "quit", A_QUIT }, 141191930Sdelphij { "remove-file", A_REMOVE_FILE }, 142191930Sdelphij { "repaint", A_REPAINT }, 143191930Sdelphij { "repaint-flush", A_FREPAINT }, 144191930Sdelphij { "repeat-search", A_AGAIN_SEARCH }, 145191930Sdelphij { "repeat-search-all", A_T_AGAIN_SEARCH }, 146191930Sdelphij { "reverse-search", A_REVERSE_SEARCH }, 147191930Sdelphij { "reverse-search-all", A_T_REVERSE_SEARCH }, 148191930Sdelphij { "right-scroll", A_RSHIFT }, 149191930Sdelphij { "set-mark", A_SETMARK }, 150330571Sdelphij { "set-mark-bottom", A_SETMARKBOT }, 151191930Sdelphij { "shell", A_SHELL }, 152191930Sdelphij { "status", A_STAT }, 153191930Sdelphij { "toggle-flag", A_OPT_TOGGLE }, 154191930Sdelphij { "toggle-option", A_OPT_TOGGLE }, 155191930Sdelphij { "undo-hilite", A_UNDO_SEARCH }, 156191930Sdelphij { "version", A_VERSION }, 157191930Sdelphij { "visual", A_VISUAL }, 158191930Sdelphij { NULL, 0 } 15960786Sps}; 16060786Sps 16160786Spsstruct cmdname editnames[] = 16260786Sps{ 163128345Stjr { "back-complete", EC_B_COMPLETE }, 164128345Stjr { "backspace", EC_BACKSPACE }, 165128345Stjr { "delete", EC_DELETE }, 166128345Stjr { "down", EC_DOWN }, 167128345Stjr { "end", EC_END }, 168128345Stjr { "expand", EC_EXPAND }, 169128345Stjr { "forw-complete", EC_F_COMPLETE }, 170128345Stjr { "home", EC_HOME }, 171128345Stjr { "insert", EC_INSERT }, 172128345Stjr { "invalid", EC_UINVALID }, 173128345Stjr { "kill-line", EC_LINEKILL }, 174221715Sdelphij { "abort", EC_ABORT }, 175128345Stjr { "left", EC_LEFT }, 176128345Stjr { "literal", EC_LITERAL }, 177128345Stjr { "right", EC_RIGHT }, 178128345Stjr { "up", EC_UP }, 179128345Stjr { "word-backspace", EC_W_BACKSPACE }, 180128345Stjr { "word-delete", EC_W_DELETE }, 181128345Stjr { "word-left", EC_W_LEFT }, 182128345Stjr { "word-right", EC_W_RIGHT }, 183128345Stjr { NULL, 0 } 18460786Sps}; 18560786Sps 18660786Spsstruct table 18760786Sps{ 18860786Sps struct cmdname *names; 18960786Sps char *pbuffer; 19060786Sps char buffer[MAX_USERCMD]; 19160786Sps}; 19260786Sps 19360786Spsstruct table cmdtable; 19460786Spsstruct table edittable; 19560786Spsstruct table vartable; 19660786Spsstruct table *currtable = &cmdtable; 19760786Sps 19860786Spschar fileheader[] = { 19960786Sps C0_LESSKEY_MAGIC, 20060786Sps C1_LESSKEY_MAGIC, 20160786Sps C2_LESSKEY_MAGIC, 20260786Sps C3_LESSKEY_MAGIC 20360786Sps}; 20460786Spschar filetrailer[] = { 20560786Sps C0_END_LESSKEY_MAGIC, 20660786Sps C1_END_LESSKEY_MAGIC, 20760786Sps C2_END_LESSKEY_MAGIC 20860786Sps}; 20960786Spschar cmdsection[1] = { CMD_SECTION }; 21060786Spschar editsection[1] = { EDIT_SECTION }; 21160786Spschar varsection[1] = { VAR_SECTION }; 21260786Spschar endsection[1] = { END_SECTION }; 21360786Sps 21460786Spschar *infile = NULL; 21560786Spschar *outfile = NULL ; 21660786Sps 21760786Spsint linenum; 21860786Spsint errors; 21960786Sps 22060786Spsextern char version[]; 22160786Sps 22260786Sps void 22360786Spsusage() 22460786Sps{ 22560786Sps fprintf(stderr, "usage: lesskey [-o output] [input]\n"); 22660786Sps exit(1); 22760786Sps} 22860786Sps 22960786Sps char * 23060786Spsmkpathname(dirname, filename) 23160786Sps char *dirname; 23260786Sps char *filename; 23360786Sps{ 23460786Sps char *pathname; 23560786Sps 23660786Sps pathname = calloc(strlen(dirname) + strlen(filename) + 2, sizeof(char)); 23760786Sps strcpy(pathname, dirname); 23860786Sps strcat(pathname, PATHNAME_SEP); 23960786Sps strcat(pathname, filename); 24060786Sps return (pathname); 24160786Sps} 24260786Sps 24360786Sps/* 24460786Sps * Figure out the name of a default file (in the user's HOME directory). 24560786Sps */ 24660786Sps char * 24760786Spshomefile(filename) 24860786Sps char *filename; 24960786Sps{ 25060786Sps char *p; 25160786Sps char *pathname; 25260786Sps 25360786Sps if ((p = getenv("HOME")) != NULL && *p != '\0') 25460786Sps pathname = mkpathname(p, filename); 25560786Sps#if OS2 25660786Sps else if ((p = getenv("INIT")) != NULL && *p != '\0') 25760786Sps pathname = mkpathname(p, filename); 25860786Sps#endif 25960786Sps else 26060786Sps { 26160786Sps fprintf(stderr, "cannot find $HOME - using current directory\n"); 26260786Sps pathname = mkpathname(".", filename); 26360786Sps } 26460786Sps return (pathname); 26560786Sps} 26660786Sps 26760786Sps/* 26860786Sps * Parse command line arguments. 26960786Sps */ 27060786Sps void 27160786Spsparse_args(argc, argv) 27260786Sps int argc; 27360786Sps char **argv; 27460786Sps{ 27560786Sps char *arg; 27660786Sps 27760786Sps outfile = NULL; 27860786Sps while (--argc > 0) 27960786Sps { 28060786Sps arg = *++argv; 28160786Sps if (arg[0] != '-') 28260786Sps /* Arg does not start with "-"; it's not an option. */ 28360786Sps break; 28460786Sps if (arg[1] == '\0') 28560786Sps /* "-" means standard input. */ 28660786Sps break; 28760786Sps if (arg[1] == '-' && arg[2] == '\0') 28860786Sps { 28960786Sps /* "--" means end of options. */ 29060786Sps argc--; 29160786Sps argv++; 29260786Sps break; 29360786Sps } 29460786Sps switch (arg[1]) 29560786Sps { 29660786Sps case '-': 29760786Sps if (strncmp(arg, "--output", 8) == 0) 29860786Sps { 29960786Sps if (arg[8] == '\0') 30060786Sps outfile = &arg[8]; 30160786Sps else if (arg[8] == '=') 30260786Sps outfile = &arg[9]; 30360786Sps else 30460786Sps usage(); 30560786Sps goto opt_o; 30660786Sps } 30760786Sps if (strcmp(arg, "--version") == 0) 30860786Sps { 30960786Sps goto opt_V; 31060786Sps } 31160786Sps usage(); 31260786Sps break; 31360786Sps case 'o': 31460786Sps outfile = &argv[0][2]; 31560786Sps opt_o: 31660786Sps if (*outfile == '\0') 31760786Sps { 31860786Sps if (--argc <= 0) 31960786Sps usage(); 32060786Sps outfile = *(++argv); 32160786Sps } 32260786Sps break; 32360786Sps case 'V': 32460786Sps opt_V: 32560786Sps printf("lesskey version %s\n", version); 32660786Sps exit(0); 32760786Sps default: 32860786Sps usage(); 32960786Sps } 33060786Sps } 33160786Sps if (argc > 1) 33260786Sps usage(); 33360786Sps /* 33460786Sps * Open the input file, or use DEF_LESSKEYINFILE if none specified. 33560786Sps */ 33660786Sps if (argc > 0) 33760786Sps infile = *argv; 33860786Sps else 33960786Sps infile = homefile(DEF_LESSKEYINFILE); 34060786Sps} 34160786Sps 34260786Sps/* 34360786Sps * Initialize data structures. 34460786Sps */ 34560786Sps void 34660786Spsinit_tables() 34760786Sps{ 34860786Sps cmdtable.names = cmdnames; 34960786Sps cmdtable.pbuffer = cmdtable.buffer; 35060786Sps 35160786Sps edittable.names = editnames; 35260786Sps edittable.pbuffer = edittable.buffer; 35360786Sps 35460786Sps vartable.names = NULL; 35560786Sps vartable.pbuffer = vartable.buffer; 35660786Sps} 35760786Sps 35860786Sps/* 35960786Sps * Parse one character of a string. 36060786Sps */ 36160786Sps char * 362128345Stjrtstr(pp, xlate) 36360786Sps char **pp; 364128345Stjr int xlate; 36560786Sps{ 366330571Sdelphij char *p; 367330571Sdelphij char ch; 368330571Sdelphij int i; 36960786Sps static char buf[10]; 37060786Sps static char tstr_control_k[] = 37160786Sps { SK_SPECIAL_KEY, SK_CONTROL_K, 6, 1, 1, 1, '\0' }; 37260786Sps 37360786Sps p = *pp; 37460786Sps switch (*p) 37560786Sps { 37660786Sps case '\\': 37760786Sps ++p; 37860786Sps switch (*p) 37960786Sps { 38060786Sps case '0': case '1': case '2': case '3': 38160786Sps case '4': case '5': case '6': case '7': 38260786Sps /* 38360786Sps * Parse an octal number. 38460786Sps */ 38560786Sps ch = 0; 38660786Sps i = 0; 38760786Sps do 38860786Sps ch = 8*ch + (*p - '0'); 38960786Sps while (*++p >= '0' && *p <= '7' && ++i < 3); 39060786Sps *pp = p; 391128345Stjr if (xlate && ch == CONTROL('K')) 39260786Sps return tstr_control_k; 39360786Sps buf[0] = ch; 39460786Sps buf[1] = '\0'; 39560786Sps return (buf); 39660786Sps case 'b': 39760786Sps *pp = p+1; 39860786Sps return ("\b"); 39960786Sps case 'e': 40060786Sps *pp = p+1; 40160786Sps buf[0] = ESC; 40260786Sps buf[1] = '\0'; 40360786Sps return (buf); 40460786Sps case 'n': 40560786Sps *pp = p+1; 40660786Sps return ("\n"); 40760786Sps case 'r': 40860786Sps *pp = p+1; 40960786Sps return ("\r"); 41060786Sps case 't': 41160786Sps *pp = p+1; 41260786Sps return ("\t"); 41360786Sps case 'k': 414128345Stjr if (xlate) 41560786Sps { 416128345Stjr switch (*++p) 417128345Stjr { 418128345Stjr case 'u': ch = SK_UP_ARROW; break; 419128345Stjr case 'd': ch = SK_DOWN_ARROW; break; 420128345Stjr case 'r': ch = SK_RIGHT_ARROW; break; 421128345Stjr case 'l': ch = SK_LEFT_ARROW; break; 422128345Stjr case 'U': ch = SK_PAGE_UP; break; 423128345Stjr case 'D': ch = SK_PAGE_DOWN; break; 424128345Stjr case 'h': ch = SK_HOME; break; 425128345Stjr case 'e': ch = SK_END; break; 426128345Stjr case 'x': ch = SK_DELETE; break; 427128345Stjr default: 428330571Sdelphij error("illegal char after \\k", NULL_PARG); 429128345Stjr *pp = p+1; 430128345Stjr return (""); 431128345Stjr } 43289019Sps *pp = p+1; 433128345Stjr buf[0] = SK_SPECIAL_KEY; 434128345Stjr buf[1] = ch; 435128345Stjr buf[2] = 6; 436128345Stjr buf[3] = 1; 437128345Stjr buf[4] = 1; 438128345Stjr buf[5] = 1; 439128345Stjr buf[6] = '\0'; 440128345Stjr return (buf); 44160786Sps } 442128345Stjr /* FALLTHRU */ 44360786Sps default: 44460786Sps /* 44560786Sps * Backslash followed by any other char 44660786Sps * just means that char. 44760786Sps */ 44860786Sps *pp = p+1; 44960786Sps buf[0] = *p; 45060786Sps buf[1] = '\0'; 451128345Stjr if (xlate && buf[0] == CONTROL('K')) 45260786Sps return tstr_control_k; 45360786Sps return (buf); 45460786Sps } 45560786Sps case '^': 45660786Sps /* 457238730Sdelphij * Caret means CONTROL. 45860786Sps */ 45960786Sps *pp = p+2; 46060786Sps buf[0] = CONTROL(p[1]); 46160786Sps buf[1] = '\0'; 46260786Sps if (buf[0] == CONTROL('K')) 46360786Sps return tstr_control_k; 46460786Sps return (buf); 46560786Sps } 46660786Sps *pp = p+1; 46760786Sps buf[0] = *p; 46860786Sps buf[1] = '\0'; 469128345Stjr if (xlate && buf[0] == CONTROL('K')) 47060786Sps return tstr_control_k; 47160786Sps return (buf); 47260786Sps} 47360786Sps 47460786Sps/* 47560786Sps * Skip leading spaces in a string. 47660786Sps */ 47760786Sps public char * 47860786Spsskipsp(s) 479330571Sdelphij char *s; 48060786Sps{ 48160786Sps while (*s == ' ' || *s == '\t') 48260786Sps s++; 48360786Sps return (s); 48460786Sps} 48560786Sps 48660786Sps/* 48760786Sps * Skip non-space characters in a string. 48860786Sps */ 48960786Sps public char * 49060786Spsskipnsp(s) 491330571Sdelphij char *s; 49260786Sps{ 49360786Sps while (*s != '\0' && *s != ' ' && *s != '\t') 49460786Sps s++; 49560786Sps return (s); 49660786Sps} 49760786Sps 49860786Sps/* 49960786Sps * Clean up an input line: 50060786Sps * strip off the trailing newline & any trailing # comment. 50160786Sps */ 50260786Sps char * 50360786Spsclean_line(s) 50460786Sps char *s; 50560786Sps{ 506330571Sdelphij int i; 50760786Sps 50860786Sps s = skipsp(s); 50960786Sps for (i = 0; s[i] != '\n' && s[i] != '\r' && s[i] != '\0'; i++) 51060786Sps if (s[i] == '#' && (i == 0 || s[i-1] != '\\')) 51160786Sps break; 51260786Sps s[i] = '\0'; 51360786Sps return (s); 51460786Sps} 51560786Sps 51660786Sps/* 51760786Sps * Add a byte to the output command table. 51860786Sps */ 51960786Sps void 52060786Spsadd_cmd_char(c) 52160786Sps int c; 52260786Sps{ 52360786Sps if (currtable->pbuffer >= currtable->buffer + MAX_USERCMD) 52460786Sps { 525330571Sdelphij error("too many commands", NULL_PARG); 52660786Sps exit(1); 52760786Sps } 52860786Sps *(currtable->pbuffer)++ = c; 52960786Sps} 53060786Sps 53160786Sps/* 53260786Sps * Add a string to the output command table. 53360786Sps */ 53460786Sps void 53560786Spsadd_cmd_str(s) 53660786Sps char *s; 53760786Sps{ 53860786Sps for ( ; *s != '\0'; s++) 53960786Sps add_cmd_char(*s); 54060786Sps} 54160786Sps 54260786Sps/* 54360786Sps * See if we have a special "control" line. 54460786Sps */ 54560786Sps int 54660786Spscontrol_line(s) 54760786Sps char *s; 54860786Sps{ 549191930Sdelphij#define PREFIX(str,pat) (strncmp(str,pat,strlen(pat)) == 0) 55060786Sps 55160786Sps if (PREFIX(s, "#line-edit")) 55260786Sps { 55360786Sps currtable = &edittable; 55460786Sps return (1); 55560786Sps } 55660786Sps if (PREFIX(s, "#command")) 55760786Sps { 55860786Sps currtable = &cmdtable; 55960786Sps return (1); 56060786Sps } 56160786Sps if (PREFIX(s, "#env")) 56260786Sps { 56360786Sps currtable = &vartable; 56460786Sps return (1); 56560786Sps } 56660786Sps if (PREFIX(s, "#stop")) 56760786Sps { 56860786Sps add_cmd_char('\0'); 56960786Sps add_cmd_char(A_END_LIST); 57060786Sps return (1); 57160786Sps } 57260786Sps return (0); 57360786Sps} 57460786Sps 57560786Sps/* 57660786Sps * Output some bytes. 57760786Sps */ 57860786Sps void 57960786Spsfputbytes(fd, buf, len) 58060786Sps FILE *fd; 58160786Sps char *buf; 58260786Sps int len; 58360786Sps{ 58460786Sps while (len-- > 0) 58560786Sps { 58660786Sps fwrite(buf, sizeof(char), 1, fd); 58760786Sps buf++; 58860786Sps } 58960786Sps} 59060786Sps 59160786Sps/* 59260786Sps * Output an integer, in special KRADIX form. 59360786Sps */ 59460786Sps void 59560786Spsfputint(fd, val) 59660786Sps FILE *fd; 59760786Sps unsigned int val; 59860786Sps{ 59960786Sps char c; 60060786Sps 60160786Sps if (val >= KRADIX*KRADIX) 60260786Sps { 60360786Sps fprintf(stderr, "error: integer too big (%d > %d)\n", 60460786Sps val, KRADIX*KRADIX); 60560786Sps exit(1); 60660786Sps } 60760786Sps c = val % KRADIX; 60860786Sps fwrite(&c, sizeof(char), 1, fd); 60960786Sps c = val / KRADIX; 61060786Sps fwrite(&c, sizeof(char), 1, fd); 61160786Sps} 61260786Sps 61360786Sps/* 61460786Sps * Find an action, given the name of the action. 61560786Sps */ 61660786Sps int 61760786Spsfindaction(actname) 61860786Sps char *actname; 61960786Sps{ 62060786Sps int i; 62160786Sps 62260786Sps for (i = 0; currtable->names[i].cn_name != NULL; i++) 62360786Sps if (strcmp(currtable->names[i].cn_name, actname) == 0) 62460786Sps return (currtable->names[i].cn_action); 625330571Sdelphij error("unknown action", NULL_PARG); 62660786Sps return (A_INVALID); 62760786Sps} 62860786Sps 62960786Sps void 630330571Sdelphijerror(s, parg) 63160786Sps char *s; 632330571Sdelphij PARG *parg; 63360786Sps{ 63460786Sps fprintf(stderr, "line %d: %s\n", linenum, s); 63560786Sps errors++; 636330571Sdelphij (void) parg; 63760786Sps} 63860786Sps 63960786Sps 64060786Sps void 64160786Spsparse_cmdline(p) 64260786Sps char *p; 64360786Sps{ 64460786Sps int cmdlen; 64560786Sps char *actname; 64660786Sps int action; 64760786Sps char *s; 64860786Sps char c; 64960786Sps 65060786Sps /* 65160786Sps * Parse the command string and store it in the current table. 65260786Sps */ 65360786Sps cmdlen = 0; 65460786Sps do 65560786Sps { 656128345Stjr s = tstr(&p, 1); 657294286Sdelphij cmdlen += (int) strlen(s); 65860786Sps if (cmdlen > MAX_CMDLEN) 659330571Sdelphij error("command too long", NULL_PARG); 66060786Sps else 66160786Sps add_cmd_str(s); 66260786Sps } while (*p != ' ' && *p != '\t' && *p != '\0'); 66360786Sps /* 66460786Sps * Terminate the command string with a null byte. 66560786Sps */ 66660786Sps add_cmd_char('\0'); 66760786Sps 66860786Sps /* 66960786Sps * Skip white space between the command string 67060786Sps * and the action name. 67160786Sps * Terminate the action name with a null byte. 67260786Sps */ 67360786Sps p = skipsp(p); 67460786Sps if (*p == '\0') 67560786Sps { 676330571Sdelphij error("missing action", NULL_PARG); 67760786Sps return; 67860786Sps } 67960786Sps actname = p; 68060786Sps p = skipnsp(p); 68160786Sps c = *p; 68260786Sps *p = '\0'; 68360786Sps 68460786Sps /* 68560786Sps * Parse the action name and store it in the current table. 68660786Sps */ 68760786Sps action = findaction(actname); 68860786Sps 68960786Sps /* 69060786Sps * See if an extra string follows the action name. 69160786Sps */ 69260786Sps *p = c; 69360786Sps p = skipsp(p); 69460786Sps if (*p == '\0') 69560786Sps { 69660786Sps add_cmd_char(action); 69760786Sps } else 69860786Sps { 69960786Sps /* 70060786Sps * OR the special value A_EXTRA into the action byte. 70160786Sps * Put the extra string after the action byte. 70260786Sps */ 70360786Sps add_cmd_char(action | A_EXTRA); 70460786Sps while (*p != '\0') 705128345Stjr add_cmd_str(tstr(&p, 0)); 70660786Sps add_cmd_char('\0'); 70760786Sps } 70860786Sps} 70960786Sps 71060786Sps void 71160786Spsparse_varline(p) 71260786Sps char *p; 71360786Sps{ 71460786Sps char *s; 71560786Sps 71660786Sps do 71760786Sps { 718128345Stjr s = tstr(&p, 0); 71960786Sps add_cmd_str(s); 72060786Sps } while (*p != ' ' && *p != '\t' && *p != '=' && *p != '\0'); 72160786Sps /* 72260786Sps * Terminate the variable name with a null byte. 72360786Sps */ 72460786Sps add_cmd_char('\0'); 72560786Sps 72660786Sps p = skipsp(p); 72760786Sps if (*p++ != '=') 72860786Sps { 729330571Sdelphij error("missing =", NULL_PARG); 73060786Sps return; 73160786Sps } 73260786Sps 73360786Sps add_cmd_char(EV_OK|A_EXTRA); 73460786Sps 73560786Sps p = skipsp(p); 73660786Sps while (*p != '\0') 73760786Sps { 738128345Stjr s = tstr(&p, 0); 73960786Sps add_cmd_str(s); 74060786Sps } 74160786Sps add_cmd_char('\0'); 74260786Sps} 74360786Sps 74460786Sps/* 74560786Sps * Parse a line from the lesskey file. 74660786Sps */ 74760786Sps void 74860786Spsparse_line(line) 74960786Sps char *line; 75060786Sps{ 75160786Sps char *p; 75260786Sps 75360786Sps /* 75460786Sps * See if it is a control line. 75560786Sps */ 75660786Sps if (control_line(line)) 75760786Sps return; 75860786Sps /* 75960786Sps * Skip leading white space. 76060786Sps * Replace the final newline with a null byte. 76160786Sps * Ignore blank lines and comments. 76260786Sps */ 76360786Sps p = clean_line(line); 76460786Sps if (*p == '\0') 76560786Sps return; 76660786Sps 76760786Sps if (currtable == &vartable) 76860786Sps parse_varline(p); 76960786Sps else 77060786Sps parse_cmdline(p); 77160786Sps} 77260786Sps 77360786Sps int 77460786Spsmain(argc, argv) 77560786Sps int argc; 77660786Sps char *argv[]; 77760786Sps{ 77860786Sps FILE *desc; 77960786Sps FILE *out; 780128345Stjr char line[1024]; 78160786Sps 78260786Sps#ifdef WIN32 78360786Sps if (getenv("HOME") == NULL) 78460786Sps { 78560786Sps /* 78660786Sps * If there is no HOME environment variable, 78760786Sps * try the concatenation of HOMEDRIVE + HOMEPATH. 78860786Sps */ 78960786Sps char *drive = getenv("HOMEDRIVE"); 79060786Sps char *path = getenv("HOMEPATH"); 79160786Sps if (drive != NULL && path != NULL) 79260786Sps { 79360786Sps char *env = (char *) calloc(strlen(drive) + 79460786Sps strlen(path) + 6, sizeof(char)); 79560786Sps strcpy(env, "HOME="); 79660786Sps strcat(env, drive); 79760786Sps strcat(env, path); 79860786Sps putenv(env); 79960786Sps } 80060786Sps } 80160786Sps#endif /* WIN32 */ 80260786Sps 80360786Sps /* 80460786Sps * Process command line arguments. 80560786Sps */ 80660786Sps parse_args(argc, argv); 80760786Sps init_tables(); 80860786Sps 80960786Sps /* 81060786Sps * Open the input file. 81160786Sps */ 81260786Sps if (strcmp(infile, "-") == 0) 81360786Sps desc = stdin; 81460786Sps else if ((desc = fopen(infile, "r")) == NULL) 81560786Sps { 81660786Sps#if HAVE_PERROR 81760786Sps perror(infile); 81860786Sps#else 81960786Sps fprintf(stderr, "Cannot open %s\n", infile); 82060786Sps#endif 82160786Sps usage(); 82260786Sps } 82360786Sps 82460786Sps /* 82560786Sps * Read and parse the input file, one line at a time. 82660786Sps */ 82760786Sps errors = 0; 82860786Sps linenum = 0; 82960786Sps while (fgets(line, sizeof(line), desc) != NULL) 83060786Sps { 83160786Sps ++linenum; 83260786Sps parse_line(line); 83360786Sps } 83460786Sps 83560786Sps /* 83660786Sps * Write the output file. 83760786Sps * If no output file was specified, use "$HOME/.less" 83860786Sps */ 83960786Sps if (errors > 0) 84060786Sps { 84160786Sps fprintf(stderr, "%d errors; no output produced\n", errors); 84260786Sps exit(1); 84360786Sps } 84460786Sps 84560786Sps if (outfile == NULL) 84660786Sps outfile = getenv("LESSKEY"); 84760786Sps if (outfile == NULL) 84860786Sps outfile = homefile(LESSKEYFILE); 84960786Sps if ((out = fopen(outfile, "wb")) == NULL) 85060786Sps { 85160786Sps#if HAVE_PERROR 85260786Sps perror(outfile); 85360786Sps#else 85460786Sps fprintf(stderr, "Cannot open %s\n", outfile); 85560786Sps#endif 85660786Sps exit(1); 85760786Sps } 85860786Sps 85960786Sps /* File header */ 86060786Sps fputbytes(out, fileheader, sizeof(fileheader)); 86160786Sps 86260786Sps /* Command key section */ 86360786Sps fputbytes(out, cmdsection, sizeof(cmdsection)); 86460786Sps fputint(out, cmdtable.pbuffer - cmdtable.buffer); 86560786Sps fputbytes(out, (char *)cmdtable.buffer, cmdtable.pbuffer-cmdtable.buffer); 86660786Sps /* Edit key section */ 86760786Sps fputbytes(out, editsection, sizeof(editsection)); 86860786Sps fputint(out, edittable.pbuffer - edittable.buffer); 86960786Sps fputbytes(out, (char *)edittable.buffer, edittable.pbuffer-edittable.buffer); 87060786Sps 87160786Sps /* Environment variable section */ 87260786Sps fputbytes(out, varsection, sizeof(varsection)); 87360786Sps fputint(out, vartable.pbuffer - vartable.buffer); 87460786Sps fputbytes(out, (char *)vartable.buffer, vartable.pbuffer-vartable.buffer); 87560786Sps 87660786Sps /* File trailer */ 87760786Sps fputbytes(out, endsection, sizeof(endsection)); 87860786Sps fputbytes(out, filetrailer, sizeof(filetrailer)); 87960786Sps return (0); 88060786Sps} 881