1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * util.c: string utility things
19 *
20 * 3/21/93 Rob McCool
21 * 1995-96 Many changes by the Apache Software Foundation
22 *
23 */
24
25/* Debugging aid:
26 * #define DEBUG            to trace all cfg_open*()/cfg_closefile() calls
27 * #define DEBUG_CFG_LINES  to trace every line read from the config files
28 */
29
30#include "apr.h"
31#include "apr_strings.h"
32#include "apr_lib.h"
33
34#define APR_WANT_STDIO
35#define APR_WANT_STRFUNC
36#include "apr_want.h"
37
38#if APR_HAVE_UNISTD_H
39#include <unistd.h>
40#endif
41#if APR_HAVE_NETDB_H
42#include <netdb.h>              /* for gethostbyname() */
43#endif
44
45#define CORE_PRIVATE
46
47#include "ap_config.h"
48#include "apr_base64.h"
49#include "httpd.h"
50#include "http_main.h"
51#include "http_log.h"
52#include "http_protocol.h"
53#include "http_config.h"
54#include "util_ebcdic.h"
55
56#ifdef HAVE_PWD_H
57#include <pwd.h>
58#endif
59#ifdef HAVE_GRP_H
60#include <grp.h>
61#endif
62
63/* A bunch of functions in util.c scan strings looking for certain characters.
64 * To make that more efficient we encode a lookup table.  The test_char_table
65 * is generated automatically by gen_test_char.c.
66 */
67#include "test_char.h"
68
69/* we assume the folks using this ensure 0 <= c < 256... which means
70 * you need a cast to (unsigned char) first, you can't just plug a
71 * char in here and get it to work, because if char is signed then it
72 * will first be sign extended.
73 */
74#define TEST_CHAR(c, f)        (test_char_table[(unsigned)(c)] & (f))
75
76/* Win32/NetWare/OS2 need to check for both forward and back slashes
77 * in ap_getparents() and ap_escape_url.
78 */
79#ifdef CASE_BLIND_FILESYSTEM
80#define IS_SLASH(s) ((s == '/') || (s == '\\'))
81#else
82#define IS_SLASH(s) (s == '/')
83#endif
84
85/* same as APR_SIZE_MAX which doesn't appear until APR 1.3 */
86#define UTIL_SIZE_MAX (~((apr_size_t)0))
87
88/*
89 * Examine a field value (such as a media-/content-type) string and return
90 * it sans any parameters; e.g., strip off any ';charset=foo' and the like.
91 */
92AP_DECLARE(char *) ap_field_noparam(apr_pool_t *p, const char *intype)
93{
94    const char *semi;
95
96    if (intype == NULL) return NULL;
97
98    semi = ap_strchr_c(intype, ';');
99    if (semi == NULL) {
100        return apr_pstrdup(p, intype);
101    }
102    else {
103        while ((semi > intype) && apr_isspace(semi[-1])) {
104            semi--;
105        }
106        return apr_pstrndup(p, intype, semi - intype);
107    }
108}
109
110AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt,
111                              int gmt)
112{
113    apr_size_t retcode;
114    char ts[MAX_STRING_LEN];
115    char tf[MAX_STRING_LEN];
116    apr_time_exp_t xt;
117
118    if (gmt) {
119        const char *f;
120        char *strp;
121
122        apr_time_exp_gmt(&xt, t);
123        /* Convert %Z to "GMT" and %z to "+0000";
124         * on hosts that do not have a time zone string in struct tm,
125         * strftime must assume its argument is local time.
126         */
127        for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f)
128            ; f++, strp++) {
129            if (*f != '%') continue;
130            switch (f[1]) {
131            case '%':
132                *++strp = *++f;
133                break;
134            case 'Z':
135                *strp++ = 'G';
136                *strp++ = 'M';
137                *strp = 'T';
138                f++;
139                break;
140            case 'z': /* common extension */
141                *strp++ = '+';
142                *strp++ = '0';
143                *strp++ = '0';
144                *strp++ = '0';
145                *strp = '0';
146                f++;
147                break;
148            }
149        }
150        *strp = '\0';
151        fmt = tf;
152    }
153    else {
154        apr_time_exp_lt(&xt, t);
155    }
156
157    /* check return code? */
158    apr_strftime(ts, &retcode, MAX_STRING_LEN, fmt, &xt);
159    ts[MAX_STRING_LEN - 1] = '\0';
160    return apr_pstrdup(p, ts);
161}
162
163/* Roy owes Rob beer. */
164/* Rob owes Roy dinner. */
165
166/* These legacy comments would make a lot more sense if Roy hadn't
167 * replaced the old later_than() routine with util_date.c.
168 *
169 * Well, okay, they still wouldn't make any sense.
170 */
171
172/* Match = 0, NoMatch = 1, Abort = -1
173 * Based loosely on sections of wildmat.c by Rich Salz
174 * Hmmm... shouldn't this really go component by component?
175 */
176AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected)
177{
178    int x, y;
179
180    for (x = 0, y = 0; expected[y]; ++y, ++x) {
181        if ((!str[x]) && (expected[y] != '*'))
182            return -1;
183        if (expected[y] == '*') {
184            while (expected[++y] == '*');
185            if (!expected[y])
186                return 0;
187            while (str[x]) {
188                int ret;
189                if ((ret = ap_strcmp_match(&str[x++], &expected[y])) != 1)
190                    return ret;
191            }
192            return -1;
193        }
194        else if ((expected[y] != '?') && (str[x] != expected[y]))
195            return 1;
196    }
197    return (str[x] != '\0');
198}
199
200AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected)
201{
202    int x, y;
203
204    for (x = 0, y = 0; expected[y]; ++y, ++x) {
205        if (!str[x] && expected[y] != '*')
206            return -1;
207        if (expected[y] == '*') {
208            while (expected[++y] == '*');
209            if (!expected[y])
210                return 0;
211            while (str[x]) {
212                int ret;
213                if ((ret = ap_strcasecmp_match(&str[x++], &expected[y])) != 1)
214                    return ret;
215            }
216            return -1;
217        }
218        else if (expected[y] != '?'
219                 && apr_tolower(str[x]) != apr_tolower(expected[y]))
220            return 1;
221    }
222    return (str[x] != '\0');
223}
224
225/* We actually compare the canonical root to this root, (but we don't
226 * waste time checking the case), since every use of this function in
227 * httpd-2.1 tests if the path is 'proper', meaning we've already passed
228 * it through apr_filepath_merge, or we haven't.
229 */
230AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, const char *dir)
231{
232    const char *newpath;
233    const char *ourdir = dir;
234    if (apr_filepath_root(&newpath, &dir, 0, p) != APR_SUCCESS
235            || strncmp(newpath, ourdir, strlen(newpath)) != 0) {
236        return 0;
237    }
238    return 1;
239}
240
241AP_DECLARE(int) ap_is_matchexp(const char *str)
242{
243    register int x;
244
245    for (x = 0; str[x]; x++)
246        if ((str[x] == '*') || (str[x] == '?'))
247            return 1;
248    return 0;
249}
250
251/*
252 * Here's a pool-based interface to the POSIX-esque ap_regcomp().
253 * Note that we return ap_regex_t instead of being passed one.
254 * The reason is that if you use an already-used ap_regex_t structure,
255 * the memory that you've already allocated gets forgotten, and
256 * regfree() doesn't clear it. So we don't allow it.
257 */
258
259static apr_status_t regex_cleanup(void *preg)
260{
261    ap_regfree((ap_regex_t *) preg);
262    return APR_SUCCESS;
263}
264
265AP_DECLARE(ap_regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern,
266                                     int cflags)
267{
268    ap_regex_t *preg = apr_palloc(p, sizeof *preg);
269
270    if (ap_regcomp(preg, pattern, cflags)) {
271        return NULL;
272    }
273
274    apr_pool_cleanup_register(p, (void *) preg, regex_cleanup,
275                              apr_pool_cleanup_null);
276
277    return preg;
278}
279
280AP_DECLARE(void) ap_pregfree(apr_pool_t *p, ap_regex_t *reg)
281{
282    ap_regfree(reg);
283    apr_pool_cleanup_kill(p, (void *) reg, regex_cleanup);
284}
285
286/*
287 * Similar to standard strstr() but we ignore case in this version.
288 * Based on the strstr() implementation further below.
289 */
290AP_DECLARE(char *) ap_strcasestr(const char *s1, const char *s2)
291{
292    char *p1, *p2;
293    if (*s2 == '\0') {
294        /* an empty s2 */
295        return((char *)s1);
296    }
297    while(1) {
298        for ( ; (*s1 != '\0') && (apr_tolower(*s1) != apr_tolower(*s2)); s1++);
299        if (*s1 == '\0') {
300            return(NULL);
301        }
302        /* found first character of s2, see if the rest matches */
303        p1 = (char *)s1;
304        p2 = (char *)s2;
305        for (++p1, ++p2; apr_tolower(*p1) == apr_tolower(*p2); ++p1, ++p2) {
306            if (*p1 == '\0') {
307                /* both strings ended together */
308                return((char *)s1);
309            }
310        }
311        if (*p2 == '\0') {
312            /* second string ended, a match */
313            break;
314        }
315        /* didn't find a match here, try starting at next character in s1 */
316        s1++;
317    }
318    return((char *)s1);
319}
320
321/*
322 * Returns an offsetted pointer in bigstring immediately after
323 * prefix. Returns bigstring if bigstring doesn't start with
324 * prefix or if prefix is longer than bigstring while still matching.
325 * NOTE: pointer returned is relative to bigstring, so we
326 * can use standard pointer comparisons in the calling function
327 * (eg: test if ap_stripprefix(a,b) == a)
328 */
329AP_DECLARE(const char *) ap_stripprefix(const char *bigstring,
330                                        const char *prefix)
331{
332    const char *p1;
333
334    if (*prefix == '\0')
335        return bigstring;
336
337    p1 = bigstring;
338    while (*p1 && *prefix) {
339        if (*p1++ != *prefix++)
340            return bigstring;
341    }
342    if (*prefix == '\0')
343        return p1;
344
345    /* hit the end of bigstring! */
346    return bigstring;
347}
348
349/* This function substitutes for $0-$9, filling in regular expression
350 * submatches. Pass it the same nmatch and pmatch arguments that you
351 * passed ap_regexec(). pmatch should not be greater than the maximum number
352 * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t.
353 *
354 * input should be the string with the $-expressions, source should be the
355 * string that was matched against.
356 *
357 * It returns the substituted string, or NULL on error.
358 *
359 * Parts of this code are based on Henry Spencer's regsub(), from his
360 * AT&T V8 regexp package.
361 */
362
363static apr_status_t regsub_core(apr_pool_t *p, char **result,
364                                const char *input,
365                                const char *source, apr_size_t nmatch,
366                                ap_regmatch_t pmatch[], apr_size_t maxlen)
367{
368    const char *src = input;
369    char *dst;
370    char c;
371    apr_size_t no;
372    apr_size_t len = 0;
373
374    AP_DEBUG_ASSERT(result && p);
375    if (!source || nmatch>AP_MAX_REG_MATCH)
376        return APR_EINVAL;
377    if (!nmatch) {
378        len = strlen(src);
379        if (maxlen > 0 && len >= maxlen)
380            return APR_ENOMEM;
381        *result = apr_pstrmemdup(p, src, len);
382        return APR_SUCCESS;
383    }
384
385    /* First pass, find the size */
386    while ((c = *src++) != '\0') {
387        if (c == '&')
388            no = 0;
389        else if (c == '$' && apr_isdigit(*src))
390            no = *src++ - '0';
391        else
392            no = AP_MAX_REG_MATCH;
393
394        if (no >= AP_MAX_REG_MATCH) {  /* Ordinary character. */
395            if (c == '\\' && (*src == '$' || *src == '&'))
396                src++;
397            len++;
398        }
399        else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
400            if (UTIL_SIZE_MAX - len <= pmatch[no].rm_eo - pmatch[no].rm_so) {
401                ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
402                             "integer overflow or out of memory condition." );
403                return APR_ENOMEM;
404            }
405            len += pmatch[no].rm_eo - pmatch[no].rm_so;
406        }
407
408    }
409
410    if (len >= maxlen && maxlen > 0)
411        return APR_ENOMEM;
412
413    *result = dst = apr_palloc(p, len + 1);
414
415    /* Now actually fill in the string */
416
417    src = input;
418
419    while ((c = *src++) != '\0') {
420        if (c == '&')
421            no = 0;
422        else if (c == '$' && apr_isdigit(*src))
423            no = *src++ - '0';
424        else
425            no = AP_MAX_REG_MATCH;
426
427        if (no >= AP_MAX_REG_MATCH) {  /* Ordinary character. */
428            if (c == '\\' && (*src == '$' || *src == '&'))
429                c = *src++;
430            *dst++ = c;
431        }
432        else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
433            len = pmatch[no].rm_eo - pmatch[no].rm_so;
434            memcpy(dst, source + pmatch[no].rm_so, len);
435            dst += len;
436        }
437
438    }
439    *dst = '\0';
440
441    return APR_SUCCESS;
442}
443
444#ifndef AP_PREGSUB_MAXLEN
445/* No API control so far in this released branch, so make it large */
446#define AP_PREGSUB_MAXLEN   (64 * 1024 * 1024)
447#endif
448AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
449                              const char *source, size_t nmatch,
450                              ap_regmatch_t pmatch[])
451{
452    char *result;
453    apr_status_t rc = regsub_core(p, &result, input, source, nmatch,
454                                  pmatch, AP_PREGSUB_MAXLEN);
455    if (rc != APR_SUCCESS)
456        result = NULL;
457    return result;
458}
459
460AP_DECLARE(apr_status_t) ap_pregsub_ex(apr_pool_t *p, char **result,
461                                       const char *input, const char *source,
462                                       apr_size_t nmatch, ap_regmatch_t pmatch[],
463                                       apr_size_t maxlen)
464{
465    apr_status_t rc = regsub_core(p, result, input, source, nmatch,
466                                  pmatch, maxlen);
467    if (rc != APR_SUCCESS)
468        *result = NULL;
469    return rc;
470}
471
472/*
473 * Parse .. so we don't compromise security
474 */
475AP_DECLARE(void) ap_getparents(char *name)
476{
477    char *next;
478    int l, w, first_dot;
479
480    /* Four paseses, as per RFC 1808 */
481    /* a) remove ./ path segments */
482    for (next = name; *next && (*next != '.'); next++) {
483    }
484
485    l = w = first_dot = next - name;
486    while (name[l] != '\0') {
487        if (name[l] == '.' && IS_SLASH(name[l + 1])
488            && (l == 0 || IS_SLASH(name[l - 1])))
489            l += 2;
490        else
491            name[w++] = name[l++];
492    }
493
494    /* b) remove trailing . path, segment */
495    if (w == 1 && name[0] == '.')
496        w--;
497    else if (w > 1 && name[w - 1] == '.' && IS_SLASH(name[w - 2]))
498        w--;
499    name[w] = '\0';
500
501    /* c) remove all xx/../ segments. (including leading ../ and /../) */
502    l = first_dot;
503
504    while (name[l] != '\0') {
505        if (name[l] == '.' && name[l + 1] == '.' && IS_SLASH(name[l + 2])
506            && (l == 0 || IS_SLASH(name[l - 1]))) {
507            register int m = l + 3, n;
508
509            l = l - 2;
510            if (l >= 0) {
511                while (l >= 0 && !IS_SLASH(name[l]))
512                    l--;
513                l++;
514            }
515            else
516                l = 0;
517            n = l;
518            while ((name[n] = name[m]))
519                (++n, ++m);
520        }
521        else
522            ++l;
523    }
524
525    /* d) remove trailing xx/.. segment. */
526    if (l == 2 && name[0] == '.' && name[1] == '.')
527        name[0] = '\0';
528    else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
529             && IS_SLASH(name[l - 3])) {
530        l = l - 4;
531        if (l >= 0) {
532            while (l >= 0 && !IS_SLASH(name[l]))
533                l--;
534            l++;
535        }
536        else
537            l = 0;
538        name[l] = '\0';
539    }
540}
541
542AP_DECLARE(void) ap_no2slash(char *name)
543{
544    char *d, *s;
545
546    s = d = name;
547
548#ifdef HAVE_UNC_PATHS
549    /* Check for UNC names.  Leave leading two slashes. */
550    if (s[0] == '/' && s[1] == '/')
551        *d++ = *s++;
552#endif
553
554    while (*s) {
555        if ((*d++ = *s) == '/') {
556            do {
557                ++s;
558            } while (*s == '/');
559        }
560        else {
561            ++s;
562        }
563    }
564    *d = '\0';
565}
566
567
568/*
569 * copy at most n leading directories of s into d
570 * d should be at least as large as s plus 1 extra byte
571 * assumes n > 0
572 * the return value is the ever useful pointer to the trailing \0 of d
573 *
574 * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments,
575 * so that if n == 0, "/" is returned in d with n == 1
576 * and s == "e:/test.html", "e:/" is returned in d
577 * *** See also directory_walk in modules/http/http_request.c
578
579 * examples:
580 *    /a/b, 0  ==> /  (true for all platforms)
581 *    /a/b, 1  ==> /
582 *    /a/b, 2  ==> /a/
583 *    /a/b, 3  ==> /a/b/
584 *    /a/b, 4  ==> /a/b/
585 *
586 *    c:/a/b 0 ==> /
587 *    c:/a/b 1 ==> c:/
588 *    c:/a/b 2 ==> c:/a/
589 *    c:/a/b 3 ==> c:/a/b
590 *    c:/a/b 4 ==> c:/a/b
591 */
592AP_DECLARE(char *) ap_make_dirstr_prefix(char *d, const char *s, int n)
593{
594    if (n < 1) {
595        *d = '/';
596        *++d = '\0';
597        return (d);
598    }
599
600    for (;;) {
601        if (*s == '\0' || (*s == '/' && (--n) == 0)) {
602            *d = '/';
603            break;
604        }
605        *d++ = *s++;
606    }
607    *++d = 0;
608    return (d);
609}
610
611
612/*
613 * return the parent directory name including trailing / of the file s
614 */
615AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s)
616{
617    const char *last_slash = ap_strrchr_c(s, '/');
618    char *d;
619    int l;
620
621    if (last_slash == NULL) {
622        return apr_pstrdup(p, "");
623    }
624    l = (last_slash - s) + 1;
625    d = apr_palloc(p, l + 1);
626    memcpy(d, s, l);
627    d[l] = 0;
628    return (d);
629}
630
631
632AP_DECLARE(int) ap_count_dirs(const char *path)
633{
634    register int x, n;
635
636    for (x = 0, n = 0; path[x]; x++)
637        if (path[x] == '/')
638            n++;
639    return n;
640}
641
642AP_DECLARE(char *) ap_getword_nc(apr_pool_t *atrans, char **line, char stop)
643{
644    return ap_getword(atrans, (const char **) line, stop);
645}
646
647AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop)
648{
649    const char *pos = *line;
650    int len;
651    char *res;
652
653    while ((*pos != stop) && *pos) {
654        ++pos;
655    }
656
657    len = pos - *line;
658    res = (char *)apr_palloc(atrans, len + 1);
659    memcpy(res, *line, len);
660    res[len] = 0;
661
662    if (stop) {
663        while (*pos == stop) {
664            ++pos;
665        }
666    }
667    *line = pos;
668
669    return res;
670}
671
672AP_DECLARE(char *) ap_getword_white_nc(apr_pool_t *atrans, char **line)
673{
674    return ap_getword_white(atrans, (const char **) line);
675}
676
677AP_DECLARE(char *) ap_getword_white(apr_pool_t *atrans, const char **line)
678{
679    const char *pos = *line;
680    int len;
681    char *res;
682
683    while (!apr_isspace(*pos) && *pos) {
684        ++pos;
685    }
686
687    len = pos - *line;
688    res = (char *)apr_palloc(atrans, len + 1);
689    memcpy(res, *line, len);
690    res[len] = 0;
691
692    while (apr_isspace(*pos)) {
693        ++pos;
694    }
695
696    *line = pos;
697
698    return res;
699}
700
701AP_DECLARE(char *) ap_getword_nulls_nc(apr_pool_t *atrans, char **line,
702                                       char stop)
703{
704    return ap_getword_nulls(atrans, (const char **) line, stop);
705}
706
707AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line,
708                                    char stop)
709{
710    const char *pos = ap_strchr_c(*line, stop);
711    char *res;
712
713    if (!pos) {
714        res = apr_pstrdup(atrans, *line);
715        *line += strlen(*line);
716        return res;
717    }
718
719    res = apr_pstrndup(atrans, *line, pos - *line);
720
721    ++pos;
722
723    *line = pos;
724
725    return res;
726}
727
728/* Get a word, (new) config-file style --- quoted strings and backslashes
729 * all honored
730 */
731
732static char *substring_conf(apr_pool_t *p, const char *start, int len,
733                            char quote)
734{
735    char *result = apr_palloc(p, len + 2);
736    char *resp = result;
737    int i;
738
739    for (i = 0; i < len; ++i) {
740        if (start[i] == '\\' && (start[i + 1] == '\\'
741                                 || (quote && start[i + 1] == quote)))
742            *resp++ = start[++i];
743        else
744            *resp++ = start[i];
745    }
746
747    *resp++ = '\0';
748#if RESOLVE_ENV_PER_TOKEN
749    return (char *)ap_resolve_env(p,result);
750#else
751    return result;
752#endif
753}
754
755AP_DECLARE(char *) ap_getword_conf_nc(apr_pool_t *p, char **line)
756{
757    return ap_getword_conf(p, (const char **) line);
758}
759
760AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
761{
762    const char *str = *line, *strend;
763    char *res;
764    char quote;
765
766    while (*str && apr_isspace(*str))
767        ++str;
768
769    if (!*str) {
770        *line = str;
771        return "";
772    }
773
774    if ((quote = *str) == '"' || quote == '\'') {
775        strend = str + 1;
776        while (*strend && *strend != quote) {
777            if (*strend == '\\' && strend[1] &&
778                (strend[1] == quote || strend[1] == '\\')) {
779                strend += 2;
780            }
781            else {
782                ++strend;
783            }
784        }
785        res = substring_conf(p, str + 1, strend - str - 1, quote);
786
787        if (*strend == quote)
788            ++strend;
789    }
790    else {
791        strend = str;
792        while (*strend && !apr_isspace(*strend))
793            ++strend;
794
795        res = substring_conf(p, str, strend - str, 0);
796    }
797
798    while (*strend && apr_isspace(*strend))
799        ++strend;
800    *line = strend;
801    return res;
802}
803
804/* Check a string for any ${ENV} environment variable
805 * construct and replace each them by the value of
806 * that environment variable, if it exists. If the
807 * environment value does not exist, leave the ${ENV}
808 * construct alone; it means something else.
809 */
810AP_DECLARE(const char *) ap_resolve_env(apr_pool_t *p, const char * word)
811{
812# define SMALL_EXPANSION 5
813    struct sll {
814        struct sll *next;
815        const char *string;
816        apr_size_t len;
817    } *result, *current, sresult[SMALL_EXPANSION];
818    char *res_buf, *cp;
819    const char *s, *e, *ep;
820    unsigned spc;
821    apr_size_t outlen;
822
823    s = ap_strchr_c(word, '$');
824    if (!s) {
825        return word;
826    }
827
828    /* well, actually something to do */
829    ep = word + strlen(word);
830    spc = 0;
831    result = current = &(sresult[spc++]);
832    current->next = NULL;
833    current->string = word;
834    current->len = s - word;
835    outlen = current->len;
836
837    do {
838        /* prepare next entry */
839        if (current->len) {
840            current->next = (spc < SMALL_EXPANSION)
841                            ? &(sresult[spc++])
842                            : (struct sll *)apr_palloc(p,
843                                                       sizeof(*current->next));
844            current = current->next;
845            current->next = NULL;
846            current->len = 0;
847        }
848
849        if (*s == '$') {
850            if (s[1] == '{' && (e = ap_strchr_c(s, '}'))) {
851                word = getenv(apr_pstrndup(p, s+2, e-s-2));
852                if (word) {
853                    current->string = word;
854                    current->len = strlen(word);
855                    outlen += current->len;
856                }
857                else {
858                    current->string = s;
859                    current->len = e - s + 1;
860                    outlen += current->len;
861                }
862                s = e + 1;
863            }
864            else {
865                current->string = s++;
866                current->len = 1;
867                ++outlen;
868            }
869        }
870        else {
871            word = s;
872            s = ap_strchr_c(s, '$');
873            current->string = word;
874            current->len = s ? s - word : ep - word;
875            outlen += current->len;
876        }
877    } while (s && *s);
878
879    /* assemble result */
880    res_buf = cp = apr_palloc(p, outlen + 1);
881    do {
882        if (result->len) {
883            memcpy(cp, result->string, result->len);
884            cp += result->len;
885        }
886        result = result->next;
887    } while (result);
888    res_buf[outlen] = '\0';
889
890    return res_buf;
891}
892
893AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp)
894{
895#ifdef DEBUG
896    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
897        "Done with config file %s", cfp->name);
898#endif
899    return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
900}
901
902static apr_status_t cfg_close(void *param)
903{
904    apr_file_t *cfp = (apr_file_t *) param;
905    return (apr_file_close(cfp));
906}
907
908static int cfg_getch(void *param)
909{
910    char ch;
911    apr_file_t *cfp = (apr_file_t *) param;
912    if (apr_file_getc(&ch, cfp) == APR_SUCCESS)
913        return ch;
914    return (int)EOF;
915}
916
917static void *cfg_getstr(void *buf, size_t bufsiz, void *param)
918{
919    apr_file_t *cfp = (apr_file_t *) param;
920    apr_status_t rv;
921    rv = apr_file_gets(buf, bufsiz, cfp);
922    if (rv == APR_SUCCESS) {
923        return buf;
924    }
925    return NULL;
926}
927
928/* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */
929AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
930                                          apr_pool_t *p, const char *name)
931{
932    ap_configfile_t *new_cfg;
933    apr_file_t *file = NULL;
934    apr_finfo_t finfo;
935    apr_status_t status;
936#ifdef DEBUG
937    char buf[120];
938#endif
939
940    if (name == NULL) {
941        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
942               "Internal error: pcfg_openfile() called with NULL filename");
943        return APR_EBADF;
944    }
945
946    status = apr_file_open(&file, name, APR_READ | APR_BUFFERED,
947                           APR_OS_DEFAULT, p);
948#ifdef DEBUG
949    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
950                "Opening config file %s (%s)",
951                name, (status != APR_SUCCESS) ?
952                apr_strerror(status, buf, sizeof(buf)) : "successful");
953#endif
954    if (status != APR_SUCCESS)
955        return status;
956
957    status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file);
958    if (status != APR_SUCCESS)
959        return status;
960
961    if (finfo.filetype != APR_REG &&
962#if defined(WIN32) || defined(OS2) || defined(NETWARE)
963        strcasecmp(apr_filepath_name_get(name), "nul") != 0) {
964#else
965        strcmp(name, "/dev/null") != 0) {
966#endif /* WIN32 || OS2 */
967        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
968                     "Access to file %s denied by server: not a regular file",
969                     name);
970        apr_file_close(file);
971        return APR_EBADF;
972    }
973
974#ifdef WIN32
975    /* Some twisted character [no pun intended] at MS decided that a
976     * zero width joiner as the lead wide character would be ideal for
977     * describing Unicode text files.  This was further convoluted to
978     * another MSism that the same character mapped into utf-8, EF BB BF
979     * would signify utf-8 text files.
980     *
981     * Since MS configuration files are all protecting utf-8 encoded
982     * Unicode path, file and resource names, we already have the correct
983     * WinNT encoding.  But at least eat the stupid three bytes up front.
984     */
985    {
986        unsigned char buf[4];
987        apr_size_t len = 3;
988        status = apr_file_read(file, buf, &len);
989        if ((status != APR_SUCCESS) || (len < 3)
990              || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) {
991            apr_off_t zero = 0;
992            apr_file_seek(file, APR_SET, &zero);
993        }
994    }
995#endif
996
997    new_cfg = apr_palloc(p, sizeof(*new_cfg));
998    new_cfg->param = file;
999    new_cfg->name = apr_pstrdup(p, name);
1000    new_cfg->getch = (int (*)(void *)) cfg_getch;
1001    new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr;
1002    new_cfg->close = (int (*)(void *)) cfg_close;
1003    new_cfg->line_number = 0;
1004    *ret_cfg = new_cfg;
1005    return APR_SUCCESS;
1006}
1007
1008
1009/* Allocate a ap_configfile_t handle with user defined functions and params */
1010AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(apr_pool_t *p,
1011                       const char *descr,
1012                       void *param,
1013                       int(*getch)(void *param),
1014                       void *(*getstr) (void *buf, size_t bufsiz, void *param),
1015                       int(*close_func)(void *param))
1016{
1017    ap_configfile_t *new_cfg = apr_palloc(p, sizeof(*new_cfg));
1018#ifdef DEBUG
1019    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1020                 "Opening config handler %s", descr);
1021#endif
1022    new_cfg->param = param;
1023    new_cfg->name = descr;
1024    new_cfg->getch = getch;
1025    new_cfg->getstr = getstr;
1026    new_cfg->close = close_func;
1027    new_cfg->line_number = 0;
1028    return new_cfg;
1029}
1030
1031/* Read one character from a configfile_t */
1032AP_DECLARE(int) ap_cfg_getc(ap_configfile_t *cfp)
1033{
1034    register int ch = cfp->getch(cfp->param);
1035    if (ch == LF)
1036        ++cfp->line_number;
1037    return ch;
1038}
1039
1040/* Read one line from open ap_configfile_t, strip LF, increase line number */
1041/* If custom handler does not define a getstr() function, read char by char */
1042AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
1043{
1044    /* If a "get string" function is defined, use it */
1045    if (cfp->getstr != NULL) {
1046        char *src, *dst;
1047        char *cp;
1048        char *cbuf = buf;
1049        size_t cbufsize = bufsize;
1050
1051        while (1) {
1052            ++cfp->line_number;
1053            if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL)
1054                return 1;
1055
1056            /*
1057             *  check for line continuation,
1058             *  i.e. match [^\\]\\[\r]\n only
1059             */
1060            cp = cbuf;
1061            while (cp < cbuf+cbufsize && *cp != '\0')
1062                cp++;
1063            if (cp > cbuf && cp[-1] == LF) {
1064                cp--;
1065                if (cp > cbuf && cp[-1] == CR)
1066                    cp--;
1067                if (cp > cbuf && cp[-1] == '\\') {
1068                    cp--;
1069                    if (!(cp > cbuf && cp[-1] == '\\')) {
1070                        /*
1071                         * line continuation requested -
1072                         * then remove backslash and continue
1073                         */
1074                        cbufsize -= (cp-cbuf);
1075                        cbuf = cp;
1076                        continue;
1077                    }
1078                    else {
1079                        /*
1080                         * no real continuation because escaped -
1081                         * then just remove escape character
1082                         */
1083                        for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++)
1084                            cp[0] = cp[1];
1085                    }
1086                }
1087            }
1088            break;
1089        }
1090
1091        /*
1092         * Leading and trailing white space is eliminated completely
1093         */
1094        src = buf;
1095        while (apr_isspace(*src))
1096            ++src;
1097        /* blast trailing whitespace */
1098        dst = &src[strlen(src)];
1099        while (--dst >= src && apr_isspace(*dst))
1100            *dst = '\0';
1101        /* Zap leading whitespace by shifting */
1102        if (src != buf)
1103            for (dst = buf; (*dst++ = *src++) != '\0'; )
1104                ;
1105
1106#ifdef DEBUG_CFG_LINES
1107        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Read config: %s", buf);
1108#endif
1109        return 0;
1110    } else {
1111        /* No "get string" function defined; read character by character */
1112        register int c;
1113        register size_t i = 0;
1114
1115        buf[0] = '\0';
1116        /* skip leading whitespace */
1117        do {
1118            c = cfp->getch(cfp->param);
1119        } while (c == '\t' || c == ' ');
1120
1121        if (c == EOF)
1122            return 1;
1123
1124        if(bufsize < 2) {
1125            /* too small, assume caller is crazy */
1126            return 1;
1127        }
1128
1129        while (1) {
1130            if ((c == '\t') || (c == ' ')) {
1131                buf[i++] = ' ';
1132                while ((c == '\t') || (c == ' '))
1133                    c = cfp->getch(cfp->param);
1134            }
1135            if (c == CR) {
1136                /* silently ignore CR (_assume_ that a LF follows) */
1137                c = cfp->getch(cfp->param);
1138            }
1139            if (c == LF) {
1140                /* increase line number and return on LF */
1141                ++cfp->line_number;
1142            }
1143            if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) {
1144                /*
1145                 *  check for line continuation
1146                 */
1147                if (i > 0 && buf[i-1] == '\\') {
1148                    i--;
1149                    if (!(i > 0 && buf[i-1] == '\\')) {
1150                        /* line is continued */
1151                        c = cfp->getch(cfp->param);
1152                        continue;
1153                    }
1154                    /* else nothing needs be done because
1155                     * then the backslash is escaped and
1156                     * we just strip to a single one
1157                     */
1158                }
1159                /* blast trailing whitespace */
1160                while (i > 0 && apr_isspace(buf[i - 1]))
1161                    --i;
1162                buf[i] = '\0';
1163#ifdef DEBUG_CFG_LINES
1164                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1165                             "Read config: %s", buf);
1166#endif
1167                return 0;
1168            }
1169            buf[i] = c;
1170            ++i;
1171            c = cfp->getch(cfp->param);
1172        }
1173    }
1174}
1175
1176/* Size an HTTP header field list item, as separated by a comma.
1177 * The return value is a pointer to the beginning of the non-empty list item
1178 * within the original string (or NULL if there is none) and the address
1179 * of field is shifted to the next non-comma, non-whitespace character.
1180 * len is the length of the item excluding any beginning whitespace.
1181 */
1182AP_DECLARE(const char *) ap_size_list_item(const char **field, int *len)
1183{
1184    const unsigned char *ptr = (const unsigned char *)*field;
1185    const unsigned char *token;
1186    int in_qpair, in_qstr, in_com;
1187
1188    /* Find first non-comma, non-whitespace byte */
1189
1190    while (*ptr == ',' || apr_isspace(*ptr))
1191        ++ptr;
1192
1193    token = ptr;
1194
1195    /* Find the end of this item, skipping over dead bits */
1196
1197    for (in_qpair = in_qstr = in_com = 0;
1198         *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1199         ++ptr) {
1200
1201        if (in_qpair) {
1202            in_qpair = 0;
1203        }
1204        else {
1205            switch (*ptr) {
1206                case '\\': in_qpair = 1;      /* quoted-pair         */
1207                           break;
1208                case '"' : if (!in_com)       /* quoted string delim */
1209                               in_qstr = !in_qstr;
1210                           break;
1211                case '(' : if (!in_qstr)      /* comment (may nest)  */
1212                               ++in_com;
1213                           break;
1214                case ')' : if (in_com)        /* end comment         */
1215                               --in_com;
1216                           break;
1217                default  : break;
1218            }
1219        }
1220    }
1221
1222    if ((*len = (ptr - token)) == 0) {
1223        *field = (const char *)ptr;
1224        return NULL;
1225    }
1226
1227    /* Advance field pointer to the next non-comma, non-white byte */
1228
1229    while (*ptr == ',' || apr_isspace(*ptr))
1230        ++ptr;
1231
1232    *field = (const char *)ptr;
1233    return (const char *)token;
1234}
1235
1236/* Retrieve an HTTP header field list item, as separated by a comma,
1237 * while stripping insignificant whitespace and lowercasing anything not in
1238 * a quoted string or comment.  The return value is a new string containing
1239 * the converted list item (or NULL if none) and the address pointed to by
1240 * field is shifted to the next non-comma, non-whitespace.
1241 */
1242AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, const char **field)
1243{
1244    const char *tok_start;
1245    const unsigned char *ptr;
1246    unsigned char *pos;
1247    char *token;
1248    int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0;
1249
1250    /* Find the beginning and maximum length of the list item so that
1251     * we can allocate a buffer for the new string and reset the field.
1252     */
1253    if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) {
1254        return NULL;
1255    }
1256    token = apr_palloc(p, tok_len + 1);
1257
1258    /* Scan the token again, but this time copy only the good bytes.
1259     * We skip extra whitespace and any whitespace around a '=', '/',
1260     * or ';' and lowercase normal characters not within a comment,
1261     * quoted-string or quoted-pair.
1262     */
1263    for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token;
1264         *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1265         ++ptr) {
1266
1267        if (in_qpair) {
1268            in_qpair = 0;
1269            *pos++ = *ptr;
1270        }
1271        else {
1272            switch (*ptr) {
1273                case '\\': in_qpair = 1;
1274                           if (addspace == 1)
1275                               *pos++ = ' ';
1276                           *pos++ = *ptr;
1277                           addspace = 0;
1278                           break;
1279                case '"' : if (!in_com)
1280                               in_qstr = !in_qstr;
1281                           if (addspace == 1)
1282                               *pos++ = ' ';
1283                           *pos++ = *ptr;
1284                           addspace = 0;
1285                           break;
1286                case '(' : if (!in_qstr)
1287                               ++in_com;
1288                           if (addspace == 1)
1289                               *pos++ = ' ';
1290                           *pos++ = *ptr;
1291                           addspace = 0;
1292                           break;
1293                case ')' : if (in_com)
1294                               --in_com;
1295                           *pos++ = *ptr;
1296                           addspace = 0;
1297                           break;
1298                case ' ' :
1299                case '\t': if (addspace)
1300                               break;
1301                           if (in_com || in_qstr)
1302                               *pos++ = *ptr;
1303                           else
1304                               addspace = 1;
1305                           break;
1306                case '=' :
1307                case '/' :
1308                case ';' : if (!(in_com || in_qstr))
1309                               addspace = -1;
1310                           *pos++ = *ptr;
1311                           break;
1312                default  : if (addspace == 1)
1313                               *pos++ = ' ';
1314                           *pos++ = (in_com || in_qstr) ? *ptr
1315                                                        : apr_tolower(*ptr);
1316                           addspace = 0;
1317                           break;
1318            }
1319        }
1320    }
1321    *pos = '\0';
1322
1323    return token;
1324}
1325
1326/* Find an item in canonical form (lowercase, no extra spaces) within
1327 * an HTTP field value list.  Returns 1 if found, 0 if not found.
1328 * This would be much more efficient if we stored header fields as
1329 * an array of list items as they are received instead of a plain string.
1330 */
1331AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
1332                                  const char *tok)
1333{
1334    const unsigned char *pos;
1335    const unsigned char *ptr = (const unsigned char *)line;
1336    int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0;
1337
1338    if (!line || !tok)
1339        return 0;
1340
1341    do {  /* loop for each item in line's list */
1342
1343        /* Find first non-comma, non-whitespace byte */
1344
1345        while (*ptr == ',' || apr_isspace(*ptr))
1346            ++ptr;
1347
1348        if (*ptr)
1349            good = 1;  /* until proven otherwise for this item */
1350        else
1351            break;     /* no items left and nothing good found */
1352
1353        /* We skip extra whitespace and any whitespace around a '=', '/',
1354         * or ';' and lowercase normal characters not within a comment,
1355         * quoted-string or quoted-pair.
1356         */
1357        for (pos = (const unsigned char *)tok;
1358             *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1359             ++ptr) {
1360
1361            if (in_qpair) {
1362                in_qpair = 0;
1363                if (good)
1364                    good = (*pos++ == *ptr);
1365            }
1366            else {
1367                switch (*ptr) {
1368                    case '\\': in_qpair = 1;
1369                               if (addspace == 1)
1370                                   good = good && (*pos++ == ' ');
1371                               good = good && (*pos++ == *ptr);
1372                               addspace = 0;
1373                               break;
1374                    case '"' : if (!in_com)
1375                                   in_qstr = !in_qstr;
1376                               if (addspace == 1)
1377                                   good = good && (*pos++ == ' ');
1378                               good = good && (*pos++ == *ptr);
1379                               addspace = 0;
1380                               break;
1381                    case '(' : if (!in_qstr)
1382                                   ++in_com;
1383                               if (addspace == 1)
1384                                   good = good && (*pos++ == ' ');
1385                               good = good && (*pos++ == *ptr);
1386                               addspace = 0;
1387                               break;
1388                    case ')' : if (in_com)
1389                                   --in_com;
1390                               good = good && (*pos++ == *ptr);
1391                               addspace = 0;
1392                               break;
1393                    case ' ' :
1394                    case '\t': if (addspace || !good)
1395                                   break;
1396                               if (in_com || in_qstr)
1397                                   good = (*pos++ == *ptr);
1398                               else
1399                                   addspace = 1;
1400                               break;
1401                    case '=' :
1402                    case '/' :
1403                    case ';' : if (!(in_com || in_qstr))
1404                                   addspace = -1;
1405                               good = good && (*pos++ == *ptr);
1406                               break;
1407                    default  : if (!good)
1408                                   break;
1409                               if (addspace == 1)
1410                                   good = (*pos++ == ' ');
1411                               if (in_com || in_qstr)
1412                                   good = good && (*pos++ == *ptr);
1413                               else
1414                                   good = good && (*pos++ == apr_tolower(*ptr));
1415                               addspace = 0;
1416                               break;
1417                }
1418            }
1419        }
1420        if (good && *pos)
1421            good = 0;          /* not good if only a prefix was matched */
1422
1423    } while (*ptr && !good);
1424
1425    return good;
1426}
1427
1428
1429/* Retrieve a token, spacing over it and returning a pointer to
1430 * the first non-white byte afterwards.  Note that these tokens
1431 * are delimited by semis and commas; and can also be delimited
1432 * by whitespace at the caller's option.
1433 */
1434
1435AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line,
1436                                int accept_white)
1437{
1438    const char *ptr = *accept_line;
1439    const char *tok_start;
1440    char *token;
1441    int tok_len;
1442
1443    /* Find first non-white byte */
1444
1445    while (*ptr && apr_isspace(*ptr))
1446        ++ptr;
1447
1448    tok_start = ptr;
1449
1450    /* find token end, skipping over quoted strings.
1451     * (comments are already gone).
1452     */
1453
1454    while (*ptr && (accept_white || !apr_isspace(*ptr))
1455           && *ptr != ';' && *ptr != ',') {
1456        if (*ptr++ == '"')
1457            while (*ptr)
1458                if (*ptr++ == '"')
1459                    break;
1460    }
1461
1462    tok_len = ptr - tok_start;
1463    token = apr_pstrndup(p, tok_start, tok_len);
1464
1465    /* Advance accept_line pointer to the next non-white byte */
1466
1467    while (*ptr && apr_isspace(*ptr))
1468        ++ptr;
1469
1470    *accept_line = ptr;
1471    return token;
1472}
1473
1474
1475/* find http tokens, see the definition of token from RFC2068 */
1476AP_DECLARE(int) ap_find_token(apr_pool_t *p, const char *line, const char *tok)
1477{
1478    const unsigned char *start_token;
1479    const unsigned char *s;
1480
1481    if (!line)
1482        return 0;
1483
1484    s = (const unsigned char *)line;
1485    for (;;) {
1486        /* find start of token, skip all stop characters, note NUL
1487         * isn't a token stop, so we don't need to test for it
1488         */
1489        while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
1490            ++s;
1491        }
1492        if (!*s) {
1493            return 0;
1494        }
1495        start_token = s;
1496        /* find end of the token */
1497        while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
1498            ++s;
1499        }
1500        if (!strncasecmp((const char *)start_token, (const char *)tok,
1501                         s - start_token)) {
1502            return 1;
1503        }
1504        if (!*s) {
1505            return 0;
1506        }
1507    }
1508}
1509
1510
1511AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line,
1512                                   const char *tok)
1513{
1514    int llen, tlen, lidx;
1515
1516    if (!line)
1517        return 0;
1518
1519    llen = strlen(line);
1520    tlen = strlen(tok);
1521    lidx = llen - tlen;
1522
1523    if (lidx < 0 ||
1524        (lidx > 0 && !(apr_isspace(line[lidx - 1]) || line[lidx - 1] == ',')))
1525        return 0;
1526
1527    return (strncasecmp(&line[lidx], tok, tlen) == 0);
1528}
1529
1530AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t *p, const char *str)
1531{
1532    char *cmd;
1533    unsigned char *d;
1534    const unsigned char *s;
1535
1536    cmd = apr_palloc(p, 2 * strlen(str) + 1);        /* Be safe */
1537    d = (unsigned char *)cmd;
1538    s = (const unsigned char *)str;
1539    for (; *s; ++s) {
1540
1541#if defined(OS2) || defined(WIN32)
1542        /*
1543         * Newlines to Win32/OS2 CreateProcess() are ill advised.
1544         * Convert them to spaces since they are effectively white
1545         * space to most applications
1546         */
1547        if (*s == '\r' || *s == '\n') {
1548             *d++ = ' ';
1549             continue;
1550         }
1551#endif
1552
1553        if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
1554            *d++ = '\\';
1555        }
1556        *d++ = *s;
1557    }
1558    *d = '\0';
1559
1560    return cmd;
1561}
1562
1563static char x2c(const char *what)
1564{
1565    register char digit;
1566
1567#if !APR_CHARSET_EBCDIC
1568    digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10
1569             : (what[0] - '0'));
1570    digit *= 16;
1571    digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10
1572              : (what[1] - '0'));
1573#else /*APR_CHARSET_EBCDIC*/
1574    char xstr[5];
1575    xstr[0]='0';
1576    xstr[1]='x';
1577    xstr[2]=what[0];
1578    xstr[3]=what[1];
1579    xstr[4]='\0';
1580    digit = apr_xlate_conv_byte(ap_hdrs_from_ascii,
1581                                0xFF & strtol(xstr, NULL, 16));
1582#endif /*APR_CHARSET_EBCDIC*/
1583    return (digit);
1584}
1585
1586/*
1587 * Unescapes a URL.
1588 * Returns 0 on success, non-zero on error
1589 * Failure is due to
1590 *   bad % escape       returns HTTP_BAD_REQUEST
1591 *
1592 *   decoding %00 -> \0  (the null character)
1593 *   decoding %2f -> /   (a special character)
1594 *                      returns HTTP_NOT_FOUND
1595 */
1596AP_DECLARE(int) ap_unescape_url(char *url)
1597{
1598    register int badesc, badpath;
1599    char *x, *y;
1600
1601    badesc = 0;
1602    badpath = 0;
1603    /* Initial scan for first '%'. Don't bother writing values before
1604     * seeing a '%' */
1605    y = strchr(url, '%');
1606    if (y == NULL) {
1607        return OK;
1608    }
1609    for (x = y; *y; ++x, ++y) {
1610        if (*y != '%')
1611            *x = *y;
1612        else {
1613            if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
1614                badesc = 1;
1615                *x = '%';
1616            }
1617            else {
1618                *x = x2c(y + 1);
1619                y += 2;
1620                if (IS_SLASH(*x) || *x == '\0')
1621                    badpath = 1;
1622            }
1623        }
1624    }
1625    *x = '\0';
1626    if (badesc)
1627        return HTTP_BAD_REQUEST;
1628    else if (badpath)
1629        return HTTP_NOT_FOUND;
1630    else
1631        return OK;
1632}
1633
1634AP_DECLARE(int) ap_unescape_url_keep2f_ex(char *url, int decode_2f)
1635{
1636    register int badesc, badpath;
1637    char *x, *y;
1638
1639    badesc = 0;
1640    badpath = 0;
1641    /* Initial scan for first '%'. Don't bother writing values before
1642     * seeing a '%' */
1643    y = strchr(url, '%');
1644    if (y == NULL) {
1645        return OK;
1646    }
1647    for (x = y; *y; ++x, ++y) {
1648        if (*y != '%') {
1649            *x = *y;
1650        }
1651        else {
1652            if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
1653                badesc = 1;
1654                *x = '%';
1655            }
1656            else {
1657                char decoded;
1658                decoded = x2c(y + 1);
1659                if (decoded == '\0') {
1660                    badpath = 1;
1661                }
1662                else if (IS_SLASH(decoded) && !decode_2f) {
1663                    /* do not decode, just let it go by as-is */
1664                    *x = *y;
1665                }
1666                else {
1667                    *x = decoded;
1668                    y += 2;
1669                }
1670            }
1671        }
1672    }
1673    *x = '\0';
1674    if (badesc) {
1675        return HTTP_BAD_REQUEST;
1676    }
1677    else if (badpath) {
1678        return HTTP_NOT_FOUND;
1679    }
1680    else {
1681        return OK;
1682    }
1683}
1684
1685AP_DECLARE(int) ap_unescape_url_keep2f(char *url)
1686{
1687    return ap_unescape_url_keep2f_ex(url, 1);
1688}
1689
1690AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
1691                                       apr_port_t port, const request_rec *r)
1692{
1693    if (ap_is_default_port(port, r)) {
1694        return apr_pstrdup(p, hostname);
1695    }
1696    else {
1697        return apr_psprintf(p, "%s:%u", hostname, port);
1698    }
1699}
1700
1701/* c2x takes an unsigned, and expects the caller has guaranteed that
1702 * 0 <= what < 256... which usually means that you have to cast to
1703 * unsigned char first, because (unsigned)(char)(x) first goes through
1704 * signed extension to an int before the unsigned cast.
1705 *
1706 * The reason for this assumption is to assist gcc code generation --
1707 * the unsigned char -> unsigned extension is already done earlier in
1708 * both uses of this code, so there's no need to waste time doing it
1709 * again.
1710 */
1711static const char c2x_table[] = "0123456789abcdef";
1712
1713static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
1714                                     unsigned char *where)
1715{
1716#if APR_CHARSET_EBCDIC
1717    what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what);
1718#endif /*APR_CHARSET_EBCDIC*/
1719    *where++ = prefix;
1720    *where++ = c2x_table[what >> 4];
1721    *where++ = c2x_table[what & 0xf];
1722    return where;
1723}
1724
1725/*
1726 * escape_path_segment() escapes a path segment, as defined in RFC 1808. This
1727 * routine is (should be) OS independent.
1728 *
1729 * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all
1730 * cases if a ':' occurs before the first '/' in the URL, the URL should be
1731 * prefixed with "./" (or the ':' escaped). In the case of Unix, this means
1732 * leaving '/' alone, but otherwise doing what escape_path_segment() does. For
1733 * efficiency reasons, we don't use escape_path_segment(), which is provided for
1734 * reference. Again, RFC 1808 is where this stuff is defined.
1735 *
1736 * If partial is set, os_escape_path() assumes that the path will be appended to
1737 * something with a '/' in it (and thus does not prefix "./").
1738 */
1739
1740AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
1741{
1742    char *copy = apr_palloc(p, 3 * strlen(segment) + 1);
1743    const unsigned char *s = (const unsigned char *)segment;
1744    unsigned char *d = (unsigned char *)copy;
1745    unsigned c;
1746
1747    while ((c = *s)) {
1748        if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
1749            d = c2x(c, '%', d);
1750        }
1751        else {
1752            *d++ = c;
1753        }
1754        ++s;
1755    }
1756    *d = '\0';
1757    return copy;
1758}
1759
1760AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partial)
1761{
1762    char *copy = apr_palloc(p, 3 * strlen(path) + 3);
1763    const unsigned char *s = (const unsigned char *)path;
1764    unsigned char *d = (unsigned char *)copy;
1765    unsigned c;
1766
1767    if (!partial) {
1768        const char *colon = ap_strchr_c(path, ':');
1769        const char *slash = ap_strchr_c(path, '/');
1770
1771        if (colon && (!slash || colon < slash)) {
1772            *d++ = '.';
1773            *d++ = '/';
1774        }
1775    }
1776    while ((c = *s)) {
1777        if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
1778            d = c2x(c, '%', d);
1779        }
1780        else {
1781            *d++ = c;
1782        }
1783        ++s;
1784    }
1785    *d = '\0';
1786    return copy;
1787}
1788
1789/* ap_escape_uri is now a macro for os_escape_path */
1790
1791AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
1792{
1793    int i, j;
1794    char *x;
1795
1796    /* first, count the number of extra characters */
1797    for (i = 0, j = 0; s[i] != '\0'; i++)
1798        if (s[i] == '<' || s[i] == '>')
1799            j += 3;
1800        else if (s[i] == '&')
1801            j += 4;
1802        else if (s[i] == '"')
1803            j += 5;
1804        else if (toasc && !apr_isascii(s[i]))
1805            j += 5;
1806
1807    if (j == 0)
1808        return apr_pstrmemdup(p, s, i);
1809
1810    x = apr_palloc(p, i + j + 1);
1811    for (i = 0, j = 0; s[i] != '\0'; i++, j++)
1812        if (s[i] == '<') {
1813            memcpy(&x[j], "&lt;", 4);
1814            j += 3;
1815        }
1816        else if (s[i] == '>') {
1817            memcpy(&x[j], "&gt;", 4);
1818            j += 3;
1819        }
1820        else if (s[i] == '&') {
1821            memcpy(&x[j], "&amp;", 5);
1822            j += 4;
1823        }
1824        else if (s[i] == '"') {
1825            memcpy(&x[j], "&quot;", 6);
1826            j += 5;
1827        }
1828        else if (toasc && !apr_isascii(s[i])) {
1829            char *esc = apr_psprintf(p, "&#%3.3d;", (unsigned char)s[i]);
1830            memcpy(&x[j], esc, 6);
1831            j += 5;
1832        }
1833        else
1834            x[j] = s[i];
1835
1836    x[j] = '\0';
1837    return x;
1838}
1839AP_DECLARE(char *) ap_escape_html(apr_pool_t *p, const char *s)
1840{
1841    return ap_escape_html2(p, s, 0);
1842}
1843AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str)
1844{
1845    char *ret;
1846    unsigned char *d;
1847    const unsigned char *s;
1848    apr_size_t length, escapes = 0;
1849
1850    if (!str) {
1851        return NULL;
1852    }
1853
1854    /* Compute how many characters need to be escaped */
1855    s = (const unsigned char *)str;
1856    for (; *s; ++s) {
1857        if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
1858            escapes++;
1859        }
1860    }
1861
1862    /* Compute the length of the input string, including NULL */
1863    length = s - (const unsigned char *)str + 1;
1864
1865    /* Fast path: nothing to escape */
1866    if (escapes == 0) {
1867        return apr_pmemdup(p, str, length);
1868    }
1869
1870    /* Each escaped character needs up to 3 extra bytes (0 --> \x00) */
1871    ret = apr_palloc(p, length + 3 * escapes);
1872    d = (unsigned char *)ret;
1873    s = (const unsigned char *)str;
1874    for (; *s; ++s) {
1875        if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
1876            *d++ = '\\';
1877            switch(*s) {
1878            case '\b':
1879                *d++ = 'b';
1880                break;
1881            case '\n':
1882                *d++ = 'n';
1883                break;
1884            case '\r':
1885                *d++ = 'r';
1886                break;
1887            case '\t':
1888                *d++ = 't';
1889                break;
1890            case '\v':
1891                *d++ = 'v';
1892                break;
1893            case '\\':
1894            case '"':
1895                *d++ = *s;
1896                break;
1897            default:
1898                c2x(*s, 'x', d);
1899                d += 3;
1900            }
1901        }
1902        else {
1903            *d++ = *s;
1904        }
1905    }
1906    *d = '\0';
1907
1908    return ret;
1909}
1910
1911AP_DECLARE(apr_size_t) ap_escape_errorlog_item(char *dest, const char *source,
1912                                               apr_size_t buflen)
1913{
1914    unsigned char *d, *ep;
1915    const unsigned char *s;
1916
1917    if (!source || !buflen) { /* be safe */
1918        return 0;
1919    }
1920
1921    d = (unsigned char *)dest;
1922    s = (const unsigned char *)source;
1923    ep = d + buflen - 1;
1924
1925    for (; d < ep && *s; ++s) {
1926
1927        if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
1928            *d++ = '\\';
1929            if (d >= ep) {
1930                --d;
1931                break;
1932            }
1933
1934            switch(*s) {
1935            case '\b':
1936                *d++ = 'b';
1937                break;
1938            case '\n':
1939                *d++ = 'n';
1940                break;
1941            case '\r':
1942                *d++ = 'r';
1943                break;
1944            case '\t':
1945                *d++ = 't';
1946                break;
1947            case '\v':
1948                *d++ = 'v';
1949                break;
1950            case '\\':
1951                *d++ = *s;
1952                break;
1953            case '"': /* no need for this in error log */
1954                d[-1] = *s;
1955                break;
1956            default:
1957                if (d >= ep - 2) {
1958                    ep = --d; /* break the for loop as well */
1959                    break;
1960                }
1961                c2x(*s, 'x', d);
1962                d += 3;
1963            }
1964        }
1965        else {
1966            *d++ = *s;
1967        }
1968    }
1969    *d = '\0';
1970
1971    return (d - (unsigned char *)dest);
1972}
1973
1974AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path)
1975{
1976    apr_finfo_t finfo;
1977
1978    if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS)
1979        return 0;                /* in error condition, just return no */
1980
1981    return (finfo.filetype == APR_DIR);
1982}
1983
1984AP_DECLARE(int) ap_is_rdirectory(apr_pool_t *p, const char *path)
1985{
1986    apr_finfo_t finfo;
1987
1988    if (apr_stat(&finfo, path, APR_FINFO_LINK | APR_FINFO_TYPE, p) != APR_SUCCESS)
1989        return 0;                /* in error condition, just return no */
1990
1991    return (finfo.filetype == APR_DIR);
1992}
1993
1994AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1,
1995                                  const char *src2)
1996{
1997    apr_size_t len1, len2;
1998    char *path;
1999
2000    len1 = strlen(src1);
2001    len2 = strlen(src2);
2002     /* allocate +3 for '/' delimiter, trailing NULL and overallocate
2003      * one extra byte to allow the caller to add a trailing '/'
2004      */
2005    path = (char *)apr_palloc(a, len1 + len2 + 3);
2006    if (len1 == 0) {
2007        *path = '/';
2008        memcpy(path + 1, src2, len2 + 1);
2009    }
2010    else {
2011        char *next;
2012        memcpy(path, src1, len1);
2013        next = path + len1;
2014        if (next[-1] != '/') {
2015            *next++ = '/';
2016        }
2017        memcpy(next, src2, len2 + 1);
2018    }
2019    return path;
2020}
2021
2022/*
2023 * Check for an absoluteURI syntax (see section 3.2 in RFC2068).
2024 */
2025AP_DECLARE(int) ap_is_url(const char *u)
2026{
2027    register int x;
2028
2029    for (x = 0; u[x] != ':'; x++) {
2030        if ((!u[x]) ||
2031            ((!apr_isalpha(u[x])) && (!apr_isdigit(u[x])) &&
2032             (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) {
2033            return 0;
2034        }
2035    }
2036
2037    return (x ? 1 : 0);                /* If the first character is ':', it's broken, too */
2038}
2039
2040AP_DECLARE(int) ap_ind(const char *s, char c)
2041{
2042    const char *p = ap_strchr_c(s, c);
2043
2044    if (p == NULL)
2045        return -1;
2046    return p - s;
2047}
2048
2049AP_DECLARE(int) ap_rind(const char *s, char c)
2050{
2051    const char *p = ap_strrchr_c(s, c);
2052
2053    if (p == NULL)
2054        return -1;
2055    return p - s;
2056}
2057
2058AP_DECLARE(void) ap_str_tolower(char *str)
2059{
2060    while (*str) {
2061        *str = apr_tolower(*str);
2062        ++str;
2063    }
2064}
2065
2066/*
2067 * We must return a FQDN
2068 */
2069char *ap_get_local_host(apr_pool_t *a)
2070{
2071#ifndef MAXHOSTNAMELEN
2072#define MAXHOSTNAMELEN 256
2073#endif
2074    char str[MAXHOSTNAMELEN + 1];
2075    char *server_hostname = NULL;
2076    apr_sockaddr_t *sockaddr;
2077    char *hostname;
2078
2079    if (apr_gethostname(str, sizeof(str) - 1, a) != APR_SUCCESS) {
2080        ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a,
2081                     "%s: apr_gethostname() failed to determine ServerName",
2082                     ap_server_argv0);
2083    } else {
2084        str[sizeof(str) - 1] = '\0';
2085        if (apr_sockaddr_info_get(&sockaddr, str, APR_UNSPEC, 0, 0, a) == APR_SUCCESS) {
2086            if ( (apr_getnameinfo(&hostname, sockaddr, 0) == APR_SUCCESS) &&
2087                (ap_strchr_c(hostname, '.')) ) {
2088                server_hostname = apr_pstrdup(a, hostname);
2089                return server_hostname;
2090            } else if (ap_strchr_c(str, '.')) {
2091                server_hostname = apr_pstrdup(a, str);
2092            } else {
2093                apr_sockaddr_ip_get(&hostname, sockaddr);
2094                server_hostname = apr_pstrdup(a, hostname);
2095            }
2096        } else {
2097            ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a,
2098                         "%s: apr_sockaddr_info_get() failed for %s",
2099                         ap_server_argv0, str);
2100        }
2101    }
2102
2103    if (!server_hostname)
2104        server_hostname = apr_pstrdup(a, "127.0.0.1");
2105
2106    ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a,
2107                 "%s: Could not reliably determine the server's fully qualified "
2108                 "domain name, using %s for ServerName",
2109                 ap_server_argv0, server_hostname);
2110
2111    return server_hostname;
2112}
2113
2114/* simple 'pool' alloc()ing glue to apr_base64.c
2115 */
2116AP_DECLARE(char *) ap_pbase64decode(apr_pool_t *p, const char *bufcoded)
2117{
2118    char *decoded;
2119    int l;
2120
2121    decoded = (char *) apr_palloc(p, 1 + apr_base64_decode_len(bufcoded));
2122    l = apr_base64_decode(decoded, bufcoded);
2123    decoded[l] = '\0'; /* make binary sequence into string */
2124
2125    return decoded;
2126}
2127
2128AP_DECLARE(char *) ap_pbase64encode(apr_pool_t *p, char *string)
2129{
2130    char *encoded;
2131    int l = strlen(string);
2132
2133    encoded = (char *) apr_palloc(p, 1 + apr_base64_encode_len(l));
2134    l = apr_base64_encode(encoded, string, l);
2135    encoded[l] = '\0'; /* make binary sequence into string */
2136
2137    return encoded;
2138}
2139
2140/* we want to downcase the type/subtype for comparison purposes
2141 * but nothing else because ;parameter=foo values are case sensitive.
2142 * XXX: in truth we want to downcase parameter names... but really,
2143 * apache has never handled parameters and such correctly.  You
2144 * also need to compress spaces and such to be able to compare
2145 * properly. -djg
2146 */
2147AP_DECLARE(void) ap_content_type_tolower(char *str)
2148{
2149    char *semi;
2150
2151    semi = strchr(str, ';');
2152    if (semi) {
2153        *semi = '\0';
2154    }
2155
2156    ap_str_tolower(str);
2157
2158    if (semi) {
2159        *semi = ';';
2160    }
2161}
2162
2163/*
2164 * Given a string, replace any bare " with \" .
2165 */
2166AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring)
2167{
2168    int newlen = 0;
2169    const char *inchr = instring;
2170    char *outchr, *outstring;
2171
2172    /*
2173     * Look through the input string, jogging the length of the output
2174     * string up by an extra byte each time we find an unescaped ".
2175     */
2176    while (*inchr != '\0') {
2177        newlen++;
2178        if (*inchr == '"') {
2179            newlen++;
2180        }
2181        /*
2182         * If we find a slosh, and it's not the last byte in the string,
2183         * it's escaping something - advance past both bytes.
2184         */
2185        if ((*inchr == '\\') && (inchr[1] != '\0')) {
2186            inchr++;
2187            newlen++;
2188        }
2189        inchr++;
2190    }
2191    outstring = apr_palloc(p, newlen + 1);
2192    inchr = instring;
2193    outchr = outstring;
2194    /*
2195     * Now copy the input string to the output string, inserting a slosh
2196     * in front of every " that doesn't already have one.
2197     */
2198    while (*inchr != '\0') {
2199        if ((*inchr == '\\') && (inchr[1] != '\0')) {
2200            *outchr++ = *inchr++;
2201            *outchr++ = *inchr++;
2202        }
2203        if (*inchr == '"') {
2204            *outchr++ = '\\';
2205        }
2206        if (*inchr != '\0') {
2207            *outchr++ = *inchr++;
2208        }
2209    }
2210    *outchr = '\0';
2211    return outstring;
2212}
2213
2214/*
2215 * Given a string, append the PID deliminated by delim.
2216 * Usually used to create a pid-appended filepath name
2217 * (eg: /a/b/foo -> /a/b/foo.6726). A function, and not
2218 * a macro, to avoid unistd.h dependency
2219 */
2220AP_DECLARE(char *) ap_append_pid(apr_pool_t *p, const char *string,
2221                                    const char *delim)
2222{
2223    return apr_psprintf(p, "%s%s%" APR_PID_T_FMT, string,
2224                        delim, getpid());
2225
2226}
2227
2228/**
2229 * Parse a given timeout parameter string into an apr_interval_time_t value.
2230 * The unit of the time interval is given as postfix string to the numeric
2231 * string. Currently the following units are understood:
2232 *
2233 * ms    : milliseconds
2234 * s     : seconds
2235 * mi[n] : minutes
2236 * h     : hours
2237 *
2238 * If no unit is contained in the given timeout parameter the default_time_unit
2239 * will be used instead.
2240 * @param timeout_parameter The string containing the timeout parameter.
2241 * @param timeout The timeout value to be returned.
2242 * @param default_time_unit The default time unit to use if none is specified
2243 * in timeout_parameter.
2244 * @return Status value indicating whether the parsing was successful or not.
2245 */
2246AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
2247                                               const char *timeout_parameter,
2248                                               apr_interval_time_t *timeout,
2249                                               const char *default_time_unit)
2250{
2251    char *endp;
2252    const char *time_str;
2253    apr_int64_t tout;
2254
2255    tout = apr_strtoi64(timeout_parameter, &endp, 10);
2256    if (errno) {
2257        return errno;
2258    }
2259    if (!endp || !*endp) {
2260        time_str = default_time_unit;
2261    }
2262    else {
2263        time_str = endp;
2264    }
2265
2266    switch (*time_str) {
2267        /* Time is in seconds */
2268    case 's':
2269        *timeout = (apr_interval_time_t) apr_time_from_sec(tout);
2270        break;
2271    case 'h':
2272        /* Time is in hours */
2273        *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 3600);
2274        break;
2275    case 'm':
2276        switch (*(++time_str)) {
2277        /* Time is in miliseconds */
2278        case 's':
2279            *timeout = (apr_interval_time_t) tout * 1000;
2280            break;
2281        /* Time is in minutes */
2282        case 'i':
2283            *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 60);
2284            break;
2285        default:
2286            return APR_EGENERAL;
2287        }
2288        break;
2289    default:
2290        return APR_EGENERAL;
2291    }
2292    return APR_SUCCESS;
2293}
2294
2295AP_DECLARE(char *) ap_get_exec_line(apr_pool_t *p,
2296                                    const char *cmd,
2297                                    const char * const * argv)
2298{
2299    char buf[MAX_STRING_LEN];
2300    apr_procattr_t *procattr;
2301    apr_proc_t *proc;
2302    apr_file_t *fp;
2303    apr_size_t nbytes = 1;
2304    char c;
2305    int k;
2306
2307    if (apr_procattr_create(&procattr, p) != APR_SUCCESS)
2308        return NULL;
2309    if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_FULL_BLOCK,
2310                            APR_FULL_BLOCK) != APR_SUCCESS)
2311        return NULL;
2312    if (apr_procattr_dir_set(procattr,
2313                             ap_make_dirstr_parent(p, cmd)) != APR_SUCCESS)
2314        return NULL;
2315    if (apr_procattr_cmdtype_set(procattr, APR_PROGRAM) != APR_SUCCESS)
2316        return NULL;
2317    proc = apr_pcalloc(p, sizeof(apr_proc_t));
2318    if (apr_proc_create(proc, cmd, argv, NULL, procattr, p) != APR_SUCCESS)
2319        return NULL;
2320    fp = proc->out;
2321
2322    if (fp == NULL)
2323        return NULL;
2324    /* XXX: we are reading 1 byte at a time here */
2325    for (k = 0; apr_file_read(fp, &c, &nbytes) == APR_SUCCESS
2326                && nbytes == 1 && (k < MAX_STRING_LEN-1)     ; ) {
2327        if (c == '\n' || c == '\r')
2328            break;
2329        buf[k++] = c;
2330    }
2331    buf[k] = '\0';
2332    apr_file_close(fp);
2333
2334    return apr_pstrndup(p, buf, k);
2335}
2336