130a1828cSXin LI /* 2*c77c4889SXin LI * Copyright (C) 1984-2024 Mark Nudelman 330a1828cSXin LI * 430a1828cSXin LI * You may distribute under the terms of either the GNU General Public 530a1828cSXin LI * License or the Less License, as specified in the README file. 630a1828cSXin LI * 730a1828cSXin LI * For more information, see the README file. 830a1828cSXin LI */ 930a1828cSXin LI 10d713e089SXin LI #include "defines.h" 1130a1828cSXin LI #include <stdio.h> 1230a1828cSXin LI #include <string.h> 1330a1828cSXin LI #include <stdlib.h> 1430a1828cSXin LI #include "lesskey.h" 1530a1828cSXin LI #include "cmd.h" 1630a1828cSXin LI #include "xbuf.h" 1730a1828cSXin LI 1830a1828cSXin LI #define CONTROL(c) ((c)&037) 1930a1828cSXin LI #define ESC CONTROL('[') 2030a1828cSXin LI 2130a1828cSXin LI extern void lesskey_parse_error(char *msg); 2230a1828cSXin LI extern char *homefile(char *filename); 23*c77c4889SXin LI extern void *ecalloc(size_t count, size_t size); 24d713e089SXin LI extern int lstrtoi(char *str, char **end, int radix); 2595270f73SXin LI extern char version[]; 2630a1828cSXin LI 2730a1828cSXin LI static int linenum; 2830a1828cSXin LI static int errors; 2995270f73SXin LI static int less_version = 0; 30*c77c4889SXin LI static char *lesskey_file = NULL; 3130a1828cSXin LI 32*c77c4889SXin LI static constant struct lesskey_cmdname cmdnames[] = 3330a1828cSXin LI { 3430a1828cSXin LI { "back-bracket", A_B_BRACKET }, 3530a1828cSXin LI { "back-line", A_B_LINE }, 3630a1828cSXin LI { "back-line-force", A_BF_LINE }, 3730a1828cSXin LI { "back-screen", A_B_SCREEN }, 3830a1828cSXin LI { "back-scroll", A_B_SCROLL }, 3930a1828cSXin LI { "back-search", A_B_SEARCH }, 4030a1828cSXin LI { "back-window", A_B_WINDOW }, 4130a1828cSXin LI { "clear-mark", A_CLRMARK }, 42*c77c4889SXin LI { "clear-search", A_CLR_SEARCH }, 4330a1828cSXin LI { "debug", A_DEBUG }, 4430a1828cSXin LI { "digit", A_DIGIT }, 4530a1828cSXin LI { "display-flag", A_DISP_OPTION }, 4630a1828cSXin LI { "display-option", A_DISP_OPTION }, 4730a1828cSXin LI { "end", A_GOEND }, 4830a1828cSXin LI { "end-scroll", A_RRSHIFT }, 4930a1828cSXin LI { "examine", A_EXAMINE }, 5030a1828cSXin LI { "filter", A_FILTER }, 5130a1828cSXin LI { "first-cmd", A_FIRSTCMD }, 5230a1828cSXin LI { "firstcmd", A_FIRSTCMD }, 5330a1828cSXin LI { "flush-repaint", A_FREPAINT }, 5430a1828cSXin LI { "forw-bracket", A_F_BRACKET }, 5530a1828cSXin LI { "forw-forever", A_F_FOREVER }, 5630a1828cSXin LI { "forw-line", A_F_LINE }, 5730a1828cSXin LI { "forw-line-force", A_FF_LINE }, 5830a1828cSXin LI { "forw-screen", A_F_SCREEN }, 5930a1828cSXin LI { "forw-screen-force", A_FF_SCREEN }, 6030a1828cSXin LI { "forw-scroll", A_F_SCROLL }, 6130a1828cSXin LI { "forw-search", A_F_SEARCH }, 62*c77c4889SXin LI { "forw-until-hilite", A_F_UNTIL_HILITE }, 6330a1828cSXin LI { "forw-window", A_F_WINDOW }, 6430a1828cSXin LI { "goto-end", A_GOEND }, 6530a1828cSXin LI { "goto-end-buffered", A_GOEND_BUF }, 6630a1828cSXin LI { "goto-line", A_GOLINE }, 6730a1828cSXin LI { "goto-mark", A_GOMARK }, 6830a1828cSXin LI { "help", A_HELP }, 6930a1828cSXin LI { "index-file", A_INDEX_FILE }, 7030a1828cSXin LI { "invalid", A_UINVALID }, 7130a1828cSXin LI { "left-scroll", A_LSHIFT }, 7230a1828cSXin LI { "next-file", A_NEXT_FILE }, 7330a1828cSXin LI { "next-tag", A_NEXT_TAG }, 7430a1828cSXin LI { "no-scroll", A_LLSHIFT }, 75*c77c4889SXin LI { "noaction", A_NOACTION }, 76*c77c4889SXin LI { "osc8-forw-search", A_OSC8_F_SEARCH }, 77*c77c4889SXin LI { "osc8-back-search", A_OSC8_B_SEARCH }, 78*c77c4889SXin LI { "osc8-open", A_OSC8_OPEN }, 7930a1828cSXin LI { "percent", A_PERCENT }, 8030a1828cSXin LI { "pipe", A_PIPE }, 8130a1828cSXin LI { "prev-file", A_PREV_FILE }, 8230a1828cSXin LI { "prev-tag", A_PREV_TAG }, 83*c77c4889SXin LI { "pshell", A_PSHELL }, 8430a1828cSXin LI { "quit", A_QUIT }, 8530a1828cSXin LI { "remove-file", A_REMOVE_FILE }, 8630a1828cSXin LI { "repaint", A_REPAINT }, 8730a1828cSXin LI { "repaint-flush", A_FREPAINT }, 8830a1828cSXin LI { "repeat-search", A_AGAIN_SEARCH }, 8930a1828cSXin LI { "repeat-search-all", A_T_AGAIN_SEARCH }, 9030a1828cSXin LI { "reverse-search", A_REVERSE_SEARCH }, 9130a1828cSXin LI { "reverse-search-all", A_T_REVERSE_SEARCH }, 9230a1828cSXin LI { "right-scroll", A_RSHIFT }, 9330a1828cSXin LI { "set-mark", A_SETMARK }, 9430a1828cSXin LI { "set-mark-bottom", A_SETMARKBOT }, 9530a1828cSXin LI { "shell", A_SHELL }, 9630a1828cSXin LI { "status", A_STAT }, 9730a1828cSXin LI { "toggle-flag", A_OPT_TOGGLE }, 9830a1828cSXin LI { "toggle-option", A_OPT_TOGGLE }, 9930a1828cSXin LI { "undo-hilite", A_UNDO_SEARCH }, 10030a1828cSXin LI { "version", A_VERSION }, 10130a1828cSXin LI { "visual", A_VISUAL }, 10230a1828cSXin LI { NULL, 0 } 10330a1828cSXin LI }; 10430a1828cSXin LI 105*c77c4889SXin LI static constant struct lesskey_cmdname editnames[] = 10630a1828cSXin LI { 10730a1828cSXin LI { "back-complete", EC_B_COMPLETE }, 10830a1828cSXin LI { "backspace", EC_BACKSPACE }, 10930a1828cSXin LI { "delete", EC_DELETE }, 11030a1828cSXin LI { "down", EC_DOWN }, 11130a1828cSXin LI { "end", EC_END }, 11230a1828cSXin LI { "expand", EC_EXPAND }, 11330a1828cSXin LI { "forw-complete", EC_F_COMPLETE }, 11430a1828cSXin LI { "home", EC_HOME }, 11530a1828cSXin LI { "insert", EC_INSERT }, 11630a1828cSXin LI { "invalid", EC_UINVALID }, 11730a1828cSXin LI { "kill-line", EC_LINEKILL }, 11830a1828cSXin LI { "abort", EC_ABORT }, 11930a1828cSXin LI { "left", EC_LEFT }, 12030a1828cSXin LI { "literal", EC_LITERAL }, 12130a1828cSXin LI { "right", EC_RIGHT }, 12230a1828cSXin LI { "up", EC_UP }, 12330a1828cSXin LI { "word-backspace", EC_W_BACKSPACE }, 12430a1828cSXin LI { "word-delete", EC_W_DELETE }, 12530a1828cSXin LI { "word-left", EC_W_LEFT }, 12630a1828cSXin LI { "word-right", EC_W_RIGHT }, 12730a1828cSXin LI { NULL, 0 } 12830a1828cSXin LI }; 12930a1828cSXin LI 13030a1828cSXin LI /* 13130a1828cSXin LI * Print a parse error message. 13230a1828cSXin LI */ 133*c77c4889SXin LI static void parse_error(constant char *fmt, constant char *arg1) 13430a1828cSXin LI { 13530a1828cSXin LI char buf[1024]; 136*c77c4889SXin LI int n = SNPRINTF2(buf, sizeof(buf), "%s: line %d: ", lesskey_file, linenum); 137*c77c4889SXin LI if (n >= 0) 138*c77c4889SXin LI { 139*c77c4889SXin LI size_t len = (size_t) n; 140*c77c4889SXin LI if (len < sizeof(buf)) 141*c77c4889SXin LI SNPRINTF1(buf+len, sizeof(buf)-len, fmt, arg1); 142*c77c4889SXin LI } 14330a1828cSXin LI ++errors; 14430a1828cSXin LI lesskey_parse_error(buf); 14530a1828cSXin LI } 14630a1828cSXin LI 14730a1828cSXin LI /* 14830a1828cSXin LI * Initialize lesskey_tables. 14930a1828cSXin LI */ 150d713e089SXin LI static void init_tables(struct lesskey_tables *tables) 15130a1828cSXin LI { 15230a1828cSXin LI tables->currtable = &tables->cmdtable; 15330a1828cSXin LI 15430a1828cSXin LI tables->cmdtable.names = cmdnames; 15530a1828cSXin LI tables->cmdtable.is_var = 0; 15630a1828cSXin LI xbuf_init(&tables->cmdtable.buf); 15730a1828cSXin LI 15830a1828cSXin LI tables->edittable.names = editnames; 15930a1828cSXin LI tables->edittable.is_var = 0; 16030a1828cSXin LI xbuf_init(&tables->edittable.buf); 16130a1828cSXin LI 16230a1828cSXin LI tables->vartable.names = NULL; 16330a1828cSXin LI tables->vartable.is_var = 1; 16430a1828cSXin LI xbuf_init(&tables->vartable.buf); 16530a1828cSXin LI } 16630a1828cSXin LI 16795270f73SXin LI #define CHAR_STRING_LEN 8 16895270f73SXin LI 169*c77c4889SXin LI static constant char * char_string(char *buf, char ch, int lit) 17095270f73SXin LI { 17195270f73SXin LI if (lit || (ch >= 0x20 && ch < 0x7f)) 17295270f73SXin LI { 17395270f73SXin LI buf[0] = ch; 17495270f73SXin LI buf[1] = '\0'; 17595270f73SXin LI } else 17695270f73SXin LI { 177*c77c4889SXin LI SNPRINTF1(buf, CHAR_STRING_LEN, "\\x%02x", ch); 17895270f73SXin LI } 17995270f73SXin LI return buf; 18095270f73SXin LI } 18195270f73SXin LI 18295270f73SXin LI /* 18395270f73SXin LI * Increment char pointer by one up to terminating nul byte. 18495270f73SXin LI */ 185d713e089SXin LI static char * increment_pointer(char *p) 18695270f73SXin LI { 18795270f73SXin LI if (*p == '\0') 18895270f73SXin LI return p; 18995270f73SXin LI return p+1; 19095270f73SXin LI } 19195270f73SXin LI 19230a1828cSXin LI /* 19330a1828cSXin LI * Parse one character of a string. 19430a1828cSXin LI */ 195*c77c4889SXin LI static constant char * tstr(char **pp, int xlate) 19630a1828cSXin LI { 19730a1828cSXin LI char *p; 19830a1828cSXin LI char ch; 19930a1828cSXin LI int i; 20095270f73SXin LI static char buf[CHAR_STRING_LEN]; 20130a1828cSXin LI static char tstr_control_k[] = 20230a1828cSXin LI { SK_SPECIAL_KEY, SK_CONTROL_K, 6, 1, 1, 1, '\0' }; 20330a1828cSXin LI 20430a1828cSXin LI p = *pp; 20530a1828cSXin LI switch (*p) 20630a1828cSXin LI { 20730a1828cSXin LI case '\\': 20830a1828cSXin LI ++p; 20930a1828cSXin LI switch (*p) 21030a1828cSXin LI { 21130a1828cSXin LI case '0': case '1': case '2': case '3': 21230a1828cSXin LI case '4': case '5': case '6': case '7': 21330a1828cSXin LI /* 21430a1828cSXin LI * Parse an octal number. 21530a1828cSXin LI */ 21630a1828cSXin LI ch = 0; 21730a1828cSXin LI i = 0; 21830a1828cSXin LI do 219*c77c4889SXin LI ch = (char) (8*ch + (*p - '0')); 22030a1828cSXin LI while (*++p >= '0' && *p <= '7' && ++i < 3); 22130a1828cSXin LI *pp = p; 22230a1828cSXin LI if (xlate && ch == CONTROL('K')) 22330a1828cSXin LI return tstr_control_k; 22495270f73SXin LI return char_string(buf, ch, 1); 22530a1828cSXin LI case 'b': 22630a1828cSXin LI *pp = p+1; 22730a1828cSXin LI return ("\b"); 22830a1828cSXin LI case 'e': 22930a1828cSXin LI *pp = p+1; 23095270f73SXin LI return char_string(buf, ESC, 1); 23130a1828cSXin LI case 'n': 23230a1828cSXin LI *pp = p+1; 23330a1828cSXin LI return ("\n"); 23430a1828cSXin LI case 'r': 23530a1828cSXin LI *pp = p+1; 23630a1828cSXin LI return ("\r"); 23730a1828cSXin LI case 't': 23830a1828cSXin LI *pp = p+1; 23930a1828cSXin LI return ("\t"); 24030a1828cSXin LI case 'k': 24130a1828cSXin LI if (xlate) 24230a1828cSXin LI { 24330a1828cSXin LI switch (*++p) 24430a1828cSXin LI { 24595270f73SXin LI case 'b': ch = SK_BACKSPACE; break; 24695270f73SXin LI case 'B': ch = SK_CTL_BACKSPACE; break; 24730a1828cSXin LI case 'd': ch = SK_DOWN_ARROW; break; 24830a1828cSXin LI case 'D': ch = SK_PAGE_DOWN; break; 24930a1828cSXin LI case 'e': ch = SK_END; break; 25095270f73SXin LI case 'h': ch = SK_HOME; break; 25195270f73SXin LI case 'i': ch = SK_INSERT; break; 25295270f73SXin LI case 'l': ch = SK_LEFT_ARROW; break; 25395270f73SXin LI case 'L': ch = SK_CTL_LEFT_ARROW; break; 25495270f73SXin LI case 'r': ch = SK_RIGHT_ARROW; break; 25595270f73SXin LI case 'R': ch = SK_CTL_RIGHT_ARROW; break; 25695270f73SXin LI case 't': ch = SK_BACKTAB; break; 25795270f73SXin LI case 'u': ch = SK_UP_ARROW; break; 25895270f73SXin LI case 'U': ch = SK_PAGE_UP; break; 25930a1828cSXin LI case 'x': ch = SK_DELETE; break; 26095270f73SXin LI case 'X': ch = SK_CTL_DELETE; break; 26195270f73SXin LI case '1': ch = SK_F1; break; 26295270f73SXin LI default: 26395270f73SXin LI parse_error("invalid escape sequence \"\\k%s\"", char_string(buf, *p, 0)); 26495270f73SXin LI *pp = increment_pointer(p); 26595270f73SXin LI return (""); 26630a1828cSXin LI } 26730a1828cSXin LI *pp = p+1; 26830a1828cSXin LI buf[0] = SK_SPECIAL_KEY; 26930a1828cSXin LI buf[1] = ch; 27030a1828cSXin LI buf[2] = 6; 27130a1828cSXin LI buf[3] = 1; 27230a1828cSXin LI buf[4] = 1; 27330a1828cSXin LI buf[5] = 1; 27430a1828cSXin LI buf[6] = '\0'; 27530a1828cSXin LI return (buf); 27630a1828cSXin LI } 27730a1828cSXin LI /* FALLTHRU */ 27830a1828cSXin LI default: 27930a1828cSXin LI /* 28030a1828cSXin LI * Backslash followed by any other char 28130a1828cSXin LI * just means that char. 28230a1828cSXin LI */ 28395270f73SXin LI *pp = increment_pointer(p); 28495270f73SXin LI char_string(buf, *p, 1); 28530a1828cSXin LI if (xlate && buf[0] == CONTROL('K')) 28630a1828cSXin LI return tstr_control_k; 28730a1828cSXin LI return (buf); 28830a1828cSXin LI } 28930a1828cSXin LI case '^': 29030a1828cSXin LI /* 29130a1828cSXin LI * Caret means CONTROL. 29230a1828cSXin LI */ 29395270f73SXin LI *pp = increment_pointer(p+1); 29495270f73SXin LI char_string(buf, CONTROL(p[1]), 1); 29530a1828cSXin LI if (xlate && buf[0] == CONTROL('K')) 29630a1828cSXin LI return tstr_control_k; 29730a1828cSXin LI return (buf); 29830a1828cSXin LI } 29995270f73SXin LI *pp = increment_pointer(p); 30095270f73SXin LI char_string(buf, *p, 1); 30130a1828cSXin LI if (xlate && buf[0] == CONTROL('K')) 30230a1828cSXin LI return tstr_control_k; 30330a1828cSXin LI return (buf); 30430a1828cSXin LI } 30530a1828cSXin LI 306d713e089SXin LI static int issp(char ch) 30730a1828cSXin LI { 30830a1828cSXin LI return (ch == ' ' || ch == '\t'); 30930a1828cSXin LI } 31030a1828cSXin LI 31130a1828cSXin LI /* 31230a1828cSXin LI * Skip leading spaces in a string. 31330a1828cSXin LI */ 314d713e089SXin LI static char * skipsp(char *s) 31530a1828cSXin LI { 31630a1828cSXin LI while (issp(*s)) 31730a1828cSXin LI s++; 31830a1828cSXin LI return (s); 31930a1828cSXin LI } 32030a1828cSXin LI 32130a1828cSXin LI /* 32230a1828cSXin LI * Skip non-space characters in a string. 32330a1828cSXin LI */ 324d713e089SXin LI static char * skipnsp(char *s) 32530a1828cSXin LI { 32630a1828cSXin LI while (*s != '\0' && !issp(*s)) 32730a1828cSXin LI s++; 32830a1828cSXin LI return (s); 32930a1828cSXin LI } 33030a1828cSXin LI 33130a1828cSXin LI /* 33230a1828cSXin LI * Clean up an input line: 33330a1828cSXin LI * strip off the trailing newline & any trailing # comment. 33430a1828cSXin LI */ 335d713e089SXin LI static char * clean_line(char *s) 33630a1828cSXin LI { 33730a1828cSXin LI int i; 33830a1828cSXin LI 33930a1828cSXin LI s = skipsp(s); 34030a1828cSXin LI for (i = 0; s[i] != '\0' && s[i] != '\n' && s[i] != '\r'; i++) 34130a1828cSXin LI if (s[i] == '#' && (i == 0 || s[i-1] != '\\')) 34230a1828cSXin LI break; 34330a1828cSXin LI s[i] = '\0'; 34430a1828cSXin LI return (s); 34530a1828cSXin LI } 34630a1828cSXin LI 34730a1828cSXin LI /* 34830a1828cSXin LI * Add a byte to the output command table. 34930a1828cSXin LI */ 350d713e089SXin LI static void add_cmd_char(unsigned char c, struct lesskey_tables *tables) 35130a1828cSXin LI { 352d713e089SXin LI xbuf_add_byte(&tables->currtable->buf, c); 35330a1828cSXin LI } 35430a1828cSXin LI 355d713e089SXin LI static void erase_cmd_char(struct lesskey_tables *tables) 35695270f73SXin LI { 35795270f73SXin LI xbuf_pop(&tables->currtable->buf); 35895270f73SXin LI } 35995270f73SXin LI 36030a1828cSXin LI /* 36130a1828cSXin LI * Add a string to the output command table. 36230a1828cSXin LI */ 363*c77c4889SXin LI static void add_cmd_str(constant char *s, struct lesskey_tables *tables) 36430a1828cSXin LI { 36530a1828cSXin LI for ( ; *s != '\0'; s++) 366*c77c4889SXin LI add_cmd_char((unsigned char) *s, tables); 36730a1828cSXin LI } 36830a1828cSXin LI 36930a1828cSXin LI /* 37095270f73SXin LI * Does a given version number match the running version? 37195270f73SXin LI * Operator compares the running version to the given version. 37230a1828cSXin LI */ 373d713e089SXin LI static int match_version(char op, int ver) 37495270f73SXin LI { 37595270f73SXin LI switch (op) 37695270f73SXin LI { 37795270f73SXin LI case '>': return less_version > ver; 37895270f73SXin LI case '<': return less_version < ver; 37995270f73SXin LI case '+': return less_version >= ver; 38095270f73SXin LI case '-': return less_version <= ver; 38195270f73SXin LI case '=': return less_version == ver; 38295270f73SXin LI case '!': return less_version != ver; 38395270f73SXin LI default: return 0; /* cannot happen */ 38495270f73SXin LI } 38595270f73SXin LI } 38695270f73SXin LI 38795270f73SXin LI /* 38895270f73SXin LI * Handle a #version line. 38995270f73SXin LI * If the version matches, return the part of the line that should be executed. 39095270f73SXin LI * Otherwise, return NULL. 39195270f73SXin LI */ 392*c77c4889SXin LI static char * version_line(char *s) 39395270f73SXin LI { 39495270f73SXin LI char op; 39595270f73SXin LI int ver; 39695270f73SXin LI char *e; 39795270f73SXin LI char buf[CHAR_STRING_LEN]; 39895270f73SXin LI 39995270f73SXin LI s += strlen("#version"); 40095270f73SXin LI s = skipsp(s); 40195270f73SXin LI op = *s++; 40295270f73SXin LI /* Simplify 2-char op to one char. */ 40395270f73SXin LI switch (op) 40495270f73SXin LI { 40595270f73SXin LI case '<': if (*s == '=') { s++; op = '-'; } break; 40695270f73SXin LI case '>': if (*s == '=') { s++; op = '+'; } break; 40795270f73SXin LI case '=': if (*s == '=') { s++; } break; 40895270f73SXin LI case '!': if (*s == '=') { s++; } break; 40995270f73SXin LI default: 41095270f73SXin LI parse_error("invalid operator '%s' in #version line", char_string(buf, op, 0)); 41195270f73SXin LI return (NULL); 41295270f73SXin LI } 41395270f73SXin LI s = skipsp(s); 414d713e089SXin LI ver = lstrtoi(s, &e, 10); 41595270f73SXin LI if (e == s) 41695270f73SXin LI { 41795270f73SXin LI parse_error("non-numeric version number in #version line", ""); 41895270f73SXin LI return (NULL); 41995270f73SXin LI } 42095270f73SXin LI if (!match_version(op, ver)) 42195270f73SXin LI return (NULL); 42295270f73SXin LI return (e); 42395270f73SXin LI } 42495270f73SXin LI 42595270f73SXin LI /* 42695270f73SXin LI * See if we have a special "control" line. 42795270f73SXin LI */ 428d713e089SXin LI static char * control_line(char *s, struct lesskey_tables *tables) 42930a1828cSXin LI { 43030a1828cSXin LI #define PREFIX(str,pat) (strncmp(str,pat,strlen(pat)) == 0) 43130a1828cSXin LI 43230a1828cSXin LI if (PREFIX(s, "#line-edit")) 43330a1828cSXin LI { 43430a1828cSXin LI tables->currtable = &tables->edittable; 43595270f73SXin LI return (NULL); 43630a1828cSXin LI } 43730a1828cSXin LI if (PREFIX(s, "#command")) 43830a1828cSXin LI { 43930a1828cSXin LI tables->currtable = &tables->cmdtable; 44095270f73SXin LI return (NULL); 44130a1828cSXin LI } 44230a1828cSXin LI if (PREFIX(s, "#env")) 44330a1828cSXin LI { 44430a1828cSXin LI tables->currtable = &tables->vartable; 44595270f73SXin LI return (NULL); 44630a1828cSXin LI } 44730a1828cSXin LI if (PREFIX(s, "#stop")) 44830a1828cSXin LI { 44930a1828cSXin LI add_cmd_char('\0', tables); 45030a1828cSXin LI add_cmd_char(A_END_LIST, tables); 45195270f73SXin LI return (NULL); 45230a1828cSXin LI } 45395270f73SXin LI if (PREFIX(s, "#version")) 45495270f73SXin LI { 455*c77c4889SXin LI return (version_line(s)); 45695270f73SXin LI } 45795270f73SXin LI return (s); 45830a1828cSXin LI } 45930a1828cSXin LI 46030a1828cSXin LI /* 46130a1828cSXin LI * Find an action, given the name of the action. 46230a1828cSXin LI */ 463d713e089SXin LI static int findaction(char *actname, struct lesskey_tables *tables) 46430a1828cSXin LI { 46530a1828cSXin LI int i; 46630a1828cSXin LI 46730a1828cSXin LI for (i = 0; tables->currtable->names[i].cn_name != NULL; i++) 46830a1828cSXin LI if (strcmp(tables->currtable->names[i].cn_name, actname) == 0) 46930a1828cSXin LI return (tables->currtable->names[i].cn_action); 47095270f73SXin LI parse_error("unknown action: \"%s\"", actname); 47130a1828cSXin LI return (A_INVALID); 47230a1828cSXin LI } 47330a1828cSXin LI 47430a1828cSXin LI /* 47530a1828cSXin LI * Parse a line describing one key binding, of the form 47630a1828cSXin LI * KEY ACTION [EXTRA] 47730a1828cSXin LI * where KEY is the user key sequence, ACTION is the 47830a1828cSXin LI * resulting less action, and EXTRA is an "extra" user 47930a1828cSXin LI * key sequence injected after the action. 48030a1828cSXin LI */ 481d713e089SXin LI static void parse_cmdline(char *p, struct lesskey_tables *tables) 48230a1828cSXin LI { 48330a1828cSXin LI char *actname; 48430a1828cSXin LI int action; 485*c77c4889SXin LI constant char *s; 48630a1828cSXin LI char c; 48730a1828cSXin LI 48830a1828cSXin LI /* 48930a1828cSXin LI * Parse the command string and store it in the current table. 49030a1828cSXin LI */ 49130a1828cSXin LI do 49230a1828cSXin LI { 49330a1828cSXin LI s = tstr(&p, 1); 49430a1828cSXin LI add_cmd_str(s, tables); 49530a1828cSXin LI } while (*p != '\0' && !issp(*p)); 49630a1828cSXin LI /* 49730a1828cSXin LI * Terminate the command string with a null byte. 49830a1828cSXin LI */ 49930a1828cSXin LI add_cmd_char('\0', tables); 50030a1828cSXin LI 50130a1828cSXin LI /* 50230a1828cSXin LI * Skip white space between the command string 50330a1828cSXin LI * and the action name. 50430a1828cSXin LI * Terminate the action name with a null byte. 50530a1828cSXin LI */ 50630a1828cSXin LI p = skipsp(p); 50730a1828cSXin LI if (*p == '\0') 50830a1828cSXin LI { 50930a1828cSXin LI parse_error("missing action", ""); 51030a1828cSXin LI return; 51130a1828cSXin LI } 51230a1828cSXin LI actname = p; 51330a1828cSXin LI p = skipnsp(p); 51430a1828cSXin LI c = *p; 51530a1828cSXin LI *p = '\0'; 51630a1828cSXin LI 51730a1828cSXin LI /* 51830a1828cSXin LI * Parse the action name and store it in the current table. 51930a1828cSXin LI */ 52030a1828cSXin LI action = findaction(actname, tables); 52130a1828cSXin LI 52230a1828cSXin LI /* 52330a1828cSXin LI * See if an extra string follows the action name. 52430a1828cSXin LI */ 52530a1828cSXin LI *p = c; 52630a1828cSXin LI p = skipsp(p); 52730a1828cSXin LI if (*p == '\0') 52830a1828cSXin LI { 529d713e089SXin LI add_cmd_char((unsigned char) action, tables); 53030a1828cSXin LI } else 53130a1828cSXin LI { 53230a1828cSXin LI /* 53330a1828cSXin LI * OR the special value A_EXTRA into the action byte. 53430a1828cSXin LI * Put the extra string after the action byte. 53530a1828cSXin LI */ 536d713e089SXin LI add_cmd_char((unsigned char) (action | A_EXTRA), tables); 53730a1828cSXin LI while (*p != '\0') 53830a1828cSXin LI add_cmd_str(tstr(&p, 0), tables); 53930a1828cSXin LI add_cmd_char('\0', tables); 54030a1828cSXin LI } 54130a1828cSXin LI } 54230a1828cSXin LI 54330a1828cSXin LI /* 54430a1828cSXin LI * Parse a variable definition line, of the form 54530a1828cSXin LI * NAME = VALUE 54630a1828cSXin LI */ 547d713e089SXin LI static void parse_varline(char *line, struct lesskey_tables *tables) 54830a1828cSXin LI { 549*c77c4889SXin LI constant char *s; 55030a1828cSXin LI char *p = line; 55195270f73SXin LI char *eq; 55230a1828cSXin LI 55395270f73SXin LI eq = strchr(line, '='); 55495270f73SXin LI if (eq != NULL && eq > line && eq[-1] == '+') 55595270f73SXin LI { 55695270f73SXin LI /* 55795270f73SXin LI * Rather ugly way of handling a += line. 55895270f73SXin LI * {{ Note that we ignore the variable name and 55995270f73SXin LI * just append to the previously defined variable. }} 56095270f73SXin LI */ 56195270f73SXin LI erase_cmd_char(tables); /* backspace over the final null */ 56295270f73SXin LI p = eq+1; 56395270f73SXin LI } else 56495270f73SXin LI { 56530a1828cSXin LI do 56630a1828cSXin LI { 56730a1828cSXin LI s = tstr(&p, 0); 56830a1828cSXin LI add_cmd_str(s, tables); 56930a1828cSXin LI } while (*p != '\0' && !issp(*p) && *p != '='); 57030a1828cSXin LI /* 57130a1828cSXin LI * Terminate the variable name with a null byte. 57230a1828cSXin LI */ 57330a1828cSXin LI add_cmd_char('\0', tables); 57430a1828cSXin LI p = skipsp(p); 57530a1828cSXin LI if (*p++ != '=') 57630a1828cSXin LI { 57795270f73SXin LI parse_error("missing = in variable definition", ""); 57830a1828cSXin LI return; 57930a1828cSXin LI } 58030a1828cSXin LI add_cmd_char(EV_OK|A_EXTRA, tables); 58195270f73SXin LI } 58230a1828cSXin LI p = skipsp(p); 58330a1828cSXin LI while (*p != '\0') 58430a1828cSXin LI { 58530a1828cSXin LI s = tstr(&p, 0); 58630a1828cSXin LI add_cmd_str(s, tables); 58730a1828cSXin LI } 58830a1828cSXin LI add_cmd_char('\0', tables); 58930a1828cSXin LI } 59030a1828cSXin LI 59130a1828cSXin LI /* 59230a1828cSXin LI * Parse a line from the lesskey file. 59330a1828cSXin LI */ 594d713e089SXin LI static void parse_line(char *line, struct lesskey_tables *tables) 59530a1828cSXin LI { 59630a1828cSXin LI char *p; 59730a1828cSXin LI 59830a1828cSXin LI /* 59930a1828cSXin LI * See if it is a control line. 60030a1828cSXin LI */ 60195270f73SXin LI p = control_line(line, tables); 60295270f73SXin LI if (p == NULL) 60330a1828cSXin LI return; 60430a1828cSXin LI /* 60530a1828cSXin LI * Skip leading white space. 60630a1828cSXin LI * Replace the final newline with a null byte. 60730a1828cSXin LI * Ignore blank lines and comments. 60830a1828cSXin LI */ 60995270f73SXin LI p = clean_line(p); 61030a1828cSXin LI if (*p == '\0') 61130a1828cSXin LI return; 61230a1828cSXin LI 61330a1828cSXin LI if (tables->currtable->is_var) 61430a1828cSXin LI parse_varline(p, tables); 61530a1828cSXin LI else 61630a1828cSXin LI parse_cmdline(p, tables); 61730a1828cSXin LI } 61830a1828cSXin LI 61930a1828cSXin LI /* 62030a1828cSXin LI * Parse a lesskey source file and store result in tables. 62130a1828cSXin LI */ 622*c77c4889SXin LI int parse_lesskey(constant char *infile, struct lesskey_tables *tables) 62330a1828cSXin LI { 62430a1828cSXin LI FILE *desc; 62530a1828cSXin LI char line[1024]; 62630a1828cSXin LI 627*c77c4889SXin LI lesskey_file = (infile != NULL) ? strdup(infile) : homefile(DEF_LESSKEYINFILE); 628*c77c4889SXin LI if (lesskey_file == NULL) 629*c77c4889SXin LI return (-1); 63030a1828cSXin LI 63130a1828cSXin LI init_tables(tables); 63230a1828cSXin LI errors = 0; 63330a1828cSXin LI linenum = 0; 63495270f73SXin LI if (less_version == 0) 635d713e089SXin LI less_version = lstrtoi(version, NULL, 10); 63630a1828cSXin LI 63730a1828cSXin LI /* 63830a1828cSXin LI * Open the input file. 63930a1828cSXin LI */ 640*c77c4889SXin LI if (strcmp(lesskey_file, "-") == 0) 64130a1828cSXin LI desc = stdin; 642*c77c4889SXin LI else if ((desc = fopen(lesskey_file, "r")) == NULL) 64330a1828cSXin LI { 644*c77c4889SXin LI /* parse_error("cannot open lesskey file %s", lesskey_file); */ 645*c77c4889SXin LI errors = -1; 64630a1828cSXin LI } 64730a1828cSXin LI 64830a1828cSXin LI /* 64930a1828cSXin LI * Read and parse the input file, one line at a time. 65030a1828cSXin LI */ 651*c77c4889SXin LI if (desc != NULL) 652*c77c4889SXin LI { 65330a1828cSXin LI while (fgets(line, sizeof(line), desc) != NULL) 65430a1828cSXin LI { 65530a1828cSXin LI ++linenum; 65630a1828cSXin LI parse_line(line, tables); 65730a1828cSXin LI } 658*c77c4889SXin LI if (desc != stdin) 65995270f73SXin LI fclose(desc); 660*c77c4889SXin LI } 661*c77c4889SXin LI free(lesskey_file); 662*c77c4889SXin LI lesskey_file = NULL; 663*c77c4889SXin LI return (errors); 664*c77c4889SXin LI } 665*c77c4889SXin LI 666*c77c4889SXin LI /* 667*c77c4889SXin LI * Parse a lesskey source content and store result in tables. 668*c77c4889SXin LI */ 669*c77c4889SXin LI int parse_lesskey_content(constant char *content, struct lesskey_tables *tables) 670*c77c4889SXin LI { 671*c77c4889SXin LI size_t cx = 0; 672*c77c4889SXin LI 673*c77c4889SXin LI lesskey_file = "lesskey-content"; 674*c77c4889SXin LI init_tables(tables); 675*c77c4889SXin LI errors = 0; 676*c77c4889SXin LI linenum = 0; 677*c77c4889SXin LI if (less_version == 0) 678*c77c4889SXin LI less_version = lstrtoi(version, NULL, 10); 679*c77c4889SXin LI 680*c77c4889SXin LI while (content[cx] != '\0') 681*c77c4889SXin LI { 682*c77c4889SXin LI /* Extract a line from the content buffer and parse it. */ 683*c77c4889SXin LI char line[1024]; 684*c77c4889SXin LI size_t lx = 0; 685*c77c4889SXin LI while (content[cx] != '\0' && content[cx] != '\n' && content[cx] != ';') 686*c77c4889SXin LI { 687*c77c4889SXin LI if (lx >= sizeof(line)-1) break; 688*c77c4889SXin LI if (content[cx] == '\\' && content[cx+1] == ';') 689*c77c4889SXin LI ++cx; /* escaped semicolon: skip the backslash */ 690*c77c4889SXin LI line[lx++] = content[cx++]; 691*c77c4889SXin LI } 692*c77c4889SXin LI line[lx] = '\0'; 693*c77c4889SXin LI ++linenum; 694*c77c4889SXin LI parse_line(line, tables); 695*c77c4889SXin LI if (content[cx] != '\0') ++cx; /* skip newline or semicolon */ 696*c77c4889SXin LI } 697*c77c4889SXin LI lesskey_file = NULL; 69830a1828cSXin LI return (errors); 69930a1828cSXin LI } 700