From 1a16ba84f9eeab08a1272ea264762372116cc70e Mon Sep 17 00:00:00 2001 From: ketmar Date: Sun, 22 Apr 2012 19:57:31 +0300 Subject: [PATCH] added simple config parsing utilities --- src/Jamfile | 1 + src/sterm.c | 137 +++++++++++++++++++++++++++++++++++++++-- src/stini.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/stini.h | 26 ++++++++ 4 files changed, 356 insertions(+), 6 deletions(-) create mode 100644 src/stini.c create mode 100644 src/stini.h diff --git a/src/Jamfile b/src/Jamfile index 6fb0a66..ded20a4 100644 --- a/src/Jamfile +++ b/src/Jamfile @@ -3,5 +3,6 @@ SubDir TOP src ; Main sterm : sterm.c + stini.c dbglog.c ; diff --git a/src/sterm.c b/src/sterm.c index 41debf6..6d4a3ff 100644 --- a/src/sterm.c +++ b/src/sterm.c @@ -32,9 +32,12 @@ #include #include +#include "stini.h" #include "dbglog.h" +#define DO_INI + #define MOUSE_REPORTING_ALWAYS //#define DUMP_PROG_OUTPUT @@ -382,6 +385,10 @@ static Atom XA_UTF8 = None; static Atom XA_TARGETS = None; +static int opt_doubleclick_timeout = DOUBLECLICK_TIMEOUT; +static int opt_tripleclick_timeout = TRIPLECLICK_TIMEOUT; + + //////////////////////////////////////////////////////////////////////////////// // UTF-8 static int utf8decode (const char *s, long *u) { @@ -532,11 +539,10 @@ static __attribute__((noreturn)) __attribute__((format(printf,1,2))) void die (c static __attribute__((noreturn)) void execsh (void) { char **args; char *envshell = getenv("SHELL"); - char *term = SPrintf("TERM=%s", opt_term ? opt_term : TNAME); // DEFAULT(envshell, SHELL); - putenv(term); - free(term); + //putenv(term); + setenv("TERM", opt_term ? opt_term : TNAME, 1); args = opt_cmd ? opt_cmd : (char*[]){envshell, "-i", NULL}; execvp(args[0], args); exit(EXIT_FAILURE); @@ -925,13 +931,13 @@ static void brelease (XEvent *e) { term.dirty[sel.ey] = 2; sel.bx = -1; gettimeofday(&now, NULL); - if (TIMEDIFF(now, sel.tclick2) <= TRIPLECLICK_TIMEOUT) { + if (TIMEDIFF(now, sel.tclick2) <= opt_tripleclick_timeout) { /* triple click on the line */ sel.b.x = sel.bx = 0; sel.e.x = sel.ex = term.col; sel.b.y = sel.e.y = sel.ey; selcopy(); - } else if (TIMEDIFF(now, sel.tclick1) <= DOUBLECLICK_TIMEOUT) { + } else if (TIMEDIFF(now, sel.tclick1) <= opt_doubleclick_timeout) { /* double click to select word */ sel.bx = sel.ex; while (sel.bx > 0 && term.line[sel.ey][sel.bx-1].state & GLYPH_SET && term.line[sel.ey][sel.bx-1].c[0] != ' ') --sel.bx; @@ -2711,6 +2717,14 @@ static void kpress (XEvent *ev) { char buf[32]; // len = Xutf8LookupString(xw.xic, e, buf, sizeof(buf), &ksym, &status); + /* + { + const char *name = XKeysymToString(ksym); + KeySym ks = XStringToKeysym(name); + // + fprintf(stderr, "ks: [%s] (%d:%d)\n", name, (int)ksym, (int)ks); + } + */ if (len > 0) { buf[len] = 0; //fprintf(stderr, "[%s]\n", buf); @@ -2766,8 +2780,8 @@ static void kpress (XEvent *ev) { if (IS_SET(MODE_CRLF)) ttywrite("\r\n", 2); else ttywrite("\r", 1); } break; - /* 3. X lookup */ default: + /* 3. X lookup */ //if (len < 1) len = XmbLookupString(xw.xic, e, buf, sizeof(buf), &ksym, &status); if (len > 0) { if (meta && len == 1) ttywrite("\033", 1); @@ -2885,9 +2899,120 @@ static void run (void) { //////////////////////////////////////////////////////////////////////////////// +// <0: file not found +// >0: file loading error +// 0: ok +#ifdef DO_INI +typedef const char *(*IniHandlerFn) (const char *optname, const char *fmt, char *argstr, void *udata); + + +static const char *inifnGenericOneArg (const char *optname, const char *fmt, char *argstr, void *udata) { + return iniParseArguments(argstr, fmt, udata); +} + + +static const char *inifnGenericOneStr (const char *optname, const char *fmt, char *argstr, void *udata) { + char *s = NULL; + const char *err = iniParseArguments(argstr, fmt, &s); + // + if (err != NULL) return err; + if ((s = strdup(s)) == NULL) return "out of memory"; + if (udata) { + char **ustr = (char **)udata; + // + *ustr = s; + } + return NULL; +} + + +static const struct { + const char *name; + const char *fmt; + void *udata; + IniHandlerFn fn; +} iniHandlers[] = { + {"termname", "s!-", &opt_term, inifnGenericOneStr}, + {"winclass", "s!-", &opt_class, inifnGenericOneStr}, + {"wintitle", "s!-", &opt_title, inifnGenericOneStr}, + {"doubleclick_timeout", "i{0,10000}", &opt_doubleclick_timeout, inifnGenericOneArg}, + {"tripleclick_timeout", "i{0,10000}", &opt_tripleclick_timeout, inifnGenericOneArg}, + {NULL, NULL, NULL, NULL} +}; + + +#define INI_LINE_SIZE (32768) +static int loadConfig (const char *fname) { + FILE *fi = fopen(fname, "r"); + const char *err = NULL; + char *line; + int lineno = 0; + // + if (fi == NULL) return -1; + if ((line = malloc(INI_LINE_SIZE)) == NULL) { err = "out of memory"; goto quit; } + // + while (fgets(line, INI_LINE_SIZE-1, fi) != NULL) { + char *optname, *argstr; + int goodoption = 0; + // + ++lineno; + line[INI_LINE_SIZE-1] = 0; + // get option name + for (optname = line; *optname && isspace(*optname); ++optname) ; + if (!optname[0] || optname[0] == '#') continue; // comment + if (!isalnum(optname[0])) { err = "invalid option name"; goto quit; } + argstr = optname; + while (*argstr) { + if (!argstr[0] || isspace(argstr[0])) break; + if (!isalnum(argstr[0]) && argstr[0] != '_' && argstr[0] != '.') { err = "invalid option name"; goto quit; } + *argstr = tolower(*argstr); + ++argstr; + } + if (*argstr) *argstr++ = 0; + // ok, we have option name in `optname` and arguments in `argstr` + /* + { + while (*argstr && isspace(argstr[strlen(argstr)-1])) argstr[strlen(argstr)-1] = 0; + fprintf(stderr, "[%s] [%s]\n", optname, argstr); + } + */ + for (int f = 0; iniHandlers[f].name != NULL; ++f) { + if (strcmp(iniHandlers[f].name, optname) == 0) { + if ((err = iniHandlers[f].fn(optname, iniHandlers[f].fmt, argstr, iniHandlers[f].udata)) != NULL) goto quit; + goodoption = 1; + break; + } + } + if (!goodoption) { err = "unknown option"; goto quit; } + } +quit: + if (line != NULL) free(line); + fclose(fi); + if (err != NULL) { + fprintf(stderr, "ini error at line %d: %s\n", lineno, err); + return 1; + } + return 0; +} +#endif + + +//////////////////////////////////////////////////////////////////////////////// int main (int argc, char *argv[]) { //dbgLogInit(); // +#ifdef DO_INI + loadConfig("sterm.rc"); + // + fprintf(stderr, "term: [%s]\n", opt_term); + fprintf(stderr, "class: [%s]\n", opt_class); + fprintf(stderr, "title: [%s]\n", opt_title); + fprintf(stderr, "doubleclick_timeout: [%d]\n", opt_doubleclick_timeout); + fprintf(stderr, "tripleclick_timeout: [%d]\n", opt_tripleclick_timeout); + // + exit(1); +#endif + // for (int i = 1; i < argc; i++) { switch (argv[i][0] != '-' || argv[i][2] ? -1 : argv[i][1]) { case 't': diff --git a/src/stini.c b/src/stini.c new file mode 100644 index 0000000..b0267c0 --- /dev/null +++ b/src/stini.c @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include + +#include + + +// UGLY! REWRITE! +const char *iniParseArguments (char *line, const char *fmt, ...) { + va_list ap; + int inOptional = 0; + // + if (line == NULL) return "alas"; + va_start(ap, fmt); + while (*fmt) { + char spec = *fmt++, *start; + // + if (spec == '|') { inOptional = 1; continue; } + // + while (*line && isspace(*line)) ++line; + if (!line[0] || line[0] == '#') { + // end of line, stop right here + va_end(ap); + if (!inOptional) return "out of args"; + return NULL; + } + // + start = line; + if (line[0] == '"' || line[0] == '\'') { + // quoted string, hard + char *dest = start, qch = *line++; + int n; + // + while (*line && *line != qch) { + if (*line == '\\') { + switch (*(++line)) { + case 'n': *dest++ = '\n'; ++line; break; + case 'r': *dest++ = '\r'; ++line; break; + case 't': *dest++ = '\t'; ++line; break; + case 'a': *dest++ = '\a'; ++line; break; + case 'e': *dest++ = '\x1b'; ++line; break; // esc + case 'x': // hex + ++line; + if (!isxdigit(*line)) { va_end(ap); return "invalid hex escape"; } + n = toupper(*line)-'0'; if (n > 9) n -= 7; + ++line; + if (isxdigit(*line)) { + int b = toupper(*line)-'0'; if (b > 9) b -= 7; + // + n = (n*16)+b; + ++line; + } + *dest++ = n; + break; + case '0': // octal + n = 0; + for (int f = 0; f < 4; ++f) { + if (*line < '0' || *line > '7') break; + n = (n*8)+(line[0]-'0'); + if (n > 255) { va_end(ap); return "invalid oct escape"; } + ++line; + } + *dest++ = n; + break; + case '1'...'9': // decimal + n = 0; + for (int f = 0; f < 3; ++f) { + if (*line < '0' || *line > '9') break; + n = (n*8)+(line[0]-'0'); + if (n > 255) { va_end(ap); return "invalid dec escape"; } + ++line; + } + *dest++ = n; + break; + default: + *dest++ = *line++; + break; + } + } else { + *dest++ = *line++; + } + } + *line++ = 0; // kill quote and skip, it's safe + } else { + // unquoted string, easy + while (*line && !isspace(*line) && *line != '#') ++line; + if (*line) { + // have more chars + if (*line == '#') *line = 0; // stop right here + else *line++ = 0; // change space to 0 and skip it + } + } + // now process and convert argument + switch (spec) { + case '*': /* skip */ + break; + case 's': { /* string */ + int noempty = 0, trim = 0; + char **p; + // + for (;;) { + if (*fmt == '!') { noempty = 1; ++fmt; } + else if (*fmt == '-') { trim = 1; ++fmt; } + else break; + } + // + if (trim) { + char *e; + // + while (*start && isspace(*start)) ++start; + for (e = start+strlen(start); e > start; --e) if (!isspace(e[-1])) break; + if (e <= start) *start = 0; else *e = 0; + } + // + if (noempty && !start[0]) { va_end(ap); return "invalid empty string"; } + p = va_arg(ap, char **); + if (p != NULL) *p = start; + } break; + case 'i': /* int */ + if (!start[0]) { + va_end(ap); + return "invalid integer"; + } else { + int *p = va_arg(ap, int *); + long int n; + char *eptr; + // + n = strtol(start, &eptr, 0); + if (*eptr) { va_end(ap); return "invalid integer"; } + // + if (*fmt == '{') { + // check min/max + int minmax[2], haveminmax[2]; + // + haveminmax[0] = haveminmax[1] = 0; + minmax[0] = minmax[1] = 0; + ++fmt; + for (int f = 0; f < 2; ++f) { + if (isdigit(*fmt)) { + haveminmax[f] = 1; + while (isdigit(*fmt)) { + minmax[f] = minmax[f]*10+(fmt[0]-'0'); + ++fmt; + } + //fprintf(stderr, "got: %d\n", minmax[f]); + } + if (*fmt == ',') { + if (f == 1) { va_end(ap); return "invalid integer bounds: extra comma"; } + // do nothing, we are happy + ++fmt; + } else if (*fmt == '}') { + // ok, done + break; + } else { va_end(ap); return "invalid integer bounds"; } + } + if (*fmt != '}') { va_end(ap); return "invalid integer bounds"; } + ++fmt; + // + //fprintf(stderr, "b: (%d,%d) (%d,%d)\n", haveminmax[0], minmax[0], haveminmax[1], minmax[1]); + if ((haveminmax[0] && n < minmax[0]) || (haveminmax[1] && n > minmax[1])) { va_end(ap); return "integer out of bounds"; } + } + // + if (p) *p = n; + } + break; + case 'b': { /* bool */ + char *e; + int *p = va_arg(ap, int *); + // + while (*start && isspace(*start)) ++start; + for (e = start+strlen(start); e > start; --e) if (!isspace(e[-1])) break; + if (e <= start) *start = 0; else *e = 0; + // + if (!start[0]) { va_end(ap); return "invalid boolean"; } + if (strcasecmp(start, "true") == 0 || strcasecmp(start, "on") == 0 || + strcasecmp(start, "tan") == 0 || strcasecmp(start, "1") == 0) { + if (p) *p = 1; + } else if (strcasecmp(start, "false") == 0 || strcasecmp(start, "off") == 0 || + strcasecmp(start, "ona") == 0 || strcasecmp(start, "0") == 0) { + + if (p) *p = 1; + } else { + va_end(ap); + return "invalid boolean"; + } + } break; + default: + va_end(ap); + return "invalid format specifier"; + } + } + va_end(ap); + while (*line && isspace(*line)) ++line; + if (!line[0] || line[0] == '#') return NULL; + return "extra args"; +} diff --git a/src/stini.h b/src/stini.h new file mode 100644 index 0000000..4e633ac --- /dev/null +++ b/src/stini.h @@ -0,0 +1,26 @@ +/* See LICENSE for licence details. */ +#ifndef STINI_H +#define STINI_H + + +// parse the argument list +// return error message or NULL +// format: +// '*': skip +// 's': string (char *) +// 'i': integer (int *) +// 'b': boolean (int *) +// '|': optional arguments follows +// string modifiers: +// '!' -- don't allow empty strings +// '-' -- trim spaces +// int modifiers: +// {lo,hi} +// {,hi} +// {lo} +// WARNING! `line` will be modified! +// WARNING! string pointers will point INSIDE `line`, so don't discard it! +extern const char *iniParseArguments (char *line, const char *fmt, ...); + + +#endif -- 2.11.4.GIT