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_PROCESS_H
42#include <process.h>            /* for getpid() on Win32 */
43#endif
44#if APR_HAVE_NETDB_H
45#include <netdb.h>              /* for gethostbyname() */
46#endif
47
48#include "ap_config.h"
49#include "apr_base64.h"
50#include "httpd.h"
51#include "http_main.h"
52#include "http_log.h"
53#include "http_protocol.h"
54#include "http_config.h"
55#include "http_core.h"
56#include "util_ebcdic.h"
57#include "util_varbuf.h"
58
59#ifdef HAVE_PWD_H
60#include <pwd.h>
61#endif
62#ifdef HAVE_GRP_H
63#include <grp.h>
64#endif
65#ifdef HAVE_SYS_LOADAVG_H
66#include <sys/loadavg.h>
67#endif
68
69#include "ap_mpm.h"
70
71/* A bunch of functions in util.c scan strings looking for certain characters.
72 * To make that more efficient we encode a lookup table.  The test_char_table
73 * is generated automatically by gen_test_char.c.
74 */
75#include "test_char.h"
76
77/* we assume the folks using this ensure 0 <= c < 256... which means
78 * you need a cast to (unsigned char) first, you can't just plug a
79 * char in here and get it to work, because if char is signed then it
80 * will first be sign extended.
81 */
82#define TEST_CHAR(c, f)        (test_char_table[(unsigned)(c)] & (f))
83
84/* Win32/NetWare/OS2 need to check for both forward and back slashes
85 * in ap_getparents() and ap_escape_url.
86 */
87#ifdef CASE_BLIND_FILESYSTEM
88#define IS_SLASH(s) ((s == '/') || (s == '\\'))
89#define SLASHES "/\\"
90#else
91#define IS_SLASH(s) (s == '/')
92#define SLASHES "/"
93#endif
94
95/* we know core's module_index is 0 */
96#undef APLOG_MODULE_INDEX
97#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
98
99
100/*
101 * Examine a field value (such as a media-/content-type) string and return
102 * it sans any parameters; e.g., strip off any ';charset=foo' and the like.
103 */
104AP_DECLARE(char *) ap_field_noparam(apr_pool_t *p, const char *intype)
105{
106    const char *semi;
107
108    if (intype == NULL) return NULL;
109
110    semi = ap_strchr_c(intype, ';');
111    if (semi == NULL) {
112        return apr_pstrdup(p, intype);
113    }
114    else {
115        while ((semi > intype) && apr_isspace(semi[-1])) {
116            semi--;
117        }
118        return apr_pstrmemdup(p, intype, semi - intype);
119    }
120}
121
122AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt,
123                              int gmt)
124{
125    apr_size_t retcode;
126    char ts[MAX_STRING_LEN];
127    char tf[MAX_STRING_LEN];
128    apr_time_exp_t xt;
129
130    if (gmt) {
131        const char *f;
132        char *strp;
133
134        apr_time_exp_gmt(&xt, t);
135        /* Convert %Z to "GMT" and %z to "+0000";
136         * on hosts that do not have a time zone string in struct tm,
137         * strftime must assume its argument is local time.
138         */
139        for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f)
140            ; f++, strp++) {
141            if (*f != '%') continue;
142            switch (f[1]) {
143            case '%':
144                *++strp = *++f;
145                break;
146            case 'Z':
147                *strp++ = 'G';
148                *strp++ = 'M';
149                *strp = 'T';
150                f++;
151                break;
152            case 'z': /* common extension */
153                *strp++ = '+';
154                *strp++ = '0';
155                *strp++ = '0';
156                *strp++ = '0';
157                *strp = '0';
158                f++;
159                break;
160            }
161        }
162        *strp = '\0';
163        fmt = tf;
164    }
165    else {
166        apr_time_exp_lt(&xt, t);
167    }
168
169    /* check return code? */
170    apr_strftime(ts, &retcode, MAX_STRING_LEN, fmt, &xt);
171    ts[MAX_STRING_LEN - 1] = '\0';
172    return apr_pstrdup(p, ts);
173}
174
175/* Roy owes Rob beer. */
176/* Rob owes Roy dinner. */
177
178/* These legacy comments would make a lot more sense if Roy hadn't
179 * replaced the old later_than() routine with util_date.c.
180 *
181 * Well, okay, they still wouldn't make any sense.
182 */
183
184/* Match = 0, NoMatch = 1, Abort = -1
185 * Based loosely on sections of wildmat.c by Rich Salz
186 * Hmmm... shouldn't this really go component by component?
187 */
188AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected)
189{
190    int x, y;
191
192    for (x = 0, y = 0; expected[y]; ++y, ++x) {
193        if ((!str[x]) && (expected[y] != '*'))
194            return -1;
195        if (expected[y] == '*') {
196            while (expected[++y] == '*');
197            if (!expected[y])
198                return 0;
199            while (str[x]) {
200                int ret;
201                if ((ret = ap_strcmp_match(&str[x++], &expected[y])) != 1)
202                    return ret;
203            }
204            return -1;
205        }
206        else if ((expected[y] != '?') && (str[x] != expected[y]))
207            return 1;
208    }
209    return (str[x] != '\0');
210}
211
212AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected)
213{
214    int x, y;
215
216    for (x = 0, y = 0; expected[y]; ++y, ++x) {
217        if (!str[x] && expected[y] != '*')
218            return -1;
219        if (expected[y] == '*') {
220            while (expected[++y] == '*');
221            if (!expected[y])
222                return 0;
223            while (str[x]) {
224                int ret;
225                if ((ret = ap_strcasecmp_match(&str[x++], &expected[y])) != 1)
226                    return ret;
227            }
228            return -1;
229        }
230        else if (expected[y] != '?'
231                 && apr_tolower(str[x]) != apr_tolower(expected[y]))
232            return 1;
233    }
234    return (str[x] != '\0');
235}
236
237/* We actually compare the canonical root to this root, (but we don't
238 * waste time checking the case), since every use of this function in
239 * httpd-2.1 tests if the path is 'proper', meaning we've already passed
240 * it through apr_filepath_merge, or we haven't.
241 */
242AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, const char *dir)
243{
244    const char *newpath;
245    const char *ourdir = dir;
246    if (apr_filepath_root(&newpath, &dir, 0, p) != APR_SUCCESS
247            || strncmp(newpath, ourdir, strlen(newpath)) != 0) {
248        return 0;
249    }
250    return 1;
251}
252
253AP_DECLARE(int) ap_is_matchexp(const char *str)
254{
255    register int x;
256
257    for (x = 0; str[x]; x++)
258        if ((str[x] == '*') || (str[x] == '?'))
259            return 1;
260    return 0;
261}
262
263/*
264 * Here's a pool-based interface to the POSIX-esque ap_regcomp().
265 * Note that we return ap_regex_t instead of being passed one.
266 * The reason is that if you use an already-used ap_regex_t structure,
267 * the memory that you've already allocated gets forgotten, and
268 * regfree() doesn't clear it. So we don't allow it.
269 */
270
271static apr_status_t regex_cleanup(void *preg)
272{
273    ap_regfree((ap_regex_t *) preg);
274    return APR_SUCCESS;
275}
276
277AP_DECLARE(ap_regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern,
278                                     int cflags)
279{
280    ap_regex_t *preg = apr_palloc(p, sizeof *preg);
281    int err = ap_regcomp(preg, pattern, cflags);
282    if (err) {
283        if (err == AP_REG_ESPACE)
284            ap_abort_on_oom();
285        return NULL;
286    }
287
288    apr_pool_cleanup_register(p, (void *) preg, regex_cleanup,
289                              apr_pool_cleanup_null);
290
291    return preg;
292}
293
294AP_DECLARE(void) ap_pregfree(apr_pool_t *p, ap_regex_t *reg)
295{
296    ap_regfree(reg);
297    apr_pool_cleanup_kill(p, (void *) reg, regex_cleanup);
298}
299
300/*
301 * Similar to standard strstr() but we ignore case in this version.
302 * Based on the strstr() implementation further below.
303 */
304AP_DECLARE(char *) ap_strcasestr(const char *s1, const char *s2)
305{
306    char *p1, *p2;
307    if (*s2 == '\0') {
308        /* an empty s2 */
309        return((char *)s1);
310    }
311    while(1) {
312        for ( ; (*s1 != '\0') && (apr_tolower(*s1) != apr_tolower(*s2)); s1++);
313        if (*s1 == '\0') {
314            return(NULL);
315        }
316        /* found first character of s2, see if the rest matches */
317        p1 = (char *)s1;
318        p2 = (char *)s2;
319        for (++p1, ++p2; apr_tolower(*p1) == apr_tolower(*p2); ++p1, ++p2) {
320            if (*p1 == '\0') {
321                /* both strings ended together */
322                return((char *)s1);
323            }
324        }
325        if (*p2 == '\0') {
326            /* second string ended, a match */
327            break;
328        }
329        /* didn't find a match here, try starting at next character in s1 */
330        s1++;
331    }
332    return((char *)s1);
333}
334
335/*
336 * Returns an offsetted pointer in bigstring immediately after
337 * prefix. Returns bigstring if bigstring doesn't start with
338 * prefix or if prefix is longer than bigstring while still matching.
339 * NOTE: pointer returned is relative to bigstring, so we
340 * can use standard pointer comparisons in the calling function
341 * (eg: test if ap_stripprefix(a,b) == a)
342 */
343AP_DECLARE(const char *) ap_stripprefix(const char *bigstring,
344                                        const char *prefix)
345{
346    const char *p1;
347
348    if (*prefix == '\0')
349        return bigstring;
350
351    p1 = bigstring;
352    while (*p1 && *prefix) {
353        if (*p1++ != *prefix++)
354            return bigstring;
355    }
356    if (*prefix == '\0')
357        return p1;
358
359    /* hit the end of bigstring! */
360    return bigstring;
361}
362
363/* This function substitutes for $0-$9, filling in regular expression
364 * submatches. Pass it the same nmatch and pmatch arguments that you
365 * passed ap_regexec(). pmatch should not be greater than the maximum number
366 * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t.
367 *
368 * nmatch must be <=AP_MAX_REG_MATCH (10).
369 *
370 * input should be the string with the $-expressions, source should be the
371 * string that was matched against.
372 *
373 * It returns the substituted string, or NULL if a vbuf is used.
374 * On errors, returns the orig string.
375 *
376 * Parts of this code are based on Henry Spencer's regsub(), from his
377 * AT&T V8 regexp package.
378 */
379
380static apr_status_t regsub_core(apr_pool_t *p, char **result,
381                                struct ap_varbuf *vb, const char *input,
382                                const char *source, apr_size_t nmatch,
383                                ap_regmatch_t pmatch[], apr_size_t maxlen)
384{
385    const char *src = input;
386    char *dst;
387    char c;
388    apr_size_t no;
389    apr_size_t len = 0;
390
391    AP_DEBUG_ASSERT((result && p && !vb) || (vb && !p && !result));
392    if (!source || nmatch>AP_MAX_REG_MATCH)
393        return APR_EINVAL;
394    if (!nmatch) {
395        len = strlen(src);
396        if (maxlen > 0 && len >= maxlen)
397            return APR_ENOMEM;
398        if (!vb) {
399            *result = apr_pstrmemdup(p, src, len);
400            return APR_SUCCESS;
401        }
402        else {
403            ap_varbuf_strmemcat(vb, src, len);
404            return APR_SUCCESS;
405        }
406    }
407
408    /* First pass, find the size */
409    while ((c = *src++) != '\0') {
410        if (c == '$' && apr_isdigit(*src))
411            no = *src++ - '0';
412        else
413            no = AP_MAX_REG_MATCH;
414
415        if (no >= AP_MAX_REG_MATCH) {  /* Ordinary character. */
416            if (c == '\\' && *src)
417                src++;
418            len++;
419        }
420        else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
421            if (APR_SIZE_MAX - len <= pmatch[no].rm_eo - pmatch[no].rm_so)
422                return APR_ENOMEM;
423            len += pmatch[no].rm_eo - pmatch[no].rm_so;
424        }
425
426    }
427
428    if (len >= maxlen && maxlen > 0)
429        return APR_ENOMEM;
430
431    if (!vb) {
432        *result = dst = apr_palloc(p, len + 1);
433    }
434    else {
435        if (vb->strlen == AP_VARBUF_UNKNOWN)
436            vb->strlen = strlen(vb->buf);
437        ap_varbuf_grow(vb, vb->strlen + len);
438        dst = vb->buf + vb->strlen;
439        vb->strlen += len;
440    }
441
442    /* Now actually fill in the string */
443
444    src = input;
445
446    while ((c = *src++) != '\0') {
447        if (c == '$' && apr_isdigit(*src))
448            no = *src++ - '0';
449        else
450            no = AP_MAX_REG_MATCH;
451
452        if (no >= AP_MAX_REG_MATCH) {  /* Ordinary character. */
453            if (c == '\\' && *src)
454                c = *src++;
455            *dst++ = c;
456        }
457        else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
458            len = pmatch[no].rm_eo - pmatch[no].rm_so;
459            memcpy(dst, source + pmatch[no].rm_so, len);
460            dst += len;
461        }
462
463    }
464    *dst = '\0';
465
466    return APR_SUCCESS;
467}
468
469#ifndef AP_PREGSUB_MAXLEN
470#define AP_PREGSUB_MAXLEN   (HUGE_STRING_LEN * 8)
471#endif
472AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
473                              const char *source, apr_size_t nmatch,
474                              ap_regmatch_t pmatch[])
475{
476    char *result;
477    apr_status_t rc = regsub_core(p, &result, NULL, input, source, nmatch,
478                                  pmatch, AP_PREGSUB_MAXLEN);
479    if (rc != APR_SUCCESS)
480        result = NULL;
481    return result;
482}
483
484AP_DECLARE(apr_status_t) ap_pregsub_ex(apr_pool_t *p, char **result,
485                                       const char *input, const char *source,
486                                       apr_size_t nmatch, ap_regmatch_t pmatch[],
487                                       apr_size_t maxlen)
488{
489    apr_status_t rc = regsub_core(p, result, NULL, input, source, nmatch,
490                                  pmatch, maxlen);
491    if (rc != APR_SUCCESS)
492        *result = NULL;
493    return rc;
494}
495
496/*
497 * Parse .. so we don't compromise security
498 */
499AP_DECLARE(void) ap_getparents(char *name)
500{
501    char *next;
502    int l, w, first_dot;
503
504    /* Four paseses, as per RFC 1808 */
505    /* a) remove ./ path segments */
506    for (next = name; *next && (*next != '.'); next++) {
507    }
508
509    l = w = first_dot = next - name;
510    while (name[l] != '\0') {
511        if (name[l] == '.' && IS_SLASH(name[l + 1])
512            && (l == 0 || IS_SLASH(name[l - 1])))
513            l += 2;
514        else
515            name[w++] = name[l++];
516    }
517
518    /* b) remove trailing . path, segment */
519    if (w == 1 && name[0] == '.')
520        w--;
521    else if (w > 1 && name[w - 1] == '.' && IS_SLASH(name[w - 2]))
522        w--;
523    name[w] = '\0';
524
525    /* c) remove all xx/../ segments. (including leading ../ and /../) */
526    l = first_dot;
527
528    while (name[l] != '\0') {
529        if (name[l] == '.' && name[l + 1] == '.' && IS_SLASH(name[l + 2])
530            && (l == 0 || IS_SLASH(name[l - 1]))) {
531            register int m = l + 3, n;
532
533            l = l - 2;
534            if (l >= 0) {
535                while (l >= 0 && !IS_SLASH(name[l]))
536                    l--;
537                l++;
538            }
539            else
540                l = 0;
541            n = l;
542            while ((name[n] = name[m]))
543                (++n, ++m);
544        }
545        else
546            ++l;
547    }
548
549    /* d) remove trailing xx/.. segment. */
550    if (l == 2 && name[0] == '.' && name[1] == '.')
551        name[0] = '\0';
552    else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
553             && IS_SLASH(name[l - 3])) {
554        l = l - 4;
555        if (l >= 0) {
556            while (l >= 0 && !IS_SLASH(name[l]))
557                l--;
558            l++;
559        }
560        else
561            l = 0;
562        name[l] = '\0';
563    }
564}
565
566AP_DECLARE(void) ap_no2slash(char *name)
567{
568    char *d, *s;
569
570    s = d = name;
571
572#ifdef HAVE_UNC_PATHS
573    /* Check for UNC names.  Leave leading two slashes. */
574    if (s[0] == '/' && s[1] == '/')
575        *d++ = *s++;
576#endif
577
578    while (*s) {
579        if ((*d++ = *s) == '/') {
580            do {
581                ++s;
582            } while (*s == '/');
583        }
584        else {
585            ++s;
586        }
587    }
588    *d = '\0';
589}
590
591
592/*
593 * copy at most n leading directories of s into d
594 * d should be at least as large as s plus 1 extra byte
595 * assumes n > 0
596 * the return value is the ever useful pointer to the trailing \0 of d
597 *
598 * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments,
599 * so that if n == 0, "/" is returned in d with n == 1
600 * and s == "e:/test.html", "e:/" is returned in d
601 * *** See also directory_walk in modules/http/http_request.c
602
603 * examples:
604 *    /a/b, 0  ==> /  (true for all platforms)
605 *    /a/b, 1  ==> /
606 *    /a/b, 2  ==> /a/
607 *    /a/b, 3  ==> /a/b/
608 *    /a/b, 4  ==> /a/b/
609 *
610 *    c:/a/b 0 ==> /
611 *    c:/a/b 1 ==> c:/
612 *    c:/a/b 2 ==> c:/a/
613 *    c:/a/b 3 ==> c:/a/b
614 *    c:/a/b 4 ==> c:/a/b
615 */
616AP_DECLARE(char *) ap_make_dirstr_prefix(char *d, const char *s, int n)
617{
618    if (n < 1) {
619        *d = '/';
620        *++d = '\0';
621        return (d);
622    }
623
624    for (;;) {
625        if (*s == '\0' || (*s == '/' && (--n) == 0)) {
626            *d = '/';
627            break;
628        }
629        *d++ = *s++;
630    }
631    *++d = 0;
632    return (d);
633}
634
635
636/*
637 * return the parent directory name including trailing / of the file s
638 */
639AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s)
640{
641    const char *last_slash = ap_strrchr_c(s, '/');
642    char *d;
643    int l;
644
645    if (last_slash == NULL) {
646        return apr_pstrdup(p, "");
647    }
648    l = (last_slash - s) + 1;
649    d = apr_pstrmemdup(p, s, l);
650
651    return (d);
652}
653
654
655AP_DECLARE(int) ap_count_dirs(const char *path)
656{
657    register int x, n;
658
659    for (x = 0, n = 0; path[x]; x++)
660        if (path[x] == '/')
661            n++;
662    return n;
663}
664
665AP_DECLARE(char *) ap_getword_nc(apr_pool_t *atrans, char **line, char stop)
666{
667    return ap_getword(atrans, (const char **) line, stop);
668}
669
670AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop)
671{
672    const char *pos = *line;
673    int len;
674    char *res;
675
676    while ((*pos != stop) && *pos) {
677        ++pos;
678    }
679
680    len = pos - *line;
681    res = apr_pstrmemdup(atrans, *line, len);
682
683    if (stop) {
684        while (*pos == stop) {
685            ++pos;
686        }
687    }
688    *line = pos;
689
690    return res;
691}
692
693AP_DECLARE(char *) ap_getword_white_nc(apr_pool_t *atrans, char **line)
694{
695    return ap_getword_white(atrans, (const char **) line);
696}
697
698AP_DECLARE(char *) ap_getword_white(apr_pool_t *atrans, const char **line)
699{
700    const char *pos = *line;
701    int len;
702    char *res;
703
704    while (!apr_isspace(*pos) && *pos) {
705        ++pos;
706    }
707
708    len = pos - *line;
709    res = apr_pstrmemdup(atrans, *line, len);
710
711    while (apr_isspace(*pos)) {
712        ++pos;
713    }
714
715    *line = pos;
716
717    return res;
718}
719
720AP_DECLARE(char *) ap_getword_nulls_nc(apr_pool_t *atrans, char **line,
721                                       char stop)
722{
723    return ap_getword_nulls(atrans, (const char **) line, stop);
724}
725
726AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line,
727                                    char stop)
728{
729    const char *pos = ap_strchr_c(*line, stop);
730    char *res;
731
732    if (!pos) {
733        apr_size_t len = strlen(*line);
734        res = apr_pstrmemdup(atrans, *line, len);
735        *line += len;
736        return res;
737    }
738
739    res = apr_pstrmemdup(atrans, *line, pos - *line);
740
741    ++pos;
742
743    *line = pos;
744
745    return res;
746}
747
748/* Get a word, (new) config-file style --- quoted strings and backslashes
749 * all honored
750 */
751
752static char *substring_conf(apr_pool_t *p, const char *start, int len,
753                            char quote)
754{
755    char *result = apr_palloc(p, len + 1);
756    char *resp = result;
757    int i;
758
759    for (i = 0; i < len; ++i) {
760        if (start[i] == '\\' && (start[i + 1] == '\\'
761                                 || (quote && start[i + 1] == quote)))
762            *resp++ = start[++i];
763        else
764            *resp++ = start[i];
765    }
766
767    *resp++ = '\0';
768#if RESOLVE_ENV_PER_TOKEN
769    return (char *)ap_resolve_env(p,result);
770#else
771    return result;
772#endif
773}
774
775AP_DECLARE(char *) ap_getword_conf_nc(apr_pool_t *p, char **line)
776{
777    return ap_getword_conf(p, (const char **) line);
778}
779
780AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
781{
782    const char *str = *line, *strend;
783    char *res;
784    char quote;
785
786    while (apr_isspace(*str))
787        ++str;
788
789    if (!*str) {
790        *line = str;
791        return "";
792    }
793
794    if ((quote = *str) == '"' || quote == '\'') {
795        strend = str + 1;
796        while (*strend && *strend != quote) {
797            if (*strend == '\\' && strend[1] &&
798                (strend[1] == quote || strend[1] == '\\')) {
799                strend += 2;
800            }
801            else {
802                ++strend;
803            }
804        }
805        res = substring_conf(p, str + 1, strend - str - 1, quote);
806
807        if (*strend == quote)
808            ++strend;
809    }
810    else {
811        strend = str;
812        while (*strend && !apr_isspace(*strend))
813            ++strend;
814
815        res = substring_conf(p, str, strend - str, 0);
816    }
817
818    while (apr_isspace(*strend))
819        ++strend;
820    *line = strend;
821    return res;
822}
823
824AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp)
825{
826#ifdef DEBUG
827    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00551)
828        "Done with config file %s", cfp->name);
829#endif
830    return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
831}
832
833/* we can't use apr_file_* directly because of linking issues on Windows */
834static apr_status_t cfg_close(void *param)
835{
836    return apr_file_close(param);
837}
838
839static apr_status_t cfg_getch(char *ch, void *param)
840{
841    return apr_file_getc(ch, param);
842}
843
844static apr_status_t cfg_getstr(void *buf, apr_size_t bufsiz, void *param)
845{
846    return apr_file_gets(buf, bufsiz, param);
847}
848
849/* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */
850AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
851                                          apr_pool_t *p, const char *name)
852{
853    ap_configfile_t *new_cfg;
854    apr_file_t *file = NULL;
855    apr_finfo_t finfo;
856    apr_status_t status;
857#ifdef DEBUG
858    char buf[120];
859#endif
860
861    if (name == NULL) {
862        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00552)
863               "Internal error: pcfg_openfile() called with NULL filename");
864        return APR_EBADF;
865    }
866
867    status = apr_file_open(&file, name, APR_READ | APR_BUFFERED,
868                           APR_OS_DEFAULT, p);
869#ifdef DEBUG
870    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00553)
871                "Opening config file %s (%s)",
872                name, (status != APR_SUCCESS) ?
873                apr_strerror(status, buf, sizeof(buf)) : "successful");
874#endif
875    if (status != APR_SUCCESS)
876        return status;
877
878    status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file);
879    if (status != APR_SUCCESS)
880        return status;
881
882    if (finfo.filetype != APR_REG &&
883#if defined(WIN32) || defined(OS2) || defined(NETWARE)
884        strcasecmp(apr_filepath_name_get(name), "nul") != 0) {
885#else
886        strcmp(name, "/dev/null") != 0) {
887#endif /* WIN32 || OS2 */
888        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00554)
889                     "Access to file %s denied by server: not a regular file",
890                     name);
891        apr_file_close(file);
892        return APR_EBADF;
893    }
894
895#ifdef WIN32
896    /* Some twisted character [no pun intended] at MS decided that a
897     * zero width joiner as the lead wide character would be ideal for
898     * describing Unicode text files.  This was further convoluted to
899     * another MSism that the same character mapped into utf-8, EF BB BF
900     * would signify utf-8 text files.
901     *
902     * Since MS configuration files are all protecting utf-8 encoded
903     * Unicode path, file and resource names, we already have the correct
904     * WinNT encoding.  But at least eat the stupid three bytes up front.
905     */
906    {
907        unsigned char buf[4];
908        apr_size_t len = 3;
909        status = apr_file_read(file, buf, &len);
910        if ((status != APR_SUCCESS) || (len < 3)
911              || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) {
912            apr_off_t zero = 0;
913            apr_file_seek(file, APR_SET, &zero);
914        }
915    }
916#endif
917
918    new_cfg = apr_palloc(p, sizeof(*new_cfg));
919    new_cfg->param = file;
920    new_cfg->name = apr_pstrdup(p, name);
921    new_cfg->getch = cfg_getch;
922    new_cfg->getstr = cfg_getstr;
923    new_cfg->close = cfg_close;
924    new_cfg->line_number = 0;
925    *ret_cfg = new_cfg;
926    return APR_SUCCESS;
927}
928
929
930/* Allocate a ap_configfile_t handle with user defined functions and params */
931AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(
932            apr_pool_t *p, const char *descr, void *param,
933            apr_status_t (*getc_func) (char *ch, void *param),
934            apr_status_t (*gets_func) (void *buf, apr_size_t bufsize, void *param),
935            apr_status_t (*close_func) (void *param))
936{
937    ap_configfile_t *new_cfg = apr_palloc(p, sizeof(*new_cfg));
938    new_cfg->param = param;
939    new_cfg->name = descr;
940    new_cfg->getch = getc_func;
941    new_cfg->getstr = gets_func;
942    new_cfg->close = close_func;
943    new_cfg->line_number = 0;
944    return new_cfg;
945}
946
947/* Read one character from a configfile_t */
948AP_DECLARE(apr_status_t) ap_cfg_getc(char *ch, ap_configfile_t *cfp)
949{
950    apr_status_t rc = cfp->getch(ch, cfp->param);
951    if (rc == APR_SUCCESS && *ch == LF)
952        ++cfp->line_number;
953    return rc;
954}
955
956AP_DECLARE(const char *) ap_pcfg_strerror(apr_pool_t *p, ap_configfile_t *cfp,
957                                          apr_status_t rc)
958{
959    if (rc == APR_SUCCESS)
960        return NULL;
961
962    if (rc == APR_ENOSPC)
963        return apr_psprintf(p, "Error reading %s at line %d: Line too long",
964                            cfp->name, cfp->line_number);
965
966    return apr_psprintf(p, "Error reading %s at line %d: %pm",
967                        cfp->name, cfp->line_number, &rc);
968}
969
970/* Read one line from open ap_configfile_t, strip LF, increase line number */
971/* If custom handler does not define a getstr() function, read char by char */
972static apr_status_t ap_cfg_getline_core(char *buf, apr_size_t bufsize,
973                                        ap_configfile_t *cfp)
974{
975    apr_status_t rc;
976    /* If a "get string" function is defined, use it */
977    if (cfp->getstr != NULL) {
978        char *cp;
979        char *cbuf = buf;
980        apr_size_t cbufsize = bufsize;
981
982        while (1) {
983            ++cfp->line_number;
984            rc = cfp->getstr(cbuf, cbufsize, cfp->param);
985            if (rc == APR_EOF) {
986                if (cbuf != buf) {
987                    *cbuf = '\0';
988                    break;
989                }
990                else {
991                    return APR_EOF;
992                }
993            }
994            if (rc != APR_SUCCESS) {
995                return rc;
996            }
997
998            /*
999             *  check for line continuation,
1000             *  i.e. match [^\\]\\[\r]\n only
1001             */
1002            cp = cbuf;
1003            cp += strlen(cp);
1004            if (cp > cbuf && cp[-1] == LF) {
1005                cp--;
1006                if (cp > cbuf && cp[-1] == CR)
1007                    cp--;
1008                if (cp > cbuf && cp[-1] == '\\') {
1009                    cp--;
1010                    /*
1011                     * line continuation requested -
1012                     * then remove backslash and continue
1013                     */
1014                    cbufsize -= (cp-cbuf);
1015                    cbuf = cp;
1016                    continue;
1017                }
1018            }
1019            else if (cp - buf >= bufsize - 1) {
1020                return APR_ENOSPC;
1021            }
1022            break;
1023        }
1024    } else {
1025        /* No "get string" function defined; read character by character */
1026        apr_size_t i = 0;
1027
1028        if (bufsize < 2) {
1029            /* too small, assume caller is crazy */
1030            return APR_EINVAL;
1031        }
1032        buf[0] = '\0';
1033
1034        while (1) {
1035            char c;
1036            rc = cfp->getch(&c, cfp->param);
1037            if (rc == APR_EOF) {
1038                if (i > 0)
1039                    break;
1040                else
1041                    return APR_EOF;
1042            }
1043            if (rc != APR_SUCCESS)
1044                return rc;
1045            if (c == LF) {
1046                ++cfp->line_number;
1047                /* check for line continuation */
1048                if (i > 0 && buf[i-1] == '\\') {
1049                    i--;
1050                    continue;
1051                }
1052                else {
1053                    break;
1054                }
1055            }
1056            else if (i >= bufsize - 2) {
1057                return APR_ENOSPC;
1058            }
1059            buf[i] = c;
1060            ++i;
1061        }
1062        buf[i] = '\0';
1063    }
1064    return APR_SUCCESS;
1065}
1066
1067static int cfg_trim_line(char *buf)
1068{
1069    char *start, *end;
1070    /*
1071     * Leading and trailing white space is eliminated completely
1072     */
1073    start = buf;
1074    while (apr_isspace(*start))
1075        ++start;
1076    /* blast trailing whitespace */
1077    end = &start[strlen(start)];
1078    while (--end >= start && apr_isspace(*end))
1079        *end = '\0';
1080    /* Zap leading whitespace by shifting */
1081    if (start != buf)
1082        memmove(buf, start, end - start + 2);
1083#ifdef DEBUG_CFG_LINES
1084    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf);
1085#endif
1086    return end - start + 1;
1087}
1088
1089/* Read one line from open ap_configfile_t, strip LF, increase line number */
1090/* If custom handler does not define a getstr() function, read char by char */
1091AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, apr_size_t bufsize,
1092                                        ap_configfile_t *cfp)
1093{
1094    apr_status_t rc = ap_cfg_getline_core(buf, bufsize, cfp);
1095    if (rc == APR_SUCCESS)
1096        cfg_trim_line(buf);
1097    return rc;
1098}
1099
1100AP_DECLARE(apr_status_t) ap_varbuf_cfg_getline(struct ap_varbuf *vb,
1101                                               ap_configfile_t *cfp,
1102                                               apr_size_t max_len)
1103{
1104    apr_status_t rc;
1105    apr_size_t new_len;
1106    vb->strlen = 0;
1107    *vb->buf = '\0';
1108
1109    if (vb->strlen == AP_VARBUF_UNKNOWN)
1110        vb->strlen = strlen(vb->buf);
1111    if (vb->avail - vb->strlen < 3) {
1112        new_len = vb->avail * 2;
1113        if (new_len > max_len)
1114            new_len = max_len;
1115        else if (new_len < 3)
1116            new_len = 3;
1117        ap_varbuf_grow(vb, new_len);
1118    }
1119
1120    for (;;) {
1121        rc = ap_cfg_getline_core(vb->buf + vb->strlen, vb->avail - vb->strlen, cfp);
1122        if (rc == APR_ENOSPC || rc == APR_SUCCESS)
1123            vb->strlen += strlen(vb->buf + vb->strlen);
1124        if (rc != APR_ENOSPC)
1125            break;
1126        if (vb->avail >= max_len)
1127            return APR_ENOSPC;
1128        new_len = vb->avail * 2;
1129        if (new_len > max_len)
1130            new_len = max_len;
1131        ap_varbuf_grow(vb, new_len);
1132        --cfp->line_number;
1133    }
1134    if (vb->strlen > max_len)
1135        return APR_ENOSPC;
1136    if (rc == APR_SUCCESS)
1137        vb->strlen = cfg_trim_line(vb->buf);
1138    return rc;
1139}
1140
1141/* Size an HTTP header field list item, as separated by a comma.
1142 * The return value is a pointer to the beginning of the non-empty list item
1143 * within the original string (or NULL if there is none) and the address
1144 * of field is shifted to the next non-comma, non-whitespace character.
1145 * len is the length of the item excluding any beginning whitespace.
1146 */
1147AP_DECLARE(const char *) ap_size_list_item(const char **field, int *len)
1148{
1149    const unsigned char *ptr = (const unsigned char *)*field;
1150    const unsigned char *token;
1151    int in_qpair, in_qstr, in_com;
1152
1153    /* Find first non-comma, non-whitespace byte */
1154
1155    while (*ptr == ',' || apr_isspace(*ptr))
1156        ++ptr;
1157
1158    token = ptr;
1159
1160    /* Find the end of this item, skipping over dead bits */
1161
1162    for (in_qpair = in_qstr = in_com = 0;
1163         *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1164         ++ptr) {
1165
1166        if (in_qpair) {
1167            in_qpair = 0;
1168        }
1169        else {
1170            switch (*ptr) {
1171                case '\\': in_qpair = 1;      /* quoted-pair         */
1172                           break;
1173                case '"' : if (!in_com)       /* quoted string delim */
1174                               in_qstr = !in_qstr;
1175                           break;
1176                case '(' : if (!in_qstr)      /* comment (may nest)  */
1177                               ++in_com;
1178                           break;
1179                case ')' : if (in_com)        /* end comment         */
1180                               --in_com;
1181                           break;
1182                default  : break;
1183            }
1184        }
1185    }
1186
1187    if ((*len = (ptr - token)) == 0) {
1188        *field = (const char *)ptr;
1189        return NULL;
1190    }
1191
1192    /* Advance field pointer to the next non-comma, non-white byte */
1193
1194    while (*ptr == ',' || apr_isspace(*ptr))
1195        ++ptr;
1196
1197    *field = (const char *)ptr;
1198    return (const char *)token;
1199}
1200
1201/* Retrieve an HTTP header field list item, as separated by a comma,
1202 * while stripping insignificant whitespace and lowercasing anything not in
1203 * a quoted string or comment.  The return value is a new string containing
1204 * the converted list item (or NULL if none) and the address pointed to by
1205 * field is shifted to the next non-comma, non-whitespace.
1206 */
1207AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, const char **field)
1208{
1209    const char *tok_start;
1210    const unsigned char *ptr;
1211    unsigned char *pos;
1212    char *token;
1213    int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0;
1214
1215    /* Find the beginning and maximum length of the list item so that
1216     * we can allocate a buffer for the new string and reset the field.
1217     */
1218    if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) {
1219        return NULL;
1220    }
1221    token = apr_palloc(p, tok_len + 1);
1222
1223    /* Scan the token again, but this time copy only the good bytes.
1224     * We skip extra whitespace and any whitespace around a '=', '/',
1225     * or ';' and lowercase normal characters not within a comment,
1226     * quoted-string or quoted-pair.
1227     */
1228    for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token;
1229         *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1230         ++ptr) {
1231
1232        if (in_qpair) {
1233            in_qpair = 0;
1234            *pos++ = *ptr;
1235        }
1236        else {
1237            switch (*ptr) {
1238                case '\\': in_qpair = 1;
1239                           if (addspace == 1)
1240                               *pos++ = ' ';
1241                           *pos++ = *ptr;
1242                           addspace = 0;
1243                           break;
1244                case '"' : if (!in_com)
1245                               in_qstr = !in_qstr;
1246                           if (addspace == 1)
1247                               *pos++ = ' ';
1248                           *pos++ = *ptr;
1249                           addspace = 0;
1250                           break;
1251                case '(' : if (!in_qstr)
1252                               ++in_com;
1253                           if (addspace == 1)
1254                               *pos++ = ' ';
1255                           *pos++ = *ptr;
1256                           addspace = 0;
1257                           break;
1258                case ')' : if (in_com)
1259                               --in_com;
1260                           *pos++ = *ptr;
1261                           addspace = 0;
1262                           break;
1263                case ' ' :
1264                case '\t': if (addspace)
1265                               break;
1266                           if (in_com || in_qstr)
1267                               *pos++ = *ptr;
1268                           else
1269                               addspace = 1;
1270                           break;
1271                case '=' :
1272                case '/' :
1273                case ';' : if (!(in_com || in_qstr))
1274                               addspace = -1;
1275                           *pos++ = *ptr;
1276                           break;
1277                default  : if (addspace == 1)
1278                               *pos++ = ' ';
1279                           *pos++ = (in_com || in_qstr) ? *ptr
1280                                                        : apr_tolower(*ptr);
1281                           addspace = 0;
1282                           break;
1283            }
1284        }
1285    }
1286    *pos = '\0';
1287
1288    return token;
1289}
1290
1291typedef enum ap_etag_e {
1292    AP_ETAG_NONE,
1293    AP_ETAG_WEAK,
1294    AP_ETAG_STRONG
1295} ap_etag_e;
1296
1297/* Find an item in canonical form (lowercase, no extra spaces) within
1298 * an HTTP field value list.  Returns 1 if found, 0 if not found.
1299 * This would be much more efficient if we stored header fields as
1300 * an array of list items as they are received instead of a plain string.
1301 */
1302static int find_list_item(apr_pool_t *p, const char *line,
1303                                  const char *tok, ap_etag_e type)
1304{
1305    const unsigned char *pos;
1306    const unsigned char *ptr = (const unsigned char *)line;
1307    int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0;
1308
1309    if (!line || !tok) {
1310        return 0;
1311    }
1312    if (type == AP_ETAG_STRONG && *tok != '\"') {
1313        return 0;
1314    }
1315    if (type == AP_ETAG_WEAK) {
1316        if (*tok == 'W' && (*(tok+1)) == '/' && (*(tok+2)) == '\"') {
1317            tok += 2;
1318        }
1319        else if (*tok != '\"') {
1320            return 0;
1321        }
1322    }
1323
1324    do {  /* loop for each item in line's list */
1325
1326        /* Find first non-comma, non-whitespace byte */
1327        while (*ptr == ',' || apr_isspace(*ptr)) {
1328            ++ptr;
1329        }
1330
1331        /* Account for strong or weak Etags, depending on our search */
1332        if (type == AP_ETAG_STRONG && *ptr != '\"') {
1333            break;
1334        }
1335        if (type == AP_ETAG_WEAK) {
1336            if (*ptr == 'W' && (*(ptr+1)) == '/' && (*(ptr+2)) == '\"') {
1337                ptr += 2;
1338            }
1339            else if (*ptr != '\"') {
1340                break;
1341            }
1342        }
1343
1344        if (*ptr)
1345            good = 1;  /* until proven otherwise for this item */
1346        else
1347            break;     /* no items left and nothing good found */
1348
1349        /* We skip extra whitespace and any whitespace around a '=', '/',
1350         * or ';' and lowercase normal characters not within a comment,
1351         * quoted-string or quoted-pair.
1352         */
1353        for (pos = (const unsigned char *)tok;
1354             *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1355             ++ptr) {
1356
1357            if (in_qpair) {
1358                in_qpair = 0;
1359                if (good)
1360                    good = (*pos++ == *ptr);
1361            }
1362            else {
1363                switch (*ptr) {
1364                    case '\\': in_qpair = 1;
1365                               if (addspace == 1)
1366                                   good = good && (*pos++ == ' ');
1367                               good = good && (*pos++ == *ptr);
1368                               addspace = 0;
1369                               break;
1370                    case '"' : if (!in_com)
1371                                   in_qstr = !in_qstr;
1372                               if (addspace == 1)
1373                                   good = good && (*pos++ == ' ');
1374                               good = good && (*pos++ == *ptr);
1375                               addspace = 0;
1376                               break;
1377                    case '(' : if (!in_qstr)
1378                                   ++in_com;
1379                               if (addspace == 1)
1380                                   good = good && (*pos++ == ' ');
1381                               good = good && (*pos++ == *ptr);
1382                               addspace = 0;
1383                               break;
1384                    case ')' : if (in_com)
1385                                   --in_com;
1386                               good = good && (*pos++ == *ptr);
1387                               addspace = 0;
1388                               break;
1389                    case ' ' :
1390                    case '\t': if (addspace || !good)
1391                                   break;
1392                               if (in_com || in_qstr)
1393                                   good = (*pos++ == *ptr);
1394                               else
1395                                   addspace = 1;
1396                               break;
1397                    case '=' :
1398                    case '/' :
1399                    case ';' : if (!(in_com || in_qstr))
1400                                   addspace = -1;
1401                               good = good && (*pos++ == *ptr);
1402                               break;
1403                    default  : if (!good)
1404                                   break;
1405                               if (addspace == 1)
1406                                   good = (*pos++ == ' ');
1407                               if (in_com || in_qstr)
1408                                   good = good && (*pos++ == *ptr);
1409                               else
1410                                   good = good
1411                                       && (apr_tolower(*pos++) == apr_tolower(*ptr));
1412                               addspace = 0;
1413                               break;
1414                }
1415            }
1416        }
1417        if (good && *pos)
1418            good = 0;          /* not good if only a prefix was matched */
1419
1420    } while (*ptr && !good);
1421
1422    return good;
1423}
1424
1425/* Find an item in canonical form (lowercase, no extra spaces) within
1426 * an HTTP field value list.  Returns 1 if found, 0 if not found.
1427 * This would be much more efficient if we stored header fields as
1428 * an array of list items as they are received instead of a plain string.
1429 */
1430AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
1431                                  const char *tok)
1432{
1433    return find_list_item(p, line, tok, AP_ETAG_NONE);
1434}
1435
1436/* Find a strong Etag in canonical form (lowercase, no extra spaces) within
1437 * an HTTP field value list.  Returns 1 if found, 0 if not found.
1438 */
1439AP_DECLARE(int) ap_find_etag_strong(apr_pool_t *p, const char *line,
1440                                    const char *tok)
1441{
1442    return find_list_item(p, line, tok, AP_ETAG_STRONG);
1443}
1444
1445/* Find a weak ETag in canonical form (lowercase, no extra spaces) within
1446 * an HTTP field value list.  Returns 1 if found, 0 if not found.
1447 */
1448AP_DECLARE(int) ap_find_etag_weak(apr_pool_t *p, const char *line,
1449                                  const char *tok)
1450{
1451    return find_list_item(p, line, tok, AP_ETAG_WEAK);
1452}
1453
1454/* Retrieve a token, spacing over it and returning a pointer to
1455 * the first non-white byte afterwards.  Note that these tokens
1456 * are delimited by semis and commas; and can also be delimited
1457 * by whitespace at the caller's option.
1458 */
1459
1460AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line,
1461                                int accept_white)
1462{
1463    const char *ptr = *accept_line;
1464    const char *tok_start;
1465    char *token;
1466
1467    /* Find first non-white byte */
1468
1469    while (apr_isspace(*ptr))
1470        ++ptr;
1471
1472    tok_start = ptr;
1473
1474    /* find token end, skipping over quoted strings.
1475     * (comments are already gone).
1476     */
1477
1478    while (*ptr && (accept_white || !apr_isspace(*ptr))
1479           && *ptr != ';' && *ptr != ',') {
1480        if (*ptr++ == '"')
1481            while (*ptr)
1482                if (*ptr++ == '"')
1483                    break;
1484    }
1485
1486    token = apr_pstrmemdup(p, tok_start, ptr - tok_start);
1487
1488    /* Advance accept_line pointer to the next non-white byte */
1489
1490    while (apr_isspace(*ptr))
1491        ++ptr;
1492
1493    *accept_line = ptr;
1494    return token;
1495}
1496
1497
1498/* find http tokens, see the definition of token from RFC2068 */
1499AP_DECLARE(int) ap_find_token(apr_pool_t *p, const char *line, const char *tok)
1500{
1501    const unsigned char *start_token;
1502    const unsigned char *s;
1503
1504    if (!line)
1505        return 0;
1506
1507    s = (const unsigned char *)line;
1508    for (;;) {
1509        /* find start of token, skip all stop characters, note NUL
1510         * isn't a token stop, so we don't need to test for it
1511         */
1512        while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
1513            ++s;
1514        }
1515        if (!*s) {
1516            return 0;
1517        }
1518        start_token = s;
1519        /* find end of the token */
1520        while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
1521            ++s;
1522        }
1523        if (!strncasecmp((const char *)start_token, (const char *)tok,
1524                         s - start_token)) {
1525            return 1;
1526        }
1527        if (!*s) {
1528            return 0;
1529        }
1530    }
1531}
1532
1533
1534AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line,
1535                                   const char *tok)
1536{
1537    int llen, tlen, lidx;
1538
1539    if (!line)
1540        return 0;
1541
1542    llen = strlen(line);
1543    tlen = strlen(tok);
1544    lidx = llen - tlen;
1545
1546    if (lidx < 0 ||
1547        (lidx > 0 && !(apr_isspace(line[lidx - 1]) || line[lidx - 1] == ',')))
1548        return 0;
1549
1550    return (strncasecmp(&line[lidx], tok, tlen) == 0);
1551}
1552
1553AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t *p, const char *str)
1554{
1555    char *cmd;
1556    unsigned char *d;
1557    const unsigned char *s;
1558
1559    cmd = apr_palloc(p, 2 * strlen(str) + 1);        /* Be safe */
1560    d = (unsigned char *)cmd;
1561    s = (const unsigned char *)str;
1562    for (; *s; ++s) {
1563
1564#if defined(OS2) || defined(WIN32)
1565        /*
1566         * Newlines to Win32/OS2 CreateProcess() are ill advised.
1567         * Convert them to spaces since they are effectively white
1568         * space to most applications
1569         */
1570        if (*s == '\r' || *s == '\n') {
1571             *d++ = ' ';
1572             continue;
1573         }
1574#endif
1575
1576        if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
1577            *d++ = '\\';
1578        }
1579        *d++ = *s;
1580    }
1581    *d = '\0';
1582
1583    return cmd;
1584}
1585
1586static char x2c(const char *what)
1587{
1588    register char digit;
1589
1590#if !APR_CHARSET_EBCDIC
1591    digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10
1592             : (what[0] - '0'));
1593    digit *= 16;
1594    digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10
1595              : (what[1] - '0'));
1596#else /*APR_CHARSET_EBCDIC*/
1597    char xstr[5];
1598    xstr[0]='0';
1599    xstr[1]='x';
1600    xstr[2]=what[0];
1601    xstr[3]=what[1];
1602    xstr[4]='\0';
1603    digit = apr_xlate_conv_byte(ap_hdrs_from_ascii,
1604                                0xFF & strtol(xstr, NULL, 16));
1605#endif /*APR_CHARSET_EBCDIC*/
1606    return (digit);
1607}
1608
1609/*
1610 * Unescapes a URL, leaving reserved characters intact.
1611 * Returns 0 on success, non-zero on error
1612 * Failure is due to
1613 *   bad % escape       returns HTTP_BAD_REQUEST
1614 *
1615 *   decoding %00 or a forbidden character returns HTTP_NOT_FOUND
1616 */
1617
1618static int unescape_url(char *url, const char *forbid, const char *reserved)
1619{
1620    register int badesc, badpath;
1621    char *x, *y;
1622
1623    badesc = 0;
1624    badpath = 0;
1625    /* Initial scan for first '%'. Don't bother writing values before
1626     * seeing a '%' */
1627    y = strchr(url, '%');
1628    if (y == NULL) {
1629        return OK;
1630    }
1631    for (x = y; *y; ++x, ++y) {
1632        if (*y != '%') {
1633            *x = *y;
1634        }
1635        else {
1636            if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
1637                badesc = 1;
1638                *x = '%';
1639            }
1640            else {
1641                char decoded;
1642                decoded = x2c(y + 1);
1643                if ((decoded == '\0')
1644                    || (forbid && ap_strchr_c(forbid, decoded))) {
1645                    badpath = 1;
1646                    *x = decoded;
1647                    y += 2;
1648                }
1649                else if (reserved && ap_strchr_c(reserved, decoded)) {
1650                    *x++ = *y++;
1651                    *x++ = *y++;
1652                    *x = *y;
1653                }
1654                else {
1655                    *x = decoded;
1656                    y += 2;
1657                }
1658            }
1659        }
1660    }
1661    *x = '\0';
1662    if (badesc) {
1663        return HTTP_BAD_REQUEST;
1664    }
1665    else if (badpath) {
1666        return HTTP_NOT_FOUND;
1667    }
1668    else {
1669        return OK;
1670    }
1671}
1672AP_DECLARE(int) ap_unescape_url(char *url)
1673{
1674    /* Traditional */
1675    return unescape_url(url, SLASHES, NULL);
1676}
1677AP_DECLARE(int) ap_unescape_url_keep2f(char *url, int decode_slashes)
1678{
1679    /* AllowEncodedSlashes (corrected) */
1680    if (decode_slashes) {
1681        /* no chars reserved */
1682        return unescape_url(url, NULL, NULL);
1683    } else {
1684        /* reserve (do not decode) encoded slashes */
1685        return unescape_url(url, NULL, SLASHES);
1686    }
1687}
1688#ifdef NEW_APIS
1689/* IFDEF these out until they've been thought through.
1690 * Just a germ of an API extension for now
1691 */
1692AP_DECLARE(int) ap_unescape_url_proxy(char *url)
1693{
1694    /* leave RFC1738 reserved characters intact, * so proxied URLs
1695     * don't get mangled.  Where does that leave encoded '&' ?
1696     */
1697    return unescape_url(url, NULL, "/;?");
1698}
1699AP_DECLARE(int) ap_unescape_url_reserved(char *url, const char *reserved)
1700{
1701    return unescape_url(url, NULL, reserved);
1702}
1703#endif
1704
1705AP_DECLARE(int) ap_unescape_urlencoded(char *query)
1706{
1707    char *slider;
1708
1709    /* replace plus with a space */
1710    if (query) {
1711        for (slider = query; *slider; slider++) {
1712            if (*slider == '+') {
1713                *slider = ' ';
1714            }
1715        }
1716    }
1717
1718    /* unescape everything else */
1719    return unescape_url(query, NULL, NULL);
1720}
1721
1722AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
1723                                       apr_port_t port, const request_rec *r)
1724{
1725    if (ap_is_default_port(port, r)) {
1726        return apr_pstrdup(p, hostname);
1727    }
1728    else {
1729        return apr_psprintf(p, "%s:%u", hostname, port);
1730    }
1731}
1732
1733AP_DECLARE(int) ap_unescape_all(char *url)
1734{
1735    return unescape_url(url, NULL, NULL);
1736}
1737
1738/* c2x takes an unsigned, and expects the caller has guaranteed that
1739 * 0 <= what < 256... which usually means that you have to cast to
1740 * unsigned char first, because (unsigned)(char)(x) first goes through
1741 * signed extension to an int before the unsigned cast.
1742 *
1743 * The reason for this assumption is to assist gcc code generation --
1744 * the unsigned char -> unsigned extension is already done earlier in
1745 * both uses of this code, so there's no need to waste time doing it
1746 * again.
1747 */
1748static const char c2x_table[] = "0123456789abcdef";
1749
1750static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
1751                                     unsigned char *where)
1752{
1753#if APR_CHARSET_EBCDIC
1754    what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what);
1755#endif /*APR_CHARSET_EBCDIC*/
1756    *where++ = prefix;
1757    *where++ = c2x_table[what >> 4];
1758    *where++ = c2x_table[what & 0xf];
1759    return where;
1760}
1761
1762/*
1763 * escape_path_segment() escapes a path segment, as defined in RFC 1808. This
1764 * routine is (should be) OS independent.
1765 *
1766 * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all
1767 * cases if a ':' occurs before the first '/' in the URL, the URL should be
1768 * prefixed with "./" (or the ':' escaped). In the case of Unix, this means
1769 * leaving '/' alone, but otherwise doing what escape_path_segment() does. For
1770 * efficiency reasons, we don't use escape_path_segment(), which is provided for
1771 * reference. Again, RFC 1808 is where this stuff is defined.
1772 *
1773 * If partial is set, os_escape_path() assumes that the path will be appended to
1774 * something with a '/' in it (and thus does not prefix "./").
1775 */
1776
1777AP_DECLARE(char *) ap_escape_path_segment_buffer(char *copy, const char *segment)
1778{
1779    const unsigned char *s = (const unsigned char *)segment;
1780    unsigned char *d = (unsigned char *)copy;
1781    unsigned c;
1782
1783    while ((c = *s)) {
1784        if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
1785            d = c2x(c, '%', d);
1786        }
1787        else {
1788            *d++ = c;
1789        }
1790        ++s;
1791    }
1792    *d = '\0';
1793    return copy;
1794}
1795
1796AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
1797{
1798    return ap_escape_path_segment_buffer(apr_palloc(p, 3 * strlen(segment) + 1), segment);
1799}
1800
1801AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partial)
1802{
1803    char *copy = apr_palloc(p, 3 * strlen(path) + 3);
1804    const unsigned char *s = (const unsigned char *)path;
1805    unsigned char *d = (unsigned char *)copy;
1806    unsigned c;
1807
1808    if (!partial) {
1809        const char *colon = ap_strchr_c(path, ':');
1810        const char *slash = ap_strchr_c(path, '/');
1811
1812        if (colon && (!slash || colon < slash)) {
1813            *d++ = '.';
1814            *d++ = '/';
1815        }
1816    }
1817    while ((c = *s)) {
1818        if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
1819            d = c2x(c, '%', d);
1820        }
1821        else {
1822            *d++ = c;
1823        }
1824        ++s;
1825    }
1826    *d = '\0';
1827    return copy;
1828}
1829
1830AP_DECLARE(char *) ap_escape_urlencoded_buffer(char *copy, const char *buffer)
1831{
1832    const unsigned char *s = (const unsigned char *)buffer;
1833    unsigned char *d = (unsigned char *)copy;
1834    unsigned c;
1835
1836    while ((c = *s)) {
1837        if (TEST_CHAR(c, T_ESCAPE_URLENCODED)) {
1838            d = c2x(c, '%', d);
1839        }
1840        else if (c == ' ') {
1841            *d++ = '+';
1842        }
1843        else {
1844            *d++ = c;
1845        }
1846        ++s;
1847    }
1848    *d = '\0';
1849    return copy;
1850}
1851
1852AP_DECLARE(char *) ap_escape_urlencoded(apr_pool_t *p, const char *buffer)
1853{
1854    return ap_escape_urlencoded_buffer(apr_palloc(p, 3 * strlen(buffer) + 1), buffer);
1855}
1856
1857/* ap_escape_uri is now a macro for os_escape_path */
1858
1859AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
1860{
1861    int i, j;
1862    char *x;
1863
1864    /* first, count the number of extra characters */
1865    for (i = 0, j = 0; s[i] != '\0'; i++)
1866        if (s[i] == '<' || s[i] == '>')
1867            j += 3;
1868        else if (s[i] == '&')
1869            j += 4;
1870        else if (s[i] == '"')
1871            j += 5;
1872        else if (toasc && !apr_isascii(s[i]))
1873            j += 5;
1874
1875    if (j == 0)
1876        return apr_pstrmemdup(p, s, i);
1877
1878    x = apr_palloc(p, i + j + 1);
1879    for (i = 0, j = 0; s[i] != '\0'; i++, j++)
1880        if (s[i] == '<') {
1881            memcpy(&x[j], "&lt;", 4);
1882            j += 3;
1883        }
1884        else if (s[i] == '>') {
1885            memcpy(&x[j], "&gt;", 4);
1886            j += 3;
1887        }
1888        else if (s[i] == '&') {
1889            memcpy(&x[j], "&amp;", 5);
1890            j += 4;
1891        }
1892        else if (s[i] == '"') {
1893            memcpy(&x[j], "&quot;", 6);
1894            j += 5;
1895        }
1896        else if (toasc && !apr_isascii(s[i])) {
1897            char *esc = apr_psprintf(p, "&#%3.3d;", (unsigned char)s[i]);
1898            memcpy(&x[j], esc, 6);
1899            j += 5;
1900        }
1901        else
1902            x[j] = s[i];
1903
1904    x[j] = '\0';
1905    return x;
1906}
1907AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str)
1908{
1909    char *ret;
1910    unsigned char *d;
1911    const unsigned char *s;
1912    apr_size_t length, escapes = 0;
1913
1914    if (!str) {
1915        return NULL;
1916    }
1917
1918    /* Compute how many characters need to be escaped */
1919    s = (const unsigned char *)str;
1920    for (; *s; ++s) {
1921        if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
1922            escapes++;
1923        }
1924    }
1925
1926    /* Compute the length of the input string, including NULL */
1927    length = s - (const unsigned char *)str + 1;
1928
1929    /* Fast path: nothing to escape */
1930    if (escapes == 0) {
1931        return apr_pmemdup(p, str, length);
1932    }
1933
1934    /* Each escaped character needs up to 3 extra bytes (0 --> \x00) */
1935    ret = apr_palloc(p, length + 3 * escapes);
1936    d = (unsigned char *)ret;
1937    s = (const unsigned char *)str;
1938    for (; *s; ++s) {
1939        if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
1940            *d++ = '\\';
1941            switch(*s) {
1942            case '\b':
1943                *d++ = 'b';
1944                break;
1945            case '\n':
1946                *d++ = 'n';
1947                break;
1948            case '\r':
1949                *d++ = 'r';
1950                break;
1951            case '\t':
1952                *d++ = 't';
1953                break;
1954            case '\v':
1955                *d++ = 'v';
1956                break;
1957            case '\\':
1958            case '"':
1959                *d++ = *s;
1960                break;
1961            default:
1962                c2x(*s, 'x', d);
1963                d += 3;
1964            }
1965        }
1966        else {
1967            *d++ = *s;
1968        }
1969    }
1970    *d = '\0';
1971
1972    return ret;
1973}
1974
1975AP_DECLARE(apr_size_t) ap_escape_errorlog_item(char *dest, const char *source,
1976                                               apr_size_t buflen)
1977{
1978    unsigned char *d, *ep;
1979    const unsigned char *s;
1980
1981    if (!source || !buflen) { /* be safe */
1982        return 0;
1983    }
1984
1985    d = (unsigned char *)dest;
1986    s = (const unsigned char *)source;
1987    ep = d + buflen - 1;
1988
1989    for (; d < ep && *s; ++s) {
1990
1991        if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
1992            *d++ = '\\';
1993            if (d >= ep) {
1994                --d;
1995                break;
1996            }
1997
1998            switch(*s) {
1999            case '\b':
2000                *d++ = 'b';
2001                break;
2002            case '\n':
2003                *d++ = 'n';
2004                break;
2005            case '\r':
2006                *d++ = 'r';
2007                break;
2008            case '\t':
2009                *d++ = 't';
2010                break;
2011            case '\v':
2012                *d++ = 'v';
2013                break;
2014            case '\\':
2015                *d++ = *s;
2016                break;
2017            case '"': /* no need for this in error log */
2018                d[-1] = *s;
2019                break;
2020            default:
2021                if (d >= ep - 2) {
2022                    ep = --d; /* break the for loop as well */
2023                    break;
2024                }
2025                c2x(*s, 'x', d);
2026                d += 3;
2027            }
2028        }
2029        else {
2030            *d++ = *s;
2031        }
2032    }
2033    *d = '\0';
2034
2035    return (d - (unsigned char *)dest);
2036}
2037
2038AP_DECLARE(void) ap_bin2hex(const void *src, apr_size_t srclen, char *dest)
2039{
2040    const unsigned char *in = src;
2041    apr_size_t i;
2042
2043    for (i = 0; i < srclen; i++) {
2044        *dest++ = c2x_table[in[i] >> 4];
2045        *dest++ = c2x_table[in[i] & 0xf];
2046    }
2047    *dest = '\0';
2048}
2049
2050AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path)
2051{
2052    apr_finfo_t finfo;
2053
2054    if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS)
2055        return 0;                /* in error condition, just return no */
2056
2057    return (finfo.filetype == APR_DIR);
2058}
2059
2060AP_DECLARE(int) ap_is_rdirectory(apr_pool_t *p, const char *path)
2061{
2062    apr_finfo_t finfo;
2063
2064    if (apr_stat(&finfo, path, APR_FINFO_LINK | APR_FINFO_TYPE, p) != APR_SUCCESS)
2065        return 0;                /* in error condition, just return no */
2066
2067    return (finfo.filetype == APR_DIR);
2068}
2069
2070AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1,
2071                                  const char *src2)
2072{
2073    apr_size_t len1, len2;
2074    char *path;
2075
2076    len1 = strlen(src1);
2077    len2 = strlen(src2);
2078     /* allocate +3 for '/' delimiter, trailing NULL and overallocate
2079      * one extra byte to allow the caller to add a trailing '/'
2080      */
2081    path = (char *)apr_palloc(a, len1 + len2 + 3);
2082    if (len1 == 0) {
2083        *path = '/';
2084        memcpy(path + 1, src2, len2 + 1);
2085    }
2086    else {
2087        char *next;
2088        memcpy(path, src1, len1);
2089        next = path + len1;
2090        if (next[-1] != '/') {
2091            *next++ = '/';
2092        }
2093        memcpy(next, src2, len2 + 1);
2094    }
2095    return path;
2096}
2097
2098/*
2099 * Check for an absoluteURI syntax (see section 3.2 in RFC2068).
2100 */
2101AP_DECLARE(int) ap_is_url(const char *u)
2102{
2103    register int x;
2104
2105    for (x = 0; u[x] != ':'; x++) {
2106        if ((!u[x]) ||
2107            ((!apr_isalpha(u[x])) && (!apr_isdigit(u[x])) &&
2108             (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) {
2109            return 0;
2110        }
2111    }
2112
2113    return (x ? 1 : 0);                /* If the first character is ':', it's broken, too */
2114}
2115
2116AP_DECLARE(int) ap_ind(const char *s, char c)
2117{
2118    const char *p = ap_strchr_c(s, c);
2119
2120    if (p == NULL)
2121        return -1;
2122    return p - s;
2123}
2124
2125AP_DECLARE(int) ap_rind(const char *s, char c)
2126{
2127    const char *p = ap_strrchr_c(s, c);
2128
2129    if (p == NULL)
2130        return -1;
2131    return p - s;
2132}
2133
2134AP_DECLARE(void) ap_str_tolower(char *str)
2135{
2136    while (*str) {
2137        *str = apr_tolower(*str);
2138        ++str;
2139    }
2140}
2141
2142AP_DECLARE(void) ap_str_toupper(char *str)
2143{
2144    while (*str) {
2145        *str = apr_toupper(*str);
2146        ++str;
2147    }
2148}
2149
2150/*
2151 * We must return a FQDN
2152 */
2153char *ap_get_local_host(apr_pool_t *a)
2154{
2155#ifndef MAXHOSTNAMELEN
2156#define MAXHOSTNAMELEN 256
2157#endif
2158    char str[MAXHOSTNAMELEN + 1];
2159    char *server_hostname = NULL;
2160    apr_sockaddr_t *sockaddr;
2161    char *hostname;
2162
2163    if (apr_gethostname(str, sizeof(str) - 1, a) != APR_SUCCESS) {
2164        ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, APLOGNO(00556)
2165                     "%s: apr_gethostname() failed to determine ServerName",
2166                     ap_server_argv0);
2167    } else {
2168        str[sizeof(str) - 1] = '\0';
2169        if (apr_sockaddr_info_get(&sockaddr, str, APR_UNSPEC, 0, 0, a) == APR_SUCCESS) {
2170            if ( (apr_getnameinfo(&hostname, sockaddr, 0) == APR_SUCCESS) &&
2171                (ap_strchr_c(hostname, '.')) ) {
2172                server_hostname = apr_pstrdup(a, hostname);
2173                return server_hostname;
2174            } else if (ap_strchr_c(str, '.')) {
2175                server_hostname = apr_pstrdup(a, str);
2176            } else {
2177                apr_sockaddr_ip_get(&hostname, sockaddr);
2178                server_hostname = apr_pstrdup(a, hostname);
2179            }
2180        } else {
2181            ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, APLOGNO(00557)
2182                         "%s: apr_sockaddr_info_get() failed for %s",
2183                         ap_server_argv0, str);
2184        }
2185    }
2186
2187    if (!server_hostname)
2188        server_hostname = apr_pstrdup(a, "127.0.0.1");
2189
2190    ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a, APLOGNO(00558)
2191                 "%s: Could not reliably determine the server's fully qualified "
2192                 "domain name, using %s. Set the 'ServerName' directive globally "
2193                 "to suppress this message",
2194                 ap_server_argv0, server_hostname);
2195
2196    return server_hostname;
2197}
2198
2199/* simple 'pool' alloc()ing glue to apr_base64.c
2200 */
2201AP_DECLARE(char *) ap_pbase64decode(apr_pool_t *p, const char *bufcoded)
2202{
2203    char *decoded;
2204    int l;
2205
2206    decoded = (char *) apr_palloc(p, 1 + apr_base64_decode_len(bufcoded));
2207    l = apr_base64_decode(decoded, bufcoded);
2208    decoded[l] = '\0'; /* make binary sequence into string */
2209
2210    return decoded;
2211}
2212
2213AP_DECLARE(char *) ap_pbase64encode(apr_pool_t *p, char *string)
2214{
2215    char *encoded;
2216    int l = strlen(string);
2217
2218    encoded = (char *) apr_palloc(p, 1 + apr_base64_encode_len(l));
2219    l = apr_base64_encode(encoded, string, l);
2220    encoded[l] = '\0'; /* make binary sequence into string */
2221
2222    return encoded;
2223}
2224
2225/* we want to downcase the type/subtype for comparison purposes
2226 * but nothing else because ;parameter=foo values are case sensitive.
2227 * XXX: in truth we want to downcase parameter names... but really,
2228 * apache has never handled parameters and such correctly.  You
2229 * also need to compress spaces and such to be able to compare
2230 * properly. -djg
2231 */
2232AP_DECLARE(void) ap_content_type_tolower(char *str)
2233{
2234    char *semi;
2235
2236    semi = strchr(str, ';');
2237    if (semi) {
2238        *semi = '\0';
2239    }
2240
2241    ap_str_tolower(str);
2242
2243    if (semi) {
2244        *semi = ';';
2245    }
2246}
2247
2248/*
2249 * Given a string, replace any bare " with \" .
2250 */
2251AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring)
2252{
2253    int newlen = 0;
2254    const char *inchr = instring;
2255    char *outchr, *outstring;
2256
2257    /*
2258     * Look through the input string, jogging the length of the output
2259     * string up by an extra byte each time we find an unescaped ".
2260     */
2261    while (*inchr != '\0') {
2262        newlen++;
2263        if (*inchr == '"') {
2264            newlen++;
2265        }
2266        /*
2267         * If we find a slosh, and it's not the last byte in the string,
2268         * it's escaping something - advance past both bytes.
2269         */
2270        if ((*inchr == '\\') && (inchr[1] != '\0')) {
2271            inchr++;
2272            newlen++;
2273        }
2274        inchr++;
2275    }
2276    outstring = apr_palloc(p, newlen + 1);
2277    inchr = instring;
2278    outchr = outstring;
2279    /*
2280     * Now copy the input string to the output string, inserting a slosh
2281     * in front of every " that doesn't already have one.
2282     */
2283    while (*inchr != '\0') {
2284        if ((*inchr == '\\') && (inchr[1] != '\0')) {
2285            *outchr++ = *inchr++;
2286            *outchr++ = *inchr++;
2287        }
2288        if (*inchr == '"') {
2289            *outchr++ = '\\';
2290        }
2291        if (*inchr != '\0') {
2292            *outchr++ = *inchr++;
2293        }
2294    }
2295    *outchr = '\0';
2296    return outstring;
2297}
2298
2299/*
2300 * Given a string, append the PID deliminated by delim.
2301 * Usually used to create a pid-appended filepath name
2302 * (eg: /a/b/foo -> /a/b/foo.6726). A function, and not
2303 * a macro, to avoid unistd.h dependency
2304 */
2305AP_DECLARE(char *) ap_append_pid(apr_pool_t *p, const char *string,
2306                                    const char *delim)
2307{
2308    return apr_psprintf(p, "%s%s%" APR_PID_T_FMT, string,
2309                        delim, getpid());
2310
2311}
2312
2313/**
2314 * Parse a given timeout parameter string into an apr_interval_time_t value.
2315 * The unit of the time interval is given as postfix string to the numeric
2316 * string. Currently the following units are understood:
2317 *
2318 * ms    : milliseconds
2319 * s     : seconds
2320 * mi[n] : minutes
2321 * h     : hours
2322 *
2323 * If no unit is contained in the given timeout parameter the default_time_unit
2324 * will be used instead.
2325 * @param timeout_parameter The string containing the timeout parameter.
2326 * @param timeout The timeout value to be returned.
2327 * @param default_time_unit The default time unit to use if none is specified
2328 * in timeout_parameter.
2329 * @return Status value indicating whether the parsing was successful or not.
2330 */
2331AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
2332                                               const char *timeout_parameter,
2333                                               apr_interval_time_t *timeout,
2334                                               const char *default_time_unit)
2335{
2336    char *endp;
2337    const char *time_str;
2338    apr_int64_t tout;
2339
2340    tout = apr_strtoi64(timeout_parameter, &endp, 10);
2341    if (errno) {
2342        return errno;
2343    }
2344    if (!endp || !*endp) {
2345        time_str = default_time_unit;
2346    }
2347    else {
2348        time_str = endp;
2349    }
2350
2351    switch (*time_str) {
2352        /* Time is in seconds */
2353    case 's':
2354        *timeout = (apr_interval_time_t) apr_time_from_sec(tout);
2355        break;
2356    case 'h':
2357        /* Time is in hours */
2358        *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 3600);
2359        break;
2360    case 'm':
2361        switch (*(++time_str)) {
2362        /* Time is in milliseconds */
2363        case 's':
2364            *timeout = (apr_interval_time_t) tout * 1000;
2365            break;
2366        /* Time is in minutes */
2367        case 'i':
2368            *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 60);
2369            break;
2370        default:
2371            return APR_EGENERAL;
2372        }
2373        break;
2374    default:
2375        return APR_EGENERAL;
2376    }
2377    return APR_SUCCESS;
2378}
2379
2380/**
2381 * Determine if a request has a request body or not.
2382 *
2383 * @param r the request_rec of the request
2384 * @return truth value
2385 */
2386AP_DECLARE(int) ap_request_has_body(request_rec *r)
2387{
2388    apr_off_t cl;
2389    char *estr;
2390    const char *cls;
2391    int has_body;
2392
2393    has_body = (!r->header_only
2394                && (r->kept_body
2395                    || apr_table_get(r->headers_in, "Transfer-Encoding")
2396                    || ( (cls = apr_table_get(r->headers_in, "Content-Length"))
2397                        && (apr_strtoff(&cl, cls, &estr, 10) == APR_SUCCESS)
2398                        && (!*estr)
2399                        && (cl > 0) )
2400                    )
2401                );
2402    return has_body;
2403}
2404
2405AP_DECLARE_NONSTD(apr_status_t) ap_pool_cleanup_set_null(void *data_)
2406{
2407    void **ptr = (void **)data_;
2408    *ptr = NULL;
2409    return APR_SUCCESS;
2410}
2411
2412AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest) {
2413
2414    for ( ; *src; src++, dest++)
2415    {
2416        if (!apr_isprint(*src))
2417            *dest = 'x';
2418        else if (!apr_isalnum(*src))
2419            *dest = '_';
2420        else
2421            *dest = (char)*src;
2422    }
2423    *dest = '\0';
2424    return APR_SUCCESS;
2425
2426}
2427
2428AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src,
2429                                        const char **dest)
2430{
2431    char *new = apr_palloc(p, strlen(src)+1);
2432    if (!new)
2433        return APR_ENOMEM;
2434    *dest = new;
2435    return ap_str2_alnum(src, new);
2436}
2437
2438/**
2439 * Read the body and parse any form found, which must be of the
2440 * type application/x-www-form-urlencoded.
2441 *
2442 * Name/value pairs are returned in an array, with the names as
2443 * strings with a maximum length of HUGE_STRING_LEN, and the
2444 * values as bucket brigades. This allows values to be arbitrarily
2445 * large.
2446 *
2447 * All url-encoding is removed from both the names and the values
2448 * on the fly. The names are interpreted as strings, while the
2449 * values are interpreted as blocks of binary data, that may
2450 * contain the 0 character.
2451 *
2452 * In order to ensure that resource limits are not exceeded, a
2453 * maximum size must be provided. If the sum of the lengths of
2454 * the names and the values exceed this size, this function
2455 * will return HTTP_REQUEST_ENTITY_TOO_LARGE.
2456 *
2457 * An optional number of parameters can be provided, if the number
2458 * of parameters provided exceeds this amount, this function will
2459 * return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
2460 * no limit is imposed, and the number of parameters is in turn
2461 * constrained by the size parameter above.
2462 *
2463 * This function honours any kept_body configuration, and the
2464 * original raw request body will be saved to the kept_body brigade
2465 * if so configured, just as ap_discard_request_body does.
2466 *
2467 * NOTE: File upload is not yet supported, but can be without change
2468 * to the function call.
2469 */
2470
2471/* form parsing stuff */
2472typedef enum {
2473    FORM_NORMAL,
2474    FORM_AMP,
2475    FORM_NAME,
2476    FORM_VALUE,
2477    FORM_PERCENTA,
2478    FORM_PERCENTB,
2479    FORM_ABORT
2480} ap_form_type_t;
2481
2482AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f,
2483                                   apr_array_header_t **ptr,
2484                                   apr_size_t num, apr_size_t usize)
2485{
2486    apr_bucket_brigade *bb = NULL;
2487    int seen_eos = 0;
2488    char buffer[HUGE_STRING_LEN + 1];
2489    const char *ct;
2490    apr_size_t offset = 0;
2491    apr_ssize_t size;
2492    ap_form_type_t state = FORM_NAME, percent = FORM_NORMAL;
2493    ap_form_pair_t *pair = NULL;
2494    apr_array_header_t *pairs = apr_array_make(r->pool, 4, sizeof(ap_form_pair_t));
2495
2496    char hi = 0;
2497    char low = 0;
2498
2499    *ptr = pairs;
2500
2501    /* sanity check - we only support forms for now */
2502    ct = apr_table_get(r->headers_in, "Content-Type");
2503    if (!ct || strncasecmp("application/x-www-form-urlencoded", ct, 33)) {
2504        return ap_discard_request_body(r);
2505    }
2506
2507    if (usize > APR_SIZE_MAX >> 1)
2508        size = APR_SIZE_MAX >> 1;
2509    else
2510        size = usize;
2511
2512    if (!f) {
2513        f = r->input_filters;
2514    }
2515
2516    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
2517    do {
2518        apr_bucket *bucket = NULL, *last = NULL;
2519
2520        int rv = ap_get_brigade(f, bb, AP_MODE_READBYTES,
2521                                APR_BLOCK_READ, HUGE_STRING_LEN);
2522        if (rv != APR_SUCCESS) {
2523            apr_brigade_destroy(bb);
2524            return (rv == AP_FILTER_ERROR) ? rv : HTTP_BAD_REQUEST;
2525        }
2526
2527        for (bucket = APR_BRIGADE_FIRST(bb);
2528             bucket != APR_BRIGADE_SENTINEL(bb);
2529             last = bucket, bucket = APR_BUCKET_NEXT(bucket)) {
2530            const char *data;
2531            apr_size_t len, slide;
2532
2533            if (last) {
2534                apr_bucket_delete(last);
2535            }
2536            if (APR_BUCKET_IS_EOS(bucket)) {
2537                seen_eos = 1;
2538                break;
2539            }
2540            if (bucket->length == 0) {
2541                continue;
2542            }
2543
2544            rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
2545            if (rv != APR_SUCCESS) {
2546                apr_brigade_destroy(bb);
2547                return HTTP_BAD_REQUEST;
2548            }
2549
2550            slide = len;
2551            while (state != FORM_ABORT && slide-- > 0 && size >= 0 && num != 0) {
2552                char c = *data++;
2553                if ('+' == c) {
2554                    c = ' ';
2555                }
2556                else if ('&' == c) {
2557                    state = FORM_AMP;
2558                }
2559                if ('%' == c) {
2560                    percent = FORM_PERCENTA;
2561                    continue;
2562                }
2563                if (FORM_PERCENTA == percent) {
2564                    if (c >= 'a') {
2565                        hi = c - 'a' + 10;
2566                    }
2567                    else if (c >= 'A') {
2568                        hi = c - 'A' + 10;
2569                    }
2570                    else if (c >= '0') {
2571                        hi = c - '0';
2572                    }
2573                    hi = hi << 4;
2574                    percent = FORM_PERCENTB;
2575                    continue;
2576                }
2577                if (FORM_PERCENTB == percent) {
2578                    if (c >= 'a') {
2579                        low = c - 'a' + 10;
2580                    }
2581                    else if (c >= 'A') {
2582                        low = c - 'A' + 10;
2583                    }
2584                    else if (c >= '0') {
2585                        low = c - '0';
2586                    }
2587                    c = low | hi;
2588                    percent = FORM_NORMAL;
2589                }
2590                switch (state) {
2591                    case FORM_AMP:
2592                        if (pair) {
2593                            const char *tmp = apr_pmemdup(r->pool, buffer, offset);
2594                            apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
2595                            APR_BRIGADE_INSERT_TAIL(pair->value, b);
2596                        }
2597                        state = FORM_NAME;
2598                        pair = NULL;
2599                        offset = 0;
2600                        num--;
2601                        break;
2602                    case FORM_NAME:
2603                        if (offset < HUGE_STRING_LEN) {
2604                            if ('=' == c) {
2605                                buffer[offset] = 0;
2606                                offset = 0;
2607                                pair = (ap_form_pair_t *) apr_array_push(pairs);
2608                                pair->name = apr_pstrdup(r->pool, buffer);
2609                                pair->value = apr_brigade_create(r->pool, r->connection->bucket_alloc);
2610                                state = FORM_VALUE;
2611                            }
2612                            else {
2613                                buffer[offset++] = c;
2614                                size--;
2615                            }
2616                        }
2617                        else {
2618                            state = FORM_ABORT;
2619                        }
2620                        break;
2621                    case FORM_VALUE:
2622                        if (offset >= HUGE_STRING_LEN) {
2623                            const char *tmp = apr_pmemdup(r->pool, buffer, offset);
2624                            apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
2625                            APR_BRIGADE_INSERT_TAIL(pair->value, b);
2626                            offset = 0;
2627                        }
2628                        buffer[offset++] = c;
2629                        size--;
2630                        break;
2631                    default:
2632                        break;
2633                }
2634            }
2635
2636        }
2637
2638        apr_brigade_cleanup(bb);
2639    } while (!seen_eos);
2640
2641    if (FORM_ABORT == state || size < 0 || num == 0) {
2642        return HTTP_REQUEST_ENTITY_TOO_LARGE;
2643    }
2644    else if (FORM_VALUE == state && pair && offset > 0) {
2645        const char *tmp = apr_pmemdup(r->pool, buffer, offset);
2646        apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
2647        APR_BRIGADE_INSERT_TAIL(pair->value, b);
2648    }
2649
2650    return OK;
2651
2652}
2653
2654#define VARBUF_SMALL_SIZE 2048
2655#define VARBUF_MAX_SIZE   (APR_SIZE_MAX - 1 -                                \
2656                           APR_ALIGN_DEFAULT(sizeof(struct ap_varbuf_info)))
2657
2658struct ap_varbuf_info {
2659    struct apr_memnode_t *node;
2660    apr_allocator_t *allocator;
2661};
2662
2663static apr_status_t varbuf_cleanup(void *info_)
2664{
2665    struct ap_varbuf_info *info = info_;
2666    info->node->next = NULL;
2667    apr_allocator_free(info->allocator, info->node);
2668    return APR_SUCCESS;
2669}
2670
2671const char nul = '\0';
2672static char * const varbuf_empty = (char *)&nul;
2673
2674AP_DECLARE(void) ap_varbuf_init(apr_pool_t *p, struct ap_varbuf *vb,
2675                                apr_size_t init_size)
2676{
2677    vb->buf = varbuf_empty;
2678    vb->avail = 0;
2679    vb->strlen = AP_VARBUF_UNKNOWN;
2680    vb->pool = p;
2681    vb->info = NULL;
2682
2683    ap_varbuf_grow(vb, init_size);
2684}
2685
2686AP_DECLARE(void) ap_varbuf_grow(struct ap_varbuf *vb, apr_size_t new_len)
2687{
2688    apr_memnode_t *new_node = NULL;
2689    apr_allocator_t *allocator;
2690    struct ap_varbuf_info *new_info;
2691    char *new;
2692
2693    AP_DEBUG_ASSERT(vb->strlen == AP_VARBUF_UNKNOWN || vb->avail >= vb->strlen);
2694
2695    if (new_len <= vb->avail)
2696        return;
2697
2698    if (new_len < 2 * vb->avail && vb->avail < VARBUF_MAX_SIZE/2) {
2699        /* at least double the size, to avoid repeated reallocations */
2700        new_len = 2 * vb->avail;
2701    }
2702    else if (new_len > VARBUF_MAX_SIZE) {
2703        apr_abortfunc_t abort_fn = apr_pool_abort_get(vb->pool);
2704        ap_assert(abort_fn != NULL);
2705        abort_fn(APR_ENOMEM);
2706        return;
2707    }
2708
2709    new_len++;  /* add space for trailing \0 */
2710    if (new_len <= VARBUF_SMALL_SIZE) {
2711        new_len = APR_ALIGN_DEFAULT(new_len);
2712        new = apr_palloc(vb->pool, new_len);
2713        if (vb->avail && vb->strlen != 0) {
2714            AP_DEBUG_ASSERT(vb->buf != NULL);
2715            AP_DEBUG_ASSERT(vb->buf != varbuf_empty);
2716            if (new == vb->buf + vb->avail + 1) {
2717                /* We are lucky: the new memory lies directly after our old
2718                 * buffer, we can now use both.
2719                 */
2720                vb->avail += new_len;
2721                return;
2722            }
2723            else {
2724                /* copy up to vb->strlen + 1 bytes */
2725                memcpy(new, vb->buf, vb->strlen == AP_VARBUF_UNKNOWN ?
2726                                     vb->avail + 1 : vb->strlen + 1);
2727            }
2728        }
2729        else {
2730            *new = '\0';
2731        }
2732        vb->avail = new_len - 1;
2733        vb->buf = new;
2734        return;
2735    }
2736
2737    /* The required block is rather larger. Use allocator directly so that
2738     * the memory can be freed independently from the pool. */
2739    allocator = apr_pool_allocator_get(vb->pool);
2740    if (new_len <= VARBUF_MAX_SIZE)
2741        new_node = apr_allocator_alloc(allocator,
2742                                       new_len + APR_ALIGN_DEFAULT(sizeof(*new_info)));
2743    if (!new_node) {
2744        apr_abortfunc_t abort_fn = apr_pool_abort_get(vb->pool);
2745        ap_assert(abort_fn != NULL);
2746        abort_fn(APR_ENOMEM);
2747        return;
2748    }
2749    new_info = (struct ap_varbuf_info *)new_node->first_avail;
2750    new_node->first_avail += APR_ALIGN_DEFAULT(sizeof(*new_info));
2751    new_info->node = new_node;
2752    new_info->allocator = allocator;
2753    new = new_node->first_avail;
2754    AP_DEBUG_ASSERT(new_node->endp - new_node->first_avail >= new_len);
2755    new_len = new_node->endp - new_node->first_avail;
2756
2757    if (vb->avail && vb->strlen != 0)
2758        memcpy(new, vb->buf, vb->strlen == AP_VARBUF_UNKNOWN ?
2759                             vb->avail + 1 : vb->strlen + 1);
2760    else
2761        *new = '\0';
2762    if (vb->info)
2763        apr_pool_cleanup_run(vb->pool, vb->info, varbuf_cleanup);
2764    apr_pool_cleanup_register(vb->pool, new_info, varbuf_cleanup,
2765                              apr_pool_cleanup_null);
2766    vb->info = new_info;
2767    vb->buf = new;
2768    vb->avail = new_len - 1;
2769}
2770
2771AP_DECLARE(void) ap_varbuf_strmemcat(struct ap_varbuf *vb, const char *str,
2772                                     int len)
2773{
2774    if (len == 0)
2775        return;
2776    if (!vb->avail) {
2777        ap_varbuf_grow(vb, len);
2778        memcpy(vb->buf, str, len);
2779        vb->buf[len] = '\0';
2780        vb->strlen = len;
2781        return;
2782    }
2783    if (vb->strlen == AP_VARBUF_UNKNOWN)
2784        vb->strlen = strlen(vb->buf);
2785    ap_varbuf_grow(vb, vb->strlen + len);
2786    memcpy(vb->buf + vb->strlen, str, len);
2787    vb->strlen += len;
2788    vb->buf[vb->strlen] = '\0';
2789}
2790
2791AP_DECLARE(void) ap_varbuf_free(struct ap_varbuf *vb)
2792{
2793    if (vb->info) {
2794        apr_pool_cleanup_run(vb->pool, vb->info, varbuf_cleanup);
2795        vb->info = NULL;
2796    }
2797    vb->buf = NULL;
2798}
2799
2800AP_DECLARE(char *) ap_varbuf_pdup(apr_pool_t *p, struct ap_varbuf *buf,
2801                                  const char *prepend, apr_size_t prepend_len,
2802                                  const char *append, apr_size_t append_len,
2803                                  apr_size_t *new_len)
2804{
2805    apr_size_t i = 0;
2806    struct iovec vec[3];
2807
2808    if (prepend) {
2809        vec[i].iov_base = (void *)prepend;
2810        vec[i].iov_len = prepend_len;
2811        i++;
2812    }
2813    if (buf->avail && buf->strlen) {
2814        if (buf->strlen == AP_VARBUF_UNKNOWN)
2815            buf->strlen = strlen(buf->buf);
2816        vec[i].iov_base = (void *)buf->buf;
2817        vec[i].iov_len = buf->strlen;
2818        i++;
2819    }
2820    if (append) {
2821        vec[i].iov_base = (void *)append;
2822        vec[i].iov_len = append_len;
2823        i++;
2824    }
2825    if (i)
2826        return apr_pstrcatv(p, vec, i, new_len);
2827
2828    if (new_len)
2829        *new_len = 0;
2830    return "";
2831}
2832
2833AP_DECLARE(apr_status_t) ap_varbuf_regsub(struct ap_varbuf *vb,
2834                                          const char *input,
2835                                          const char *source,
2836                                          apr_size_t nmatch,
2837                                          ap_regmatch_t pmatch[],
2838                                          apr_size_t maxlen)
2839{
2840    return regsub_core(NULL, NULL, vb, input, source, nmatch, pmatch, maxlen);
2841}
2842
2843static const char * const oom_message = "[crit] Memory allocation failed, "
2844                                        "aborting process." APR_EOL_STR;
2845
2846AP_DECLARE(void) ap_abort_on_oom()
2847{
2848    int written, count = strlen(oom_message);
2849    const char *buf = oom_message;
2850    do {
2851        written = write(STDERR_FILENO, buf, count);
2852        if (written == count)
2853            break;
2854        if (written > 0) {
2855            buf += written;
2856            count -= written;
2857        }
2858    } while (written >= 0 || errno == EINTR);
2859    abort();
2860}
2861
2862AP_DECLARE(void *) ap_malloc(size_t size)
2863{
2864    void *p = malloc(size);
2865    if (p == NULL && size != 0)
2866        ap_abort_on_oom();
2867    return p;
2868}
2869
2870AP_DECLARE(void *) ap_calloc(size_t nelem, size_t size)
2871{
2872    void *p = calloc(nelem, size);
2873    if (p == NULL && nelem != 0 && size != 0)
2874        ap_abort_on_oom();
2875    return p;
2876}
2877
2878AP_DECLARE(void *) ap_realloc(void *ptr, size_t size)
2879{
2880    void *p = realloc(ptr, size);
2881    if (p == NULL && size != 0)
2882        ap_abort_on_oom();
2883    return p;
2884}
2885
2886AP_DECLARE(void) ap_get_sload(ap_sload_t *ld)
2887{
2888    int i, j, server_limit, thread_limit;
2889    int ready = 0;
2890    int busy = 0;
2891    int total;
2892    ap_generation_t mpm_generation;
2893
2894    /* preload errored fields, we overwrite */
2895    ld->idle = -1;
2896    ld->busy = -1;
2897    ld->bytes_served = 0;
2898    ld->access_count = 0;
2899
2900    ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation);
2901    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
2902    ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
2903
2904    for (i = 0; i < server_limit; i++) {
2905        process_score *ps;
2906        ps = ap_get_scoreboard_process(i);
2907
2908        for (j = 0; j < thread_limit; j++) {
2909            int res;
2910            worker_score *ws = NULL;
2911            ws = &ap_scoreboard_image->servers[i][j];
2912            res = ws->status;
2913
2914            if (!ps->quiescing && ps->pid) {
2915                if (res == SERVER_READY && ps->generation == mpm_generation) {
2916                    ready++;
2917                }
2918                else if (res != SERVER_DEAD &&
2919                         res != SERVER_STARTING && res != SERVER_IDLE_KILL &&
2920                         ps->generation == mpm_generation) {
2921                    busy++;
2922                }
2923            }
2924
2925            if (ap_extended_status && !ps->quiescing && ps->pid) {
2926                if (ws->access_count != 0
2927                    || (res != SERVER_READY && res != SERVER_DEAD)) {
2928                    ld->access_count += ws->access_count;
2929                    ld->bytes_served += ws->bytes_served;
2930                }
2931            }
2932        }
2933    }
2934    total = busy + ready;
2935    if (total) {
2936        ld->idle = ready * 100 / total;
2937        ld->busy = busy * 100 / total;
2938    }
2939}
2940
2941AP_DECLARE(void) ap_get_loadavg(ap_loadavg_t *ld)
2942{
2943    /* preload errored fields, we overwrite */
2944    ld->loadavg = -1.0;
2945    ld->loadavg5 = -1.0;
2946    ld->loadavg15 = -1.0;
2947
2948#if HAVE_GETLOADAVG
2949    {
2950        double la[3];
2951        int num;
2952
2953        num = getloadavg(la, 3);
2954        if (num > 0) {
2955            ld->loadavg = (float)la[0];
2956        }
2957        if (num > 1) {
2958            ld->loadavg5 = (float)la[1];
2959        }
2960        if (num > 2) {
2961            ld->loadavg15 = (float)la[2];
2962        }
2963    }
2964#endif
2965}
2966
2967AP_DECLARE(char *) ap_get_exec_line(apr_pool_t *p,
2968                                    const char *cmd,
2969                                    const char * const * argv)
2970{
2971    char buf[MAX_STRING_LEN];
2972    apr_procattr_t *procattr;
2973    apr_proc_t *proc;
2974    apr_file_t *fp;
2975    apr_size_t nbytes = 1;
2976    char c;
2977    int k;
2978
2979    if (apr_procattr_create(&procattr, p) != APR_SUCCESS)
2980        return NULL;
2981    if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_FULL_BLOCK,
2982                            APR_FULL_BLOCK) != APR_SUCCESS)
2983        return NULL;
2984    if (apr_procattr_dir_set(procattr,
2985                             ap_make_dirstr_parent(p, cmd)) != APR_SUCCESS)
2986        return NULL;
2987    if (apr_procattr_cmdtype_set(procattr, APR_PROGRAM) != APR_SUCCESS)
2988        return NULL;
2989    proc = apr_pcalloc(p, sizeof(apr_proc_t));
2990    if (apr_proc_create(proc, cmd, argv, NULL, procattr, p) != APR_SUCCESS)
2991        return NULL;
2992    fp = proc->out;
2993
2994    if (fp == NULL)
2995        return NULL;
2996    /* XXX: we are reading 1 byte at a time here */
2997    for (k = 0; apr_file_read(fp, &c, &nbytes) == APR_SUCCESS
2998                && nbytes == 1 && (k < MAX_STRING_LEN-1)     ; ) {
2999        if (c == '\n' || c == '\r')
3000            break;
3001        buf[k++] = c;
3002    }
3003    buf[k] = '\0';
3004    apr_file_close(fp);
3005
3006    return apr_pstrndup(p, buf, k);
3007}
3008