diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2019-05-02 07:50:07 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2019-05-02 17:14:30 +0300 |
commit | 9d03ad09aaff5aee8a969942140464c9ad921544 (patch) | |
tree | bc06cf2b61407d147b30d8eb5f07c3b9ade30891 | |
parent | 11abd4c01540ee38878e817de7130115c42e1b89 (diff) | |
download | rush-9d03ad09aaff5aee8a969942140464c9ad921544.tar.gz rush-9d03ad09aaff5aee8a969942140464c9ad921544.tar.bz2 |
Begin major rewrite
* configure.ac: Version 1.9.90.
Check for yacc and lex
* src/Makefile.am: Add new files.
* src/cf.c: New file.
* src/cf.h: New file.
* src/cfgram.y: New file.
* src/cflex.l: New file.
* src/config.c: Use new I/O functions and control structures.
* src/limits.c (limits_record_create)
(limits_record_add): New functions.
(parse_limits): Use them. Improve error reporting.
* src/map.c: New built-in rush variable "argc".
(rush_request_getvar,rush_request_delvar): New functions.
(getvar): Support negative positional argument indexes.
* src/rush.c: Rewrite test, transformation and environment evaluator.
Use cfparse to process configuration file.
* src/rush.h (transform_target_type): New enum.
(transform_target): New struct.
(transform_node,test_node): Complete rewrite.
(test_type): New enum.
(cmp_match,cmp_in): New opcodes.
(test_numeric_node,test_arg_node): Remove.
(rush_num_t): New data type.
(rush_bool_t): New data type.
(rush_error): New struct.
(rush_rule): Remove test_head, test_tail; Use test_node instead.
Remove error_msg, error_fd; Use single pointer instead.
New fields: clrenv, envar_head, envar_tail.
(vlogmsg): New proto.
(limits_record_create)
(limits_record_add)
(rush_request_getvar)
(rush_request_delvar)
(new_standard_error)
(new_error)
(rush_error_msg): New protos.
(envar_type): New enum.
(envar): New struct.
* lib/wordsplit.c (wordsplit_finish): Return single empty word in
ws_wordv[0] on empty input.
* tests/unsetvar.at: Fix typo
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | lib/wordsplit.c | 40 | ||||
-rw-r--r-- | src/.gitignore | 5 | ||||
-rw-r--r-- | src/Makefile.am | 8 | ||||
-rw-r--r-- | src/cf.c | 563 | ||||
-rw-r--r-- | src/cf.h | 110 | ||||
-rw-r--r-- | src/cfgram.y | 646 | ||||
-rw-r--r-- | src/cflex.l | 430 | ||||
-rw-r--r-- | src/config.c | 588 | ||||
-rw-r--r-- | src/dump.c | 2 | ||||
-rw-r--r-- | src/limits.c | 223 | ||||
-rw-r--r-- | src/map.c | 98 | ||||
-rw-r--r-- | src/rush.c | 950 | ||||
-rw-r--r-- | src/rush.h | 152 | ||||
-rw-r--r-- | tests/unsetvar.at | 2 |
17 files changed, 2881 insertions, 946 deletions
@@ -23,6 +23,8 @@ config.log config.status configure confpaths.h +core gnu m4 stamp-h1 +tmp @@ -1,8 +1,10 @@ -GNU Rush NEWS -- history of user-visible changes. 2019-04-24 +GNU Rush NEWS -- history of user-visible changes. 2019-05-01 See the end of file for copying conditions. Please send bug reports to <bug-rush@gnu.org.ua> +Version 1.9.90 (git) + Version 1.9, 2019-04-24 * Backreference expansion diff --git a/configure.ac b/configure.ac index c7084c9..94d5cfd 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with GNU Rush. If not, see <http://www.gnu.org/licenses/>. AC_PREREQ(2.63) -AC_INIT([GNU rush], [1.9], [bug-rush@gnu.org]) +AC_INIT([GNU rush], [1.9.90], [bug-rush@gnu.org]) AC_CONFIG_SRCDIR([src/rush.c]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_AUX_DIR([build-aux]) @@ -29,6 +29,8 @@ AC_PROG_CC gl_EARLY AC_PROG_INSTALL AC_PROG_RANLIB +AC_PROG_YACC +AC_PROG_LEX # Checks for libraries. diff --git a/lib/wordsplit.c b/lib/wordsplit.c index f94015a..14001ea 100644 --- a/lib/wordsplit.c +++ b/lib/wordsplit.c @@ -805,25 +805,32 @@ wordsplit_finish (struct wordsplit *wsp) n++; } - if (n == 0 && (wsp->ws_flags & WRDSF_INCREMENTAL)) + if (n == 0) { - /* The loop above have eliminated all nodes. Restart the - processing, if there's any input left. */ - if (wsp->ws_endp < wsp->ws_len) - { - int rc; - if (wsp->ws_flags & WRDSF_SHOWDBG) - wsp->ws_debug (_("Restarting")); - rc = wordsplit_process_list (wsp, skip_delim (wsp)); - if (rc) - return rc; - } - else + /* The loop above have eliminated all nodes. */ + if (wsp->ws_flags & WRDSF_INCREMENTAL) { - wsp->ws_error = WRDSE_EOF; - return WRDSE_EOF; + /* Restart the processing, if there's any input left. */ + if (wsp->ws_endp < wsp->ws_len) + { + int rc; + if (wsp->ws_flags & WRDSF_SHOWDBG) + wsp->ws_debug (_("Restarting")); + rc = wordsplit_process_list (wsp, skip_delim (wsp)); + if (rc) + return rc; + } + else + { + wsp->ws_error = WRDSE_EOF; + return WRDSE_EOF; + } + goto again; } - goto again; + + if (wordsplit_add_segm (wsp, 0, 0, _WSNF_EMPTYOK)) + return wsp->ws_errno; + n = 1; } if (alloc_space (wsp, n + 1)) @@ -2412,6 +2419,7 @@ wordsplit_process_list (struct wordsplit *wsp, size_t start) } } } + return wsp->ws_errno; } diff --git a/src/.gitignore b/src/.gitignore index 0b832a5..aaf3634 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -5,3 +5,8 @@ rushwho rlopt.h rushopt.h rwopt.h +cfgram.h +cfgram.c +cflex.c +y.output +y.tab.c diff --git a/src/Makefile.am b/src/Makefile.am index b29b1e7..45cb983 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -29,7 +29,8 @@ rush_SOURCES=\ rush.h\ rushopt.h\ socket.c\ - transform.c + transform.c\ + cflex.l cfgram.y cf.h cf.c rushlast_SOURCES=rushlast.c rlopt.h rushwho_SOURCES=rushwho.c rwopt.h @@ -73,3 +74,8 @@ SUFFIXES=.opt .c .h .opt.h: $(AM_V_GEN)m4 -s $(srcdir)/getopt.m4 $< | sed '1d' > $@ + +AM_YFLAGS=-dt +AM_LFLAGS=-d +cflex.c: cfgram.h +cfgram.h: cfgram.c diff --git a/src/cf.c b/src/cf.c new file mode 100644 index 0000000..6fe2a39 --- /dev/null +++ b/src/cf.c @@ -0,0 +1,563 @@ +#include <rush.h> +#include <cf.h> + +void +stringbuf_init(struct stringbuf *sb) +{ + sb->buffer = NULL; + sb->size = 0; + sb->pos = 0; +} + +void +stringbuf_free(struct stringbuf *sb) +{ + free(sb->buffer); + stringbuf_init(sb); +} + +void +stringbuf_add_char(struct stringbuf *sb, int c) +{ + if (sb->pos + 1 > sb->size) + sb->buffer = x2realloc(sb->buffer, &sb->size); + sb->buffer[sb->pos++] = c; +} + +void +stringbuf_add_array(struct stringbuf *sb, char const *str, size_t len) +{ + while (sb->pos + len > sb->size) + sb->buffer = x2realloc(sb->buffer, &sb->size); + memcpy(sb->buffer + sb->pos, str, len); + sb->pos += len; +} + +void +stringbuf_add_string(struct stringbuf *sb, char const *str) +{ + stringbuf_add_array(sb, str, strlen(str)); +} + +void +stringbuf_add_num(struct stringbuf *sb, unsigned n) +{ + size_t i = sb->pos, j; + do { + static char dig[] = "0123456789"; + stringbuf_add_char(sb, dig[n % 10]); + n /= 10; + } while (n > 0); + for (j = sb->pos-1; j > i; i++, j--) { + char c = sb->buffer[i]; + sb->buffer[i] = sb->buffer[j]; + sb->buffer[j] = c; + } +} + +void +stringbuf_finish(struct stringbuf *sb) +{ + stringbuf_add_char(sb, 0); +} + +void +cfpoint_format(struct cfpoint const *cfp, struct stringbuf *sb) +{ + if (cfp->filename) { + stringbuf_add_string(sb, cfp->filename); + stringbuf_add_char(sb, ':'); + stringbuf_add_num(sb, cfp->line); + if (cfp->column) { + stringbuf_add_char(sb, '.'); + stringbuf_add_num(sb, cfp->column); + } + } +} + +void +cfloc_format(struct cfloc const *cfl, struct stringbuf *sb) +{ + cfpoint_format(&cfl->beg, sb); + if (cfl->end.filename) { + if (cfl->beg.filename != cfl->end.filename) { + stringbuf_add_char(sb, '-'); + cfpoint_format(&cfl->end, sb); + } else if (cfl->beg.line != cfl->end.line) { + stringbuf_add_char(sb, '-'); + stringbuf_add_num(sb, cfl->end.line); + if (cfl->end.column) { + stringbuf_add_char(sb, '.'); + stringbuf_add_num(sb, cfl->end.column); + } + } else if (cfl->beg.column + && cfl->beg.column != cfl->end.column) { + stringbuf_add_char(sb, '-'); + stringbuf_add_num(sb, cfl->end.column); + } + } +} + +void +cfloc_print(struct cfloc const *cfl, FILE *fp) +{ + struct stringbuf sb; + stringbuf_init(&sb); + cfloc_format(cfl, &sb); + stringbuf_finish(&sb); + fwrite(sb.buffer, sb.pos, 1, fp); + stringbuf_free(&sb); +} + +void +vcferror(struct cfloc const *loc, char const *fmt, va_list ap) +{ + struct stringbuf sb; + stringbuf_init(&sb); + cfloc_format(loc, &sb); + stringbuf_add_array(&sb, ": ", 2); + stringbuf_add_string(&sb, fmt); + vlogmsg(LOG_ERR, sb.buffer, ap); + stringbuf_free(&sb); +} + +void +cferror(struct cfloc const *loc, char const *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vcferror(loc, fmt, ap); + va_end(ap); +} + +struct cfstream_file { + CFSTREAM base; + FILE *fp; +}; + +#define CFSTREAM_BUFSIZE 1024 + +CFSTREAM * +cfstream_open_file(char const *filename) +{ + int fd; + struct stat st; + CFSTREAM *cf; + + if (stat(filename, &st)) { + die(system_error, NULL, _("cannot stat file %s: %s"), + filename, strerror(errno)); + } + if (check_config_permissions(filename, &st)) + die(config_error, NULL, _("%s: file is not safe"), filename); + + fd = open(filename, O_RDONLY); + if (fd == -1) + die(system_error, NULL, _("cannot open file %s: %s"), + filename, strerror(errno)); + + cf = xmalloc(sizeof(*cf)); + cf->fd = fd; + cf->buffer = xmalloc(CFSTREAM_BUFSIZE); + cf->size = CFSTREAM_BUFSIZE; + cf->level = 0; + cf->pos = 0; + + return cf; +} + +CFSTREAM * +cfstream_open_mem(char const *buffer, size_t len) +{ + CFSTREAM *cf; + + cf = xmalloc(sizeof(*cf)); + cf->fd = -1; + cf->buffer = xmalloc(len); + memcpy(cf->buffer, buffer, len); + cf->size = len; + cf->level = len; + cf->pos = 0; + + return cf; +} + +void +cfstream_close(CFSTREAM *cf) +{ + if (cf->fd != -1) + close(cf->fd); + free(cf->buffer); + free(cf); +} + +static inline size_t +cfstream_buf_avail(CFSTREAM *cf) +{ + return cf->level - cf->pos; +} + +static size_t +cfstream_avail(CFSTREAM *cf) +{ + size_t avail = cfstream_buf_avail(cf); + if (avail == 0) { + if (cf->fd == -1) + return 0; + else { + ssize_t rc; + + rc = read(cf->fd, cf->buffer, cf->size); + if (rc == -1) + die(system_error, NULL, + "read: %s", + strerror(errno)); + cf->level = rc; + cf->pos = 0; + if (rc == 0) + return 0; + avail = cfstream_buf_avail(cf); + } + } + return avail; +} + +static inline size_t +cfstream_buf_free(CFSTREAM *cf) +{ + return cf->size - cf->level; +} + +static inline char const * +cfstream_buf_ptr(CFSTREAM *cf) +{ + return cf->buffer + cf->pos; +} + +static inline void +cfstream_buf_advance(CFSTREAM *cf, size_t n) +{ + cf->pos += n; +} + +ssize_t +cfstream_read(CFSTREAM *cf, char *bufptr, size_t bufsize) +{ + size_t nrd = 0; + + while (nrd < bufsize) { + size_t n = bufsize - nrd; + size_t avail = cfstream_avail(cf); + if (avail == 0) + break; + if (n > avail) + n = avail; + memcpy(bufptr + nrd, cfstream_buf_ptr(cf), n); + cfstream_buf_advance(cf, n); + nrd += n; + } + + return nrd; +} + +void +cfstream_putback(CFSTREAM *cf) +{ + if (cf->pos > 0) + cf->pos--; +} + +const char default_entry[] = "" +#ifdef RUSH_DEFAULT_CONFIG +RUSH_DEFAULT_CONFIG +#endif +; + +static char abc[] = { + [0] = 'v', + [1] = 'e', + [2] = 'r', + [3] = 's', + [4] = 'i', + [5] = 'o', + [6] = 'n', + [7] = ' ', + [8] = '\t', + [9] = '\n', + [10] = '#', + [11] = '.', + [12] = '0', + [13] = '1', + [14] = '2', + [15] = '3', + [16] = '4', + [17] = '5', + [18] = '6', + [19] = '7', + [20] = '8', + [21] = '9', + [22] = 0 +}; + +static inline int +char2abc(int c) +{ + char *p = strchr(abc, c); + if (p) + return p - abc; + return 22; +} + +enum { + state_error = 0, + state_initial = 1, + state_final = 14, + state_major = 10, + state_minor = 12, + state_old_format = 18, + NUM_STATES = 19 +}; + +static int transition[][sizeof(abc)] = { + [1] = { + [0] = 2, + [1] = 18, + [2] = 18, + [3] = 18, + [4] = 18, + [5] = 18, + [6] = 18, + [7] = 16, + [8] = 16, + [9] = 1, + [10] = 17, + [11] = 18, + [12] = 18, + [13] = 18, + [14] = 18, + [15] = 18, + [16] = 18, + [17] = 18, + [18] = 18, + [19] = 18, + [20] = 18, + [21] = 18, + [22] = 17 + }, + [2] = { + [1] = 3, + }, + [3] = { + [2] = 4, + }, + [4] = { + [3] = 5, + }, + [5] = { + [4] = 6, + }, + [6] = { + [5] = 7, + }, + [7] = { + [6] = 8 + }, + [8] = { + [7] = 9, + [8] = 9, + }, + [9] = { + // whitespace + [7] = 9, + [8] = 9, + [12] = 10, + [13] = 10, + [14] = 10, + [15] = 10, + [16] = 10, + [17] = 10, + [18] = 10, + [19] = 10, + [20] = 10, + [21] = 10, + }, + [10] = { + // major number + [11] = 11, + [12] = 10, + [13] = 10, + [14] = 10, + [15] = 10, + [16] = 10, + [17] = 10, + [18] = 10, + [19] = 10, + [20] = 10, + [21] = 10, + }, + + [11] = { + [12] = 12, + [13] = 12, + [14] = 12, + [15] = 12, + [16] = 12, + [17] = 12, + [18] = 12, + [19] = 12, + [20] = 12, + [21] = 12, + }, + + [12] = { + // minor number + [7] = 13, + [8] = 13, + [9] = 14, + [10] = 15, + [12] = 12, + [13] = 12, + [14] = 12, + [15] = 12, + [16] = 12, + [17] = 12, + [18] = 12, + [19] = 12, + [20] = 12, + [21] = 12, + }, + + [13] = { + // optional whitespace after minor number + [7] = 13, + [8] = 13, + [9] = 14, + [10] = 15, + }, + [14] = { + // Final state + }, + [15] = { + // comment after minor + [0] = 15, + [1] = 15, + [2] = 15, + [3] = 15, + [4] = 15, + [5] = 15, + [6] = 15, + [7] = 15, + [8] = 15, + [9] = 14, + [10] = 15, + [11] = 15, + [12] = 15, + [13] = 15, + [14] = 15, + [15] = 15, + [16] = 15, + [17] = 15, + [18] = 15, + [19] = 15, + [20] = 15, + [21] = 15, + }, + [16] = { + // Initial whitespace + [0] = 2, + [7] = 16, + [8] = 16, + [9] = 1, + }, + [17] = { + // comment + [0] = 17, + [1] = 17, + [2] = 17, + [3] = 17, + [4] = 17, + [5] = 17, + [6] = 17, + [7] = 17, + [8] = 17, + [9] = 1, + [10] = 17, + [11] = 17, + [12] = 17, + [13] = 17, + [14] = 17, + [15] = 17, + [16] = 17, + [17] = 17, + [18] = 17, + [19] = 17, + [20] = 17, + [21] = 17, + }, + [18] = { + // exit (old format) + } +}; + +void +cfparse(void) +{ + CFSTREAM *cf; + char const *config_file_name; + int line; + int state; + int major = 0; + int minor = 0; + + if (access(rush_config_file, F_OK) == 0) { + cf = cfstream_open_file(rush_config_file); + config_file_name = rush_config_file; + } else if (default_entry[0]) { + cf = cfstream_open_mem(default_entry, + sizeof(default_entry) - 1); + config_file_name = "<built-in>"; + } else { + die(usage_error, NULL, _("configuration file does not exist and no default is provided")); + } + + line = 1; + state = state_initial; + + while (1) { + int ch; + + ch = cfstream_getc(cf); + if (ch == 0) + die(config_error, + NULL, _("unrecognized config file format")); + if (ch == '\n') + line++; + state = transition[state][char2abc(ch)]; + switch (state) { + case state_major: + major = major * 10 + ch - '0'; + break; + case state_minor: + minor = minor * 10 + ch - '0'; + break; + case state_error: + die(config_error, + NULL, _("unrecognized config file format")); + case state_old_format: + cfstream_putback(cf); + cfparse_old(cf, config_file_name, line); + return; + case state_final: + cfparse_versioned(cf, config_file_name, + line, major, minor); + return; + default: + break; + } + } +} + + + + + diff --git a/src/cf.h b/src/cf.h new file mode 100644 index 0000000..8e35166 --- /dev/null +++ b/src/cf.h @@ -0,0 +1,110 @@ +struct stringbuf { + char *buffer; + size_t size; + size_t pos; +}; + +void stringbuf_init(struct stringbuf *sb); +void stringbuf_free(struct stringbuf *sb); +void stringbuf_add_char(struct stringbuf *sb, int c); +void stringbuf_add_string(struct stringbuf *sb, char const *str); +void stringbuf_add_array(struct stringbuf *sb, char const *str, size_t len); +void stringbuf_add_num(struct stringbuf *sb, unsigned n); +void stringbuf_finish(struct stringbuf *sb); + +struct cfpoint { + char const *filename; + int line; + int column; +}; + +struct cfloc { + struct cfpoint beg; + struct cfpoint end; +}; + +#define YYLTYPE struct cfloc + +#define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + { \ + if (N) \ + { \ + (Current).beg = YYRHSLOC(Rhs, 1).beg; \ + (Current).end = YYRHSLOC(Rhs, N).end; \ + } \ + else \ + { \ + (Current).beg = YYRHSLOC(Rhs, 0).end; \ + (Current).end = (Current).beg; \ + } \ + } while (0) + +#define YY_LOCATION_PRINT(File, Loc) \ + cfloc_print(&(Loc), File) + +void cfpoint_format(struct cfpoint const *cfp, struct stringbuf *sb); +void cfloc_format(struct cfloc const *cfl, struct stringbuf *sb); +void cfloc_format(struct cfloc const *cfl, struct stringbuf *sb); +void cfloc_print(struct cfloc const *cfl, FILE *fp); + +struct cfnumber { + int intval; + char *strval; +}; + +void cferror(struct cfloc const *loc, char const *fmt, ...); +void vcferror(struct cfloc const *loc, char const *fmt, va_list ap); + +void yyerror(char const *msg,...); + + +typedef struct cfstream { + int fd; + char *buffer; + size_t size; + size_t level; + size_t pos; +} CFSTREAM; + +CFSTREAM *cfstream_open_file(char const *filename); +CFSTREAM *cfstream_open_mem(char const *buffer, size_t len); +ssize_t cfstream_read(CFSTREAM *, char *, size_t); +void cfstream_close(CFSTREAM *); +static inline int +cfstream_getc(CFSTREAM *cf) +{ + char c; + if (cfstream_read(cf, &c, 1) == 0) + return 0; + return c; +} + +void cflex_setup(CFSTREAM *cf, char const *filename, int line); +int cflex_include(char const *filename, struct cfloc const *loc); + +void cfparse_old(CFSTREAM *cf, char const *filename, int line); +void cfparse_versioned(CFSTREAM *cf, char const *filename, int line, + int major, int minor); + +int yylex(void); +int yyparse(void); + +void cfparse(void); + +struct rush_rule *new_rush_rule(void); +struct transform_node *new_transform_node(struct rush_rule *rule, + enum transform_node_type type); +struct test_node *new_test_node(enum test_type type); +struct envar *new_envar(struct rush_rule *rule, + char const *name, size_t nlen, + char const *value, size_t vlen, + enum envar_type type); + +extern int re_flags; + + + + + + diff --git a/src/cfgram.y b/src/cfgram.y new file mode 100644 index 0000000..96f24f1 --- /dev/null +++ b/src/cfgram.y @@ -0,0 +1,646 @@ +%{ +#include <rush.h> +#include <cf.h> +static int errors; +int re_flags = REG_EXTENDED; +static struct rush_rule *current_rule; +struct name_entry { + struct name_entry *next; + char *name; +}; +static void add_name_list(struct name_entry *head, enum envar_type type); +%} + +%error-verbose +%locations + +%union { + char *str; + struct cfnumber num; + int intval; + regex_t regex; + struct rush_rule rule; + struct test_node *node; + struct { + char **argv; + size_t argc; + size_t argn; + } arglist; + struct { + int start; + int end; + } range; + struct { + struct name_entry *head; + struct name_entry *tail; + } name_list; + struct limits_rec *lrec; +} + +%token <str> STRING +%token <str> IDENT +%token <num> NUMBER + +%token RULE +%token EOL +%token SET +%token MAP +%token UNSET +%token MATCH +%token FALLTHROUGH +%token INCLUDE +%token LIMITS +%token CLRENV +%token SETENV +%token UNSETENV +%token KEEPENV +%token DELETE +%token EXIT +%token KEYWORD +%token BOGUS + +%left OR +%left AND +%left NOT +%nonassoc EQ NE LT LE GT GE '~' IN MEMBER + +%type <intval> fdescr index +%type <str> literal string value defval limit +%type <regex> regex +%type <node> expr compound_cond simple_cond +%type <arglist> arglist +%type <range> range +%type <lrec> resource_limits +%type <name_list> name_list + +%% +rcfile : rulelist + ; + +rulelist : rule + | rulelist rule + ; + +rule : rulehdr rulebody + ; + +rulehdr : RULE IDENT EOL + { + current_rule = new_rush_rule(); + current_rule->tag = $2; + } + ; + +rulebody : stmt + | rulebody stmt + ; + +stmt : match_stmt EOL + | set_stmt EOL + | map_stmt EOL + | delete_stmt EOL + | limits_stmt EOL + | environ_stmt EOL + | include_stmt EOL + | flowctl_stmt EOL + | action_stmt EOL + ; + +/* ****************** + Match statement + ****************** */ +match_stmt : MATCH compound_cond + { + if (current_rule->test_node) { + struct test_node *np = new_test_node(test_and); + np->v.arg[0] = current_rule->test_node; + np->v.arg[1] = $2; + current_rule->test_node = np; + } else + current_rule->test_node = $2; + } + ; + +compound_cond : simple_cond + | compound_cond AND simple_cond + { + $$ = new_test_node(test_and); + $$->v.arg[0] = $1; + $$->v.arg[1] = $3; + } + | compound_cond OR simple_cond + { + $$ = new_test_node(test_or); + $$->v.arg[0] = $1; + $$->v.arg[1] = $3; + } + ; + +simple_cond: expr + | NOT simple_cond + { + $$ = new_test_node(test_not); + $$->v.arg[0] = $2; + } + | '(' compound_cond ')' + { + $$ = $2; + } + ; + +expr : string '~' regex + { + $$ = new_test_node(test_cmps); + $$->v.cmp.op = cmp_match; + $$->v.cmp.larg = $1; + $$->v.cmp.rarg.rx = $3; + } + | string EQ literal + { + $$ = new_test_node(test_cmps); + $$->v.cmp.op = cmp_eq; + $$->v.cmp.larg = $1; + $$->v.cmp.rarg.str = $3; + } + | string NE literal + { + $$ = new_test_node(test_cmpn); + $$->v.cmp.op = cmp_ne; + $$->v.cmp.larg = $1; + $$->v.cmp.rarg.str = $3; + } + | string EQ NUMBER + { + $$ = new_test_node(test_cmpn); + $$->v.cmp.op = cmp_eq; + $$->v.cmp.larg = $1; + $$->v.cmp.rarg.num = $3.intval; + } + | string NE NUMBER + { + $$ = new_test_node(test_cmpn); + $$->v.cmp.op = cmp_ne; + $$->v.cmp.larg = $1; + $$->v.cmp.rarg.num = $3.intval; + } + | string LT NUMBER + { + $$ = new_test_node(test_cmpn); + $$->v.cmp.op = cmp_lt; + $$->v.cmp.larg = $1; + $$->v.cmp.rarg.num = $3.intval; + } + | string LE NUMBER + { + $$ = new_test_node(test_cmpn); + $$->v.cmp.op = cmp_le; + $$->v.cmp.larg = $1; + $$->v.cmp.rarg.num = $3.intval; + } + | string GT NUMBER + { + $$ = new_test_node(test_cmpn); + $$->v.cmp.op = cmp_gt; + $$->v.cmp.larg = $1; + $$->v.cmp.rarg.num = $3.intval; + } + | string GE NUMBER + { + $$ = new_test_node(test_cmpn); + $$->v.cmp.op = cmp_ge; + $$->v.cmp.larg = $1; + $$->v.cmp.rarg.num = $3.intval; + } + | string IN arglist + { + $$ = new_test_node(test_in); + $$->v.cmp.op = cmp_in; + $$->v.cmp.larg = $1; + $$->v.cmp.rarg.strv = $3.argv; + } + | MEMBER arglist + { + $$ = new_test_node(test_member); + $$->v.groups = $2.argv; + } + ; + +literal : IDENT + | STRING + ; + +string : IDENT + | STRING + | NUMBER + { + $$ = $1.strval; + } + ; + +regex : string + { + int rc = regcomp(&$$, $1, re_flags); + if (rc) { + char errbuf[512]; + regerror(rc, &$$, errbuf, sizeof(errbuf)); + cferror(&@1, _("invalid regexp: %s"), $1); + YYERROR; + } + } + ; + +/* ****************** + Set statement + ****************** */ +set_stmt : SET index value + { + struct transform_node *node; + + node = new_transform_node(current_rule, transform_set); + node->target.type = target_arg; + node->target.v.arg = $2; + node->v.xf.pattern = $3; + node->v.xf.trans = NULL; + } + | SET index '~' value + { + struct transform_node *node; + + node = new_transform_node(current_rule, transform_set); + node->target.type = target_arg; + node->target.v.arg = $2; + node->v.xf.pattern = NULL; + node->v.xf.trans = compile_transform_expr($4, re_flags); + } + | SET IDENT value + { + struct transform_node *node; + + node = new_transform_node(current_rule, transform_set); + if (strcmp($2, "command") == 0) { + node->target.type = target_command; + free($2); + } else if (strcmp($2, "program") == 0) { + node->target.type = target_program; + free($2); + } else { + node->target.type = target_var; + node->target.v.name = $2; + } + node->v.xf.pattern = $3; + node->v.xf.trans = NULL; + } + | SET IDENT '~' value + { + struct transform_node *node; + + node = new_transform_node(current_rule, transform_set); + if (strcmp($2, "command") == 0) { + node->target.type = target_command; + free($2); + } else if (strcmp($2, "program") == 0) { + node->target.type = target_program; + free($2); + } else { + node->target.type = target_var; + node->target.v.name = $2; + } + node->v.xf.pattern = NULL; + node->v.xf.trans = compile_transform_expr($4, re_flags); + } + | UNSET IDENT + { + struct transform_node *node = + new_transform_node(current_rule, transform_delete); + node->target.type = target_var; + node->target.v.name = $2; + } + ; + +map_stmt : MAP index value value value NUMBER NUMBER defval + { + struct transform_node *node; + + node = new_transform_node(current_rule, transform_map); + node->target.type = target_arg; + node->target.v.arg = $2; + node->v.map.file = $3; + node->v.map.delim = $4; + node->v.map.key = $5; + node->v.map.key_field = $6.intval; + node->v.map.val_field = $7.intval; + node->v.map.defval = $8; + + free($6.strval); + free($7.strval); + } |