diff --git a/postfix/HISTORY b/postfix/HISTORY index 56d7756d9..81d5e00d0 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -26990,11 +26990,13 @@ Apologies for any names omitted. 20230330 Safety: the long form { name = value } in import_environment - or export_environment is not documented, but accepted, and - it was stored in the process environment as the invalid - form "name = value" instead of the expected "name=value". - Found during code maintenance. Also refined an "empty name" - check. Files: clean_env.c, split_nameval.c. + or export_environment is not documented, but it is accepted, + and it was stored in the process environment as the invalid + form "name = value, thus not setting or overriding an entry + for "name". This form is now stored as the expected + "name=value". Found during code maintenance. Also refined + the "missing attribute name" detection. Files: clean_env.c, + split_nameval.c. 20230402 @@ -27014,3 +27016,23 @@ Apologies for any names omitted. example, inline maps. Added Valgrind support to the namadr_list unit test. Files: util/match_list.c, global/namadr_list.in, util/Makefile.in. + +20240406 + + Bugfix (introduced: 20230402): after a change in the DNS_RR + structure, the dns_rr_copy() function had not been updated, + causing the Postfix SMTP client to panic as it detected a + double-free() attempt. Reported by Florian Piekert. File: + dns/dns_rr.c. + + Usability: Postfix does not support #comments after other + text, but people add them anyway, with unexpected results. + The postconf command now warns for trailing comments in + main.cf files. Similar warnings are planned for database + client configuration files. Files: util/mystrtok.c, + util/mystrtok.ref, util/match_list.c, global/namadr_list.ref, + postconf/postconf_dbms.c, postconf/test71.ref. + + TODO: #comment after text in DB client configuration files. + + TOIDO: test for dns_rr_copy() + dns_rr_free(). diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc index c642c7a32..3da669025 100644 --- a/postfix/proto/stop.spell-cc +++ b/postfix/proto/stop.spell-cc @@ -1800,3 +1800,4 @@ Stringify bitcount bytecount ipproto +cw diff --git a/postfix/src/dns/dns_rr.c b/postfix/src/dns/dns_rr.c index 44918b10c..b84349267 100644 --- a/postfix/src/dns/dns_rr.c +++ b/postfix/src/dns/dns_rr.c @@ -160,6 +160,9 @@ DNS_RR *dns_rr_create(const char *qname, const char *rname, { DNS_RR *rr; + /* + * Note: if this function is changed, update dns_rr_copy(). + */ rr = (DNS_RR *) mymalloc(sizeof(*rr)); rr->qname = mystrdup(qname); rr->rname = mystrdup(rname); @@ -200,16 +203,17 @@ void dns_rr_free(DNS_RR *rr) DNS_RR *dns_rr_copy(DNS_RR *src) { - ssize_t len = sizeof(*src) + src->data_len - 1; DNS_RR *dst; /* - * Combine struct assignment and data copy in one block copy operation. + * Note: struct copy, because dns_rr_create() would not copy all fields. */ - dst = (DNS_RR *) mymalloc(len); - memcpy((void *) dst, (void *) src, len); + dst = (DNS_RR *) mymalloc(sizeof(*dst)); + memcpy((void *) dst, (void *) src, sizeof(*dst)); dst->qname = mystrdup(src->qname); dst->rname = mystrdup(src->rname); + if (dst->data) + dst->data = mymemdup(dst->data, dst->data_len); dst->next = 0; return (dst); } diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 8a382c396..dbcfba979 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20230402" +#define MAIL_RELEASE_DATE "20230406" #define MAIL_VERSION_NUMBER "3.8" #ifdef SNAPSHOT diff --git a/postfix/src/global/namadr_list.ref b/postfix/src/global/namadr_list.ref index 77ab9bdc9..b108786eb 100644 --- a/postfix/src/global/namadr_list.ref +++ b/postfix/src/global/namadr_list.ref @@ -51,9 +51,9 @@ bar/168.100.3.3: ERROR ./namadr_list: warning: non-existent:/tmp/nosuchfile is unavailable. open file /tmp/nosuchfile: No such file or directory ./namadr_list: warning: command line: non-existent:/tmp/nosuchfile: table lookup problem bar/168.100.3.3: ERROR -./namadr_list: warning: command line: comment at end of line is not supported: #text +./namadr_list: warning: command line: #comment after other text is not allowed: #text ... foo/1.2.3.4: YES -./namadr_list: warning: command line: comment at end of line is not supported: #text +./namadr_list: warning: command line: #comment after other text is not allowed: #text ... fool/1.2.3.4: NO foo/1.2.3.4: YES bar/1.2.3.4: YES diff --git a/postfix/src/postconf/Makefile.in b/postfix/src/postconf/Makefile.in index 665f36e8a..26e747c7b 100644 --- a/postfix/src/postconf/Makefile.in +++ b/postfix/src/postconf/Makefile.in @@ -55,7 +55,7 @@ tests: test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 \ test31 test32 test33 test34 test35 test36 test37 test39 test40 test41 \ test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 \ test52 test53 test54 test55 test56 test57 test58 test59 test60 test61 \ - test62 test63 test64 test65 test66 test67 test68 test69 test70 + test62 test63 test64 test65 test66 test67 test68 test69 test70 test71 root_tests: @@ -964,7 +964,24 @@ test70: $(PROG) test70.ref touch -t 197101010000 main.cf $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -nc . >test70.tmp 2>&1 diff test70.ref test70.tmp - rm -f main.cf master.cf test70.tmp test70.cf + rm -f main.cf master.cf test70.tmp + +test71: $(PROG) test71.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo "smtpd_client_restrictions = inline:{" >>main.cf + echo " { aaa0 = #aaa1 } #aaa2" >>main.cf + echo " }" >>main.cf + echo "smtpd_helo_restrictions = pcre:{" >>main.cf + echo " { /bbb0 #bbb1/ } #bbb2" >>main.cf + echo " }" >>main.cf + echo "smtpd_sender_restrictions = regexp:{" >>main.cf + echo " { /ccc0 #ccc1/ } #ccc2" >>main.cf + echo " }" >>main.cf + touch -t 197101010000 main.cf + $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -nc . >test71.tmp 2>&1 + diff test71.ref test71.tmp + rm -f main.cf master.cf test71.tmp printfck: $(OBJS) $(PROG) rm -rf printfck @@ -1072,8 +1089,10 @@ postconf_dbms.o: ../../include/dict_ht.h postconf_dbms.o: ../../include/dict_ldap.h postconf_dbms.o: ../../include/dict_memcache.h postconf_dbms.o: ../../include/dict_mysql.h +postconf_dbms.o: ../../include/dict_pcre.h postconf_dbms.o: ../../include/dict_pgsql.h postconf_dbms.o: ../../include/dict_proxy.h +postconf_dbms.o: ../../include/dict_regexp.h postconf_dbms.o: ../../include/dict_sqlite.h postconf_dbms.o: ../../include/htable.h postconf_dbms.o: ../../include/mac_expand.h diff --git a/postfix/src/postconf/postconf_dbms.c b/postfix/src/postconf/postconf_dbms.c index d21cad18f..622e7a01d 100644 --- a/postfix/src/postconf/postconf_dbms.c +++ b/postfix/src/postconf/postconf_dbms.c @@ -48,6 +48,8 @@ /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema /*--*/ /* System library. */ @@ -77,6 +79,8 @@ #include #include #include +#include +#include /* Application-specific. */ @@ -134,18 +138,31 @@ static const char *pcf_memcache_suffixes[] = { */ typedef struct { const char *db_type; + int db_class; const char **db_suffixes; } PCF_DBMS_INFO; +#define PCF_DBMS_CLASS_CLIENT (1) /* DB name is client config path */ +#define PCF_DBMS_CLASS_REGEX (2) /* DB name contains regex patterns */ + static const PCF_DBMS_INFO pcf_dbms_info[] = { - DICT_TYPE_LDAP, pcf_ldap_suffixes, - DICT_TYPE_MYSQL, pcf_mysql_suffixes, - DICT_TYPE_PGSQL, pcf_pgsql_suffixes, - DICT_TYPE_SQLITE, pcf_sqlite_suffixes, - DICT_TYPE_MEMCACHE, pcf_memcache_suffixes, - 0, + {DICT_TYPE_LDAP, PCF_DBMS_CLASS_CLIENT, pcf_ldap_suffixes}, + {DICT_TYPE_MYSQL, PCF_DBMS_CLASS_CLIENT, pcf_mysql_suffixes}, + {DICT_TYPE_PGSQL, PCF_DBMS_CLASS_CLIENT, pcf_pgsql_suffixes}, + {DICT_TYPE_SQLITE, PCF_DBMS_CLASS_CLIENT, pcf_sqlite_suffixes}, + {DICT_TYPE_MEMCACHE, PCF_DBMS_CLASS_CLIENT, pcf_memcache_suffixes}, + {DICT_TYPE_REGEXP, PCF_DBMS_CLASS_REGEX}, + {DICT_TYPE_PCRE, PCF_DBMS_CLASS_REGEX}, + {0}, }; + /* + * Workaround to prevent a false warning about "#comment after other text", + * when an inline pcre or regexp pattern contains "#text". + */ +#define PCF_DBMS_RECURSE 1 /* Parse inline {map-entry} */ +#define PCF_DBMS_NO_RECURSE 0 /* Don't parse inline {map-entry} */ + /* pcf_check_dbms_client - look for unused names in client configuration */ static void pcf_check_dbms_client(const PCF_DBMS_INFO *dp, const char *cf_file) @@ -216,7 +233,8 @@ static void pcf_check_dbms_client(const PCF_DBMS_INFO *dp, const char *cf_file) static void pcf_register_dbms_helper(char *str_value, const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *), - PCF_MASTER_ENT *local_scope) + PCF_MASTER_ENT *local_scope, + int recurse) { const PCF_DBMS_INFO *dp; char *db_type; @@ -229,7 +247,8 @@ static void pcf_register_dbms_helper(char *str_value, * Naive parsing. We don't really know if this substring specifies a * database or some other text. */ - while ((db_type = mystrtokq(&str_value, CHARS_COMMA_SP, CHARS_BRACE)) != 0) { + while ((db_type = mystrtokq_cw(&str_value, CHARS_COMMA_SP, CHARS_BRACE, + local_scope ? MASTER_CONF_FILE : MAIN_CONF_FILE)) != 0) { if (*db_type == CHARS_BRACE[0]) { if ((err = extpar(&db_type, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) { /* XXX Encapsulate this in pcf_warn() function. */ @@ -240,7 +259,9 @@ static void pcf_register_dbms_helper(char *str_value, msg_warn("%s: %s", MAIN_CONF_FILE, err); myfree(err); } - pcf_register_dbms_helper(db_type, flag_parameter, local_scope); + if (recurse) + pcf_register_dbms_helper(db_type, flag_parameter, local_scope, + recurse); continue; } @@ -267,7 +288,8 @@ static void pcf_register_dbms_helper(char *str_value, if (*prefix == '/') { for (dp = pcf_dbms_info; dp->db_type != 0; dp++) { if (strcmp(db_type, dp->db_type) == 0) { - pcf_check_dbms_client(dp, prefix); + if (dp->db_class == PCF_DBMS_CLASS_CLIENT) + pcf_check_dbms_client(dp, prefix); break; } } @@ -282,6 +304,8 @@ static void pcf_register_dbms_helper(char *str_value, * local or global namespace. */ if (*prefix != '.') { + int next_recurse = recurse; + if (*prefix == CHARS_BRACE[0]) { if ((err = extpar(&prefix, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) { /* XXX Encapsulate this in pcf_warn() function. */ @@ -293,18 +317,28 @@ static void pcf_register_dbms_helper(char *str_value, msg_warn("%s: %s", MAIN_CONF_FILE, err); myfree(err); } - pcf_register_dbms_helper(prefix, flag_parameter, local_scope); + for (dp = pcf_dbms_info; dp->db_type != 0; dp++) { + if (strcmp(db_type, dp->db_type) == 0) { + if (dp->db_class == PCF_DBMS_CLASS_REGEX) + next_recurse = PCF_DBMS_NO_RECURSE; + break; + } + } + pcf_register_dbms_helper(prefix, flag_parameter, local_scope, + next_recurse); continue; } else { for (dp = pcf_dbms_info; dp->db_type != 0; dp++) { if (strcmp(db_type, dp->db_type) == 0) { - for (cpp = dp->db_suffixes; *cpp; cpp++) { - vstring_sprintf(candidate ? candidate : + if (dp->db_class == PCF_DBMS_CLASS_CLIENT) { + for (cpp = dp->db_suffixes; *cpp; cpp++) { + vstring_sprintf(candidate ? candidate : (candidate = vstring_alloc(30)), - "%s_%s", prefix, *cpp); - flag_parameter(STR(candidate), + "%s_%s", prefix, *cpp); + flag_parameter(STR(candidate), PCF_PARAM_FLAG_DBMS | PCF_PARAM_FLAG_USER, - local_scope); + local_scope); + } } break; } @@ -332,7 +366,7 @@ void pcf_register_dbms_parameters(const char *param_value, buffer = vstring_alloc(100); bufp = pcf_expand_parameter_value(buffer, PCF_SHOW_EVAL, param_value, local_scope); - pcf_register_dbms_helper(bufp, flag_parameter, local_scope); + pcf_register_dbms_helper(bufp, flag_parameter, local_scope, PCF_DBMS_RECURSE); } #endif diff --git a/postfix/src/postconf/test71.ref b/postfix/src/postconf/test71.ref new file mode 100644 index 000000000..1c4be0cae --- /dev/null +++ b/postfix/src/postconf/test71.ref @@ -0,0 +1,8 @@ +./postconf: warning: main.cf: #comment after other text is not allowed: #aaa1 ... +./postconf: warning: main.cf: #comment after other text is not allowed: #aaa2 ... +./postconf: warning: main.cf: #comment after other text is not allowed: #ccc2 ... +./postconf: warning: main.cf: #comment after other text is not allowed: #bbb2 ... +config_directory = . +smtpd_client_restrictions = inline:{ { aaa0 = #aaa1 } #aaa2 } +smtpd_helo_restrictions = pcre:{ { /bbb0 #bbb1/ } #bbb2 } +smtpd_sender_restrictions = regexp:{ { /ccc0 #ccc1/ } #ccc2 } diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index 3ff785875..a3700f568 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -2419,6 +2419,7 @@ myrand.o: myrand.c myrand.o: myrand.h myrand.o: sys_defs.h mystrtok.o: check_arg.h +mystrtok.o: msg.h mystrtok.o: mystrtok.c mystrtok.o: stringops.h mystrtok.o: sys_defs.h diff --git a/postfix/src/util/match_list.c b/postfix/src/util/match_list.c index 924c6b4a8..f8f6b5520 100644 --- a/postfix/src/util/match_list.c +++ b/postfix/src/util/match_list.c @@ -84,6 +84,8 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema /*--*/ /* System library. */ @@ -145,12 +147,8 @@ static ARGV *match_list_parse(MATCH_LIST *match_list, ARGV *pat_list, * If there is an error, implement graceful degradation by inserting a * pseudo table whose lookups fail with a warning message. */ - while ((start = mystrtokq(&bp, delim, CHARS_BRACE)) != 0) { - if (*start == '#') { - msg_warn("%s: comment at end of line is not supported: %s %s", - match_list->pname, start, bp); - break; - } + while ((start = mystrtokq_cw(&bp, delim, CHARS_BRACE, + match_list->pname)) != 0) { for (match = init_match, item = start; *item == '!'; item++) match = !match; if (*item == 0) diff --git a/postfix/src/util/mystrtok.c b/postfix/src/util/mystrtok.c index 85b15f3b3..432719c8f 100644 --- a/postfix/src/util/mystrtok.c +++ b/postfix/src/util/mystrtok.c @@ -18,6 +18,22 @@ /* char *mystrtokdq(bufp, delimiters) /* char **bufp; /* const char *delimiters; +/* +/* char *mystrtok_cw(bufp, delimiters, blame) +/* char **bufp; +/* const char *delimiters; +/* const char *blame; +/* +/* char *mystrtokq_cw(bufp, delimiters, parens, blame) +/* char **bufp; +/* const char *delimiters; +/* const char *parens; +/* const char *blame; +/* +/* char *mystrtokdq_cw(bufp, delimiters, blame) +/* char **bufp; +/* const char *delimiters; +/* const char *blame; /* DESCRIPTION /* mystrtok() splits a buffer on the specified \fIdelimiters\fR. /* Tokens are delimited by runs of delimiters, so this routine @@ -38,6 +54,12 @@ /* /* The result value is the next token, or a null pointer when the /* end of the buffer was reached. +/* +/* mystrtok_cw(), mystrtokq_cw(), and mystrtokdq_cw, log a +/* warning and return null when the result would look like +/* comment. The \fBblame\fR argument provides context for +/* warning messages. Specify a null pointer to disable the +/* comment check. /* LICENSE /* .ad /* .fi @@ -52,20 +74,40 @@ /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema /*--*/ /* System library. */ -#include "sys_defs.h" +#include #include /* Utility library. */ -#include "stringops.h" +#include +#include + +/* mystrtok_warn - warn for #comment after other text */ + +static void mystrtok_warn(const char *start, const char *bufp, const char *blame) +{ + msg_warn("%s: #comment after other text is not allowed: %s %.20s...", + blame, start, bufp); +} + +/* mystrtok - ABI compatibility wrapper */ + +#undef mystrtok + +char *mystrtok(char **src, const char *sep) +{ + return (mystrtok_cw(src, sep, (char *) 0)); +} /* mystrtok - safe tokenizer */ -char *mystrtok(char **src, const char *sep) +char *mystrtok_cw(char **src, const char *sep, const char *blame) { char *start = *src; char *end; @@ -86,12 +128,28 @@ char *mystrtok(char **src, const char *sep) if (*end != 0) *end++ = 0; *src = end; - return (start); + + if (blame && *start == '#') { + mystrtok_warn(start, *src, blame); + return (0); + } else { + return (start); + } } -/* mystrtokq - safe tokenizer with quoting support */ +/* mystrtokq - ABI compatibility wrapper */ + +#undef mystrtokq char *mystrtokq(char **src, const char *sep, const char *parens) +{ + return (mystrtokq_cw(src, sep, parens, (char *) 0)); +} + +/* mystrtokq_cw - safe tokenizer with quoting support */ + +char *mystrtokq_cw(char **src, const char *sep, const char *parens, + const char *blame) { char *start = *src; static char *cp; @@ -121,12 +179,27 @@ char *mystrtokq(char **src, const char *sep, const char *parens) } } *src = cp; - return (start); + + if (blame && *start == '#') { + mystrtok_warn(start, *src, blame); + return (0); + } else { + return (start); + } } -/* mystrtokdq - safe tokenizer, double quote and backslash support */ +/* mystrtokdq - ABI compatibility wrapper */ + +#undef mystrtokdq char *mystrtokdq(char **src, const char *sep) +{ + return (mystrtokdq_cw(src, sep, (char *) 0)); +} + +/* mystrtokdq_cw - safe tokenizer, double quote and backslash support */ + +char *mystrtokdq_cw(char **src, const char *sep, const char *blame) { char *cp = *src; char *start; @@ -157,7 +230,13 @@ char *mystrtokdq(char **src, const char *sep) } } *src = cp; - return (start); + + if (blame && start && *start == '#') { + mystrtok_warn(start, *src, blame); + return (0); + } else { + return (start); + } } #ifdef TEST @@ -195,6 +274,12 @@ static const struct testcase testcases[] = { {"mystrtokdq", " foo\\ bar ", {"foo\\ bar"}}, {"mystrtokdq", " foo \\\" bar", {"foo", "\\\"", "bar"}}, {"mystrtokdq", " foo \" bar baz\" ", {"foo", "\" bar baz\""}}, + {"mystrtok_cw", "#after text"}, + {"mystrtok_cw", "before-text #after text", {"before-text"}}, + {"mystrtokq_cw", "#after text"}, + {"mystrtokq_cw", "{ before text } #after text", "{ before text }"}, + {"mystrtokdq_cw", "#after text"}, + {"mystrtokdq_cw", "\"before text\" #after text", {"\"before text\""}}, }; int main(void) @@ -229,6 +314,12 @@ int main(void) actual = mystrtokq(&cp, CHARS_SPACE, CHARS_BRACE); } else if (strcmp(tp->action, "mystrtokdq") == 0) { actual = mystrtokdq(&cp, CHARS_SPACE); + } else if (strcmp(tp->action, "mystrtok_cw") == 0) { + actual = mystrtok_cw(&cp, CHARS_SPACE, "test"); + } else if (strcmp(tp->action, "mystrtokq_cw") == 0) { + actual = mystrtokq_cw(&cp, CHARS_SPACE, CHARS_BRACE, "test"); + } else if (strcmp(tp->action, "mystrtokdq_cw") == 0) { + actual = mystrtokdq_cw(&cp, CHARS_SPACE, "test"); } else { msg_panic("invalid command: %s", tp->action); } diff --git a/postfix/src/util/mystrtok.ref b/postfix/src/util/mystrtok.ref index 4f920f994..d0ca58a35 100644 --- a/postfix/src/util/mystrtok.ref +++ b/postfix/src/util/mystrtok.ref @@ -28,3 +28,21 @@ unknown: RUN test case 13 mystrtokdq > foo \" bar< unknown: PASS test 13 unknown: RUN test case 14 mystrtokdq > foo " bar baz" < unknown: PASS test 14 +unknown: RUN test case 15 mystrtok_cw >#after text< +unknown: warning: test: #comment after other text is not allowed: #after text... +unknown: PASS test 15 +unknown: RUN test case 16 mystrtok_cw >before-text #after text< +unknown: warning: test: #comment after other text is not allowed: #after text... +unknown: PASS test 16 +unknown: RUN test case 17 mystrtokq_cw >#after text< +unknown: warning: test: #comment after other text is not allowed: #after text... +unknown: PASS test 17 +unknown: RUN test case 18 mystrtokq_cw >{ before text } #after text< +unknown: warning: test: #comment after other text is not allowed: #after text... +unknown: PASS test 18 +unknown: RUN test case 19 mystrtokdq_cw >#after text< +unknown: warning: test: #comment after other text is not allowed: #after text... +unknown: PASS test 19 +unknown: RUN test case 20 mystrtokdq_cw >"before text" #after text< +unknown: warning: test: #comment after other text is not allowed: #after text... +unknown: PASS test 20 diff --git a/postfix/src/util/stringops.h b/postfix/src/util/stringops.h index 8ac177b92..cd9b5753b 100644 --- a/postfix/src/util/stringops.h +++ b/postfix/src/util/stringops.h @@ -31,8 +31,15 @@ extern char *concatenate(const char *,...); extern char *mystrtok(char **, const char *); extern char *mystrtokq(char **, const char *, const char *); extern char *mystrtokdq(char **, const char *); +extern char *mystrtok_cw(char **, const char *, const char *); +extern char *mystrtokq_cw(char **, const char *, const char *, const char *); +extern char *mystrtokdq_cw(char **, const char *, const char *); extern char *translit(char *, const char *, const char *); +#define mystrtok(cp, sp) mystrtok_cw((cp), (sp), (char *) 0) +#define mystrtokq(cp, sp, pp) mystrtokq_cw((cp), (sp), (pp), (char *) 0) +#define mystrtokdq(cp, sp) mystrtokdq_cw((cp), (sp), (char *) 0) + #define printable(string, replacement) \ printable_except((string), (replacement), (char *) 0) @@ -102,6 +109,8 @@ extern int strncasecmp_utf8x(int, const char *, const char *, ssize_t); /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema /*--*/ #endif