split parser.c from qe.c
* store config parser as separate source file parser.c
This commit is contained in:
parent
48c14ba824
commit
29b51f0457
3 changed files with 304 additions and 281 deletions
4
Makefile
4
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
|
||||
|
|
|
|||
301
parser.c
Normal file
301
parser.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
280
qe.c
280
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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue