From 29b51f04576eec7d265cfec8932754b5c06a9be2 Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Sun, 2 Feb 2014 14:29:36 +0000 Subject: [PATCH] split parser.c from qe.c * store config parser as separate source file parser.c --- Makefile | 4 +- parser.c | 301 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ qe.c | 280 +-------------------------------------------------- 3 files changed, 304 insertions(+), 281 deletions(-) create mode 100644 parser.c diff --git a/Makefile b/Makefile index db94d8e..db7c45f 100644 --- a/Makefile +++ b/Makefile @@ -206,7 +206,7 @@ tqe$(EXE): tqe_g$(EXE) Makefile echo `size $@` `wc -c $@` tqe $(OPTIONS) \ | cut -d ' ' -f 7-10,13,15-40 >> STATS -$(OBJS_DIR)/tqe.o: qe.c qeconfig.h $(DEPENDS) Makefile +$(OBJS_DIR)/tqe.o: qe.c parser.c qeconfig.h variables.h $(DEPENDS) Makefile $(CC) $(DEFINES) -DCONFIG_TINY $(CFLAGS) -o $@ -c $< ffplay$(EXE): qe$(EXE) Makefile @@ -230,7 +230,7 @@ basemodules.txt: $(TSRCS) Makefile $(OBJS_DIR)/cfb.o: cfb.c cfb.h fbfrender.h $(OBJS_DIR)/charsetjis.o: charsetjis.c charsetjis.def $(OBJS_DIR)/fbfrender.o: fbfrender.c fbfrender.h libfbf.h -$(OBJS_DIR)/qe.o: qe.c qe.h qfribidi.h qeconfig.h +$(OBJS_DIR)/qe.o: qe.c parser.c qeconfig.h qe.h qfribidi.h variables.h $(OBJS_DIR)/qfribidi.o: qfribidi.c qfribidi.h $(OBJS_DIR)/%.o: %.c $(DEPENDS) Makefile diff --git a/parser.c b/parser.c new file mode 100644 index 0000000..22169a4 --- /dev/null +++ b/parser.c @@ -0,0 +1,301 @@ +/* + * QEmacs, tiny but powerful multimode editor + * + * Copyright (c) 2000-2002 Fabrice Bellard. + * Copyright (c) 2000-2014 Charlie Gordon. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* config file parsing */ + +/* CG: error messages should go to the *error* buffer. + * displayed as a popup upon start. + */ + +static int expect_token(const char **pp, int tok) +{ + skip_spaces(pp); + if (**pp == tok) { + ++*pp; + skip_spaces(pp); + return 1; + } else { + put_status(NULL, "'%c' expected", tok); + return 0; + } +} + +static int qe_cfg_parse_string(EditState *s, const char **pp, + char *dest, int size) +{ + const char *p = *pp; + int c, delim = *p++; + int res = 0; + int pos = 0; + + for (;;) { + c = *p; + if (c == '\0') { + put_status(s, "Unterminated string"); + res = -1; + break; + } + p++; + if (c == delim) + break; + if (c == '\\') { + c = *p++; + switch (c) { + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + } + } + if (pos < size - 1) + dest[pos++] = c; + } + if (pos < size) + dest[pos] = '\0'; + *pp = p; + return res; +} + +int parse_config_file(EditState *s, const char *filename) +{ + QEmacsState *qs = s->qe_state; + QErrorContext ec; + FILE *f; + char line[1024], str[1024]; + char prompt[64], cmd[128], *q, *strp; + const char *p, *r; + int line_num; + CmdDef *d; + int nb_args, c, sep, i, skip; + CmdArg args[MAX_CMD_ARGS]; + unsigned char args_type[MAX_CMD_ARGS]; + + f = fopen(filename, "r"); + if (!f) + return -1; + ec = qs->ec; + skip = 0; + line_num = 0; + /* Should parse whole config file in a single read, or load it via + * a buffer */ + for (;;) { + if (fgets(line, sizeof(line), f) == NULL) + break; + line_num++; + qs->ec.filename = filename; + qs->ec.function = NULL; + qs->ec.lineno = line_num; + + p = line; + skip_spaces(&p); + if (p[0] == '}') { + /* simplistic 1 level if block skip feature */ + p++; + skip_spaces(&p); + skip = 0; + } + if (skip) + continue; + + /* skip comments */ + while (p[0] == '/' && p[1] == '*') { + for (p += 2; *p; p++) { + if (*p == '*' && p[1] == '/') { + p += 2; + break; + } + } + skip_spaces(&p); + /* CG: unfinished comments silently unsupported */ + } + if (p[0] == '/' && p[1] == '/') + continue; + if (p[0] == '\0') + continue; + + get_str(&p, cmd, sizeof(cmd), "("); + if (*cmd == '\0') { + put_status(s, "Syntax error"); + continue; + } + /* transform '_' to '-' */ + q = cmd; + while (*q) { + if (*q == '_') + *q = '-'; + q++; + } + /* simplistic 1 level if block skip feature */ + if (strequal(cmd, "if")) { + if (!expect_token(&p, '(')) + goto fail; + skip = !strtol(p, (char**)&p, 0); + if (!expect_token(&p, ')') || !expect_token(&p, '{')) + goto fail; + continue; + } +#ifndef CONFIG_TINY + { + /* search for variable */ + struct VarDef *vp; + + vp = qe_find_variable(cmd); + if (vp) { + if (!expect_token(&p, '=')) + goto fail; + skip_spaces(&p); + if (*p == '\"' || *p == '\'') { + if (qe_cfg_parse_string(s, &p, str, countof(str))) + goto fail; + qe_set_variable(s, cmd, str, 0); + } else { + qe_set_variable(s, cmd, NULL, strtol(p, (char**)&p, 0)); + } + skip_spaces(&p); + if (*p != ';' && *p != '\n') + put_status(s, "Syntax error '%s'", cmd); + continue; + } + } +#endif + /* search for command */ + d = qe_find_cmd(cmd); + if (!d) { + put_status(s, "Unknown command '%s'", cmd); + continue; + } + nb_args = 0; + + /* first argument is always the window */ + args_type[nb_args++] = CMD_ARG_WINDOW; + + /* construct argument type list */ + r = d->name + strlen(d->name) + 1; + if (*r == '*') { + r++; + if (s->b->flags & BF_READONLY) { + put_status(s, "Buffer is read only"); + continue; + } + } + + for (;;) { + unsigned char arg_type; + int ret; + + ret = parse_arg(&r, &arg_type, prompt, countof(prompt), + NULL, 0, NULL, 0); + if (ret < 0) + goto badcmd; + if (ret == 0) + break; + if (nb_args >= MAX_CMD_ARGS) { + badcmd: + put_status(s, "Badly defined command '%s'", cmd); + goto fail; + } + args[nb_args].p = NULL; + args_type[nb_args++] = arg_type & CMD_ARG_TYPE_MASK; + } + + if (!expect_token(&p, '(')) + goto fail; + + sep = '\0'; + strp = str; + + for (i = 0; i < nb_args; i++) { + /* pseudo arguments: skip them */ + switch (args_type[i]) { + case CMD_ARG_WINDOW: + args[i].s = s; + continue; + case CMD_ARG_INTVAL: + args[i].n = (int)(intptr_t)d->val; + continue; + case CMD_ARG_STRINGVAL: + /* CG: kludge for xxx-mode functions and named kbd macros */ + args[i].p = prompt; + continue; + } + + skip_spaces(&p); + if (sep) { + /* CG: Should test for arg list too short. */ + /* CG: Could supply default arguments. */ + if (!expect_token(&p, sep)) + goto fail; + } + sep = ','; + + switch (args_type[i]) { + case CMD_ARG_INT: + r = p; + args[i].n = strtol(p, (char**)&p, 0); + if (p == r) { + put_status(s, "Number expected for arg %d", i); + goto fail; + } + break; + case CMD_ARG_STRING: + if (*p != '\"' && *p != '\'') { + /* XXX: should convert number to string */ + put_status(s, "String expected for arg %d", i); + goto fail; + } + if (qe_cfg_parse_string(s, &p, strp, + str + countof(str) - strp) < 0) + { + goto fail; + } + args[i].p = strp; + strp += strlen(strp) + 1; + break; + } + } + skip_spaces(&p); + c = ')'; + if (*p != c) { + put_status(s, "Too many arguments for %s", d->name); + goto fail; + } + + qs->this_cmd_func = d->action.func; + qs->ec.function = d->name; + call_func(d->sig, d->action, nb_args, args, args_type); + qs->last_cmd_func = qs->this_cmd_func; + if (qs->active_window) + s = qs->active_window; + continue; + + fail: + ; + } + fclose(f); + qs->ec = ec; + + return 0; +} diff --git a/qe.c b/qe.c index 6f3a65f..a560dab 100644 --- a/qe.c +++ b/qe.c @@ -7267,286 +7267,8 @@ int find_resource_file(char *path, int path_size, const char *pattern) } /******************************************************/ -/* config file parsing */ -/* CG: error messages should go to the *error* buffer. - * displayed as a popup upon start. - */ - -static int expect_token(const char **pp, int tok) -{ - skip_spaces(pp); - if (**pp == tok) { - ++*pp; - skip_spaces(pp); - return 1; - } else { - put_status(NULL, "'%c' expected", tok); - return 0; - } -} - -static int qe_cfg_parse_string(EditState *s, const char **pp, - char *dest, int size) -{ - const char *p = *pp; - int c, delim = *p++; - int res = 0; - int pos = 0; - - for (;;) { - c = *p; - if (c == '\0') { - put_status(s, "Unterminated string"); - res = -1; - break; - } - p++; - if (c == delim) - break; - if (c == '\\') { - c = *p++; - switch (c) { - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - } - } - if (pos < size - 1) - dest[pos++] = c; - } - if (pos < size) - dest[pos] = '\0'; - *pp = p; - return res; -} - -int parse_config_file(EditState *s, const char *filename) -{ - QEmacsState *qs = s->qe_state; - QErrorContext ec; - FILE *f; - char line[1024], str[1024]; - char prompt[64], cmd[128], *q, *strp; - const char *p, *r; - int line_num; - CmdDef *d; - int nb_args, c, sep, i, skip; - CmdArg args[MAX_CMD_ARGS]; - unsigned char args_type[MAX_CMD_ARGS]; - - f = fopen(filename, "r"); - if (!f) - return -1; - ec = qs->ec; - skip = 0; - line_num = 0; - /* Should parse whole config file in a single read, or load it via - * a buffer */ - for (;;) { - if (fgets(line, sizeof(line), f) == NULL) - break; - line_num++; - qs->ec.filename = filename; - qs->ec.function = NULL; - qs->ec.lineno = line_num; - - p = line; - skip_spaces(&p); - if (p[0] == '}') { - /* simplistic 1 level if block skip feature */ - p++; - skip_spaces(&p); - skip = 0; - } - if (skip) - continue; - - /* skip comments */ - while (p[0] == '/' && p[1] == '*') { - for (p += 2; *p; p++) { - if (*p == '*' && p[1] == '/') { - p += 2; - break; - } - } - skip_spaces(&p); - /* CG: unfinished comments silently unsupported */ - } - if (p[0] == '/' && p[1] == '/') - continue; - if (p[0] == '\0') - continue; - - get_str(&p, cmd, sizeof(cmd), "("); - if (*cmd == '\0') { - put_status(s, "Syntax error"); - continue; - } - /* transform '_' to '-' */ - q = cmd; - while (*q) { - if (*q == '_') - *q = '-'; - q++; - } - /* simplistic 1 level if block skip feature */ - if (strequal(cmd, "if")) { - if (!expect_token(&p, '(')) - goto fail; - skip = !strtol(p, (char**)&p, 0); - if (!expect_token(&p, ')') || !expect_token(&p, '{')) - goto fail; - continue; - } -#ifndef CONFIG_TINY - { - /* search for variable */ - struct VarDef *vp; - - vp = qe_find_variable(cmd); - if (vp) { - if (!expect_token(&p, '=')) - goto fail; - skip_spaces(&p); - if (*p == '\"' || *p == '\'') { - if (qe_cfg_parse_string(s, &p, str, countof(str))) - goto fail; - qe_set_variable(s, cmd, str, 0); - } else { - qe_set_variable(s, cmd, NULL, strtol(p, (char**)&p, 0)); - } - skip_spaces(&p); - if (*p != ';' && *p != '\n') - put_status(s, "Syntax error '%s'", cmd); - continue; - } - } -#endif - /* search for command */ - d = qe_find_cmd(cmd); - if (!d) { - put_status(s, "Unknown command '%s'", cmd); - continue; - } - nb_args = 0; - - /* first argument is always the window */ - args_type[nb_args++] = CMD_ARG_WINDOW; - - /* construct argument type list */ - r = d->name + strlen(d->name) + 1; - if (*r == '*') { - r++; - if (s->b->flags & BF_READONLY) { - put_status(s, "Buffer is read only"); - continue; - } - } - - for (;;) { - unsigned char arg_type; - int ret; - - ret = parse_arg(&r, &arg_type, prompt, countof(prompt), - NULL, 0, NULL, 0); - if (ret < 0) - goto badcmd; - if (ret == 0) - break; - if (nb_args >= MAX_CMD_ARGS) { - badcmd: - put_status(s, "Badly defined command '%s'", cmd); - goto fail; - } - args[nb_args].p = NULL; - args_type[nb_args++] = arg_type & CMD_ARG_TYPE_MASK; - } - - if (!expect_token(&p, '(')) - goto fail; - - sep = '\0'; - strp = str; - - for (i = 0; i < nb_args; i++) { - /* pseudo arguments: skip them */ - switch (args_type[i]) { - case CMD_ARG_WINDOW: - args[i].s = s; - continue; - case CMD_ARG_INTVAL: - args[i].n = (int)(intptr_t)d->val; - continue; - case CMD_ARG_STRINGVAL: - /* CG: kludge for xxx-mode functions and named kbd macros */ - args[i].p = prompt; - continue; - } - - skip_spaces(&p); - if (sep) { - /* CG: Should test for arg list too short. */ - /* CG: Could supply default arguments. */ - if (!expect_token(&p, sep)) - goto fail; - } - sep = ','; - - switch (args_type[i]) { - case CMD_ARG_INT: - r = p; - args[i].n = strtol(p, (char**)&p, 0); - if (p == r) { - put_status(s, "Number expected for arg %d", i); - goto fail; - } - break; - case CMD_ARG_STRING: - if (*p != '\"' && *p != '\'') { - /* XXX: should convert number to string */ - put_status(s, "String expected for arg %d", i); - goto fail; - } - if (qe_cfg_parse_string(s, &p, strp, - str + countof(str) - strp) < 0) - { - goto fail; - } - args[i].p = strp; - strp += strlen(strp) + 1; - break; - } - } - skip_spaces(&p); - c = ')'; - if (*p != c) { - put_status(s, "Too many arguments for %s", d->name); - goto fail; - } - - qs->this_cmd_func = d->action.func; - qs->ec.function = d->name; - call_func(d->sig, d->action, nb_args, args, args_type); - qs->last_cmd_func = qs->this_cmd_func; - if (qs->active_window) - s = qs->active_window; - continue; - - fail: - ; - } - fclose(f); - qs->ec = ec; - - return 0; -} +#include "parser.c" void do_load_config_file(EditState *e, const char *file) {