summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2011-05-03 14:49:29 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2011-05-03 18:23:21 (GMT)
commit3d679b3df641f59fb81ca1651799f4e2965ed67e (patch) (unidiff)
tree5a614ee25cff44d015ee9e6f6920e2ba19379bba
parent24ec67c9f6375d34d88e79981ed8abbe15a78169 (diff)
downloadgrecs-3d679b3df641f59fb81ca1651799f4e2965ed67e.tar.gz
grecs-3d679b3df641f59fb81ca1651799f4e2965ed67e.tar.bz2
Switch to the two-layer model. Add testsuite.
The configuration file parser creates a syntax tree. This step does not require any knowledge about which keywords are allowed. The user can then either use that tree directly, or post-process it using parser tables. The latter approach is equivalent to previous versions of grecs.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--.gitignore29
-rw-r--r--Makefile.am2
-rw-r--r--am/grecs.m421
-rw-r--r--src/.gitignore4
-rw-r--r--src/Makefile.am3
-rw-r--r--src/diag.c73
-rw-r--r--src/format.c121
-rw-r--r--src/grecs-gram.y715
-rw-r--r--src/grecs.h86
-rw-r--r--src/lookup.c272
-rw-r--r--src/tree.c773
-rw-r--r--tests/.gitignore8
-rw-r--r--tests/Makefile.am84
-rw-r--r--tests/atlocal.in10
-rw-r--r--tests/cfhelp.at76
-rw-r--r--tests/format00.at38
-rw-r--r--tests/format01.at38
-rw-r--r--tests/format02.at38
-rw-r--r--tests/gcf1.conf54
-rw-r--r--tests/gcffmt.c67
-rw-r--r--tests/gcfpeek.c76
-rw-r--r--tests/gcfset.c240
-rw-r--r--tests/peek00.at25
-rw-r--r--tests/peek01.at30
-rw-r--r--tests/peek02.at26
-rw-r--r--tests/peek03.at33
-rw-r--r--tests/set.at38
-rw-r--r--tests/testsuite.at54
28 files changed, 2318 insertions, 716 deletions
diff --git a/.gitignore b/.gitignore
index 42c88b2..eec45d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,25 @@
1gconf-gram.c 1*.a
2gconf-gram.h 2*.o
3gconf-gram.output 3*.tar.*
4gconf-lex.c 4*~
5.deps
6.emacs.desktop
7.emacs.desktop.lock
8.emacsrc
9ABOUT-NLS
10ChangeLog
11INSTALL
12Makefile
13Makefile.in
14TAGS
15aclocal.m4
16autom4te.cache
17build-aux
18config.h
19config.h.in
20config.log
21config.status
22configure
23core
24m4
25stamp-h1
diff --git a/Makefile.am b/Makefile.am
index 1bfdcf4..17f1ff6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1 +1 @@
SUBDIRS=src SUBDIRS=src @GRECS_TESTDIR@
diff --git a/am/grecs.m4 b/am/grecs.m4
index 47c9867..5fc1819 100644
--- a/am/grecs.m4
+++ b/am/grecs.m4
@@ -49,14 +49,15 @@ AC_DEFUN([_GRECS_OPTION_SWITCH],
49AC_DEFUN([_GRECS_SET_OPTIONS], 49AC_DEFUN([_GRECS_SET_OPTIONS],
50[m4_foreach_w([_GRECS_Option], [$1], [_GRECS_SET_OPTION(_GRECS_Option)])]) 50[m4_foreach_w([_GRECS_Option], [$1], [_GRECS_SET_OPTION(_GRECS_Option)])])
51 51
52# GRECS_SETUP([OPTIONS],[pp-setup-file]) 52# GRECS_SETUP([dir],[OPTIONS],[pp-setup-file])
53# 53# dir - Directory in the source tree where grecs has been cloned.
54# Options are: 54# OPTIONS are:
55# no-preproc Disable the use of preprocessor. 55# no-preproc Disable the use of preprocessor.
56# std-pp-setup Install standard pp-setup file. 56# std-pp-setup Install standard pp-setup file.
57# pp-setup-option Add the --with-pp-setup-file option to the 57# pp-setup-option Add the --with-pp-setup-file option to the
58# configuration file. The option allows user to 58# configuration file. The option allows user to
59# control whether the pp-setup file is installed. 59# control whether the pp-setup file is installed.
60# tests Build tests.
60# 61#
61# The pp-setup-file argument supplies the pathname of the preprocessor 62# The pp-setup-file argument supplies the pathname of the preprocessor
62# setup file in the source tree. It is ignored if std-pp-setup option is 63# setup file in the source tree. It is ignored if std-pp-setup option is
@@ -69,7 +70,8 @@ AC_DEFUN([GRECS_SETUP],[
69 AC_PROG_YACC 70 AC_PROG_YACC
70 AM_PROG_LEX 71 AM_PROG_LEX
71 72
72 _GRECS_SET_OPTIONS([$1]) 73 AC_SUBST([GRECS_SUBDIR],m4_if([$1],,grecs,$1))
74 _GRECS_SET_OPTIONS([$2])
73 # ********************** 75 # **********************
74 # Preprocessor 76 # Preprocessor
75 # ********************** 77 # **********************
@@ -104,7 +106,7 @@ AC_DEFUN([GRECS_SETUP],[
104 DEFAULT_PREPROCESSOR="$DEFAULT_PREPROCESSOR $PREPROC_OPTIONS" 106 DEFAULT_PREPROCESSOR="$DEFAULT_PREPROCESSOR $PREPROC_OPTIONS"
105 _GRECS_IF_OPTION_SET([std-pp-setup], 107 _GRECS_IF_OPTION_SET([std-pp-setup],
106 [PP_SETUP_FILE='pp-setup'], 108 [PP_SETUP_FILE='pp-setup'],
107 [m4_if([$2],,[PP_SETUP_FILE=],[PP_SETUP_FILE='$2'])]) 109 [m4_if([$3],,[PP_SETUP_FILE=],[PP_SETUP_FILE='$3'])])
108 AC_SUBST(PP_SETUP_FILE) 110 AC_SUBST(PP_SETUP_FILE)
109 if test -n "$PP_SETUP_FILE"; then 111 if test -n "$PP_SETUP_FILE"; then
110 _GRECS_IF_OPTION_SET([pp-setup-option], 112 _GRECS_IF_OPTION_SET([pp-setup-option],
@@ -127,5 +129,14 @@ AC_DEFUN([GRECS_SETUP],[
127 else 129 else
128 DEFAULT_PREPROCESSOR=NULL 130 DEFAULT_PREPROCESSOR=NULL
129 fi 131 fi
132 _GRECS_IF_OPTION_SET([tests],
133 [m4_pushdef([TESTDIR],m4_if([$1],,grecs,$1)/tests)
134 AC_CONFIG_TESTDIR(TESTDIR)
135 AC_CONFIG_FILES(TESTDIR/Makefile TESTDIR/atlocal)
136 m4_popdef([TESTDIR])
137 AM_MISSING_PROG([AUTOM4TE], [autom4te])
138 GRECS_TESTDIR=tests
139 ])
130 AC_SUBST([GRECS_INCLUDES]) 140 AC_SUBST([GRECS_INCLUDES])
141 AC_SUBST([GRECS_TESTDIR])
131]) 142])
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..7c38320
--- a/dev/null
+++ b/src/.gitignore
@@ -0,0 +1,4 @@
1grecs-gram.c
2grecs-gram.h
3grecs-gram.output
4grecs-lex.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 759716b..0c2c444 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -16,14 +16,17 @@
16 16
17noinst_LIBRARIES=libgrecs.a 17noinst_LIBRARIES=libgrecs.a
18libgrecs_a_SOURCES = \ 18libgrecs_a_SOURCES = \
19 diag.c\
19 format.c\ 20 format.c\
20 grecs-gram.y\ 21 grecs-gram.y\
21 grecs-lex.l\ 22 grecs-lex.l\
22 list.c\ 23 list.c\
24 lookup.c\
23 mem.c\ 25 mem.c\
24 preproc.c\ 26 preproc.c\
25 symtab.c\ 27 symtab.c\
26 text.c\ 28 text.c\
29 tree.c\
27 grecs.h\ 30 grecs.h\
28 wordsplit.c\ 31 wordsplit.c\
29 wordsplit.h 32 wordsplit.h
diff --git a/src/diag.c b/src/diag.c
new file mode 100644
index 0000000..fcb8fd2
--- a/dev/null
+++ b/src/diag.c
@@ -0,0 +1,73 @@
1/* grecs - Gray's Extensible Configuration System
2 Copyright (C) 2007-2011 Sergey Poznyakoff
3
4 Grecs is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 Grecs is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with Grecs. If not, see <http://www.gnu.org/licenses/>. */
16
17#ifdef HAVE_CONFIG_H
18# include <config.h>
19#endif
20#include <grecs.h>
21#include <stdlib.h>
22#include <stdio.h>
23#include <string.h>
24#include <errno.h>
25
26static void
27default_print_diag(grecs_locus_t *locus, int err, int errcode, const char *msg)
28{
29 if (locus)
30 fprintf(stderr, "%s:%d: ", locus->file, locus->line);
31 if (!err)
32 fprintf(stderr, "warning: ");
33 fprintf(stderr, "%s", msg);
34 if (errcode)
35 fprintf(stderr, ": %s", strerror(errno));
36 fputc('\n', stderr);
37}
38
39void (*grecs_print_diag_fun)(grecs_locus_t *, int, int, const char *msg) =
40 default_print_diag;
41
42void
43grecs_warning(grecs_locus_t *locus, int errcode, const char *fmt, ...)
44{
45 va_list ap;
46 char *buf = NULL;
47 size_t size = 0;
48
49 va_start(ap, fmt);
50 if (grecs_vasprintf(&buf, &size, fmt, ap))
51 grecs_alloc_die();
52 va_end(ap);
53 grecs_print_diag_fun(locus, 0, errcode, buf);
54 free(buf);
55}
56
57void
58grecs_error(grecs_locus_t *locus, int errcode, const char *fmt, ...)
59{
60 va_list ap;
61 char *buf = NULL;
62 size_t size = 0;
63
64 va_start(ap, fmt);
65 if (grecs_vasprintf(&buf, &size, fmt, ap))
66 grecs_alloc_die();
67 va_end(ap);
68 grecs_print_diag_fun(locus, 1, errcode, buf);
69 free(buf);
70 grecs_error_count++;
71}
72
73
diff --git a/src/format.c b/src/format.c
index fc6c8d6..11b405a 100644
--- a/src/format.c
+++ b/src/format.c
@@ -24,7 +24,7 @@
24#include <string.h> 24#include <string.h>
25 25
26const char * 26const char *
27grecs_data_type_string (enum grecs_data_type type) 27grecs_data_type_string(enum grecs_data_type type)
28{ 28{
29 switch (type) { 29 switch (type) {
30 case grecs_type_void: 30 case grecs_type_void:
@@ -68,14 +68,14 @@ grecs_data_type_string (enum grecs_data_type type)
68} 68}
69 69
70static void 70static void
71format_level(FILE *stream, unsigned level) 71format_level(unsigned level, FILE *stream)
72{ 72{
73 while (level--) 73 while (level--)
74 fprintf(stream, " "); 74 fprintf(stream, " ");
75} 75}
76 76
77void 77void
78grecs_format_docstring(FILE *stream, const char *docstring, unsigned level) 78grecs_format_docstring(const char *docstring, unsigned level, FILE *stream)
79{ 79{
80 size_t len = strlen(docstring); 80 size_t len = strlen(docstring);
81 int width = 78 - level * 2; 81 int width = 78 - level * 2;
@@ -101,7 +101,7 @@ grecs_format_docstring(FILE *stream, const char *docstring, unsigned level)
101 if (seglen == 0 || *p == 0) 101 if (seglen == 0 || *p == 0)
102 seglen = p - docstring; 102 seglen = p - docstring;
103 103
104 format_level(stream, level); 104 format_level(level, stream);
105 fprintf(stream, "# "); 105 fprintf(stream, "# ");
106 fwrite(docstring, seglen, 1, stream); 106 fwrite(docstring, seglen, 1, stream);
107 fputc('\n', stream); 107 fputc('\n', stream);
@@ -119,14 +119,14 @@ grecs_format_docstring(FILE *stream, const char *docstring, unsigned level)
119} 119}
120 120
121void 121void
122grecs_format_simple_statement(FILE *stream, struct grecs_keyword *kwp, 122grecs_format_simple_statement(struct grecs_keyword *kwp, unsigned level,
123 unsigned level) 123 FILE *stream)
124{ 124{
125 const char *argstr; 125 const char *argstr;
126 126
127 if (kwp->docstring) 127 if (kwp->docstring)
128 grecs_format_docstring(stream, kwp->docstring, level); 128 grecs_format_docstring(kwp->docstring, level, stream);
129 format_level(stream, level); 129 format_level(level, stream);
130 130
131 if (kwp->argname) 131 if (kwp->argname)
132 argstr = kwp->argname; 132 argstr = kwp->argname;
@@ -136,7 +136,7 @@ grecs_format_simple_statement(FILE *stream, struct grecs_keyword *kwp,
136 if (strchr("<[", argstr[0])) 136 if (strchr("<[", argstr[0]))
137 fprintf(stream, "%s %s;\n", kwp->ident, gettext(argstr)); 137 fprintf(stream, "%s %s;\n", kwp->ident, gettext(argstr));
138 else if (strchr (argstr, ':')) 138 else if (strchr (argstr, ':'))
139 fprintf (stream, "%s <%s>;\n", kwp->ident, gettext(argstr)); 139 fprintf(stream, "%s <%s>;\n", kwp->ident, gettext(argstr));
140 else { 140 else {
141 fprintf(stream, "%s <%s: ", kwp->ident, gettext(argstr)); 141 fprintf(stream, "%s <%s: ", kwp->ident, gettext(argstr));
142 if (GRECS_IS_LIST(kwp->type)) 142 if (GRECS_IS_LIST(kwp->type))
@@ -151,34 +151,117 @@ grecs_format_simple_statement(FILE *stream, struct grecs_keyword *kwp,
151} 151}
152 152
153void 153void
154grecs_format_block_statement(FILE *stream, struct grecs_keyword *kwp, 154grecs_format_block_statement(struct grecs_keyword *kwp, unsigned level,
155 unsigned level) 155 FILE *stream)
156{ 156{
157 if (kwp->docstring) 157 if (kwp->docstring)
158 grecs_format_docstring(stream, kwp->docstring, level); 158 grecs_format_docstring(kwp->docstring, level, stream);
159 format_level(stream, level); 159 format_level(level, stream);
160 fprintf(stream, "%s", kwp->ident); 160 fprintf(stream, "%s", kwp->ident);
161 if (kwp->argname) 161 if (kwp->argname)
162 fprintf(stream, " <%s>", gettext(kwp->argname)); 162 fprintf(stream, " <%s>", gettext(kwp->argname));
163 fprintf(stream, " {\n"); 163 fprintf(stream, " {\n");
164 grecs_format_statement_array(stream, kwp->kwd, 0, level + 1); 164 grecs_format_statement_array(kwp->kwd, 0, level + 1, stream);
165 format_level(stream, level); 165 format_level(level, stream);
166 fprintf(stream, "}\n"); 166 fprintf(stream, "}\n");
167} 167}
168 168
169void 169void
170grecs_format_statement_array(FILE *stream, struct grecs_keyword *kwp, 170grecs_format_statement_array(struct grecs_keyword *kwp,
171 unsigned n, 171 unsigned n,
172 unsigned level) 172 unsigned level,
173 FILE *stream)
173{ 174{
174 for (; kwp->ident; kwp++, n++) { 175 for (; kwp->ident; kwp++, n++) {
175 if (n) 176 if (n)
176 fputc('\n', stream); 177 fputc('\n', stream);
177 if (kwp->type == grecs_type_section) 178 if (kwp->type == grecs_type_section)
178 grecs_format_block_statement(stream, kwp, level); 179 grecs_format_block_statement(kwp, level, stream);
179 else 180 else
180 grecs_format_simple_statement(stream, kwp, level); 181 grecs_format_simple_statement(kwp, level, stream);
181 } 182 }
182} 183}
183 184
185
186void
187grecs_format_locus(grecs_locus_t *locus, FILE *fp)
188{
189 fprintf(fp, "%s:%d:", locus->file, locus->line);
190}
191
192void
193grecs_format_node_ident(struct grecs_node *node, int delim, FILE *fp)
194{
195 if (node->up)
196 grecs_format_node_ident(node->up, delim, fp);
197 fputc(delim, fp);
198 fprintf(fp, "%s", node->ident);
199 if (node->type == grecs_node_block &&
200 !GRECS_VALUE_EMPTY_P(&node->value)) {
201 fputc('=', fp);
202 grecs_format_value(&node->value, fp);
203 }
204}
205
206void
207grecs_format_value(struct grecs_value *val, FILE *fp)
208{
209 int i;
210 struct grecs_list_entry *ep;
211
212 switch (val->type) {
213 case GRECS_TYPE_STRING:
214 fprintf(fp, "\"%s\"", val->v.string); /*FIXME: Quoting*/
215 break;
216
217 case GRECS_TYPE_LIST:
218 fputc('(', fp);
219 for (ep = val->v.list->head; ep; ep = ep->next) {
220 grecs_format_value(ep->data, fp);
221 if (ep->next) {
222 fputc(',', fp);
223 fputc(' ', fp);
224 }
225 }
226 fputc(')', fp);
227 break;
228
229 case GRECS_TYPE_ARRAY:
230 for (i = 0; i < val->v.arg.c; i++) {
231 if (i)
232 fputc(' ', fp);
233 grecs_format_value(&val->v.arg.v[i], fp);
234 }
235 }
236}
237
238void
239grecs_format_node(struct grecs_node *node, int flags, FILE *fp)
240{
241 int delim = flags & 0xff;
242
243 if (!delim)
244 delim = '.';
245 switch (node->type) {
246 case grecs_node_block:
247 for (node = node->down; node; node = node->next) {
248 grecs_format_node(node, flags, fp);
249 if (node->next)
250 fputc('\n', fp);
251 }
252 break;
253
254 case grecs_node_stmt:
255 if (flags & GRECS_NODE_FLAG_LOCUS) {
256 grecs_format_locus(&node->locus, fp);
257 fputc(' ', fp);
258 }
259 grecs_format_node_ident(node, delim, fp);
260 fputc(':', fp);
261 fputc(' ', fp);
262 grecs_format_value(&node->value, fp);
263 }
264}
265
266
184 267
diff --git a/src/grecs-gram.y b/src/grecs-gram.y
index f65e2dd..c2429a4 100644
--- a/src/grecs-gram.y
+++ b/src/grecs-gram.y
@@ -22,45 +22,22 @@
22#include <grecs-gram.h> 22#include <grecs-gram.h>
23#include <stdlib.h> 23#include <stdlib.h>
24#include <stdarg.h> 24#include <stdarg.h>
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/time.h>
28#include <sys/un.h>
29#include <netinet/in.h>
30#include <arpa/inet.h>
31#include <netdb.h>
32#include <string.h> 25#include <string.h>
33#include <errno.h> 26#include <errno.h>
34 27
35typedef union 28static struct grecs_node *parse_tree;
36{
37 struct sockaddr s;
38 struct sockaddr_in s_in;
39 struct sockaddr_un s_un;
40} sockaddr_union_t;
41
42static struct grecs_keyword config_keywords;
43static struct grecs_keyword *cursect;
44#define CURRENT_BASE ((char*)(cursect ? cursect->callback_data : NULL))
45static struct grecs_list *sections;
46int grecs_error_count; 29int grecs_error_count;
47 30
48int grecs_default_port = 0; 31int grecs_default_port = 0;
49 32
50static void *target_ptr(struct grecs_keyword *kwp, char *base);
51static void stmt_begin(struct grecs_keyword *kwp, grecs_value_t tag);
52static void stmt_end(struct grecs_keyword *kwp);
53static struct grecs_keyword *find_keyword(const char *ident);
54
55static void process_ident(struct grecs_keyword *kwp, grecs_value_t *value);
56static struct grecs_list *simple_list_create(int dispose);
57%} 33%}
58 34
59%union { 35%union {
60 char *string; 36 char *string;
61 grecs_value_t value; 37 grecs_value_t value;
62 struct grecs_list *list; 38 struct grecs_list *list;
63 struct grecs_keyword *kw; 39 struct grecs_node *node;
40 struct { struct grecs_node *head, *tail; } node_list;
64} 41}
65 42
66%token <string> IDENT STRING QSTRING MSTRING 43%token <string> IDENT STRING QSTRING MSTRING
@@ -68,39 +45,47 @@ static struct grecs_list *simple_list_create(int dispose);
68%type <list> slist0 45%type <list> slist0
69%type <value> value tag vallist 46%type <value> value tag vallist
70%type <list> values list vlist 47%type <list> values list vlist
71%type <kw> ident 48%type <node> stmt simple block
49%type <node_list> stmtlist
72 50
73%% 51%%
74 52
75input : stmtlist 53input : stmtlist
54 {
55 parse_tree = $1.head;
56 }
76 ; 57 ;
77 58
78stmtlist: stmt 59stmtlist: stmt
60 {
61 $$.head = $$.tail = $1;
62 }
79 | stmtlist stmt 63 | stmtlist stmt
64 {
65 grecs_node_bind($1.tail, $2, 0);
66 }
80 ; 67 ;
81 68
82stmt : simple 69stmt : simple
83 | block 70 | block
84 ; 71 ;
85 72
86simple : ident vallist ';' 73simple : IDENT vallist ';'
87 { 74 {
88 process_ident($1, &$2); 75 $$ = grecs_node_create(grecs_node_stmt,
76 &grecs_current_locus);
77 $$->ident = $1;
78 $$->value = $2;
89 } 79 }
90 ; 80 ;
91 81
92block : ident tag { stmt_begin($<kw>1, $<value>2); } '{' stmtlist '}' opt_sc 82block : IDENT tag '{' stmtlist '}' opt_sc
93 { 83 {
94 stmt_end($1); 84 $$ = grecs_node_create(grecs_node_block,
95 } 85 &grecs_current_locus);
96 ; 86 $$->ident = $1;
97 87 $$->value = $2;
98ident : IDENT 88 grecs_node_bind($$, $4.head, 1);
99 {
100 $$ = find_keyword($1);
101 if (!$$)
102 grecs_error(&grecs_current_locus, 0,
103 _("Unknown keyword"));
104 } 89 }
105 ; 90 ;
106 91
@@ -135,7 +120,7 @@ vallist : vlist
135 120
136vlist : value 121vlist : value
137 { 122 {
138 $$ = simple_list_create(0); 123 $$ = _grecs_simple_list_create(0);
139 grecs_list_append($$, grecs_value_dup(&$1)); 124 grecs_list_append($$, grecs_value_dup(&$1));
140 } 125 }
141 | vlist value 126 | vlist value
@@ -169,7 +154,6 @@ string : STRING
169slist : slist0 154slist : slist0
170 { 155 {
171 struct grecs_list_entry *ep; 156 struct grecs_list_entry *ep;
172 const void *p;
173 157
174 grecs_line_begin(); 158 grecs_line_begin();
175 for (ep = $1->head; ep; ep = ep->next) 159 for (ep = $1->head; ep; ep = ep->next)
@@ -181,7 +165,7 @@ slist : slist0
181 165
182slist0 : QSTRING 166slist0 : QSTRING
183 { 167 {
184 $$ = simple_list_create(0); 168 $$ = _grecs_simple_list_create(0);
185 grecs_list_append($$, $1); 169 grecs_list_append($$, $1);
186 } 170 }
187 | slist0 QSTRING 171 | slist0 QSTRING
@@ -207,7 +191,7 @@ list : '(' ')'
207 191
208values : value 192values : value
209 { 193 {
210 $$ = simple_list_create(0); 194 $$ = _grecs_simple_list_create(0);
211 grecs_list_append($$, grecs_value_dup(&$1)); 195 grecs_list_append($$, grecs_value_dup(&$1));
212 } 196 }
213 | values ',' value 197 | values ',' value
@@ -236,8 +220,8 @@ listel_dispose(void *el)
236 free(el); 220 free(el);
237} 221}
238 222
239static struct grecs_list * 223struct grecs_list *
240simple_list_create(int dispose) 224_grecs_simple_list_create(int dispose)
241{ 225{
242 struct grecs_list *lp = grecs_list_create(); 226 struct grecs_list *lp = grecs_list_create();
243 if (dispose) 227 if (dispose)
@@ -307,59 +291,20 @@ grecs_asprintf(char **pbuf, size_t *psize, const char *fmt, ...)
307 return rc; 291 return rc;
308} 292}
309 293
310void 294struct grecs_node *
311grecs_warning(grecs_locus_t *locus, int errcode, const char *fmt, ...)
312{
313 va_list ap;
314 char *buf = NULL;
315 size_t size = 0;
316
317 va_start(ap, fmt);
318 if (grecs_vasprintf(&buf, &size, fmt, ap))
319 grecs_alloc_die();
320 va_end(ap);
321 grecs_print_diag(locus, 0, errcode, buf);
322 free(buf);
323}
324
325void
326grecs_error(grecs_locus_t *locus, int errcode, const char *fmt, ...)
327{
328 va_list ap;
329 char *buf = NULL;
330 size_t size = 0;
331
332 va_start(ap, fmt);
333 if (grecs_vasprintf(&buf, &size, fmt, ap))
334 grecs_alloc_die();
335 va_end(ap);
336 grecs_print_diag(locus, 1, errcode, buf);
337 free(buf);
338 grecs_error_count++;
339}
340
341void
342grecs_set_keywords(struct grecs_keyword *kwd)
343{
344 config_keywords.kwd = kwd;
345}
346
347int
348grecs_parse(const char *name) 295grecs_parse(const char *name)
349{ 296{
350 int rc; 297 int rc;
351 if (grecs_lex_begin(name)) 298 if (grecs_lex_begin(name))
352 return 1; 299 return NULL;
353 cursect = &config_keywords; 300 parse_tree = NULL;
354 if (sections) {
355 grecs_list_free(sections);
356 sections = NULL;
357 }
358 rc = yyparse(); 301 rc = yyparse();
359 grecs_lex_end(); 302 grecs_lex_end();
360 if (grecs_error_count) 303 if (grecs_error_count) {
361 rc = 1; 304 grecs_tree_free(parse_tree);
362 return rc; 305 parse_tree = NULL;
306 }
307 return parse_tree;
363} 308}
364 309
365void 310void
@@ -370,583 +315,5 @@ grecs_gram_trace(int n)
370 315
371 316
372 317
373static void *
374target_ptr(struct grecs_keyword *kwp, char *base)
375{
376 if (kwp->varptr)
377 base = (char*) kwp->varptr + kwp->offset;
378 else if (base)
379 base += kwp->offset;
380
381 return base;
382}
383
384static int
385fake_callback(enum grecs_callback_command cmd,
386 grecs_locus_t *locus,
387 void *varptr,
388 grecs_value_t *value,
389 void *cb_data)
390{
391 return 0;
392}
393
394static struct grecs_keyword fake = {
395 "*",
396 NULL,
397 NULL,
398 grecs_type_void,
399 NULL,
400 0,
401 fake_callback,
402 NULL,
403 &fake
404};
405
406static void
407stmt_begin(struct grecs_keyword *kwp, grecs_value_t tag)
408{
409 void *target;
410
411 if (!sections)
412 sections = grecs_list_create();
413 grecs_list_push(sections, cursect);
414 if (kwp) {
415 target = target_ptr(kwp, CURRENT_BASE);
416 cursect = kwp;
417 if (kwp->callback &&
418 kwp->callback(grecs_callback_section_begin,
419 &grecs_current_locus, /* FIXME */
420 target,
421 &tag,
422 &kwp->callback_data))
423 cursect = &fake;
424 } else
425 /* install "ignore-all" section */
426 cursect = kwp;
427}
428
429static void
430stmt_end(struct grecs_keyword *kwp)
431{
432 grecs_callback_fn callback = NULL;
433 void *dataptr = NULL;
434
435 if (cursect && cursect->callback) {
436 callback = cursect->callback;
437 dataptr = &cursect->callback_data;
438 }
439
440 cursect = (struct grecs_keyword *) grecs_list_pop(sections);
441 if (!cursect)
442 abort();
443 if (callback)
444 callback(grecs_callback_section_end,
445 &grecs_current_locus, /* FIXME */
446 kwp ? target_ptr(kwp, CURRENT_BASE) : NULL,
447 NULL,
448 dataptr);
449}
450
451static struct grecs_keyword *
452find_keyword(const char *ident)
453{
454 struct grecs_keyword *kwp;
455
456 if (cursect && cursect != &fake) {
457 for (kwp = cursect->kwd; kwp->ident; kwp++)
458 if (strcmp(kwp->ident, ident) == 0)
459 return kwp;
460 } else {
461 return &fake;
462 }
463 return NULL;
464}
465
466static int
467string_to_bool(const char *string, int *pval, grecs_locus_t *locus)
468{
469 if (strcmp(string, "yes") == 0
470 || strcmp(string, "true") == 0
471 || strcmp(string, "t") == 0
472 || strcmp(string, "1") == 0)
473 *pval = 1;
474 else if (strcmp(string, "no") == 0
475 || strcmp(string, "false") == 0
476 || strcmp(string, "nil") == 0
477 || strcmp(string, "0") == 0)
478 *pval = 0;
479 else {
480 grecs_error(locus, 0,
481 _("%s: not a valid boolean value"),
482 string);
483 return 1;
484 }
485 return 0;
486}
487
488static int
489string_to_host(struct in_addr *in, const char *string, grecs_locus_t *locus)
490{
491 if (inet_aton(string, in) == 0) {
492 struct hostent *hp;
493
494 hp = gethostbyname(string);
495 if (hp == NULL)
496 return 1;
497 memcpy(in, hp->h_addr, sizeof(struct in_addr));
498 }
499 return 0;
500}
501
502static int
503string_to_sockaddr(struct grecs_sockaddr *sp, const char *string,
504 grecs_locus_t *locus)
505{
506 if (string[0] == '/') {
507 struct sockaddr_un s_un;
508 if (strlen(string) >= sizeof(s_un.sun_path)) {
509 grecs_error(locus, 0,
510 _("%s: UNIX socket name too long"),
511 string);
512 return 1;
513 }
514 s_un.sun_family = AF_UNIX;
515 strcpy(s_un.sun_path, string);
516 sp->len = sizeof(s_un);
517 sp->sa = grecs_malloc(sp->len);
518 memcpy(sp->sa, &s_un, sp->len);
519 } else {
520 char *p = strchr(string, ':');
521 size_t len;
522 struct sockaddr_in sa;
523
524 sa.sin_family = AF_INET;
525 if (p)
526 len = p - string;
527 else
528 len = strlen(string);
529
530 if (len == 0)
531 sa.sin_addr.s_addr = INADDR_ANY;
532 else {
533 char *host = grecs_malloc(len + 1);
534 memcpy(host, string, len);
535 host[len] = 0;
536
537 if (string_to_host(&sa.sin_addr, host, locus)) {
538 grecs_error(locus, 0,
539 _("%s: not a valid IP address or hostname"),
540 host);
541 free(host);
542 return 1;
543 }
544 free(host);
545 }
546
547 if (p) {
548 struct servent *serv;
549
550 p++;
551 serv = getservbyname(p, "tcp");
552 if (serv != NULL)
553 sa.sin_port = serv->s_port;
554 else {
555 unsigned long l;
556 char *q;
557
558 /* Not in services, maybe a number? */
559 l = strtoul(p, &q, 0);
560
561 if (*q || l > USHRT_MAX) {
562 grecs_error(locus, 0,
563 _("%s: not a valid port number"), p);
564 return 1;
565 }
566 sa.sin_port = htons(l);
567 }
568 } else if (grecs_default_port)
569 sa.sin_port = grecs_default_port;
570 else {
571 grecs_error(locus, 0, _("missing port number"));
572 return 1;
573 }
574 sp->len = sizeof(sa);
575 sp->sa = grecs_malloc(sp->len);
576 memcpy(sp->sa, &sa, sp->len);
577 }
578 return 0;
579}
580
581
582/* The TYPE_* defines come from gnulib's intprops.h */
583
584/* True if the arithmetic type T is signed. */
585# define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
586
587/* The maximum and minimum values for the integer type T. These
588 macros have undefined behavior if T is signed and has padding bits. */
589# define TYPE_MINIMUM(t) \
590 ((t) (! TYPE_SIGNED(t) \
591 ? (t) 0 \
592 : TYPE_SIGNED_MAGNITUDE(t) \
593 ? ~ (t) 0 \
594 : ~ TYPE_MAXIMUM(t)))
595# define TYPE_MAXIMUM(t) \
596 ((t) (! TYPE_SIGNED(t) \
597 ? (t) -1 \
598 : ((((t) 1 << (sizeof(t) * CHAR_BIT - 2)) - 1) * 2 + 1)))
599# define TYPE_SIGNED_MAGNITUDE(t) ((t) ~ (t) 0 < (t) -1)
600
601
602 #define STRTONUM(s, type, base, res, limit, loc) \
603 { \
604 type sum = 0; \
605 \
606 for (; *s; s++) \
607 { \
608 type x; \
609 \
610 if ('0' <= *s && *s <= '9') \
611 x = sum * base + *s - '0'; \
612 else if (base == 16 && 'a' <= *s && *s <= 'f') \
613 x = sum * base + *s - 'a'; \
614 else if (base == 16 && 'A' <= *s && *s <= 'F') \
615 x = sum * base + *s - 'A'; \
616 else \
617 break; \
618 if (x <= sum) \
619 { \
620 grecs_error(loc, 0, _("numeric overflow")); \
621 return 1; \
622 } \
623 else if (limit && x > limit) \
624 { \
625 grecs_error(loc, 0, _("value out of allowed range"));\
626 return 1; \
627 } \
628 sum = x; \
629 } \
630 res = sum; \
631 }
632
633 #define STRxTONUM(s, type, res, limit, loc) \
634 { \
635 int base; \
636 if (*s == '0') \
637 { \
638 s++; \
639 if (*s == 0) \
640 base = 10; \
641 else if (*s == 'x' || *s == 'X') \
642 { \
643 s++; \
644 base = 16; \
645 } \
646 else \
647 base = 8; \
648 } else \
649 base = 10; \
650 STRTONUM(s, type, base, res, limit, loc); \
651 }
652
653 #define GETUNUM(str, type, res, loc) \
654 { \
655 type tmpres; \
656 const char *s = str; \
657 STRxTONUM(s, type, tmpres, 0, loc); \
658 if (*s) \
659 { \
660 grecs_error(loc, 0, _("not a number (stopped near `%s')"),\
661 s); \
662 return 1; \
663 } \
664 res = tmpres; \
665 }
666
667 #define GETSNUM(str, type, res, loc) \
668 { \
669 unsigned type tmpres; \
670 const char *s = str; \
671 int sign; \
672 unsigned type limit; \
673 \
674 if (*s == '-') \
675 { \
676 sign = 1; \
677 s++; \
678 limit = TYPE_MINIMUM(type); \
679 limit = - limit; \
680 } \
681 else \
682 { \
683 sign = 0; \
684 limit = TYPE_MAXIMUM(type); \
685 } \
686 \
687 STRxTONUM(s, unsigned type, tmpres, limit, loc); \
688 if (*s) \
689 { \
690 grecs_error(loc, 0, _("not a number (stopped near `%s')"), s);\
691 return 1; \
692 } \
693 res = sign ? - tmpres : tmpres; \
694 }
695
696
697int
698grecs_string_convert(void *target, enum grecs_data_type type,
699 const char *string, grecs_locus_t *locus)
700{
701 switch (type) {
702 case grecs_type_void:
703 abort();
704
705 case grecs_type_string:
706 *(const char**)target = string;
707 break;
708
709 case grecs_type_short:
710 GETUNUM(string, short, *(short*)target, locus);
711 break;
712
713 case grecs_type_ushort:
714 GETUNUM(string, unsigned short, *(unsigned short*)target, locus);
715 break;
716
717 case grecs_type_bool:
718 return string_to_bool(string, (int*)target, locus);
719
720 case grecs_type_int:
721 GETSNUM(string, int, *(int*)target, locus);
722 break;
723
724 case grecs_type_uint:
725 GETUNUM(string, unsigned int, *(unsigned int*)target, locus);
726 break;
727
728 case grecs_type_long:
729 GETSNUM(string, long, *(long*)target, locus);
730 break;
731
732 case grecs_type_ulong:
733 GETUNUM(string, unsigned long, *(unsigned long*)target, locus);
734 break;
735
736 case grecs_type_size:
737 GETUNUM(string, size_t, *(size_t*)target, locus);
738 break;
739 /*FIXME
740 case grecs_type_off:
741 GETSNUM(string, off_t, *(off_t*)target, locus);
742 break;
743 */
744 case grecs_type_time:
745 /*FIXME: Use getdate */
746 GETUNUM(string, time_t, *(time_t*)target, locus);
747 break;
748
749 case grecs_type_ipv4:
750 if (inet_aton(string, (struct in_addr *)target)) {
751 grecs_error(locus, 0, _("%s: not a valid IP address"),
752 string);
753 return 1;
754 }
755 break;
756
757 case grecs_type_host:
758 if (string_to_host((struct in_addr *)target, string, locus)) {
759 grecs_error(locus, 0,
760 _("%s: not a valid IP address or hostname"),
761 string);
762 return 1;
763 }
764 break;
765
766 case grecs_type_sockaddr:
767 return string_to_sockaddr((struct grecs_sockaddr *)target, string,
768 locus);
769
770 /* FIXME: */
771 case grecs_type_cidr:
772 grecs_error(locus, 0,
773 _("INTERNAL ERROR at %s:%d"), __FILE__, __LINE__);
774 abort();
775
776 case grecs_type_section:
777 grecs_error(locus, 0,
778 _("invalid use of block statement"));
779 return 1;
780 }
781 return 0;
782}
783
784struct grecs_prop
785{
786 size_t size;
787 int (*cmp)(const void *, const void *);
788};
789
790static int
791string_cmp(const void *elt1, const void *elt2)
792{
793 return strcmp((const char *)elt1,(const char *)elt2);
794}
795
796#define __grecs_name_cat__(a,b) a ## b
797#define NUMCMP(type) __grecs_name_cat__(type,_cmp)
798 #define __DECL_NUMCMP(type,ctype) \
799 static int \
800 NUMCMP(type)(const void *elt1, const void *elt2)\
801 { \
802 return memcmp(elt1, elt2, sizeof(ctype)); \
803 }
804#define DECL_NUMCMP(type) __DECL_NUMCMP(type,type)
805
806DECL_NUMCMP(short)
807DECL_NUMCMP(int)
808DECL_NUMCMP(long)
809DECL_NUMCMP(size_t)
810/*FIXME DECL_NUMCMP(off_t)*/
811DECL_NUMCMP(time_t)
812__DECL_NUMCMP(in_addr, struct in_addr)
813__DECL_NUMCMP(grecs_sockaddr, struct grecs_sockaddr)
814
815struct grecs_prop grecs_prop_tab[] = {
816 { 0, NULL }, /* grecs_type_void */
817 { sizeof(char*), string_cmp }, /* grecs_type_string */
818 { sizeof(short), NUMCMP(short) }, /* grecs_type_short */
819 { sizeof(unsigned short), NUMCMP(short) }, /* grecs_type_ushort */
820 { sizeof(int), NUMCMP(int) }, /* grecs_type_int */
821 { sizeof(unsigned int), NUMCMP(int) }, /* grecs_type_uint */
822 { sizeof(long), NUMCMP(long) }, /* grecs_type_long */
823 { sizeof(unsigned long), NUMCMP(long) }, /* grecs_type_ulong */
824 { sizeof(size_t), NUMCMP(size_t) }, /* grecs_type_size */
825#if 0
826 FIXME
827 { sizeof(off_t), NUMCMP(off_t) }, /* grecs_type_off */
828#endif
829 { sizeof(time_t), NUMCMP(time_t) }, /* grecs_type_time */
830 { sizeof(int), NUMCMP(int) }, /* grecs_type_bool */
831 { sizeof(struct in_addr), NUMCMP(in_addr) }, /* grecs_type_ipv4 */
832 { 0, NULL }, /* FIXME: grecs_type_cidr */
833 { sizeof(struct in_addr), NUMCMP(in_addr) }, /* grecs_type_host */
834 { sizeof(struct grecs_sockaddr), NUMCMP(grecs_sockaddr) },
835 /* grecs_type_sockaddr */
836 { 0, NULL } /* grecs_type_section */
837};
838#define grecs_prop_count \
839 (sizeof(grecs_prop_tab) / sizeof(grecs_prop_tab[0]))
840
841void
842grecs_process_ident(struct grecs_keyword *kwp, grecs_value_t *value,
843 void *base, grecs_locus_t *locus)
844{
845 void *target;
846
847 if (!kwp)
848 return;
849
850 target = target_ptr(kwp, (char *) base);
851
852 if (kwp->callback)
853 kwp->callback(grecs_callback_set_value,
854 locus,
855 target,
856 value,
857 &kwp->callback_data);
858 else if (value->type == GRECS_TYPE_ARRAY) {
859 grecs_error(locus, 0,
860 _("too many arguments to `%s'; missing semicolon?"),
861 kwp->ident);
862 return;
863 } else if (value->type == GRECS_TYPE_LIST) {
864 if (GRECS_IS_LIST(kwp->type)) {
865 struct grecs_list_entry *ep;
866 enum grecs_data_type type = GRECS_TYPE(kwp->type);
867 int num = 1;
868 struct grecs_list *list;
869 size_t size;
870
871 if (type >= grecs_prop_count
872 || (size = grecs_prop_tab[type].size) == 0) {
873 grecs_error(locus, 0,
874 _("INTERNAL ERROR at %s:%d: "
875 "unhandled data type %d"),
876 __FILE__, __LINE__, type);
877 abort();
878 }
879
880 list = grecs_list_create();
881 list->cmp = grecs_prop_tab[type].cmp;
882
883 for (ep = value->v.list->head; ep; ep = ep->next) {
884 const grecs_value_t *vp = ep->data;
885
886 if (vp->type != GRECS_TYPE_STRING)
887 grecs_error(locus, 0,
888 _("%s: incompatible data type in list item #%d"),
889 kwp->ident, num);
890 else if (type == grecs_type_string)
891 grecs_list_append(list,
892 (void*) vp->v.string);
893 else {
894 void *ptr = grecs_malloc(size);
895 if (grecs_string_convert(ptr,
896 type,
897 vp->v.string,
898 locus) == 0)
899 grecs_list_append(list, ptr);
900 else
901 free(ptr);
902 }
903 }
904 *(struct grecs_list**)target = list;
905 } else {
906 grecs_error(locus, 0,
907 _("incompatible data type for `%s'"),
908 kwp->ident);
909 return;
910 }
911 } else if (GRECS_IS_LIST(kwp->type)) {
912 struct grecs_list *list;
913 enum grecs_data_type type = GRECS_TYPE(kwp->type);
914 size_t size;
915 void *ptr;
916
917 if (type >= grecs_prop_count
918 || (size = grecs_prop_tab[type].size) == 0) {
919 grecs_error(locus, 0,
920 _("INTERNAL ERROR at %s:%d: unhandled data type %d"),
921 __FILE__, __LINE__, type);
922 abort();
923 }
924
925 list = grecs_list_create();
926 list->cmp = grecs_prop_tab[type].cmp;
927 list->free_entry = listel_dispose;
928 if (type == grecs_type_string)
929 grecs_list_append(list, value->v.string);
930 else {
931 ptr = grecs_malloc(size);
932 if (grecs_string_convert(ptr, type, value->v.string, locus)) {
933 free(ptr);
934 grecs_list_free(list);
935 return;
936 }
937 grecs_list_append(list, ptr);
938 }
939 *(struct grecs_list**)target = list;
940 } else
941 grecs_string_convert(target, GRECS_TYPE(kwp->type),
942 value->v.string,
943 locus);
944}
945
946static void
947process_ident(struct grecs_keyword *kwp, grecs_value_t *value)
948{
949 grecs_process_ident(kwp, value, CURRENT_BASE, &grecs_current_locus);
950}
951 318
952 319
diff --git a/src/grecs.h b/src/grecs.h
index 5769736..e0f0fa5 100644
--- a/src/grecs.h
+++ b/src/grecs.h
@@ -100,6 +100,25 @@ typedef struct grecs_value {
100 } v; 100 } v;
101} grecs_value_t; 101} grecs_value_t;
102 102
103#define GRECS_VALUE_EMPTY_P(val) \
104 (!(val) || \
105 ((val)->type == GRECS_TYPE_STRING && (val)->v.string == NULL))
106
107enum grecs_node_type {
108 grecs_node_stmt,
109 grecs_node_block
110};
111
112typedef struct grecs_node {
113 enum grecs_node_type type;
114 grecs_locus_t locus;
115 struct grecs_node *up;
116 struct grecs_node *down;
117 struct grecs_node *next;
118 char *ident;
119 struct grecs_value value;
120} grecs_node_t;
121
103typedef int (*grecs_callback_fn)( 122typedef int (*grecs_callback_fn)(
104 enum grecs_callback_command cmd, 123 enum grecs_callback_command cmd,
105 grecs_locus_t * /* locus */, 124 grecs_locus_t * /* locus */,
@@ -138,19 +157,20 @@ char *grecs_strdup(const char *str);
138 157
139grecs_value_t *grecs_value_dup(grecs_value_t *input); 158grecs_value_t *grecs_value_dup(grecs_value_t *input);
140 159
141extern void grecs_print_diag(grecs_locus_t *, int, int, const char*); 160extern void (*grecs_print_diag_fun)(grecs_locus_t *, int, int, const char*);
142 161
143void grecs_warning(grecs_locus_t *locus, int errcode, const char *fmt, ...) 162void grecs_warning(grecs_locus_t *locus, int errcode, const char *fmt, ...)
144 __attribute__ ((__format__ (__printf__, 3, 4))); 163 __attribute__ ((__format__ (__printf__, 3, 4)));
145void grecs_error(grecs_locus_t *locus, int errcode, const char *fmt, ...) 164void grecs_error(grecs_locus_t *locus, int errcode, const char *fmt, ...)
146 __attribute__ ((__format__ (__printf__, 3, 4))); 165 __attribute__ ((__format__ (__printf__, 3, 4)));
147void grecs_set_keywords(struct grecs_keyword *kwd);
148void grecs_gram_trace(int n); 166void grecs_gram_trace(int n);
149void grecs_lex_trace(int n); 167void grecs_lex_trace(int n);
150 168
151int grecs_lex_begin(const char*); 169int grecs_lex_begin(const char*);
152void grecs_lex_end(void); 170void grecs_lex_end(void);
153int grecs_parse(const char *name); 171struct grecs_node *grecs_parse(const char *name);
172
173struct grecs_list *_grecs_simple_list_create(int dispose);
154 174
155void grecs_line_begin(void); 175void grecs_line_begin(void);
156void grecs_line_add(const char *text, size_t len); 176void grecs_line_add(const char *text, size_t len);
@@ -163,6 +183,12 @@ extern void grecs_process_ident(struct grecs_keyword *kwp,
163 void *base, 183 void *base,
164 grecs_locus_t *locus); 184 grecs_locus_t *locus);
165 185
186struct grecs_node *grecs_node_create(enum grecs_node_type type,
187 grecs_locus_t *loc);
188void grecs_node_bind(struct grecs_node *master, struct grecs_node *node,
189 int dn);
190
191
166extern grecs_locus_t grecs_current_locus; 192extern grecs_locus_t grecs_current_locus;
167extern int grecs_error_count; 193extern int grecs_error_count;
168extern int grecs_default_port; 194extern int grecs_default_port;
@@ -187,16 +213,22 @@ void grecs_include_path_setup(const char *dir, ...);
187void grecs_include_path_setup_v(char **dirs); 213void grecs_include_path_setup_v(char **dirs);
188 214
189const char *grecs_data_type_string(enum grecs_data_type type); 215const char *grecs_data_type_string(enum grecs_data_type type);
190void grecs_format_docstring(FILE *stream, const char *docstring, 216void grecs_format_docstring(const char *docstring, unsigned level,
191 unsigned level); 217 FILE *stream);
192void grecs_format_simple_statement(FILE *stream, struct grecs_keyword *kwp, 218void grecs_format_simple_statement(struct grecs_keyword *kwp,
193 unsigned level); 219 unsigned level, FILE *stream);
194void grecs_format_block_statement(FILE *stream, struct grecs_keyword *kwp, 220void grecs_format_block_statement(struct grecs_keyword *kwp,
195 unsigned level); 221 unsigned level, FILE *stream);
196void grecs_format_statement_array(FILE *stream, struct grecs_keyword *kwp, 222void grecs_format_statement_array(struct grecs_keyword *kwp,
197 unsigned n, 223 unsigned n,
198 unsigned level); 224 unsigned level, FILE *stream);
199 225
226void grecs_format_locus(grecs_locus_t *locus, FILE *fp);
227void grecs_format_node_ident(struct grecs_node *node, int delim, FILE *fp);
228void grecs_format_value(struct grecs_value *val, FILE *fp);
229
230#define GRECS_NODE_FLAG_LOCUS 0x0100
231void grecs_format_node(struct grecs_node *node, int flags, FILE *fp);
200 232
201struct grecs_list *grecs_list_create(void); 233struct grecs_list *grecs_list_create(void);
202size_t grecs_list_size(struct grecs_list *lp); 234size_t grecs_list_size(struct grecs_list *lp);
@@ -240,5 +272,33 @@ int grecs_symtab_enumerate(struct grecs_symtab *st,
240 grecs_symtab_enumerator_t fun, void *data); 272 grecs_symtab_enumerator_t fun, void *data);
241 273
242size_t grecs_symtab_count_entries(struct grecs_symtab *st); 274size_t grecs_symtab_count_entries(struct grecs_symtab *st);
275
276void grecs_node_free(struct grecs_node *node);
277void grecs_tree_free(struct grecs_node *node);
278
279enum grecs_tree_recurse_op {
280 grecs_tree_recurse_set,
281 grecs_tree_recurse_pre,
282 grecs_tree_recurse_post
283};
284
285enum grecs_tree_recurse_res {
286 grecs_tree_recurse_ok,
287 grecs_tree_recurse_fail,
288 grecs_tree_recurse_skip,
289 grecs_tree_recurse_stop
290};
291
292typedef enum grecs_tree_recurse_res
293 (*grecs_tree_recursor_t)(enum grecs_tree_recurse_op,
294 struct grecs_node *, void *);
295
296int grecs_tree_recurse(struct grecs_node *node, grecs_tree_recursor_t recfun,
297 void *data);
243 298
299
300int grecs_tree_process(struct grecs_node *node, struct grecs_keyword *kwd);
301
302int grecs_value_eq(struct grecs_value *a, struct grecs_value *b);
303struct grecs_node *grecs_find_node(struct grecs_node *node, const char *path);
244 304
diff --git a/src/lookup.c b/src/lookup.c
new file mode 100644
index 0000000..fdf1ae1
--- a/dev/null
+++ b/src/lookup.c
@@ -0,0 +1,272 @@
1/* grecs - Gray's Extensible Configuration System
2 Copyright (C) 2007-2011 Sergey Poznyakoff
3
4 Grecs is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 Grecs is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with Grecs. If not, see <http://www.gnu.org/licenses/>. */
16
17#ifdef HAVE_CONFIG_H
18# include <config.h>
19#endif
20#include <string.h>
21#include <errno.h>
22#include <ctype.h>
23#include "grecs.h"
24#include "wordsplit.h"
25
26static int
27_grecs_list_eq(struct grecs_value *a, struct grecs_value *b)
28{
29 struct grecs_list_entry *aent, *bent;
30 if (grecs_list_size(a->v.list) != grecs_list_size(b->v.list))
31 return 0;
32
33 for (aent = a->v.list->head, bent = b->v.list->head;;
34 aent = aent->next, bent = bent->next) {
35 if (!aent)
36 return bent == NULL;
37 if (!bent)
38 return 0;
39 if (!grecs_value_eq(aent->data, bent->data))
40 return 0;
41 }
42 /*notreached*/
43 return 1;
44}
45
46static int
47_grecs_array_eq(struct grecs_value *a, struct grecs_value *b)
48{
49 size_t i;
50
51 if (a->v.arg.c != b->v.arg.c)
52 return 0;
53
54 for (i = 0; i < a->v.arg.c; i++)
55 if (!grecs_value_eq(&a->v.arg.v[i], &b->v.arg.v[i]))
56 return 0;
57 return 1;
58}
59
60/* Return 1 if configuration value A equals B */
61int
62grecs_value_eq(struct grecs_value *a, struct grecs_value *b)
63{
64 if (a->type != b->type)
65 return 0;
66 switch (a->type) {
67 case GRECS_TYPE_STRING:
68 if (a->v.string == NULL)
69 return b->v.string == NULL;
70 return strcmp(a->v.string, b->v.string) == 0;
71
72 case GRECS_TYPE_LIST:
73 return _grecs_list_eq(a, b);
74
75 case GRECS_TYPE_ARRAY:
76 return _grecs_array_eq(a, b);
77 }
78 return 0;
79}
80
81
82static int
83split_cfg_path(const char *path, int *pargc, char ***pargv)
84{
85 int argc;
86 char **argv;
87 char *delim = ".";
88 char static_delim[2] = { 0, 0 };
89
90 if (path[0] == '\\') {
91 argv = calloc(2, sizeof (*argv));
92 if (!argv)
93 return ENOMEM;
94 argv[0] = strdup(path + 1);
95 if (!argv[0]) {
96 free(argv);
97 return ENOMEM;
98 }
99 argv[1] = NULL;
100 argc = 1;
101 } else {
102 struct wordsplit ws;
103
104 if (ispunct(path[0])) {
105 delim = static_delim;
106 delim[0] = path[0];
107 path++;
108 }
109 ws.ws_delim = delim;
110
111 if (wordsplit(path, &ws, WRDSF_DEFFLAGS|WRDSF_DELIM))
112 return errno;
113 argc = ws.ws_wordc;
114 argv = ws.ws_wordv;
115 ws.ws_wordc = 0;
116 ws.ws_wordv = NULL;
117 wordsplit_free(&ws);
118 }
119
120 *pargc = argc;
121 *pargv = argv;
122
123 return 0;
124}
125
126static void
127free_value_mem(struct grecs_value *p)
128{
129 switch (p->type) {
130 case GRECS_TYPE_STRING:
131 free((char*)p->v.string);
132 break;
133
134 case GRECS_TYPE_LIST:
135 /* FIXME */
136 break;
137
138 case GRECS_TYPE_ARRAY: {
139 size_t i;
140 for (i = 0; i < p->v.arg.c; i++)
141 free_value_mem(&p->v.arg.v[i]);
142 }
143 }
144}
145
146static void
147destroy_value(void *p)
148{
149 struct grecs_value*val = p;
150 if (val) {
151 free_value_mem(val);
152 free(val);
153 }
154}
155
156static struct grecs_value *
157parse_label(const char *str)
158{
159 struct grecs_value *val = NULL;
160 size_t i;
161 struct wordsplit ws;
162 size_t len = strlen (str);
163
164 if (len > 1 && str[0] == '(' && str[len-1] == ')') {
165 struct grecs_list *lst;
166
167 ws.ws_delim = ",";
168 if (wordsplit_len (str + 1, len - 2, &ws,
169 WRDSF_DEFFLAGS|WRDSF_DELIM|
170 WRDSF_WS)) {
171 return NULL;
172 }
173
174 lst = grecs_list_create();
175 lst->free_entry = destroy_value;
176 for (i = 0; i < ws.ws_wordc; i++) {
177 struct grecs_value *p = grecs_malloc(sizeof(*p));
178 p->type = GRECS_TYPE_STRING;
179 p->v.string = ws.ws_wordv[i];
180 grecs_list_append(lst, p);
181 }
182 val = grecs_malloc(sizeof(*val));
183 val->type = GRECS_TYPE_LIST;
184 val->v.list = lst;
185 } else {
186 if (wordsplit(str, &ws, WRDSF_DEFFLAGS))
187 return NULL;
188 val = grecs_malloc(sizeof(*val));
189 if (ws.ws_wordc == 1) {
190 val->type = GRECS_TYPE_STRING;
191 val->v.string = ws.ws_wordv[0];
192 } else {
193 val->type = GRECS_TYPE_ARRAY;
194 val->v.arg.c = ws.ws_wordc;
195 val->v.arg.v = grecs_malloc(ws.ws_wordc *
196 sizeof(val->v.arg.v[0]));
197 for (i = 0; i < ws.ws_wordc; i++) {
198 val->v.arg.v[i].type = GRECS_TYPE_STRING;
199 val->v.arg.v[i].v.string = ws.ws_wordv[i];
200 }
201 }
202 ws.ws_wordc = 0;
203 wordsplit_free(&ws);
204 }
205 return val;
206}
207
208
209struct find_closure {
210 int argc;
211 char **argv;
212 int tag;
213 struct grecs_value *label;
214 struct grecs_node *node;
215};
216
217static void
218parse_tag(struct find_closure *fptr)
219{
220 char *p = strchr(fptr->argv[fptr->tag], '=');
221 if (p) {
222 *p++ = 0;
223 fptr->label = parse_label(p);
224 }
225 else
226 fptr->label = NULL;
227}
228
229static enum grecs_tree_recurse_res
230node_finder(enum grecs_tree_recurse_op op, struct grecs_node *node, void *data)
231{
232 struct find_closure *fdptr = data;
233
234 if (op == grecs_tree_recurse_post)
235 return grecs_tree_recurse_ok;
236
237 if (strcmp(fdptr->argv[fdptr->tag], node->ident) == 0
238 && (!fdptr->label || grecs_value_eq(fdptr->label, &node->value))) {
239 fdptr->tag++;
240 if (fdptr->tag == fdptr->argc) {
241 fdptr->node = node;
242 return grecs_tree_recurse_stop;
243 }
244 parse_tag(fdptr);
245 return grecs_tree_recurse_ok;
246 }
247
248 return node->type == grecs_node_block ?
249 grecs_tree_recurse_skip : grecs_tree_recurse_ok;
250}
251
252struct grecs_node *
253grecs_find_node(struct grecs_node *node, const char *path)
254{
255 int rc, i;
256 struct find_closure clos;
257
258 rc = split_cfg_path(path, &clos.argc, &clos.argv);
259 if (!clos.argc)
260 return NULL;
261 clos.tag = 0;
262 clos.label = NULL;
263 clos.node = NULL;
264 parse_tag(&clos);
265 grecs_tree_recurse(node, node_finder, &clos);
266 for (i = 0; i < clos.argc; i++)
267 free(clos.argv[i]);
268 free(clos.argv);
269 destroy_value(clos.label);
270 return clos.node;
271}
272
diff --git a/src/tree.c b/src/tree.c
new file mode 100644
index 0000000..52d38c0
--- a/dev/null
+++ b/src/tree.c
@@ -0,0 +1,773 @@
1/* grecs - Gray's Extensible Configuration System
2 Copyright (C) 2007-2011 Sergey Poznyakoff
3
4 Grecs is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 Grecs is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with Grecs. If not, see <http://www.gnu.org/licenses/>. */
16
17#ifdef HAVE_CONFIG_H
18# include <config.h>
19#endif
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <sys/time.h>
23#include <sys/un.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26#include <netdb.h>
27#include <stdlib.h>
28#include "grecs.h"
29
30struct grecs_node *
31grecs_node_create(enum grecs_node_type type, grecs_locus_t *loc)
32{
33 struct grecs_node *np = grecs_zalloc(sizeof(*np));
34 np->type = type;
35 if (loc)
36 np->locus = *loc;
37 return np;
38}
39
40void
41grecs_node_bind(struct grecs_node *master, struct grecs_node *node, int dn)
42{
43 struct grecs_node *np;
44
45 if (dn) {
46 if (!master->down)
47 master->down = node;
48 else {
49 for (np = master->down; np->next; np = np->next)
50 ;
51 np->next = node;
52 }
53 for (; node; node = node->next)
54 node->up = master;
55 } else {
56 if (!master->next)
57 master->next = node;
58 else {
59 for (np = master->next; np->next; np = np->next)
60 ;
61 np->next = node;
62 }
63 node->up = master->up;
64 }
65}
66
67
68static enum grecs_tree_recurse_res
69_tree_recurse(struct grecs_node *node, grecs_tree_recursor_t recfun,
70 void *data)
71{
72 enum grecs_tree_recurse_res res;
73 #define CKRES() \
74 switch (res) { \
75 case grecs_tree_recurse_fail: \
76 case grecs_tree_recurse_stop: \
77 return res; \
78 default: \
79 break; \
80 }
81
82 for (; node; node = node->next) {
83 if (node->type == grecs_node_stmt) {
84 res = recfun(grecs_tree_recurse_set, node, data);
85 CKRES();
86 } else {
87 switch (recfun(grecs_tree_recurse_pre, node, data)) {
88 case grecs_tree_recurse_ok:
89 res = _tree_recurse(node->down, recfun, data);
90 CKRES();
91 res = recfun(grecs_tree_recurse_post, node,
92 data);
93 CKRES();
94 break;
95 case grecs_tree_recurse_fail:
96 return grecs_tree_recurse_fail;
97 case grecs_tree_recurse_stop:
98 return grecs_tree_recurse_stop;
99 case grecs_tree_recurse_skip:
100 break;
101 }
102 }
103 }
104 return grecs_tree_recurse_ok;
105#undef CKRES
106}
107
108int
109grecs_tree_recurse(struct grecs_node *node, grecs_tree_recursor_t recfun,
110 void *data)
111{
112 switch (_tree_recurse(node, recfun, data)) {
113 case grecs_tree_recurse_ok:
114 case grecs_tree_recurse_stop:
115 return 0;
116 default:
117 break;
118 }
119 return 1;
120}
121
122void
123grecs_node_free(struct grecs_node *node)
124{
125 /*FIXME: value*/
126 free(node);
127}
128
129void
130grecs_tree_free(struct grecs_node *node)
131{
132 while (node) {
133 struct grecs_node *next = node->next;
134 if (node->down)
135 grecs_tree_free(node->down);
136 grecs_node_free(node);
137 node = next;
138 }
139}
140
141
142
143static int
144fake_callback(enum grecs_callback_command cmd,
145 grecs_locus_t *locus,
146 void *varptr,
147 grecs_value_t *value,
148 void *cb_data)
149{
150 return 0;
151}
152
153static struct grecs_keyword fake = {
154 "*",
155 NULL,
156 NULL,
157 grecs_type_void,
158 NULL,
159 0,
160 fake_callback,
161 NULL,
162 &fake
163};
164
165static struct grecs_keyword *
166find_keyword(struct grecs_keyword *cursect, const char *ident)
167{
168 struct grecs_keyword *kwp;
169
170 if (cursect && cursect != &fake) {
171 for (kwp = cursect->kwd; kwp->ident; kwp++)
172 if (strcmp(kwp->ident, ident) == 0)
173 return kwp;
174 } else {
175 return &fake;
176 }
177 return NULL;
178}
179
180static void *
181target_ptr(struct grecs_keyword *kwp, char *base)
182{
183 if (kwp->varptr)
184 base = (char*) kwp->varptr + kwp->offset;
185 else if (base)
186 base += kwp->offset;
187
188 return base;
189}
190
191static int
192string_to_bool(const char *string, int *pval, grecs_locus_t *locus)
193{
194 if (strcmp(string, "yes") == 0
195 || strcmp(string, "true") == 0
196 || strcmp(string, "t") == 0
197 || strcmp(string, "1") == 0)
198 *pval = 1;
199 else if (strcmp(string, "no") == 0
200 || strcmp(string, "false") == 0
201 || strcmp(string, "nil") == 0
202 || strcmp(string, "0") == 0)
203 *pval = 0;
204 else {
205 grecs_error(locus, 0,
206 _("%s: not a valid boolean value"),
207 string);
208 return 1;
209 }
210 return 0;
211}
212
213static int
214string_to_host(struct in_addr *in, const char *string, grecs_locus_t *locus)
215{
216 if (inet_aton(string, in) == 0) {
217 struct hostent *hp;
218
219 hp = gethostbyname(string);
220 if (hp == NULL)
221 return 1;
222 memcpy(in, hp->h_addr, sizeof(struct in_addr));
223 }
224 return 0;
225}
226
227static int
228string_to_sockaddr(struct grecs_sockaddr *sp, const char *string,
229 grecs_locus_t *locus)
230{
231 if (string[0] == '/') {
232 struct sockaddr_un s_un;
233 if (strlen(string) >= sizeof(s_un.sun_path)) {
234 grecs_error(locus, 0,
235 _("%s: UNIX socket name too long"),
236 string);
237 return 1;
238 }
239 s_un.sun_family = AF_UNIX;
240 strcpy(s_un.sun_path, string);
241 sp->len = sizeof(s_un);
242 sp->sa = grecs_malloc(sp->len);
243 memcpy(sp->sa, &s_un, sp->len);
244 } else {
245 char *p = strchr(string, ':');
246 size_t len;
247 struct sockaddr_in sa;
248
249 sa.sin_family = AF_INET;
250 if (p)
251 len = p - string;
252 else
253 len = strlen(string);
254
255 if (len == 0)
256 sa.sin_addr.s_addr = INADDR_ANY;
257 else {
258 char *host = grecs_malloc(len + 1);
259 memcpy(host, string, len);
260 host[len] = 0;
261
262 if (string_to_host(&sa.sin_addr, host, locus)) {
263 grecs_error(locus, 0,
264 _("%s: not a valid IP address or hostname"),
265 host);
266 free(host);
267 return 1;
268 }
269 free(host);
270 }
271
272 if (p) {
273 struct servent *serv;
274
275 p++;
276 serv = getservbyname(p, "tcp");
277 if (serv != NULL)
278 sa.sin_port = serv->s_port;
279 else {
280 unsigned long l;
281 char *q;
282
283 /* Not in services, maybe a number? */
284 l = strtoul(p, &q, 0);
285
286 if (*q || l > USHRT_MAX) {
287 grecs_error(locus, 0,
288 _("%s: not a valid port number"), p);
289 return 1;
290 }
291 sa.sin_port = htons(l);
292 }
293 } else if (grecs_default_port)
294 sa.sin_port = grecs_default_port;
295 else {
296 grecs_error(locus, 0, _("missing port number"));
297 return 1;
298 }
299 sp->len = sizeof(sa);
300 sp->sa = grecs_malloc(sp->len);
301 memcpy(sp->sa, &sa, sp->len);
302 }
303 return 0;
304}
305
306
307/* The TYPE_* defines come from gnulib's intprops.h */
308
309/* True if the arithmetic type T is signed. */
310# define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
311
312/* The maximum and minimum values for the integer type T. These
313 macros have undefined behavior if T is signed and has padding bits. */
314# define TYPE_MINIMUM(t) \
315 ((t) (! TYPE_SIGNED(t) \
316 ? (t) 0 \
317 : TYPE_SIGNED_MAGNITUDE(t) \
318 ? ~ (t) 0 \
319 : ~ TYPE_MAXIMUM(t)))
320# define TYPE_MAXIMUM(t) \
321 ((t) (! TYPE_SIGNED(t) \
322 ? (t) -1 \
323 : ((((t) 1 << (sizeof(t) * CHAR_BIT - 2)) - 1) * 2 + 1)))
324# define TYPE_SIGNED_MAGNITUDE(t) ((t) ~ (t) 0 < (t) -1)
325
326
327 #define STRTONUM(s, type, base, res, limit, loc) \
328 { \
329 type sum = 0; \
330 \
331 for (; *s; s++) \
332 { \
333 type x; \
334 \
335 if ('0' <= *s && *s <= '9') \
336 x = sum * base + *s - '0'; \
337 else if (base == 16 && 'a' <= *s && *s <= 'f') \
338 x = sum * base + *s - 'a'; \
339 else if (base == 16 && 'A' <= *s && *s <= 'F') \
340 x = sum * base + *s - 'A'; \
341 else \
342 break; \
343 if (x <= sum) \
344 { \
345 grecs_error(loc, 0, _("numeric overflow")); \
346 return 1; \
347 } \
348 else if (limit && x > limit) \
349 { \
350 grecs_error(loc, 0, _("value out of allowed range"));\
351 return 1; \
352 } \
353 sum = x; \
354 } \
355 res = sum; \
356 }
357
358 #define STRxTONUM(s, type, res, limit, loc) \
359 { \
360 int base; \
361 if (*s == '0') \
362 { \
363 s++; \
364 if (*s == 0) \
365 base = 10; \
366 else if (*s == 'x' || *s == 'X') \
367 { \
368 s++; \
369 base = 16; \
370 } \
371 else \
372 base = 8; \
373 } else \
374 base = 10; \
375 STRTONUM(s, type, base, res, limit, loc); \
376 }
377
378 #define GETUNUM(str, type, res, loc) \
379 { \
380 type tmpres; \
381 const char *s = str; \
382 STRxTONUM(s, type, tmpres, 0, loc); \
383 if (*s) \
384 { \
385 grecs_error(loc, 0, _("not a number (stopped near `%s')"),\
386 s); \
387 return 1; \
388 } \
389 res = tmpres; \
390 }
391
392 #define GETSNUM(str, type, res, loc) \
393 { \
394 unsigned type tmpres; \
395 const char *s = str; \
396 int sign; \
397 unsigned type limit; \
398 \
399 if (*s == '-') \
400 { \
401 sign = 1; \
402 s++; \
403 limit = TYPE_MINIMUM(type); \
404 limit = - limit; \
405 } \
406 else \
407 { \
408 sign = 0; \
409 limit = TYPE_MAXIMUM(type); \
410 } \
411 \
412 STRxTONUM(s, unsigned type, tmpres, limit, loc); \
413 if (*s) \
414 { \
415 grecs_error(loc, 0, _("not a number (stopped near `%s')"), s);\
416 return 1; \
417 } \
418 res = sign ? - tmpres : tmpres; \
419 }
420
421
422int
423grecs_string_convert(void *target, enum grecs_data_type type,
424 const char *string, grecs_locus_t *locus)
425{
426 switch (type) {
427 case grecs_type_void:
428 abort();
429
430 case grecs_type_string:
431 *(const char**)target = string;
432 break;
433
434 case grecs_type_short:
435 GETUNUM(string, short, *(short*)target, locus);
436 break;
437
438 case grecs_type_ushort:
439 GETUNUM(string, unsigned short, *(unsigned short*)target, locus);
440 break;
441
442 case grecs_type_bool:
443 return string_to_bool(string, (int*)target, locus);
444
445 case grecs_type_int:
446 GETSNUM(string, int, *(int*)target, locus);
447 break;
448
449 case grecs_type_uint:
450 GETUNUM(string, unsigned int, *(unsigned int*)target, locus);
451 break;
452
453 case grecs_type_long:
454 GETSNUM(string, long, *(long*)target, locus);
455 break;
456
457 case grecs_type_ulong:
458 GETUNUM(string, unsigned long, *(unsigned long*)target, locus);
459 break;
460
461 case grecs_type_size:
462 GETUNUM(string, size_t, *(size_t*)target, locus);
463 break;
464 /*FIXME
465 case grecs_type_off:
466 GETSNUM(string, off_t, *(off_t*)target, locus);
467 break;
468 */
469 case grecs_type_time:
470 /*FIXME: Use getdate */
471 GETUNUM(string, time_t, *(time_t*)target, locus);
472 break;
473
474 case grecs_type_ipv4:
475 if (inet_aton(string, (struct in_addr *)target)) {
476 grecs_error(locus, 0, _("%s: not a valid IP address"),
477 string);
478 return 1;
479 }
480 break;
481
482 case grecs_type_host:
483 if (string_to_host((struct in_addr *)target, string, locus)) {
484 grecs_error(locus, 0,
485 _("%s: not a valid IP address or hostname"),
486 string);
487 return 1;
488 }
489 break;
490
491 case grecs_type_sockaddr:
492 return string_to_sockaddr((struct grecs_sockaddr *)target, string,
493 locus);
494
495 /* FIXME: */
496 case grecs_type_cidr:
497 grecs_error(locus, 0,
498 _("INTERNAL ERROR at %s:%d"), __FILE__, __LINE__);
499 abort();
500
501 case grecs_type_section:
502 grecs_error(locus, 0,
503 _("invalid use of block statement"));
504 return 1;
505 }
506 return 0;
507}
508
509struct grecs_prop
510{
511 size_t size;
512 int (*cmp)(const void *, const void *);
513};
514
515static int
516string_cmp(const void *elt1, const void *elt2)
517{
518 return strcmp((const char *)elt1,(const char *)elt2);
519}
520
521#define __grecs_name_cat__(a,b) a ## b
522#define NUMCMP(type) __grecs_name_cat__(type,_cmp)
523 #define __DECL_NUMCMP(type,ctype) \
524 static int \
525 NUMCMP(type)(const void *elt1, const void *elt2)\
526 { \
527 return memcmp(elt1, elt2, sizeof(ctype)); \
528 }
529#define DECL_NUMCMP(type) __DECL_NUMCMP(type,type)
530
531DECL_NUMCMP(short)
532DECL_NUMCMP(int)
533DECL_NUMCMP(long)
534DECL_NUMCMP(size_t)
535/*FIXME DECL_NUMCMP(off_t)*/
536DECL_NUMCMP(time_t)
537__DECL_NUMCMP(in_addr, struct in_addr)
538__DECL_NUMCMP(grecs_sockaddr, struct grecs_sockaddr)
539
540struct grecs_prop grecs_prop_tab[] = {
541 { 0, NULL }, /* grecs_type_void */
542 { sizeof(char*), string_cmp }, /* grecs_type_string */
543 { sizeof(short), NUMCMP(short) }, /* grecs_type_short */
544 { sizeof(unsigned short), NUMCMP(short) }, /* grecs_type_ushort */
545 { sizeof(int), NUMCMP(int) }, /* grecs_type_int */
546 { sizeof(unsigned int), NUMCMP(int) }, /* grecs_type_uint */
547 { sizeof(long), NUMCMP(long) }, /* grecs_type_long */
548 { sizeof(unsigned long), NUMCMP(long) }, /* grecs_type_ulong */
549 { sizeof(size_t), NUMCMP(size_t) }, /* grecs_type_size */
550#if 0
551 FIXME
552 { sizeof(off_t), NUMCMP(off_t) }, /* grecs_type_off */
553#endif
554 { sizeof(time_t), NUMCMP(time_t) }, /* grecs_type_time */
555 { sizeof(int), NUMCMP(int) }, /* grecs_type_bool */
556 { sizeof(struct in_addr), NUMCMP(in_addr) }, /* grecs_type_ipv4 */
557 { 0, NULL }, /* FIXME: grecs_type_cidr */
558 { sizeof(struct in_addr), NUMCMP(in_addr) }, /* grecs_type_host */
559 { sizeof(struct grecs_sockaddr), NUMCMP(grecs_sockaddr) },
560 /* grecs_type_sockaddr */
561 { 0, NULL } /* grecs_type_section */
562};
563#define grecs_prop_count \
564 (sizeof(grecs_prop_tab) / sizeof(grecs_prop_tab[0]))
565
566void
567grecs_process_ident(struct grecs_keyword *kwp, grecs_value_t *value,
568 void *base, grecs_locus_t *locus)
569{
570 void *target;
571
572 if (!kwp)
573 return;
574
575 target = target_ptr(kwp, (char *) base);
576
577 if (kwp->callback)
578 kwp->callback(grecs_callback_set_value,
579 locus,
580 target,
581 value,
582 &kwp->callback_data);
583 else if (value->type == GRECS_TYPE_ARRAY) {
584 grecs_error(locus, 0,
585 _("too many arguments to `%s'; missing semicolon?"),
586 kwp->ident);
587 return;
588 } else if (value->type == GRECS_TYPE_LIST) {
589 if (GRECS_IS_LIST(kwp->type)) {
590 struct grecs_list_entry *ep;
591 enum grecs_data_type type = GRECS_TYPE(kwp->type);
592 int num = 1;
593 struct grecs_list *list;
594 size_t size;
595
596 if (type >= grecs_prop_count
597 || (size = grecs_prop_tab[type].size) == 0) {
598 grecs_error(locus, 0,
599 _("INTERNAL ERROR at %s:%d: "
600 "unhandled data type %d"),
601 __FILE__, __LINE__, type);
602 abort();
603 }
604
605 list = grecs_list_create();
606 list->cmp = grecs_prop_tab[type].cmp;
607
608 for (ep = value->v.list->head; ep; ep = ep->next) {
609 const grecs_value_t *vp = ep->data;
610
611 if (vp->type != GRECS_TYPE_STRING)
612 grecs_error(locus, 0,
613 _("%s: incompatible data type in list item #%d"),
614 kwp->ident, num);
615 else if (type == grecs_type_string)
616 grecs_list_append(list,
617 (void*) vp->v.string);
618 else {
619 void *ptr = grecs_malloc(size);
620 if (grecs_string_convert(ptr,
621 type,
622 vp->v.string,
623 locus) == 0)
624 grecs_list_append(list, ptr);
625 else
626 free(ptr);
627 }
628 }
629 *(struct grecs_list**)target = list;
630 } else {
631 grecs_error(locus, 0,
632 _("incompatible data type for `%s'"),
633 kwp->ident);
634 return;
635 }
636 } else if (GRECS_IS_LIST(kwp->type)) {
637 struct grecs_list *list;
638 enum grecs_data_type type = GRECS_TYPE(kwp->type);
639 size_t size;
640 void *ptr;
641
642 if (type >= grecs_prop_count
643 || (size = grecs_prop_tab[type].size) == 0) {
644 grecs_error(locus, 0,
645 _("INTERNAL ERROR at %s:%d: unhandled data type %d"),
646 __FILE__, __LINE__, type);
647 abort();
648 }
649
650 list = _grecs_simple_list_create(1);
651 list->cmp = grecs_prop_tab[type].cmp;
652 if (type == grecs_type_string)
653 grecs_list_append(list, value->v.string);
654 else {
655 ptr = grecs_malloc(size);
656 if (grecs_string_convert(ptr, type, value->v.string, locus)) {
657 free(ptr);
658 grecs_list_free(list);
659 return;
660 }
661 grecs_list_append(list, ptr);
662 }
663 *(struct grecs_list**)target = list;
664 } else
665 grecs_string_convert(target, GRECS_TYPE(kwp->type),
666 value->v.string,
667 locus);
668}
669
670
671struct nodeproc_closure {
672 struct grecs_keyword *cursect;
673 struct grecs_list *sections;
674};
675#define CURRENT_BASE(clos) \
676 ((char*)((clos)->cursect ? (clos)->cursect->callback_data : NULL))
677
678static void
679stmt_begin(struct nodeproc_closure *clos,
680 struct grecs_keyword *kwp, struct grecs_node *node)
681{
682 void *target;
683
684 grecs_list_push(clos->sections, clos->cursect);
685 if (kwp) {
686 target = target_ptr(kwp, CURRENT_BASE(clos));
687 clos->cursect = kwp;
688 if (kwp->callback) {
689 if (kwp->callback(grecs_callback_section_begin,
690 &node->locus,
691 target,
692 &node->value,
693 &kwp->callback_data))
694 clos->cursect = &fake;
695 } else
696 kwp->callback_data = target;
697 } else
698 /* install an "ignore-all" section */
699 clos->cursect = kwp;
700}
701
702static void
703stmt_end(struct nodeproc_closure *clos, struct grecs_node *node)
704{
705 grecs_callback_fn callback = NULL;
706 void *dataptr = NULL;
707 struct grecs_keyword *kwp = clos->cursect;
708
709 if (clos->cursect && clos->cursect->callback) {
710 callback = clos->cursect->callback;
711 dataptr = &clos->cursect->callback_data;
712 }
713
714 clos->cursect = (struct grecs_keyword *)grecs_list_pop(clos->sections);
715 if (!clos->cursect)
716 abort();
717 if (callback)
718 callback(grecs_callback_section_end,
719 &node->locus,
720 kwp ? target_ptr(kwp, CURRENT_BASE(clos)) : NULL,
721 NULL,
722 dataptr);
723 if (kwp)
724 kwp->callback_data = NULL;
725}
726
727static enum grecs_tree_recurse_res
728nodeproc(enum grecs_tree_recurse_op op, struct grecs_node *node, void *data)
729{
730 struct nodeproc_closure *clos = data;
731 struct grecs_keyword *kwp;
732
733 switch (op) {
734 case grecs_tree_recurse_set:
735 kwp = find_keyword(clos->cursect, node->ident);
736 if (!kwp) {
737 grecs_error(&node->locus, 0, _("Unknown keyword"));
738 return grecs_tree_recurse_skip;
739 }
740 grecs_process_ident(kwp, &node->value, CURRENT_BASE(clos),
741 &node->locus);
742 break;
743
744 case grecs_tree_recurse_pre:
745 kwp = find_keyword(clos->cursect, node->ident);
746 if (!kwp) {
747 grecs_error(&node->locus, 0, _("Unknown keyword"));
748 return grecs_tree_recurse_skip;
749 }
750 stmt_begin(clos, kwp, node);
751 break;
752
753 case grecs_tree_recurse_post:
754 stmt_end(clos, node);
755 break;
756 }
757 return grecs_tree_recurse_ok;
758}
759
760int
761grecs_tree_process(struct grecs_node *node, struct grecs_keyword *kwd)
762{
763 int rc;
764 struct nodeproc_closure clos;
765 struct grecs_keyword config_keywords;
766
767 config_keywords.kwd = kwd;
768 clos.cursect = &config_keywords;
769 clos.sections = grecs_list_create();
770 rc = grecs_tree_recurse(node, nodeproc, &clos);
771 grecs_list_free(clos.sections);
772 return rc;
773}
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..3110c40
--- a/dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,8 @@
1atconfig
2atlocal
3gcffmt
4gcfpeek
5gcfset
6package.m4
7testsuite
8testsuite.log
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..4312f99
--- a/dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,84 @@
1# This file is part of grecs - Gray's Extensible Configuration System
2# Copyright (C) 2007, 2009-2011 Sergey Poznyakoff
3#
4# Grecs is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Grecs is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Grecs. If not, see <http://www.gnu.org/licenses/>.
16
17EXTRA_DIST = $(TESTSUITE_AT) testsuite package.m4 gcf1.conf
18DISTCLEANFILES = atconfig $(check_SCRIPTS)
19MAINTAINERCLEANFILES = Makefile.in $(TESTSUITE)
20
21
22## ------------ ##
23## package.m4. ##
24## ------------ ##
25
26$(srcdir)/package.m4: $(top_srcdir)/configure.ac
27 $(AM_V_GEN){ \
28 echo '# Signature of the current package.'; \
29 echo 'm4_define([AT_PACKAGE_NAME], [@PACKAGE_NAME@])'; \
30 echo 'm4_define([AT_PACKAGE_TARNAME], [@PACKAGE_TARNAME@])'; \
31 echo 'm4_define([AT_PACKAGE_VERSION], [@PACKAGE_VERSION@])'; \
32 echo 'm4_define([AT_PACKAGE_STRING], [@PACKAGE_STRING@])'; \
33 echo 'm4_define([AT_PACKAGE_BUGREPORT], [@PACKAGE_BUGREPORT@])'; \
34 } >$(srcdir)/package.m4
35
36#
37
38## ------------ ##
39## Test suite. ##
40## ------------ ##
41
42TESTSUITE_AT = \
43 format00.at\
44 format01.at\
45 format02.at\
46 cfhelp.at\
47 peek00.at\
48 peek01.at\
49 peek02.at\
50 peek03.at\
51 set.at\
52 testsuite.at
53
54TESTSUITE = $(srcdir)/testsuite
55M4=m4
56
57AUTOTEST = $(AUTOM4TE) --language=autotest
58$(TESTSUITE): package.m4 $(TESTSUITE_AT)
59 $(AUTOTEST) -I $(srcdir) testsuite.at -o $@.tmp
60 mv $@.tmp $@
61
62atconfig: $(top_builddir)/config.status
63 cd $(top_builddir) && ./config.status tests/$@
64
65clean-local:
66 test ! -f $(TESTSUITE) || $(SHELL) $(TESTSUITE) --clean
67
68check-local: atconfig atlocal $(TESTSUITE)
69 $(SHELL) $(TESTSUITE)
70
71# Run the test suite on the *installed* tree.
72#installcheck-local:
73 #$(SHELL) $(TESTSUITE) AUTOTEST_PATH=$(exec_prefix)/bin
74
75noinst_PROGRAMS = \
76 gcffmt\
77 gcfpeek\
78 gcfset
79
80LDADD=../src/libgrecs.a
81INCLUDES = -I$(top_srcdir)/@GRECS_SUBDIR@/src
82
83
84
diff --git a/tests/atlocal.in b/tests/atlocal.in
new file mode 100644
index 0000000..553dae3
--- a/dev/null
+++ b/tests/atlocal.in
@@ -0,0 +1,10 @@
1# @configure_input@ -*- shell-script -*-
2# Configurable variable values for Grecs test suite.
3# Copyright (C) 2011 Sergey Poznyakoff
4
5PATH=@abs_builddir@:$PATH
6
7XFAILFILE=$abs_builddir/.badversion
8
9trap "cleanup; test -r $XFAILFILE && cat $XFAILFILE; exit $?" 1 2 13 15
10
diff --git a/tests/cfhelp.at b/tests/cfhelp.at
new file mode 100644
index 0000000..cab6624
--- a/dev/null
+++ b/tests/cfhelp.at
@@ -0,0 +1,76 @@
1# This file is part of grecs -*- Autotest -*-
2# Copyright (C) 2007, 2009-2011 Sergey Poznyakoff
3#
4# Grecs is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Grecs is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Grecs. If not, see <http://www.gnu.org/licenses/>.
16
17AT_SETUP(Help)
18AT_KEYWORDS([help])
19
20AT_CHECK([gcfset -cfhelp],
21[0],
22[# Sample configuration file structure.
23
24# Scalar string
25scalar <label: string>;
26
27# Configure logging logging
28logging {
29 # Send to syslog
30 syslog <arg: boolean>;
31
32 # Set logging facility
33 facility <name: string>;
34
35 # Tag logging messages with this string
36 tag <label: string>;
37
38 # Prefix each message with its priority
39 print-priority <arg: boolean>;
40}
41
42# Mailbox configuration
43mailbox {
44 # Default mailbox pattern
45 mailbox-pattern <arg: string>;
46
47 # Default mailbox type
48 mailbox-type <arg: string>;
49}
50
51# Subprogram configuration
52program <name: string> {
53 # Scalar string
54 scalar <label: string>;
55
56 # Configure logging logging
57 logging {
58 # Send to syslog
59 syslog <arg: boolean>;
60
61 # Set logging facility
62 facility <name: string>;
63
64 # Tag logging messages with this string
65 tag <label: string>;
66
67 # Prefix each message with its priority
68 print-priority <arg: boolean>;
69 }
70}
71
72# list variable
73listvar <arg: list of string>;
74])
75
76AT_CLEANUP
diff --git a/tests/format00.at b/tests/format00.at
new file mode 100644
index 0000000..e7ae15f
--- a/dev/null
+++ b/tests/format00.at
@@ -0,0 +1,38 @@
1# This file is part of grecs -*- Autotest -*-
2# Copyright (C) 2007, 2009-2011 Sergey Poznyakoff
3#
4# Grecs is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Grecs is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Grecs. If not, see <http://www.gnu.org/licenses/>.
16
17AT_SETUP(Format)
18AT_KEYWORDS([format format00])
19
20AT_CHECK([gcffmt $abs_srcdir/gcf1.conf],
21[0],
22[.scalar: "yes"
23.listvar: ("a", "2", "b", "c")
24.compound: "stmt" "2" "foo"
25.mailbox.mailbox-pattern: "maildir:/var/mail;type=index;param=2;user=${user}"
26.mailbox.mailbox-type: "maildir"
27.logging.syslog: "yes"
28.logging.facility: "mail"
29.program="foo".logging.syslog: "yes"
30.program="foo".logging.facility: "local1"
31.program="foo".scalar: "no"
32.program="bar".logging.syslog: "no"
33.program="bar".logging.facility: "local2"
34.program="bar".logging.tag: "baz"
35.program="bar".scalar: "25"
36])
37
38AT_CLEANUP
diff --git a/tests/format01.at b/tests/format01.at
new file mode 100644
index 0000000..b8932e4
--- a/dev/null
+++ b/tests/format01.at
@@ -0,0 +1,38 @@
1# This file is part of grecs -*- Autotest -*-
2# Copyright (C) 2007, 2009-2011 Sergey Poznyakoff
3#
4# Grecs is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Grecs is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Grecs. If not, see <http://www.gnu.org/licenses/>.
16
17AT_SETUP([Format: locus])
18AT_KEYWORDS([format format01])
19
20AT_CHECK([gcffmt -locus $abs_srcdir/gcf1.conf|sed 's|^.*/gcf1.conf||'],
21[0],
22[:19: .scalar: "yes"
23:22: .listvar: ("a", "2", "b", "c")
24:25: .compound: "stmt" "2" "foo"
25:28: .mailbox.mailbox-pattern: "maildir:/var/mail;type=index;param=2;user=${user}"
26:29: .mailbox.mailbox-type: "maildir"
27:33: .logging.syslog: "yes"
28:34: .logging.facility: "mail"
29:39: .program="foo".logging.syslog: "yes"
30:40: .program="foo".logging.facility: "local1"
31:42: .program="foo".scalar: "no"
32:47: .program="bar".logging.syslog: "no"
33:48: .program="bar".logging.facility: "local2"
34:49: .program="bar".logging.tag: "baz"
35:51: .program="bar".scalar: "25"
36])
37
38AT_CLEANUP
diff --git a/tests/format02.at b/tests/format02.at
new file mode 100644
index 0000000..3ebada6
--- a/dev/null
+++ b/tests/format02.at
@@ -0,0 +1,38 @@
1# This file is part of grecs -*- Autotest -*-
2# Copyright (C) 2007, 2009-2011 Sergey Poznyakoff
3#
4# Grecs is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Grecs is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Grecs. If not, see <http://www.gnu.org/licenses/>.
16
17AT_SETUP([Format: custom delimiter])
18AT_KEYWORDS([format format02])
19
20AT_CHECK([gcffmt -delim=/ $abs_srcdir/gcf1.conf],
21[0],
22[/scalar: "yes"
23/listvar: ("a", "2", "b", "c")
24/compound: "stmt" "2" "foo"
25/mailbox/mailbox-pattern: "maildir:/var/mail;type=index;param=2;user=${user}"
26/mailbox/mailbox-type: "maildir"
27/logging/syslog: "yes"
28/logging/facility: "mail"
29/program="foo"/logging/syslog: "yes"
30/program="foo"/logging/facility: "local1"
31/program="foo"/scalar: "no"
32/program="bar"/logging/syslog: "no"
33/program="bar"/logging/facility: "local2"
34/program="bar"/logging/tag: "baz"
35/program="bar"/scalar: "25"
36])
37
38AT_CLEANUP
diff --git a/tests/gcf1.conf b/tests/gcf1.conf
new file mode 100644
index 0000000..991fb9b
--- a/dev/null
+++ b/tests/gcf1.conf
@@ -0,0 +1,54 @@
1# This file is part of grecs - Gray's Extensible Configuration System
2# Copyright (C) 2007, 2009-2011 Sergey Poznyakoff
3#
4# Grecs is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Grecs is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Grecs. If not, see <http://www.gnu.org/licenses/>.
16
17/* Scalar variable.
18*/
19scalar yes;
20
21// List variable.
22listvar ("a", 2, "b", "c");
23
24# Compound value.
25compound stmt 2 foo;
26
27mailbox {
28 mailbox-pattern "maildir:/var/mail;type=index;param=2;user=${user}";
29 mailbox-type "maildir";
30};
31
32logging {
33 syslog yes;
34 facility mail;
35}
36
37program "foo" {
38 logging {
39 syslog yes;
40 facility local1;
41 }
42 scalar no;
43}
44
45program "bar" {
46 logging {
47 syslog no;
48 facility local2;
49 tag "baz";
50 }; # Note: semicolon after closing brace is optional.
51 scalar 25;
52}
53
54 \ No newline at end of file
diff --git a/tests/gcffmt.c b/tests/gcffmt.c
new file mode 100644
index 0000000..cc67b9a
--- a/dev/null
+++ b/tests/gcffmt.c
@@ -0,0 +1,67 @@
1/* grecs - Gray's Extensible Configuration System
2 Copyright (C) 2007-2011 Sergey Poznyakoff
3
4 Grecs is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 Grecs is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with Grecs. If not, see <http://www.gnu.org/licenses/>. */
16
17#ifdef HAVE_CONFIG_H
18# include <config.h>
19#endif
20#include <string.h>
21#include <errno.h>
22#include "grecs.h"
23
24static void
25usage(const char *arg, FILE *fp, int code)
26{
27 fprintf(fp, "usage: %s [-h] [-locus] [-delim=char] file\n", arg);
28 exit(code);
29}
30
31int
32main(int argc, char **argv)
33{
34 char *progname = argv[0];
35 char *file = NULL;
36 struct grecs_node *tree, *node;
37 int flags = 0;
38
39 while (--argc) {
40 char *arg = *++argv;
41 if (strcmp(arg, "-locus") == 0)
42 flags |= GRECS_NODE_FLAG_LOCUS;
43 else if (strncmp(arg, "-delim=", 7) == 0)
44 flags |= arg[7];
45 else if (strcmp(arg, "-h") == 0)
46 usage(progname, stdout, 0);
47 else if (arg[0] == '-')
48 usage(progname, stderr, 1);
49 else if (file)
50 usage(progname, stderr, 1);
51 else
52 file = arg;
53 }
54
55 if (!file || argc)
56 usage(progname, stderr, 1);
57
58 tree = grecs_parse(file);
59 if (!tree)
60 exit(1);
61
62 for (node = tree; node; node = node->next) {
63 grecs_format_node(node, flags, stdout);
64 fputc('\n', stdout);
65 }
66 exit(0);
67}
diff --git a/tests/gcfpeek.c b/tests/gcfpeek.c
new file mode 100644
index 0000000..360000d
--- a/dev/null
+++ b/tests/gcfpeek.c
@@ -0,0 +1,76 @@
1/* grecs - Gray's Extensible Configuration System
2 Copyright (C) 2007-2011 Sergey Poznyakoff
3
4 Grecs is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 Grecs is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with Grecs. If not, see <http://www.gnu.org/licenses/>. */
16
17#ifdef HAVE_CONFIG_H
18# include <config.h>
19#endif
20#include <string.h>
21#include <errno.h>
22#include "grecs.h"
23
24static void
25usage(const char *arg, FILE *fp, int code)
26{
27 fprintf(fp, "usage: %s [-h] [-locus] [-delim=char] file path\n", arg);
28 exit(code);
29}
30
31int
32main(int argc, char **argv)
33{
34 char *progname = argv[0];
35 char *path = NULL;
36 char *file = NULL;
37 struct grecs_node *tree, *node;
38 int flags = 0;
39 int rc = 2;
40
41 while (--argc) {
42 char *arg = *++argv;
43 if (strcmp(arg, "-locus") == 0)
44 flags |= GRECS_NODE_FLAG_LOCUS;
45 else if (strncmp(arg, "-delim=", 7) == 0)
46 flags |= arg[7];
47 else if (strcmp(arg, "-h") == 0)
48 usage(progname, stdout, 0);
49 else if (arg[0] == '-')
50 usage(progname, stderr, 1);
51 else if (file) {
52 if (path)
53 usage(progname, stderr, 1);
54 else
55 path = arg;
56 } else
57 file = arg;
58 }
59
60 if (!file || !path || argc)
61 usage(progname, stderr, 1);
62
63 tree = grecs_parse(file);
64 if (!tree)
65 exit(1);
66
67 for (node = tree; node; node = node->next) {
68 node = grecs_find_node(node, path);
69 if (!node)
70 break;
71 rc = 0;
72 grecs_format_node(node, flags, stdout);
73 fputc('\n', stdout);
74 }
75 exit(rc);
76}
diff --git a/tests/gcfset.c b/tests/gcfset.c
new file mode 100644
index 0000000..d2264e3
--- a/dev/null
+++ b/tests/gcfset.c
@@ -0,0 +1,240 @@
1/* grecs - Gray's Extensible Configuration System
2 Copyright (C) 2007-2011 Sergey Poznyakoff
3
4 Grecs is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 Grecs is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with Grecs. If not, see <http://www.gnu.org/licenses/>. */
16
17#ifdef HAVE_CONFIG_H
18# include <config.h>
19#endif
20#include <stddef.h>
21#include <string.h>
22#include <errno.h>
23#include "grecs.h"
24
25struct logging_setup {
26 int use_syslog;
27 int print_priority;
28 char *tag;
29 char *facility;
30};
31
32struct logging_setup logging_setup;
33char *scalar_string;
34char *mailbox_pattern;
35char *mailbox_type;
36struct grecs_list *listvar;
37
38struct program {
39 char *name;
40 struct logging_setup logging_setup;
41 char *scalar_string;
42 grecs_locus_t locus;
43 struct program *next;
44};
45
46struct grecs_list *proglist;
47
48static int
49cb_logging_facility(enum grecs_callback_command cmd,
50 grecs_locus_t *locus,
51 void *varptr,
52 grecs_value_t *value,
53 void *cb_data)
54{
55 if (cmd != grecs_callback_set_value) {
56 grecs_error(locus, 0, _("Unexpected block statement"));
57 return 1;
58 }
59 if (!value || value->type != GRECS_TYPE_STRING) {
60 grecs_error(locus, 0, _("expected string argument"));
61 return 1;
62 }
63
64 *(char**)varptr = grecs_strdup(value->v.string);
65 return 0;
66}
67
68static struct grecs_keyword logging_kwtab[] = {
69 { "syslog", NULL, "Send to syslog",
70 grecs_type_bool, NULL,
71 offsetof(struct logging_setup, use_syslog) },
72 { "facility", "name", "Set logging facility",
73 grecs_type_string, NULL,
74 offsetof(struct logging_setup, facility), cb_logging_facility },
75 { "tag", "label", "Tag logging messages with this string",
76 grecs_type_string, NULL,
77 offsetof(struct logging_setup, tag) },
78 { "print-priority", NULL, "Prefix each message with its priority",
79 grecs_type_bool, NULL,
80 offsetof(struct logging_setup, print_priority) },
81 { NULL },
82};
83
84static struct grecs_keyword mailbox_kwtab[] = {
85 { "mailbox-pattern", NULL, "Default mailbox pattern",
86 grecs_type_string, &mailbox_pattern },
87 { "mailbox-type", NULL, "Default mailbox type",
88 grecs_type_string, &mailbox_type },
89 { NULL },
90};
91
92
93static struct grecs_keyword program_kwtab[] = {
94 { "scalar", "label", "Scalar string",
95 grecs_type_string, NULL, offsetof(struct program,scalar_string) },
96 { "logging", NULL, N_("Configure logging logging"),
97 grecs_type_section, NULL, offsetof(struct program,logging_setup),
98 NULL, NULL, logging_kwtab },
99 { NULL }
100};
101
102static int
103cb_program(enum grecs_callback_command cmd,
104 grecs_locus_t *locus,
105 void *varptr,
106 grecs_value_t *value,
107 void *cb_data)
108{
109 struct program *prog;
110 void **pdata = cb_data;
111
112 switch (cmd) {
113 case grecs_callback_section_begin:
114 if (!value || value->type != GRECS_TYPE_STRING) {
115 grecs_error(locus, 0, _("tag must be a string"));
116 return 0;
117 }
118 prog = grecs_zalloc(sizeof(*prog));
119 prog->name = grecs_strdup(value->v.string);
120 prog->locus = *locus;
121 *pdata = prog;
122 break;
123
124 case grecs_callback_section_end:
125 prog = *pdata;
126 if (!proglist)
127 proglist = grecs_list_create();
128 grecs_list_append(proglist, prog);
129 break;
130
131 case grecs_callback_set_value:
132 grecs_error(locus, 0, _("invalid use of block statement"));
133 }
134 return 0;
135}
136
137static struct grecs_keyword main_kwtab[] = {
138 { "scalar", "label", "Scalar string",
139 grecs_type_string, &scalar_string },
140 { "logging", NULL, N_("Configure logging logging"),
141 grecs_type_section, &logging_setup, 0, NULL,
142 NULL, logging_kwtab },
143 { "mailbox", NULL, N_("Mailbox configuration"),
144 grecs_type_section, NULL, 0, NULL, NULL, mailbox_kwtab },
145 { "program", "name: string", "Subprogram configuration",
146 grecs_type_section, NULL, 0, cb_program, NULL, program_kwtab },
147 { "listvar", NULL, "list variable",
148 grecs_type_string|GRECS_LIST, &listvar },
149 { NULL }
150};
151
152
153#define S(s) ((s) ? (s) : "(null)")
154
155static void
156print_logging_setup(struct logging_setup *p)
157{
158 printf("logging: %d/%s/%s/%d\n",
159 p->use_syslog, S(p->facility), S(p->tag), p->print_priority);
160}
161
162static void
163print_program(struct program *prog)
164{
165 printf("Program %s:\n", prog->name);
166 printf("scalar = %s\n", S(prog->scalar_string));
167 print_logging_setup(&prog->logging_setup);
168}
169
170
171static void
172usage(const char *arg, FILE *fp, int code)
173{
174 fprintf(fp, "usage: %s [-h] [-cfhelp] file\n", arg);
175 exit(code);
176}
177
178int
179main(int argc, char **argv)
180{
181 char *progname = argv[0];
182 const char *file = NULL;
183 struct grecs_node *tree;
184 int cfhelp = 0;
185
186 while (--argc) {
187 char *arg = *++argv;
188 if (strcmp(arg, "-cfhelp") == 0)
189 cfhelp = 1;
190 else if (strcmp(arg, "-h") == 0)
191 usage(progname, stdout, 0);
192 else if (arg[0] == '-')
193 usage(progname, stderr, 1);
194 else if (file)
195 usage(progname, stderr, 1);
196 else
197 file = arg;
198 }
199
200 if ((!file && !cfhelp) || argc)
201 usage(progname, stderr, 1);
202
203 if (cfhelp) {
204 static char docstring[] =
205 "Sample configuration file structure.\n";
206 grecs_format_docstring(docstring, 0, stdout);
207 grecs_format_statement_array(main_kwtab, 1, 0, stdout);
208 exit(0);
209 }
210
211 tree = grecs_parse(file);
212 if (!tree)
213 exit(2);
214
215 if (grecs_tree_process(tree, main_kwtab))
216 exit(2);
217
218 printf("Global settings:\n");
219 printf("scalar = %s\n", S(scalar_string));
220 if (listvar) {
221 struct grecs_list_entry *ep;
222 printf("listvar =");
223 for (ep = listvar->head; ep; ep = ep->next)
224 printf(" \"%s\"", (char*)ep->data);
225 putchar('\n');
226 }
227
228 print_logging_setup(&logging_setup);
229
230 if (proglist) {
231 struct grecs_list_entry *ep;
232
233 printf("Programs configured: %d\n", grecs_list_size(proglist));
234 for (ep = proglist->head; ep; ep = ep->next)
235 print_program(ep->data);
236 }
237
238 exit(0);
239}
240
diff --git a/tests/peek00.at b/tests/peek00.at
new file mode 100644
index 0000000..a3d86ac
--- a/dev/null
+++ b/tests/peek00.at
@@ -0,0 +1,25 @@
1# This file is part of grecs -*- Autotest -*-
2# Copyright (C) 2007, 2009-2011 Sergey Poznyakoff
3#
4# Grecs is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Grecs is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Grecs. If not, see <http://www.gnu.org/licenses/>.
16
17AT_SETUP(Peek)
18AT_KEYWORDS([peek peek00])
19
20AT_CHECK([gcfpeek $abs_srcdir/gcf1.conf .scalar],
21[0],
22[.scalar: "yes"
23])
24
25AT_CLEANUP
diff --git a/tests/peek01.at b/tests/peek01.at
new file mode 100644
index 0000000..c76261c
--- a/dev/null
+++ b/tests/peek01.at
@@ -0,0 +1,30 @@
1# This file is part of grecs -*- Autotest -*-
2# Copyright (C) 2007, 2009-2011 Sergey Poznyakoff
3#
4# Grecs is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Grecs is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Grecs. If not, see <http://www.gnu.org/licenses/>.
16
17AT_SETUP(Peek: nested)
18AT_KEYWORDS([peek peek01])
19
20AT_CHECK([gcfpeek $abs_srcdir/gcf1.conf .program="foo".scalar],
21[0],
22[.program="foo".scalar: "no"
23])
24
25AT_CHECK([gcfpeek $abs_srcdir/gcf1.conf .program="bar".scalar],
26[0],
27[.program="bar".scalar: "25"
28])
29
30AT_CLEANUP
diff --git a/tests/peek02.at b/tests/peek02.at
new file mode 100644
index 0000000..e5c1a4a
--- a/dev/null
+++ b/tests/peek02.at
@@ -0,0 +1,26 @@
1# This file is part of grecs -*- Autotest -*-
2# Copyright (C) 2007, 2009-2011 Sergey Poznyakoff
3#
4# Grecs is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Grecs is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Grecs. If not, see <http://www.gnu.org/licenses/>.
16
17AT_SETUP(Peek: block)
18AT_KEYWORDS([peek peek02])
19
20AT_CHECK([gcfpeek $abs_srcdir/gcf1.conf .logging],
21[0],
22[.logging.syslog: "yes"
23.logging.facility: "mail"
24])
25
26AT_CLEANUP
diff --git a/tests/peek03.at b/tests/peek03.at
new file mode 100644
index 0000000..7590b0c
--- a/dev/null
+++ b/tests/peek03.at
@@ -0,0 +1,33 @@
1# This file is part of grecs -*- Autotest -*-
2# Copyright (C) 2007, 2009-2011 Sergey Poznyakoff
3#
4# Grecs is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Grecs is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Grecs. If not, see <http://www.gnu.org/licenses/>.
16
17AT_SETUP(Peek: block nested)
18AT_KEYWORDS([peek peek03])
19
20AT_CHECK([gcfpeek $abs_srcdir/gcf1.conf .program=foo.logging],
21[0],
22[.program="foo".logging.syslog: "yes"
23.program="foo".logging.facility: "local1"
24])
25
26AT_CHECK([gcfpeek $abs_srcdir/gcf1.conf .program=bar.logging],
27[0],
28[.program="bar".logging.syslog: "no"
29.program="bar".logging.facility: "local2"
30.program="bar".logging.tag: "baz"
31])
32
33AT_CLEANUP
diff --git a/tests/set.at b/tests/set.at
new file mode 100644
index 0000000..7bad831
--- a/dev/null
+++ b/tests/set.at
@@ -0,0 +1,38 @@
1# This file is part of grecs -*- Autotest -*-
2# Copyright (C) 2007, 2009-2011 Sergey Poznyakoff
3#
4# Grecs is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Grecs is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Grecs. If not, see <http://www.gnu.org/licenses/>.
16
17AT_SETUP([Set values])
18AT_KEYWORDS([set])
19
20AT_CHECK([gcfset $abs_srcdir/gcf1.conf 2>err||exit $?
21sed 's|^.*/gcf1.conf||' err >&2],
22[0],
23[Global settings:
24scalar = yes
25listvar = "a" "2" "b" "c"
26logging: 1/mail/(null)/0
27Programs configured: 2
28Program foo:
29scalar = no
30logging: 1/local1/(null)/0
31Program bar:
32scalar = 25
33logging: 0/local2/baz/0
34],
35[:25: Unknown keyword
36])
37
38AT_CLEANUP \ No newline at end of file
diff --git a/tests/testsuite.at b/tests/testsuite.at
new file mode 100644
index 0000000..3b0ec84
--- a/dev/null
+++ b/tests/testsuite.at
@@ -0,0 +1,54 @@
1# This file is part of grecs - Gray's Extensible Configuration System -*- Autotest -*-
2# Copyright (C) 2007, 2009-2011 Sergey Poznyakoff
3#
4# Grecs is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Grecs is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Grecs. If not, see <http://www.gnu.org/licenses/>.
16
17m4_version_prereq([2.52g])
18
19dnl # Standard exit codes (from sysexits.h)
20m4_define([EX_OK], 0) dnl successful termination
21m4_define([EX__BASE], 64) dnl base value for error messages
22m4_define([EX_USAGE], 64) dnl command line usage error
23m4_define([EX_DATAERR], 65) dnl data format error
24m4_define([EX_NOINPUT], 66) dnl cannot open input
25m4_define([EX_NOUSER], 67) dnl addressee unknown
26m4_define([EX_NOHOST], 68) dnl host name unknown
27m4_define([EX_UNAVAILABLE], 69) dnl service unavailable
28m4_define([EX_SOFTWARE], 70) dnl internal software error
29m4_define([EX_OSERR], 71) dnl system error (e.g., can't fork)
30m4_define([EX_OSFILE], 72) dnl critical OS file missing
31m4_define([EX_CANTCREAT], 73) dnl can't create (user) output file
32m4_define([EX_IOERR], 74) dnl input/output error
33m4_define([EX_TEMPFAIL], 75) dnl temp failure; user is invited to retry
34m4_define([EX_PROTOCOL], 76) dnl remote error in protocol
35m4_define([EX_NOPERM], 77) dnl permission denied
36m4_define([EX_CONFIG], 78) dnl configuration error
37
38m4_define([AT_SKIP_TEST],[exit 77])
39
40dnl # Begin tests
41
42AT_INIT
43m4_include([format00.at])
44m4_include([format01.at])
45m4_include([format02.at])
46
47m4_include([peek00.at])
48m4_include([peek01.at])
49m4_include([peek02.at])
50m4_include([peek03.at])
51
52m4_include([cfhelp.at])
53m4_include([set.at])
54# End of testsuite.at

Return to:

Send suggestions and report system problems to the System administrator.