aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2019-05-02 07:50:07 +0300
committerSergey Poznyakoff <gray@gnu.org>2019-05-02 17:14:30 +0300
commit9d03ad09aaff5aee8a969942140464c9ad921544 (patch)
treebc06cf2b61407d147b30d8eb5f07c3b9ade30891
parent11abd4c01540ee38878e817de7130115c42e1b89 (diff)
downloadrush-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--.gitignore2
-rw-r--r--NEWS4
-rw-r--r--configure.ac4
-rw-r--r--lib/wordsplit.c40
-rw-r--r--src/.gitignore5
-rw-r--r--src/Makefile.am8
-rw-r--r--src/cf.c563
-rw-r--r--src/cf.h110
-rw-r--r--src/cfgram.y646
-rw-r--r--src/cflex.l430
-rw-r--r--src/config.c588
-rw-r--r--src/dump.c2
-rw-r--r--src/limits.c223
-rw-r--r--src/map.c98
-rw-r--r--src/rush.c950
-rw-r--r--src/rush.h152
-rw-r--r--tests/unsetvar.at2
17 files changed, 2881 insertions, 946 deletions
diff --git a/.gitignore b/.gitignore
index 9b43e87..1a6fc2d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,8 @@ config.log
config.status
configure
confpaths.h
+core
gnu
m4
stamp-h1
+tmp
diff --git a/NEWS b/NEWS
index d5afc82..c0c6752 100644
--- a/NEWS
+++ b/NEWS
@@ -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);
+ }</