aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2014-12-26 01:22:26 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2014-12-26 01:22:26 +0200
commit75e73954ab1dc5fee42255a13457b2a092cd86e3 (patch)
tree94a65b9d10bd8c618707870b3be4f5043a660dd8
parenta03c7a67580ae57a93f6f2ea85485d612a00c46c (diff)
parentadd57c075c6f747a81c142ab48d59106de822664 (diff)
downloadgrecs-75e73954ab1dc5fee42255a13457b2a092cd86e3.tar.gz
grecs-75e73954ab1dc5fee42255a13457b2a092cd86e3.tar.bz2
Merge branch 'master' into clparse
-rw-r--r--am/grecs.m412
-rw-r--r--doc/grecs-syntax.texi25
-rw-r--r--doc/grecs_config.527
-rw-r--r--src/Make.am10
-rw-r--r--src/cidr.c215
-rw-r--r--src/format.c2
-rw-r--r--src/grecs-lex.l4
-rw-r--r--src/grecs.hin48
-rw-r--r--src/ipstr.c88
-rw-r--r--src/preproc.c63
-rw-r--r--src/sockaddr.c280
-rw-r--r--src/tree.c36
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/incl00.at39
-rw-r--r--tests/incl01.at41
-rw-r--r--tests/incl02.at51
-rw-r--r--tests/incl03.at45
-rw-r--r--tests/testsuite.at8
18 files changed, 957 insertions, 43 deletions
diff --git a/am/grecs.m4 b/am/grecs.m4
index 2787563..1c1a412 100644
--- a/am/grecs.m4
+++ b/am/grecs.m4
@@ -68,6 +68,8 @@ AC_DEFUN([_GRECS_SET_OPTIONS],
# tree-api Use alternative signature of callback functions,
# passing a pointer to grecs_node_t as an argument,
# instead of pointers to the value and locus.
+# sockaddr-list Sockaddr type keeps a singly-linked list of addresses
+# returned by getaddrinfo.
#
# The pp-setup-file argument supplies the pathname of the preprocessor
# setup file in the source tree. It is ignored if std-pp-setup option is
@@ -175,6 +177,12 @@ AC_DEFUN([GRECS_SETUP],[
AM_CONDITIONAL([GRECS_COND_INSTALLHEADERS],
_GRECS_IF_OPTION_SET([install-headers],[true],[false]))
+ AM_CONDITIONAL([GRECS_COND_SOCKADDR_LIST],
+ _GRECS_IF_OPTION_SET([sockaddr-list],[true],[false]))
+ AC_SUBST([GRECS_SOCKADDR_LIST])
+ _GRECS_IF_OPTION_SET([sockaddr-list],[GRECS_SOCKADDR_LIST=1],
+ [GRECS_SOCKADDR_LIST=0])
+
AC_SUBST([GRECS_TREE_API])
_GRECS_IF_OPTION_SET([tree-api],[GRECS_TREE_API=1],[GRECS_TREE_API=0])
@@ -187,7 +195,7 @@ AC_DEFUN([GRECS_SETUP],[
AC_SUBST([GRECS_CHANGELOG])
AC_SUBST([GRECS_DISTCK_AT])
AC_SUBST([GRECS_README])
- AC_SUBST([GRECS_INCLUDES],['-I$(top_srcdir)/]grecsdir[src]')
+ AC_SUBST([GRECS_INCLUDES],['-I$(top_srcdir)/]grecsdir[src] [-I$(top_builddir)/]grecsdir[src]')
AC_SUBST([GRECS_HOST_PROJECT_INCLUDES])
AC_SUBST([GRECS_DISTDOC])
AC_SUBST([GRECS_INCLUDE_DIR],['$(pkgincludedir)'])
@@ -215,6 +223,6 @@ AC_DEFUN([GRECS_SETUP],[
_GRECS_IF_OPTION_SET([syntax-doc],[GRECS_DISTDOC=doc/grecs-syntax.texi])
AC_CONFIG_FILES(grecsdir[src/Makefile]:grecsdir[src/Make-static.in])
])
- AC_CONFIG_FILES(grecsdir[Makefile])
+ AC_CONFIG_FILES(grecsdir[Makefile] grecsdir[src/grecs.h]:grecsdir[src/grecs.hin])
m4_popdef([grecsdir])
])
diff --git a/doc/grecs-syntax.texi b/doc/grecs-syntax.texi
index c10cab6..da59d03 100644
--- a/doc/grecs-syntax.texi
+++ b/doc/grecs-syntax.texi
@@ -1,5 +1,5 @@
@c This file is part of grecs - Gray's Extensible Configuration System
-@c Copyright (C) 2007, 2009-2012 Sergey Poznyakoff
+@c Copyright (C) 2007, 2009-2014 Sergey Poznyakoff
@c
@c Grecs is free software; you can redistribute it and/or modify
@c it under the terms of the GNU General Public License as published by
@@ -82,12 +82,23 @@ next physical newline character.
@kwindex #include
@item #include <@var{file}>
@itemx #include @var{file}
-Include the contents of the file @var{file}. If @var{file} is an
-absolute file name, both forms are equivalent. Otherwise, the form
-with angle brackets searches for the file in the @dfn{include
-search path}, while the second one looks for it in the current working
-directory first, and, if not found there, in the include search
-path.
+Include the contents of the file @var{file}. There are three possible
+use cases.
+
+If @var{file} is an absolute file name, the named file is included.
+An error message will be issued if it does not exist.
+
+If @var{file} contains wildcard characters (@samp{*}, @samp{[},
+@samp{]} or @samp{?}), it is interpreted as shell globbing pattern and
+all files matching that pattern are included, in lexicographical
+order. If no files match the pattern, the statement is silently
+ignored.
+
+Otherwise, the form with angle brackets searches for file in the
+@dfn{include search path}, while the second one looks for it in the
+current working directory first, and, if not found there, in the
+include search path. If the file is not found, an error message will
+be issued.
The default include search path is:
diff --git a/doc/grecs_config.5 b/doc/grecs_config.5
index 80f7bcc..82633d5 100644
--- a/doc/grecs_config.5
+++ b/doc/grecs_config.5
@@ -1,5 +1,5 @@
.\" This file is part of grecs -*- nroff -*-
-.\" Copyright (C) 2007, 2009-2012 Sergey Poznyakoff
+.\" Copyright (C) 2007, 2009-2014 Sergey Poznyakoff
.\"
.\" Grecs is free software; you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
.\" You should have received a copy of the GNU General Public License
.\" along with Grecs. If not, see <http://www.gnu.org/licenses/>.
.\"
-.TH GRECS_CONFIG 3 "May 4, 2011" "GRECS" "Grecs User Reference"
+.TH GRECS_CONFIG 3 "December 25, 2014" "GRECS" "Grecs User Reference"
.SH NAME
\fBGrecs\fR configuration file syntax
.SH DESCRIPTION
@@ -64,12 +64,23 @@ sign and end with the next physical newline character.
.TP
.BR "#include " "file"
.PD
-Include the contents of the file \fIfile\fR. If it is an
-absolute file name, both forms are equivalent. Otherwise, the form
-with angle brackets searches for the file in the \fIinclude
-search path\fR, while the second one looks for it in the current working
-directory first, and, if not found there, in the include search
-path.
+Include the contents of the file \fIfile\fR. There are three possible
+use cases.
+
+If \fIfile\fR is an absolute file name, the named file is included.
+An error message will be issued if it does not exist.
+
+If \fIfile\fR contains wildcard characters (\fB*\fR, \fB[\fR,
+\fB]\fR or \fB?\fR), it is interpreted as shell globbing pattern and
+all files matching that pattern are included, in lexicographical
+order. If no files match the pattern, the statement is silently
+ignored.
+
+Otherwise, the form with angle brackets searches for file in the
+\fIinclude search path\fR, while the second one looks for it in the
+current working directory first, and, if not found there, in the
+include search path. If the file is not found, an error message will
+be issued.
.sp
The default include search path is:
.sp
diff --git a/src/Make.am b/src/Make.am
index 7ded902..fe94d42 100644
--- a/src/Make.am
+++ b/src/Make.am
@@ -41,10 +41,12 @@ endif
GRECS_SRC = \
asprintf.c\
+ cidr.c\
diag.c\
format.c\
grecs-gram.y\
grecs-lex.l\
+ ipstr.c\
join.c\
lineacc.c\
list.c\
@@ -67,6 +69,10 @@ GRECS_SRC = \
$(GRECS_PARSER_GIT)\
$(GRECS_PARSER_META1)
+if GRECS_COND_SOCKADDR_LIST
+ GRECS_SRC += sockaddr.c
+endif
+
noinst_HEADERS = grecs-locus.h
EXTRA_DIST=\
@@ -80,10 +86,6 @@ EXTRA_DIST=\
BUILT_SOURCES=grecs.h
-grecs.h: $(abs_srcdir)/grecs.hin
- $(AM_V_GEN)sed 's/@''GRECS_TREE_API''@/@GRECS_TREE_API@/g' \
- $(abs_srcdir)/grecs.hin > grecs.h
-
AM_CPPFLAGS = \
-I$(srcdir)\
-I$(top_srcdir)/@GRECS_SUBDIR@\
diff --git a/src/cidr.c b/src/cidr.c
new file mode 100644
index 0000000..0f75476
--- /dev/null
+++ b/src/cidr.c
@@ -0,0 +1,215 @@
+/* grecs - Gray's Extensible Configuration System
+ Copyright (C) 2007-2014 Sergey Poznyakoff
+
+ Grecs is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ Grecs is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Grecs. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "grecs.h"
+
+static void
+uint32_to_bytes (unsigned char *bytes, uint32_t u)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ bytes[i] = u & 0xff;
+ u >>= 8;
+ }
+}
+
+int
+grecs_inaddr_to_bytes(int af, void *buf, unsigned char *bytes)
+{
+ uint32_t u;
+
+ switch (af) {
+ case AF_INET:
+ memcpy(&u, buf, sizeof u);
+ uint32_to_bytes(bytes, u);
+ return 4;
+
+ case AF_INET6:
+ memcpy(bytes, buf, 16);
+ return 16;
+ }
+ return 0;
+}
+
+int
+grecs_sockaddr_to_bytes(unsigned char *bytes, struct sockaddr const *sa)
+{
+ switch (sa->sa_family) {
+ case AF_INET:
+ uint32_to_bytes(bytes,
+ ((struct sockaddr_in*)sa)->sin_addr.s_addr);
+ return 4;
+
+ case AF_INET6:
+ memcpy(bytes, &((struct sockaddr_in6*)sa)->sin6_addr, 16);
+ return 16;
+ }
+ return 0;
+}
+
+int
+grecs_sockaddr_to_cidr(struct grecs_cidr *cidr, const struct sockaddr *sa)
+{
+ unsigned char address[GRECS_INADDR_BYTES];
+ int len;
+ int i;
+
+ len = grecs_sockaddr_to_bytes(address, sa);
+ if (len == 0)
+ return -1;
+ cidr->family = sa->sa_family;
+ cidr->len = len;
+ memcpy(cidr->address, address, sizeof(cidr->address));
+ for (i = 0; i < GRECS_INADDR_BYTES; i++)
+ cidr->netmask[i] = 0xff;
+ return 0;
+}
+
+static void
+masklen_to_netmask(unsigned char *buf, size_t len, size_t masklen)
+{
+ int i, cnt;
+
+ cnt = masklen / 8;
+ for (i = 0; i < cnt; i++)
+ buf[i] = 0xff;
+ if (i == GRECS_INADDR_BYTES)
+ return;
+ cnt = 8 - masklen % 8;
+ buf[i++] = (0xff >> cnt) << cnt;
+ for (; i < GRECS_INADDR_BYTES; i++)
+ buf[i] = 0;
+}
+
+int
+grecs_str_to_cidr(struct grecs_cidr *pcidr, const char *str,
+ grecs_locus_t const *locus)
+{
+ int rc;
+ char ipbuf[41];
+ struct grecs_cidr cidr;
+ char *p;
+ size_t len;
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ } inaddr;
+
+ p = strchr(str, '/');
+ if (p)
+ len = p - str;
+ else
+ len = strlen(str);
+
+ if (len > sizeof(ipbuf)) {
+ grecs_error(locus, 0, _("network length too big: %s"), str);
+ return -1;
+ }
+
+ memcpy(ipbuf, str, len);
+ ipbuf[len] = 0;
+
+ if (grecs_str_is_ipv4(ipbuf))
+ cidr.family = AF_INET;
+ else if (grecs_str_is_ipv6(ipbuf))
+ cidr.family = AF_INET6;
+ else {
+ grecs_error(locus, 0, _("unrecognized network family: %s"),
+ str);
+ return -1;
+ }
+
+ rc = inet_pton(cidr.family, ipbuf, &inaddr);
+ if (rc == -1) {
+ grecs_error(locus, 0, _("unrecognized network family: %s"),
+ str);
+ return -1;
+ } else if (rc != 1) {
+ grecs_error(locus, 0, _("invalid network: %s"),
+ str);
+ return -1;
+ }
+
+ cidr.len = grecs_inaddr_to_bytes(cidr.family, &inaddr, cidr.address);
+ if (cidr.len == 0) {
+ grecs_error(locus, 0, _("unrecognized network family: %s"),
+ str);
+ return -1;
+ }
+
+ if (p) {
+ char *end;
+ unsigned long masklen;
+
+ p++;
+
+ masklen = strtoul(p, &end, 10);
+ if (*end == 0)
+ masklen_to_netmask(cidr.netmask, cidr.len, masklen);
+ else if ((cidr.family == AF_INET && grecs_str_is_ipv4(p))
+ || (cidr.family == AF_INET6
+ && grecs_str_is_ipv6(ipbuf))) {
+ rc = inet_pton(cidr.family, p, &inaddr);
+ if (rc != 1) {
+ grecs_error(locus, 0, _("invalid network mask: %s"),
+ str);
+ return -1;
+ }
+ grecs_inaddr_to_bytes(cidr.family, &inaddr,
+ cidr.netmask);
+ } else {
+ grecs_error(locus, 0, _("invalid network mask: %s"),
+ str);
+ return -1;
+ }
+ } else
+ masklen_to_netmask(cidr.netmask, cidr.len, cidr.len * 8);
+
+ memcpy(pcidr, &cidr, sizeof(*pcidr));
+ return 0;
+}
+
+int
+grecs_cidr_match(struct grecs_cidr *a, struct grecs_cidr *b)
+{
+ int i;
+
+ if (a->family != b->family)
+ return 1;
+ for (i = 0; i < a->len; i++) {
+ if (a->address[i] != (b->address[i] & a->netmask[i]))
+ return 1;
+ }
+ return 0;
+}
+
+int
+grecs_sockadd_cidr_match(struct sockaddr *sa, struct grecs_cidr *cidr)
+{
+ struct grecs_cidr t;
+ if (grecs_sockaddr_to_cidr(&t, sa))
+ return 1;
+ return grecs_cidr_match(cidr, &t);
+}
diff --git a/src/format.c b/src/format.c
index c554cc9..d010729 100644
--- a/src/format.c
+++ b/src/format.c
@@ -61,7 +61,7 @@ grecs_data_type_string(enum grecs_data_type type)
return "hostname";
case grecs_type_sockaddr:
- return "sock-addr";
+ return "sockaddr";
case grecs_type_section:
return "section";
diff --git a/src/grecs-lex.l b/src/grecs-lex.l
index cbc8f8b..329c725 100644
--- a/src/grecs-lex.l
+++ b/src/grecs-lex.l
@@ -6,7 +6,7 @@
}
%{
/* grecs - Gray's Extensible Configuration System
- Copyright (C) 2007-2012 Sergey Poznyakoff
+ Copyright (C) 2007-2014 Sergey Poznyakoff
Grecs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
@@ -106,7 +106,7 @@ P [1-9][0-9]*
/* Identifiers */
<INITIAL>{ID} return ident();
/* Strings */
-[a-zA-Z0-9_\.\*/:@-]([a-zA-Z0-9_\./:@-][a-zA-Z0-9_\.\*/:@-]*)? {
+[a-zA-Z0-9_\.\*/:@\[\]-]([a-zA-Z0-9_\./:@\[\]-][a-zA-Z0-9_\.\*/:@\[\]-]*)? {
grecs_line_begin();
grecs_line_add(yytext, yyleng);
yylval.string = grecs_line_finish();
diff --git a/src/grecs.hin b/src/grecs.hin
index 8bf0826..677a237 100644
--- a/src/grecs.hin
+++ b/src/grecs.hin
@@ -41,6 +41,7 @@
#define GRECS_VERSION_MINOR 0
#define GRECS_TREE_API @GRECS_TREE_API@
+#define GRECS_SOCKADDR_LIST @GRECS_SOCKADDR_LIST@
struct grecs_version_info {
const char *package;
@@ -191,6 +192,9 @@ struct grecs_keyword {
};
struct grecs_sockaddr {
+#if GRECS_SOCKADDR_LIST
+ struct grecs_sockaddr *next;
+#endif
int len;
struct sockaddr *sa;
};
@@ -523,5 +527,49 @@ void grecs_tree_sort(struct grecs_node *node,
struct grecs_node *grecs_tree_first_node(struct grecs_node *tree);
struct grecs_node *grecs_next_node(struct grecs_node *node);
+
+int grecs_str_is_ipv4(const char *addr);
+int grecs_str_is_num(const char *s);
+int grecs_str_is_ipv6(const char *addr);
+int grecs_str_is_num(const char *s);
+int grecs_str_is_ipaddr(const char *addr);
+
+#if GRECS_SOCKADDR_LIST
+
+#define GRECS_AH_PASSIVE 0x01
+#define GRECS_HINT_SERVICE 0x02
+#define GRECS_HINT_PORT 0x04
+
+struct grecs_sockaddr_hints {
+ int flags;
+ char *service;
+ unsigned short port;
+};
+
+extern struct grecs_sockaddr_hints *grecs_sockaddr_hints;
+
+struct grecs_sockaddr *grecs_sockaddr_new(size_t s);
+void grecs_sockaddr_free(struct grecs_sockaddr *p);
+
+int grecs_str_to_sockaddr(struct grecs_sockaddr **sap,
+ const char *arg, struct grecs_sockaddr_hints *gh,
+ grecs_locus_t const *locus);
+#endif
+
+#define GRECS_INADDR_BYTES 16
+
+struct grecs_cidr
+{
+ int family;
+ int len;
+ unsigned char address[GRECS_INADDR_BYTES];
+ unsigned char netmask[GRECS_INADDR_BYTES];
+};
+
+int grecs_str_to_cidr(struct grecs_cidr *pcidr, const char *str,
+ grecs_locus_t const *locus);
+
+int grecs_sockaddr_to_cidr(struct grecs_cidr *cidr, const struct sockaddr *sa);
+int grecs_sockadd_cidr_match(struct sockaddr *sa, struct grecs_cidr *cidr);
#endif
diff --git a/src/ipstr.c b/src/ipstr.c
new file mode 100644
index 0000000..5676301
--- /dev/null
+++ b/src/ipstr.c
@@ -0,0 +1,88 @@
+/* grecs - Gray's Extensible Configuration System
+ Copyright (C) 2007-2014 Sergey Poznyakoff
+
+ Grecs is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ Grecs is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Grecs. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <string.h>
+#include <ctype.h>
+#include "grecs.h"
+
+int
+grecs_str_is_ipv4(const char *addr)
+{
+ int dot_count = 0;
+ int digit_count = 0;
+
+ for (; *addr; addr++) {
+ if (!isascii(*addr))
+ return 0;
+ if (*addr == '.') {
+ if (++dot_count > 3)
+ break;
+ digit_count = 0;
+ } else if (!(isdigit(*addr) && ++digit_count <= 3))
+ return 0;
+ }
+
+ return (dot_count == 3);
+}
+
+int
+grecs_str_is_ipv6(const char *addr)
+{
+ int col_count = 0; /* Number of colons */
+ int dcol = 0; /* Did we encounter a double-colon? */
+ int dig_count = 0; /* Number of digits in the last group */
+
+ for (; *addr; addr++) {
+ if (!isascii(*addr))
+ return 0;
+ else if (isxdigit(*addr)) {
+ if (++dig_count > 4)
+ return 0;
+ } else if (*addr == ':') {
+ if (col_count && dig_count == 0 && ++dcol > 1)
+ return 0;
+ if (++col_count > 7)
+ return 0;
+ dig_count = 0;
+ } else
+ return 0;
+ }
+
+ return (col_count == 7 || dcol);
+}
+
+int
+grecs_str_is_num(const char *s)
+{
+ for (; *s; ++s)
+ if (!isdigit(*s))
+ return 0;
+ return 1;
+}
+
+int
+grecs_str_is_ipaddr(const char *addr)
+{
+ if (strchr (addr, '.'))
+ return grecs_str_is_ipv4(addr);
+ else if (strchr (addr, ':'))
+ return grecs_str_is_ipv6(addr);
+ return 0;
+}
+
diff --git a/src/preproc.c b/src/preproc.c
index ce32a29..56dcc22 100644
--- a/src/preproc.c
+++ b/src/preproc.c
@@ -1,5 +1,5 @@
/* grecs - Gray's Extensible Configuration System
- Copyright (C) 2007-2012 Sergey Poznyakoff
+ Copyright (C) 2007-2014 Sergey Poznyakoff
Grecs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
@@ -28,6 +28,7 @@
#include <string.h>
#include <errno.h>
#include <signal.h>
+#include <glob.h>
#include <wordsplit.h>
@@ -62,6 +63,9 @@ static size_t bufsize;
static char *putback_buffer;
static size_t putback_size;
static size_t putback_max;
+static glob_t include_glob;
+static size_t include_pos;
+static int include_once;
static int push_source (const char *name, int once);
static int pop_source (void);
@@ -287,7 +291,9 @@ static int
pp_list_find(struct grecs_list *list, struct file_data *dptr)
{
struct grecs_list_entry *ep;
-
+
+ if (!list)
+ return 0;
for (ep = list->head; !dptr->found && ep; ep = ep->next) {
const char *dir = ep->data;
size_t size = strlen (dir) + 1 + dptr->namelen + 1;
@@ -395,6 +401,13 @@ incl_compare(void const *data1, void const *data2)
}
static int
+incl_copy(void *dst, void *src)
+{
+ memcpy(dst, src, sizeof(struct input_file_ident));
+ return 0;
+}
+
+static int
source_lookup(struct stat *st)
{
struct input_file_ident key;
@@ -405,7 +418,7 @@ source_lookup(struct stat *st)
sizeof(struct input_file_ident),
incl_hasher,
incl_compare,
- NULL,
+ incl_copy,
NULL,/*FIXME: alloc*/
NULL);
if (!incl_sources)
@@ -505,6 +518,14 @@ pop_source()
grecs_free(context_stack);
context_stack = ctx;
+ if (include_pos < include_glob.gl_pathc) {
+ push_source(include_glob.gl_pathv[include_pos++], include_once);
+ return 0;
+ } else if (include_glob.gl_pathc) {
+ globfree(&include_glob);
+ include_pos = include_glob.gl_pathc = 0;
+ }
+
if (!context_stack) {
if (grecs_grecs__flex_debug)
fprintf(stderr, "End of input\n");
@@ -552,6 +573,16 @@ grecs_find_include_file(const char *name, int allow_cwd)
}
static int
+isglob(const char *s)
+{
+ for (; *s; s++) {
+ if (strchr("*?[", *s))
+ return 1;
+ }
+ return 0;
+}
+
+static int
parse_include(const char *text, int once)
{
struct wordsplit ws;
@@ -579,17 +610,35 @@ parse_include(const char *text, int once)
else
allow_cwd = 1;
- if (p[0] != '/') {
- p = grecs_find_include_file(p, allow_cwd);
+ if (isglob(p)) {
+ switch (glob(p, 0, NULL, &include_glob)) {
+ case 0:
+ include_pos = 0;
+ include_once = once;
+ break;
+ case GLOB_NOSPACE:
+ grecs_alloc_die();
+ case GLOB_NOMATCH:
+ break;
+ default:
+ grecs_error(&LOCUS, 0, _("read error"));
+ }
+ p = NULL;
+ } else if (p[0] != '/') {
+ char *q = p;
+ p = grecs_find_include_file(q, allow_cwd);
if (!p)
grecs_error(&LOCUS, 0,
_("%s: No such file or directory"),
- p);
+ q);
}
- }
+ }
if (p)
rc = push_source(p, once);
+ else if (include_pos < include_glob.gl_pathc)
+ rc = push_source(include_glob.gl_pathv[include_pos++], once);
+
grecs_free(tmp);
wordsplit_free(&ws);
return rc;
diff --git a/src/sockaddr.c b/src/sockaddr.c
new file mode 100644
index 0000000..28f99b1
--- /dev/null
+++ b/src/sockaddr.c
@@ -0,0 +1,280 @@
+/* grecs - Gray's Extensible Configuration System
+ Copyright (C) 2007-2014 Sergey Poznyakoff
+
+ Grecs is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ Grecs is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Grecs. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Network-specific functions */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include "grecs.h"
+
+struct grecs_sockaddr *
+grecs_sockaddr_new(size_t s)
+{
+ struct grecs_sockaddr *sp = grecs_malloc(sizeof(*sp));
+ sp->next = NULL;
+ sp->sa = grecs_zalloc(s);
+ sp->len = s;
+ return sp;
+}
+
+void
+grecs_sockaddr_free(struct grecs_sockaddr *p)
+{
+ while (p) {
+ struct grecs_sockaddr *next = p->next;
+ free(p->sa);
+ free(p);
+ p = next;
+ }
+}
+
+static int
+parse_unix(struct grecs_sockaddr **ret, const char *arg, const char *addrstr,
+ struct grecs_sockaddr_hints *gh, grecs_locus_t const *locus)
+{
+ struct sockaddr_un *s_un;
+ size_t slen = strlen(addrstr);
+ struct grecs_sockaddr *sp;
+
+ if (slen >= sizeof s_un->sun_path) {
+ grecs_error(locus, 0, _("socket path name too long: %s"), arg);
+ return -1;
+ }
+
+ sp = grecs_sockaddr_new(sizeof(s_un[0]));
+ s_un = (struct sockaddr_un *) sp->sa;
+ s_un->sun_family = AF_UNIX;
+ strcpy(s_un->sun_path, addrstr);
+
+ *ret = sp;
+ return 0;
+}
+
+static int
+parse_inet(struct grecs_sockaddr **ret,
+ int family, const char *arg, const char *addrstr,
+ struct grecs_sockaddr_hints *gh, grecs_locus_t const *locus)
+{
+ int rc;
+ struct addrinfo hints;
+ struct addrinfo *res, *ap;
+ const char *node = NULL;
+ char *nodebuf = NULL;
+ const char *service = NULL;
+ struct grecs_sockaddr *head = NULL, *tail = NULL;
+ char portbuf[64];
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+
+ if ((family == AF_INET6 || family == AF_UNSPEC)
+ && addrstr[0] == '[') {
+ char *p = strchr(addrstr + 1, ']');
+ if (p && p > addrstr + 1) {
+ size_t len;
+
+ addrstr++;
+ len = p - addrstr;
+ nodebuf = grecs_malloc(len + 1);
+ memcpy(nodebuf, addrstr, len);
+ nodebuf[len] = 0;
+ node = nodebuf;
+ service = p + 1;
+ family = AF_INET6;
+ } else
+ service = strchr(addrstr, ':');
+ } else
+ service = strrchr(addrstr, ':');
+
+ if (service && *service) {
+ if (*service != ':') {
+ grecs_error(locus, 0,
+ _("%s: garbage near %s"), arg, service);
+ return -1;
+ }
+ service++;
+ }
+
+ if (!node) {
+ if (service) {
+ size_t len = service - addrstr - 1;
+
+ if (len == 0)
+ node = NULL;
+ else {
+ nodebuf = grecs_malloc(len + 1);
+ memcpy(nodebuf, addrstr, len);
+ nodebuf[len] = 0;
+ node = nodebuf;
+ }
+ } else {
+ if (grecs_str_is_ipaddr(addrstr))
+ node = addrstr;
+ else if (grecs_str_is_num(addrstr)) {
+ service = addrstr;
+ hints.ai_flags |= AI_NUMERICSERV;
+ }
+ }
+ }
+
+ if (!service || !*service) {
+ if (!node && addrstr[0])
+ node = addrstr;
+ if (gh->flags & GRECS_HINT_SERVICE) {
+ service = gh->service;
+ } else if (gh->flags & GRECS_HINT_PORT) {
+ snprintf(portbuf, sizeof portbuf, "%hu", gh->port);
+ service = portbuf;
+ hints.ai_flags |= AI_NUMERICSERV;
+ } else {
+ grecs_error(locus, 0,
+ _("service not specified: %s"), arg);
+ return -1;
+ }
+ }
+
+ if (!node) {
+ if (gh->flags & GRECS_AH_PASSIVE)
+ hints.ai_flags |= AI_PASSIVE;
+ }
+
+ rc = getaddrinfo(node, service, &hints, &res);
+ free(nodebuf);
+ switch (rc) {
+ case 0:
+ break;
+ case EAI_SYSTEM:
+ grecs_error(locus, 0,
+ _("%s: cannot parse address: %s"),
+ arg, strerror(errno));
+ break;
+ case EAI_BADFLAGS:
+ case EAI_SOCKTYPE:
+ grecs_error(locus, 0,
+ "%s:%d: internal error converting %s",
+ __FILE__,__LINE__,arg);
+ break;
+ case EAI_MEMORY:
+ grecs_alloc_die();
+ default:
+ grecs_error(locus, 0,
+ "%s: %s", arg, gai_strerror(rc));
+ return -1;
+ }
+
+ for (ap = res; ap; ap = ap->ai_next) {
+ if (family == AF_UNSPEC || ap->ai_addr->sa_family == family) {
+ struct grecs_sockaddr *sp =
+ grecs_sockaddr_new(ap->ai_addrlen);
+ memcpy(sp->sa, ap->ai_addr, ap->ai_addrlen);
+ sp->len = ap->ai_addrlen;
+ if (!head)
+ head = sp;
+ else
+ tail->next = sp;
+ tail = sp;
+ }
+ }
+ freeaddrinfo(res);
+ *ret = head;
+ return 0;
+}
+
+static int
+parse_inet4(struct grecs_sockaddr **ret, const char *arg, const char *addrstr,
+ struct grecs_sockaddr_hints *gh, grecs_locus_t const *locus)
+{
+ return parse_inet(ret, AF_INET, arg, addrstr, gh, locus);
+}
+
+static int
+parse_inet6(struct grecs_sockaddr **ret, const char *arg, const char *addrstr,
+ struct grecs_sockaddr_hints *gh, grecs_locus_t const *locus)
+{
+ return parse_inet(ret, AF_INET6, arg, addrstr, gh, locus);
+}
+
+struct schemetab {
+ const char *scheme;
+ size_t len;
+ int (*parser)(struct grecs_sockaddr **ret,
+ const char *arg, const char *addr,
+ struct grecs_sockaddr_hints *gh,
+ grecs_locus_t const *locus);
+};
+
+struct schemetab schemetab[] = {
+ { "inet", 4, parse_inet4 },
+ { "inet4", 5, parse_inet4 },
+ { "inet6", 5, parse_inet6 },
+ { "unix", 4, parse_unix },
+ { NULL }
+};
+
+int
+grecs_str_to_sockaddr(struct grecs_sockaddr **sap,
+ const char *arg, struct grecs_sockaddr_hints *gh,
+ grecs_locus_t const *locus)
+{
+ char *p;
+ struct grecs_sockaddr_hints ghints;
+
+ if (!gh) {
+ memset(&ghints, 0, sizeof(ghints));
+ if (grecs_default_port) {
+ ghints.flags = GRECS_HINT_PORT;
+ ghints.port = ntohs(grecs_default_port);
+ }
+ gh = &ghints;
+ }
+
+ p = strchr(arg, ':');
+ if (p && p > arg && p[1] == '/' && p[2] == '/') {
+ size_t len = p - arg;
+ struct schemetab *sp;
+
+ for (sp = schemetab; sp->scheme; sp++)
+ if (len == sp->len &&
+ memcmp(arg, sp->scheme, len) == 0)
+ return sp->parser(sap, arg, p + 3, gh, locus);
+ grecs_error(locus, 0,
+ _("unknown or unsupported scheme: %s"), arg);
+ return -1;
+ }
+
+ if (arg[0] == '/')
+ return parse_unix(sap, arg, arg, gh, locus);
+ else if (strlen(arg) > 5 && memcmp(arg, "unix:", 5) == 0) {
+ if (arg[5] != '/')
+ grecs_error(locus, 0,
+ _("%s: UNIX socket must be an absolute file name"),
+ arg);
+ return parse_unix(sap, arg, arg + 5, gh, locus);
+ }
+
+ return parse_inet(sap, AF_UNSPEC, arg, arg, gh, locus);
+}
diff --git a/src/tree.c b/src/tree.c
index a0f8736..c0c1920 100644
--- a/src/tree.c
+++ b/