%{ #include "config.h" #include #include /* readlink */ #include "nls.h" #include "kbd.h" #include "contextP.h" #include "ksyms.h" #include "paths.h" #include "parser.h" %} %top { #include "keymap.h" int stack_push(struct lk_ctx *ctx, lkfile_t *fp, void *scanner); int stack_pop(struct lk_ctx *ctx, void *scanner); } %option reentrant %option bison-bridge %option stack %option never-interactive %option noyywrap %option nounput %option noinput %option noyy_top_state %option extra-type="struct lk_ctx *" %{ int stack_push(struct lk_ctx *ctx, lkfile_t *fp, void *scanner) { int i = 0; while (ctx->stack[i]) i++; if (i == MAX_INCLUDE_DEPTH) { ERR(ctx, _("includes are nested too deeply")); return -1; } ctx->stack[i] = fp; yypush_buffer_state(yy_create_buffer(fp->fd, YY_BUF_SIZE, scanner), scanner); return 0; } int stack_pop(struct lk_ctx *ctx, void *scanner) { int i = 0; while (ctx->stack[i]) i++; if (!i) return 0; i--; /* * The top of stack is input file for library. No need to close it. */ if (i) { lk_fpclose(ctx->stack[i]); free(ctx->stack[i]); } ctx->stack[i] = NULL; yypop_buffer_state(scanner); return 0; } /* * Where shall we look for an include file? * Current strategy (undocumented, may change): * * 1. Look for a user-specified LOADKEYS_INCLUDE_PATH * 2. Try . and ../include and ../../include * 3. Try D and D/../include and D/../../include * where D is the directory from where we are loading the current file. * 4. Try KD/include and KD/#/include where KD = DATADIR/KEYMAPDIR. * * Expected layout: * KD has subdirectories amiga, atari, i386, mac, sun, include * KD/include contains architecture-independent stuff * like strings and iso-8859-x compose tables. * KD/i386 has subdirectories qwerty, ... and include; * this latter include dir contains stuff with keycode=... * * (Of course, if the present setup turns out to be reasonable, * then later also the other architectures will grow and get * subdirectories, and the hard-coded i386 below will go again.) * * People that dislike a dozen lookups for loadkeys * can easily do "loadkeys file_with_includes; dumpkeys > my_keymap" * and afterwards use only "loadkeys /fullpath/mykeymap", where no * lookups are required. */ static const char *const include_dirpath0[] = { "", 0 }; static const char *const include_dirpath1[] = { "", "../include/", "../../include/", 0 }; static const char *const include_dirpath3[] = { DATADIR "/" KEYMAPDIR "/include/", DATADIR "/" KEYMAPDIR "/i386/include/", DATADIR "/" KEYMAPDIR "/mac/include/", 0 }; static const char *const include_suffixes[] = { "", ".inc", 0 }; static int find_incl_file_near_fn(struct lk_ctx *ctx, char *s, char *fn, lkfile_t *fp) { const char *include_dirpath2[] = { 0, 0, 0, 0 }; char *t, *te, *t1 = NULL, *t2 = NULL; int len, rc = 1; if (!fn) return 1; t = strdup(fn); if (t == NULL) goto nomem; te = strrchr(t, '/'); if (te) { te[1] = 0; len = strlen(t); include_dirpath2[0] = t; include_dirpath2[1] = t1 = malloc(len + 12); include_dirpath2[2] = t2 = malloc(len + 15); if (t1 == NULL || t2 == NULL) goto nomem; strcpy(t1, t); strcat(t1, "../include/"); strcpy(t2, t); strcat(t2, "../../include/"); rc = lk_findfile(s, include_dirpath2, include_suffixes, fp); free(t1); free(t2); } free(t); return rc; nomem: ERR(ctx, _("out of memory")); if (t1) free(t1); if (t2) free(t2); if (t) free(t); return -1; } static int find_standard_incl_file(struct lk_ctx *ctx, char *s, lkfile_t *fp) { char *pathname; int rc = 1; int i = 0; while (ctx->stack[i]) i++; if (i == 0) return -1; i--; pathname = ctx->stack[i]->pathname; if (lk_findfile(s, include_dirpath1, include_suffixes, fp)) { if ((rc = find_incl_file_near_fn(ctx, s, pathname, fp)) == -1) return rc; } /* If filename is a symlink, also look near its target. */ if (rc) { char buf[MAXPATHLEN], path[MAXPATHLEN], *ptr; unsigned int n; n = readlink(pathname, buf, sizeof(buf)); if (n > 0 && n < sizeof(buf)) { buf[n] = 0; if (buf[0] == '/') { rc = find_incl_file_near_fn(ctx, s, buf, fp); } else if (strlen(pathname) + n < sizeof(path)) { strcpy(path, pathname); path[sizeof(path) - 1] = 0; ptr = strrchr(path, '/'); if (ptr) ptr[1] = 0; strcat(path, buf); rc = find_incl_file_near_fn(ctx, s, path, fp); } } } if (rc) rc = lk_findfile(s, include_dirpath3, include_suffixes, fp); return rc; } static int find_incl_file(struct lk_ctx *ctx, char *s, lkfile_t *fp) { char *ev; if (!s || !*s) return 1; if (*s == '/') /* no path required */ return (lk_findfile(s, include_dirpath0, include_suffixes, fp)); if ((ev = getenv("LOADKEYS_INCLUDE_PATH")) != NULL) { /* try user-specified path */ const char *user_dir[2] = { 0, 0 }; while (ev) { int rc; char *t = strchr(ev, ':'); char sv = 0; if (t) { sv = *t; *t = 0; } user_dir[0] = ev; if (*ev) rc = lk_findfile(s, user_dir, include_suffixes, fp); else /* empty string denotes system path */ rc = find_standard_incl_file(ctx, s, fp); if (rc <= 0) return rc; if (t) *t++ = sv; ev = t; } return 1; } return find_standard_incl_file(ctx, s, fp); } static int open_include(struct lk_ctx *ctx, char *s, yyscan_t scanner) { int rc; lkfile_t *fp; INFO(ctx, _("switching to %s"), s); fp = malloc(sizeof(lkfile_t)); if (!fp) { ERR(ctx, _("out of memory")); return -1; } memset(fp, 0, sizeof(lkfile_t)); rc = find_incl_file(ctx, s, fp); if (rc > 0) { ERR(ctx, _("cannot open include file %s"), s); free(s); return -1; } else if (rc == -1) { free(s); return -1; } free(s); return stack_push(ctx, fp, scanner); } %} %s RVALUE %x STR %x INCLSTR Comment #|! Continuation \\\n Eol \n Blank [ \t] Include include[ \t]* Decimal [1-9][0-9]* Octal 0[0-7]* Hex 0[xX][0-9a-fA-F]+ Unicode U\+([0-9a-fA-F]){4} Literal [a-zA-Z][a-zA-Z_0-9]* Octa ([0-7]){1,3} Charset charset|Charset|CharSet|CHARSET Keymaps keymaps|Keymaps|KeyMaps|KEYMAPS Keycode keycode|Keycode|KeyCode|KEYCODE String string|String|STRING Equals = Plain plain|Plain|PLAIN Shift shift|Shift|SHIFT Control control|Control|CONTROL Alt alt|Alt|ALT AltGr altgr|Altgr|AltGr|ALTGR ShiftL shiftl|ShiftL|SHIFTL ShiftR shiftr|ShiftR|SHIFTR CtrlL ctrll|CtrlL|CTRLL CtrlR ctrlr|CtrlR|CTRLR CapsShift capsshift|Capsshift|CapsShift|CAPSSHIFT AltIsMeta [aA][lL][tT][-_][iI][sS][-_][mM][eE][tT][aA] Strings strings|Strings|STRINGS Compose compose|Compose|COMPOSE As as|As|AS Usual usual|Usual|USUAL For for|For|FOR On on|On|ON To to|To|TO %% {Include} { yy_push_state(INCLSTR, yyscanner); } \"[^\"\n]+\" { char *s = strndup(yytext+1, strlen(yytext)-2); if (s == NULL) { ERR(yyextra, _("out of memory")); return(ERROR); } if (open_include(yyextra, s, yyscanner) == -1) return(ERROR); while (((struct yyguts_t*)yyscanner)->yy_start_stack_ptr) { yy_pop_state(yyscanner); } } [^"]|\"\"|\"[^"\n]*{Eol} { ERR(yyextra, _("expected filename between quotes")); return(ERROR); } <> { stack_pop(yyextra, yyscanner); if (!YY_CURRENT_BUFFER) yyterminate(); } {Continuation} { yyset_lineno(yyget_lineno(yyscanner) + 1, yyscanner); } {Eol} { yyset_lineno(yyget_lineno(yyscanner) + 1, yyscanner); while (((struct yyguts_t*)yyscanner)->yy_start_stack_ptr) { yy_pop_state(yyscanner); } return(EOL); } {Blank}+ ; /* do nothing */ {Comment}.*/{Eol} ; /* do nothing */ {Equals} { yy_push_state(RVALUE, yyscanner); lk_array_empty(yyextra->key_line); return(EQUALS); } {String} { yy_push_state(RVALUE, yyscanner); return(STRING); } {To} { yy_push_state(RVALUE, yyscanner); return(TO); } {Unicode} { yylval->num = strtol(yytext + 1, NULL, 16); if (yylval->num >= 0xf000) { ERR(yyextra, _("unicode keysym out of range: %s"), yytext); return(ERROR); } return(UNUMBER); } {Decimal}|{Octal}|{Hex} { yylval->num = strtol(yytext, NULL, 0); return(NUMBER); } {Literal} { return((yylval->num = ksymtocode(yyextra, yytext, TO_AUTO)) == -1 ? ERROR : LITERAL); } \- { return(DASH); } \, { return(COMMA); } \+ { return(PLUS); } {Charset} { return(CHARSET); } {Keymaps} { return(KEYMAPS); } {Keycode} { return(KEYCODE); } {Plain} { return(PLAIN); } {Shift} { return(SHIFT); } {Control} { return(CONTROL); } {Alt} { return(ALT); } {AltGr} { return(ALTGR); } {ShiftL} { return(SHIFTL); } {ShiftR} { return(SHIFTR); } {CtrlL} { return(CTRLL); } {CtrlR} { return(CTRLR); } {CapsShift} { return(CAPSSHIFT); } {AltIsMeta} { return(ALT_IS_META); } {Strings} { return(STRINGS); } {Compose} { return(COMPOSE); } {As} { return(AS); } {Usual} { return(USUAL); } {On} { return(ON); } {For} { return(FOR); } '\\{Octa}' { yylval->num = strtol(yytext + 2, NULL, 8); return(CCHAR); } '\\.' { yylval->num = (unsigned char) yytext[2]; return(CCHAR); } '.' { yylval->num = (unsigned char) yytext[1]; return(CCHAR); } \" { yylval->str.data[0] = '\0'; yylval->str.len = 0; yy_push_state(STR, yyscanner); } \\{Octa} { if (yylval->str.len == MAX_PARSER_STRING) { ERR(yyextra, _("string too long")); return(ERROR); } yylval->str.data[yylval->str.len++] = strtol(yytext + 1, NULL, 8); } \\\" { if (yylval->str.len == MAX_PARSER_STRING) { ERR(yyextra, _("string too long")); return(ERROR); } yylval->str.data[yylval->str.len++] = '"'; } \\\\ { if (yylval->str.len == MAX_PARSER_STRING) { ERR(yyextra, _("string too long")); return(ERROR); } yylval->str.data[yylval->str.len++] = '\\'; } \\n { if (yylval->str.len == MAX_PARSER_STRING) { ERR(yyextra, _("string too long")); return(ERROR); } yylval->str.data[yylval->str.len++] = '\n'; } [^\"\\]* { int len = strlen(yytext); if (yylval->str.len + len >= MAX_PARSER_STRING) { ERR(yyextra, _("string too long")); return(ERROR); } strcpy((char *) yylval->str.data + yylval->str.len, yytext); yylval->str.len += len; } \" { yylval->str.data[yylval->str.len] = '\0'; while (((struct yyguts_t*)yyscanner)->yy_start_stack_ptr) { yy_pop_state(yyscanner); } return(STRLITERAL); } . { return(ERROR); } %%