diff options
Diffstat (limited to 'src/conf.c')
-rw-r--r-- | src/conf.c | 380 |
1 files changed, 222 insertions, 158 deletions
@@ -23,6 +23,8 @@ * add new directives to. Who knows if I'm right though. */ +#include "common.h" +#include <regex.h> #include "conf.h" #include "acl.h" @@ -36,6 +38,7 @@ #include "upstream.h" #include "connect-ports.h" #include "basicauth.h" +#include "conf-tokens.h" /* * The configuration directives are defined in the structure below. Each @@ -46,46 +49,35 @@ * can (and likely should) be used when building the regex for the * given directive. */ -#define WS "[[:space:]]+" +#define DIGIT "[0-9]" +#define SPACE "[ \t]" +#define WS SPACE "+" #define STR "\"([^\"]+)\"" #define BOOL "(yes|on|no|off)" -#define INT "((0x)?[[:digit:]]+)" +#define INT "(()" DIGIT "+)" #define ALNUM "([-a-z0-9._]+)" #define USERNAME "([^:]*)" #define PASSWORD "([^@]*)" #define IP "((([0-9]{1,3})\\.){3}[0-9]{1,3})" -#define IPMASK "(" IP "(/[[:digit:]]+)?)" +#define IPMASK "(" IP "(/" DIGIT "+)?)" #define IPV6 "(" \ - "(([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,6})|" \ - "(([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5})|" \ - "(([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4})|" \ - "(([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3})|" \ - "(([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2})|" \ - "(([0-9a-f]{1,4}:){1,6}(:[0-9a-f]{1,4}){1,1})|" \ - "((([0-9a-f]{1,4}:){1,7}|:):)|" \ - "(:(:[0-9a-f]{1,4}){1,7})|" \ - "([0-9a-f]{1,4}(:[0-9a-f]{1,4}){1,7})|" \ - "(((([0-9a-f]{1,4}:){6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}))|" \ - "((([0-9a-f]{1,4}:){5}[0-9a-f]{1,4}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}))|" \ - "(([0-9a-f]{1,4}:){5}:[0-9a-f]{1,4}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \ - "(([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,4}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \ - "(([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,3}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \ - "(([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,2}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \ - "(([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,1}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \ - "((([0-9a-f]{1,4}:){1,5}|:):(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \ - "(:(:[0-9a-f]{1,4}){1,5}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})" \ + "(([0-9a-f:]{2,39}))|" \ + "(([0-9a-f:]{0,29}:" IP "))" \ ")" -#define IPV6MASK "(" IPV6 "(/[[:digit:]]+)?)" -#define BEGIN "^[[:space:]]*" -#define END "[[:space:]]*$" +#define IPV6MASK "(" IPV6 "(/" DIGIT "+)?)" +#define BEGIN "^" SPACE "*" +#define END SPACE "*$" /* * Limit the maximum number of substring matches to a reasonably high * number. Given the usual structure of the configuration file, sixteen * substring matches should be plenty. */ -#define RE_MAX_MATCHES 16 +#define RE_MAX_MATCHES 24 + +#define CP_WARN(FMT, ...) \ + log_message (LOG_WARNING, "line %lu: " FMT, lineno, __VA_ARGS__) /* * All configuration handling functions are REQUIRED to be defined @@ -113,15 +105,20 @@ typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *, * List all the handling functions. These are defined later, but they need * to be in-scope before the big structure below. */ -static HANDLE_FUNC (handle_nop) +static HANDLE_FUNC (handle_disabled_feature) { - return 0; -} /* do nothing function */ + fprintf (stderr, "ERROR: accessing feature that was disabled at compiletime on line %lu\n", + lineno); + + return -1; +} static HANDLE_FUNC (handle_allow); static HANDLE_FUNC (handle_basicauth); static HANDLE_FUNC (handle_anonymous); static HANDLE_FUNC (handle_bind); +static HANDLE_FUNC (handle_bindipv4mapped); +static HANDLE_FUNC (handle_bindipv6mapped); static HANDLE_FUNC (handle_bindsame); static HANDLE_FUNC (handle_connectport); static HANDLE_FUNC (handle_defaulterrorfile); @@ -161,7 +158,6 @@ static HANDLE_FUNC (handle_xtinyproxy); #ifdef UPSTREAM_SUPPORT static HANDLE_FUNC (handle_upstream); -static HANDLE_FUNC (handle_upstream_no); #endif static void config_free_regex (void); @@ -177,7 +173,7 @@ static void config_free_regex (void); * do not follow the pattern above. This macro is for convenience * only. */ -#define STDCONF(d, re, func) { BEGIN "(" d ")" WS re END, func, NULL } +#define STDCONF(d, re, func) [CD_ ## d] = { BEGIN "()" WS re END, func, NULL } /* * Holds the regular expression used to match the configuration directive, @@ -190,109 +186,118 @@ struct { CONFFILE_HANDLER handler; regex_t *cre; } directives[] = { - /* comments */ - { - BEGIN "#", handle_nop, NULL - }, - /* blank lines */ - { - "^[[:space:]]+$", handle_nop, NULL - }, /* string arguments */ - STDCONF ("logfile", STR, handle_logfile), - STDCONF ("pidfile", STR, handle_pidfile), - STDCONF ("anonymous", STR, handle_anonymous), - STDCONF ("viaproxyname", STR, handle_viaproxyname), - STDCONF ("defaulterrorfile", STR, handle_defaulterrorfile), - STDCONF ("statfile", STR, handle_statfile), - STDCONF ("stathost", STR, handle_stathost), - STDCONF ("xtinyproxy", BOOL, handle_xtinyproxy), + STDCONF (logfile, STR, handle_logfile), + STDCONF (pidfile, STR, handle_pidfile), + STDCONF (anonymous, STR, handle_anonymous), + STDCONF (viaproxyname, STR, handle_viaproxyname), + STDCONF (defaulterrorfile, STR, handle_defaulterrorfile), + STDCONF (statfile, STR, handle_statfile), + STDCONF (stathost, STR, handle_stathost), + STDCONF (xtinyproxy, BOOL, handle_xtinyproxy), /* boolean arguments */ - STDCONF ("syslog", BOOL, handle_syslog), - STDCONF ("bindsame", BOOL, handle_bindsame), - STDCONF ("disableviaheader", BOOL, handle_disableviaheader), + STDCONF (syslog, BOOL, handle_syslog), + STDCONF (bindsame, BOOL, handle_bindsame), + STDCONF (disableviaheader, BOOL, handle_disableviaheader), /* integer arguments */ - STDCONF ("port", INT, handle_port), - STDCONF ("maxclients", INT, handle_maxclients), - STDCONF ("maxspareservers", INT, handle_obsolete), - STDCONF ("minspareservers", INT, handle_obsolete), - STDCONF ("startservers", INT, handle_obsolete), - STDCONF ("maxrequestsperchild", INT, handle_obsolete), - STDCONF ("timeout", INT, handle_timeout), - STDCONF ("connectport", INT, handle_connectport), + STDCONF (port, INT, handle_port), + STDCONF (maxclients, INT, handle_maxclients), + STDCONF (maxspareservers, INT, handle_obsolete), + STDCONF (minspareservers, INT, handle_obsolete), + STDCONF (startservers, INT, handle_obsolete), + STDCONF (maxrequestsperchild, INT, handle_obsolete), + STDCONF (timeout, INT, handle_timeout), + STDCONF (connectport, INT, handle_connectport), /* alphanumeric arguments */ - STDCONF ("user", ALNUM, handle_user), - STDCONF ("group", ALNUM, handle_group), + STDCONF (user, ALNUM, handle_user), + STDCONF (group, ALNUM, handle_group), /* ip arguments */ - STDCONF ("listen", "(" IP "|" IPV6 ")", handle_listen), - STDCONF ("allow", "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", + STDCONF (listen, "(" IP "|" IPV6 ")", handle_listen), + STDCONF (allow, "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", handle_allow), - STDCONF ("deny", "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", + STDCONF (deny, "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", handle_deny), - STDCONF ("bind", "(" IP "|" IPV6 ")", handle_bind), + STDCONF (bind, "(" IP "|" IPV6 ")", handle_bind), + STDCONF (bindipv4mapped, "(" IPV6 ")", handle_bindipv4mapped), + STDCONF (bindipv6mapped, "(" IP ")", handle_bindipv6mapped), /* other */ - STDCONF ("basicauth", ALNUM WS ALNUM, handle_basicauth), - STDCONF ("errorfile", INT WS STR, handle_errorfile), - STDCONF ("addheader", STR WS STR, handle_addheader), + STDCONF (basicauth, ALNUM WS ALNUM, handle_basicauth), + STDCONF (errorfile, INT WS STR, handle_errorfile), + STDCONF (addheader, STR WS STR, handle_addheader), #ifdef FILTER_ENABLE /* filtering */ - STDCONF ("filter", STR, handle_filter), - STDCONF ("filterurls", BOOL, handle_filterurls), - STDCONF ("filterextended", BOOL, handle_filterextended), - STDCONF ("filterdefaultdeny", BOOL, handle_filterdefaultdeny), - STDCONF ("filtercasesensitive", BOOL, handle_filtercasesensitive), + STDCONF (filter, STR, handle_filter), + STDCONF (filterurls, BOOL, handle_filterurls), + STDCONF (filterextended, BOOL, handle_filterextended), + STDCONF (filterdefaultdeny, BOOL, handle_filterdefaultdeny), + STDCONF (filtercasesensitive, BOOL, handle_filtercasesensitive), #endif #ifdef REVERSE_SUPPORT /* Reverse proxy arguments */ - STDCONF ("reversebaseurl", STR, handle_reversebaseurl), - STDCONF ("reverseonly", BOOL, handle_reverseonly), - STDCONF ("reversemagic", BOOL, handle_reversemagic), - STDCONF ("reversepath", STR "(" WS STR ")?", handle_reversepath), + STDCONF (reversebaseurl, STR, handle_reversebaseurl), + STDCONF (reverseonly, BOOL, handle_reverseonly), + STDCONF (reversemagic, BOOL, handle_reversemagic), + STDCONF (reversepath, STR "(" WS STR ")?", handle_reversepath), #endif #ifdef UPSTREAM_SUPPORT - { - BEGIN "(upstream)" WS "(none)" WS STR END, handle_upstream_no, NULL - }, - { - BEGIN "(upstream)" WS "(http|socks4|socks5)" WS - "(" USERNAME /*username*/ ":" PASSWORD /*password*/ "@" ")?" - "(" IP "|" ALNUM ")" - ":" INT "(" WS STR ")?" - END, handle_upstream, NULL - }, + STDCONF (upstream, + "(" "(none)" WS STR ")|" \ + "(" "(http|socks4|socks5)" WS \ + "(" USERNAME /*username*/ ":" PASSWORD /*password*/ "@" ")?" + "(" IP "|" ALNUM ")" + ":" INT "(" WS STR ")?" ")", handle_upstream), #endif /* loglevel */ - STDCONF ("loglevel", "(critical|error|warning|notice|connect|info)", + STDCONF (loglevel, "(critical|error|warning|notice|connect|info)", handle_loglevel) }; const unsigned int ndirectives = sizeof (directives) / sizeof (directives[0]); static void -free_added_headers (vector_t add_headers) +free_added_headers (sblist* add_headers) { - ssize_t i; + size_t i; - for (i = 0; i < vector_length (add_headers); i++) { - http_header_t *header = (http_header_t *) - vector_getentry (add_headers, i, NULL); + if (!add_headers) return; + + for (i = 0; i < sblist_getsize (add_headers); i++) { + http_header_t *header = sblist_get (add_headers, i); safefree (header->name); safefree (header->value); } - vector_delete (add_headers); + sblist_free (add_headers); +} + +static void stringlist_free(sblist *sl) { + size_t i; + char **s; + if(sl) { + for(i = 0; i < sblist_getsize(sl); i++) { + s = sblist_get(sl, i); + if(s) safefree(*s); + } + sblist_free(sl); + } } -static void free_config (struct config_s *conf) +void free_config (struct config_s *conf) { + char *k; + htab_value *v; + size_t it; safefree (conf->logf_name); safefree (conf->stathost); safefree (conf->user); safefree (conf->group); - vector_delete(conf->listen_addrs); - vector_delete(conf->basicauth_list); + safefree (conf->bind_ipv4mapped); + safefree (conf->bind_ipv6mapped); + stringlist_free(conf->basicauth_list); + stringlist_free(conf->listen_addrs); + stringlist_free(conf->bind_addrs); #ifdef FILTER_ENABLE safefree (conf->filter); #endif /* FILTER_ENABLE */ @@ -304,34 +309,50 @@ static void free_config (struct config_s *conf) free_upstream_list (conf->upstream_list); #endif /* UPSTREAM_SUPPORT */ safefree (conf->pidpath); - safefree (conf->bind_address); safefree (conf->via_proxy_name); - hashmap_delete (conf->errorpages); + if (conf->errorpages) { + it = 0; + while((it = htab_next(conf->errorpages, it, &k, &v))) { + safefree(k); + safefree(v->p); + } + htab_destroy (conf->errorpages); + } free_added_headers (conf->add_headers); safefree (conf->errorpage_undef); safefree (conf->statpage); flush_access_list (conf->access_list); free_connect_ports_list (conf->connect_ports); - hashmap_delete (conf->anonymous_map); + if (conf->anonymous_map) { + it = 0; + while((it = htab_next(conf->anonymous_map, it, &k, &v))) + safefree(k); + htab_destroy (conf->anonymous_map); + } memset (conf, 0, sizeof(*conf)); } /* + * Initializes Config parser. Currently this means: * Compiles the regular expressions used by the configuration file. This * routine MUST be called before trying to parse the configuration file. * * Returns 0 on success; negative upon failure. */ int -config_compile_regex (void) +config_init (void) { unsigned int i, r; for (i = 0; i != ndirectives; ++i) { - assert (directives[i].handler); assert (!directives[i].cre); + if (!directives[i].handler) { + directives[i].handler = handle_disabled_feature; + continue; + } + directives[i].cre = (regex_t *) safemalloc (sizeof (regex_t)); if (!directives[i].cre) return -1; @@ -375,20 +396,17 @@ config_free_regex (void) * a negative number is returned. */ static int check_match (struct config_s *conf, const char *line, - unsigned long lineno) + unsigned long lineno, enum config_directive cd) { regmatch_t match[RE_MAX_MATCHES]; - unsigned int i; + unsigned int i = cd; - assert (ndirectives > 0); - - for (i = 0; i != ndirectives; ++i) { - assert (directives[i].cre); - if (!regexec - (directives[i].cre, line, RE_MAX_MATCHES, match, 0)) - return (*directives[i].handler) (conf, line, lineno, match); - } + if (!directives[i].cre) + return (*directives[i].handler) (conf, line, lineno, match); + if (!regexec + (directives[i].cre, line, RE_MAX_MATCHES, match, 0)) + return (*directives[i].handler) (conf, line, lineno, match); return -1; } @@ -397,15 +415,25 @@ static int check_match (struct config_s *conf, const char *line, */ static int config_parse (struct config_s *conf, FILE * f) { - char buffer[LINE_MAX]; + char buffer[LINE_MAX], *p, *q, c; + const struct config_directive_entry *e; unsigned long lineno = 1; - while (fgets (buffer, sizeof (buffer), f)) { - if (check_match (conf, buffer, lineno)) { - printf ("Syntax error on line %ld\n", lineno); + for (;fgets (buffer, sizeof (buffer), f);++lineno) { + if(buffer[0] == '#') continue; + p = buffer; + while(isspace(*p))p++; + if(!*p) continue; + q = p; + while(!isspace(*q))q++; + c = *q; + *q = 0; + e = config_directive_find(p, strlen(p)); + *q = c; + if (!e || e->value == CD_NIL || check_match (conf, q, lineno, e->value)) { + fprintf (stderr, "ERROR: Syntax error on line %lu\n", lineno); return 1; } - ++lineno; } return 0; } @@ -464,10 +492,6 @@ int reload_config_file (const char *config_fname, struct config_s *conf) { int ret; - log_message (LOG_NOTICE, "Reloading config file"); - - free_config (conf); - initialize_config_defaults (conf); ret = load_config_file (config_fname, conf); @@ -624,8 +648,12 @@ static HANDLE_FUNC (handle_anonymous) if (!arg) return -1; - anonymous_insert (conf, arg); - safefree (arg); + if(anonymous_insert (conf, arg) < 0) { + CP_WARN ("anonymous_insert() failed: '%s'", arg); + safefree(arg); + return -1; + } + return 0; } @@ -748,11 +776,16 @@ static HANDLE_FUNC (handle_group) return set_string_arg (&conf->group, line, &match[2]); } +static void warn_invalid_address(char *arg, unsigned long lineno) { + CP_WARN ("Invalid address %s", arg); +} + static HANDLE_FUNC (handle_allow) { char *arg = get_string_arg (line, &match[2]); - insert_acl (arg, ACL_ALLOW, &conf->access_list); + if(insert_acl (arg, ACL_ALLOW, &conf->access_list) < 0) + warn_invalid_address (arg, lineno); safefree (arg); return 0; } @@ -761,19 +794,35 @@ static HANDLE_FUNC (handle_deny) { char *arg = get_string_arg (line, &match[2]); - insert_acl (arg, ACL_DENY, &conf->access_list); + if(insert_acl (arg, ACL_DENY, &conf->access_list) < 0) + warn_invalid_address (arg, lineno); safefree (arg); return 0; } static HANDLE_FUNC (handle_bind) { - int r = set_string_arg (&conf->bind_address, line, &match[2]); + char *arg = get_string_arg (line, &match[2]); + + if (arg == NULL) { + return -1; + } + + if (conf->bind_addrs == NULL) { + conf->bind_addrs = sblist_new(sizeof(char*), 16); + if (conf->bind_addrs == NULL) { + CP_WARN ("Could not create a list " + "of bind addresses.", ""); + safefree(arg); + return -1; + } + } + + sblist_add (conf->bind_addrs, &arg); - if (r) - return r; log_message (LOG_INFO, - "Outgoing connections bound to IP %s", conf->bind_address); + "Added bind address [%s] for outgoing connections.", arg); + return 0; } @@ -786,23 +835,32 @@ static HANDLE_FUNC (handle_listen) } if (conf->listen_addrs == NULL) { - conf->listen_addrs = vector_create(); + conf->listen_addrs = sblist_new(sizeof(char*), 16); if (conf->listen_addrs == NULL) { - log_message(LOG_WARNING, "Could not create a list " - "of listen addresses."); + CP_WARN ("Could not create a list " + "of listen addresses.", ""); safefree(arg); return -1; } } - vector_append (conf->listen_addrs, arg, strlen(arg) + 1); + sblist_add (conf->listen_addrs, &arg); log_message(LOG_INFO, "Added address [%s] to listen addresses.", arg); - safefree (arg); return 0; } +static HANDLE_FUNC (handle_bindipv4mapped) +{ + return set_string_arg(&conf->bind_ipv4mapped, line, &match[2]); +} + +static HANDLE_FUNC (handle_bindipv6mapped) +{ + return set_string_arg(&conf->bind_ipv6mapped, line, &match[2]); +} + static HANDLE_FUNC (handle_errorfile) { /* @@ -815,8 +873,10 @@ static HANDLE_FUNC (handle_errorfile) unsigned long int err = get_long_arg (line, &match[2]); char *page = get_string_arg (line, &match[4]); - add_new_errorpage (page, err); - safefree (page); + if(add_new_errorpage (conf, page, err) < 0) { + CP_WARN ("add_new_errorpage() failed: '%s'", page); + safefree (page); + } return 0; } @@ -824,19 +884,16 @@ static HANDLE_FUNC (handle_addheader) { char *name = get_string_arg (line, &match[2]); char *value = get_string_arg (line, &match[3]); - http_header_t *header; + http_header_t header; if (!conf->add_headers) { - conf->add_headers = vector_create (); + conf->add_headers = sblist_new (sizeof(http_header_t), 16); } - header = (http_header_t *) safemalloc (sizeof (http_header_t)); - header->name = name; - header->value = value; - - vector_prepend (conf->add_headers, header, sizeof *header); + header.name = name; + header.value = value; - safefree (header); + sblist_add (conf->add_headers, &header); /* Don't free name or value here, as they are referenced in the * struct inserted into the vector. */ @@ -893,7 +950,7 @@ static HANDLE_FUNC (handle_basicauth) return -1; } if (!conf->basicauth_list) { - conf->basicauth_list = vector_create (); + conf->basicauth_list = sblist_new (sizeof(char*), 16); } basicauth_add (conf->basicauth_list, user, pass); @@ -997,9 +1054,26 @@ static enum proxy_type pt_from_string(const char *s) static HANDLE_FUNC (handle_upstream) { char *ip; - int port, mi = 2; + int port, mi; char *domain = 0, *user = 0, *pass = 0, *tmp; enum proxy_type pt; + enum upstream_build_error ube; + + if (match[3].rm_so != -1) { + tmp = get_string_arg (line, &match[3]); + if(!strcmp(tmp, "none")) { + safefree(tmp); + if (match[4].rm_so == -1) return -1; + domain = get_string_arg (line, &match[4]); + if (!domain) + return -1; + ube = upstream_add (NULL, 0, domain, 0, 0, PT_NONE, &conf->upstream_list); + safefree (domain); + goto check_err; + } + } + + mi = 6; tmp = get_string_arg (line, &match[mi]); pt = pt_from_string(tmp); @@ -1025,27 +1099,17 @@ static HANDLE_FUNC (handle_upstream) if (match[mi].rm_so != -1) domain = get_string_arg (line, &match[mi]); - upstream_add (ip, port, domain, user, pass, pt, &conf->upstream_list); + ube = upstream_add (ip, port, domain, user, pass, pt, &conf->upstream_list); safefree (user); safefree (pass); safefree (domain); safefree (ip); +check_err:; + if(ube != UBE_SUCCESS) + CP_WARN("%s", upstream_build_error_string(ube)); return 0; } -static HANDLE_FUNC (handle_upstream_no) -{ - char *domain; - - domain = get_string_arg (line, &match[3]); - if (!domain) - return -1; - - upstream_add (NULL, 0, domain, 0, 0, PT_NONE, &conf->upstream_list); - safefree (domain); - - return 0; -} #endif |