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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * util.c: string utility things
21 * 1995-96 Many changes by the Apache Software Foundation
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
31 #include "apr_strings.h"
34 #define APR_WANT_STDIO
35 #define APR_WANT_STRFUNC
42 #include <netdb.h> /* for gethostbyname() */
45 #include "ap_config.h"
46 #include "apr_base64.h"
48 #include "http_main.h"
50 #include "http_protocol.h"
51 #include "http_config.h"
52 #include "util_ebcdic.h"
61 /* A bunch of functions in util.c scan strings looking for certain characters.
62 * To make that more efficient we encode a lookup table. The test_char_table
63 * is generated automatically by gen_test_char.c.
65 #include "test_char.h"
67 /* we assume the folks using this ensure 0 <= c < 256... which means
68 * you need a cast to (unsigned char) first, you can't just plug a
69 * char in here and get it to work, because if char is signed then it
70 * will first be sign extended.
72 #define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f))
74 /* Win32/NetWare/OS2 need to check for both forward and back slashes
75 * in ap_getparents() and ap_escape_url.
77 #ifdef CASE_BLIND_FILESYSTEM
78 #define IS_SLASH(s) ((s == '/') || (s == '\\'))
80 #define IS_SLASH(s) (s == '/')
85 * Examine a field value (such as a media-/content-type) string and return
86 * it sans any parameters; e.g., strip off any ';charset=foo' and the like.
88 AP_DECLARE(char *) ap_field_noparam(apr_pool_t
*p
, const char *intype
)
92 if (intype
== NULL
) return NULL
;
94 semi
= ap_strchr_c(intype
, ';');
96 return apr_pstrdup(p
, intype
);
99 while ((semi
> intype
) && apr_isspace(semi
[-1])) {
102 return apr_pstrndup(p
, intype
, semi
- intype
);
106 AP_DECLARE(char *) ap_ht_time(apr_pool_t
*p
, apr_time_t t
, const char *fmt
,
110 char ts
[MAX_STRING_LEN
];
111 char tf
[MAX_STRING_LEN
];
118 apr_time_exp_gmt(&xt
, t
);
119 /* Convert %Z to "GMT" and %z to "+0000";
120 * on hosts that do not have a time zone string in struct tm,
121 * strftime must assume its argument is local time.
123 for(strp
= tf
, f
= fmt
; strp
< tf
+ sizeof(tf
) - 6 && (*strp
= *f
)
125 if (*f
!= '%') continue;
136 case 'z': /* common extension */
150 apr_time_exp_lt(&xt
, t
);
153 /* check return code? */
154 apr_strftime(ts
, &retcode
, MAX_STRING_LEN
, fmt
, &xt
);
155 ts
[MAX_STRING_LEN
- 1] = '\0';
156 return apr_pstrdup(p
, ts
);
159 /* Roy owes Rob beer. */
160 /* Rob owes Roy dinner. */
162 /* These legacy comments would make a lot more sense if Roy hadn't
163 * replaced the old later_than() routine with util_date.c.
165 * Well, okay, they still wouldn't make any sense.
168 /* Match = 0, NoMatch = 1, Abort = -1
169 * Based loosely on sections of wildmat.c by Rich Salz
170 * Hmmm... shouldn't this really go component by component?
172 AP_DECLARE(int) ap_strcmp_match(const char *str
, const char *expected
)
176 for (x
= 0, y
= 0; expected
[y
]; ++y
, ++x
) {
177 if ((!str
[x
]) && (expected
[y
] != '*'))
179 if (expected
[y
] == '*') {
180 while (expected
[++y
] == '*');
185 if ((ret
= ap_strcmp_match(&str
[x
++], &expected
[y
])) != 1)
190 else if ((expected
[y
] != '?') && (str
[x
] != expected
[y
]))
193 return (str
[x
] != '\0');
196 AP_DECLARE(int) ap_strcasecmp_match(const char *str
, const char *expected
)
200 for (x
= 0, y
= 0; expected
[y
]; ++y
, ++x
) {
201 if (!str
[x
] && expected
[y
] != '*')
203 if (expected
[y
] == '*') {
204 while (expected
[++y
] == '*');
209 if ((ret
= ap_strcasecmp_match(&str
[x
++], &expected
[y
])) != 1)
214 else if (expected
[y
] != '?'
215 && apr_tolower(str
[x
]) != apr_tolower(expected
[y
]))
218 return (str
[x
] != '\0');
221 /* We actually compare the canonical root to this root, (but we don't
222 * waste time checking the case), since every use of this function in
223 * httpd-2.1 tests if the path is 'proper', meaning we've already passed
224 * it through apr_filepath_merge, or we haven't.
226 AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t
*p
, const char *dir
)
229 const char *ourdir
= dir
;
230 if (apr_filepath_root(&newpath
, &dir
, 0, p
) != APR_SUCCESS
231 || strncmp(newpath
, ourdir
, strlen(newpath
)) != 0) {
237 AP_DECLARE(int) ap_is_matchexp(const char *str
)
241 for (x
= 0; str
[x
]; x
++)
242 if ((str
[x
] == '*') || (str
[x
] == '?'))
248 * Here's a pool-based interface to the POSIX-esque ap_regcomp().
249 * Note that we return ap_regex_t instead of being passed one.
250 * The reason is that if you use an already-used ap_regex_t structure,
251 * the memory that you've already allocated gets forgotten, and
252 * regfree() doesn't clear it. So we don't allow it.
255 static apr_status_t
regex_cleanup(void *preg
)
257 ap_regfree((ap_regex_t
*) preg
);
261 AP_DECLARE(ap_regex_t
*) ap_pregcomp(apr_pool_t
*p
, const char *pattern
,
264 ap_regex_t
*preg
= apr_palloc(p
, sizeof *preg
);
266 if (ap_regcomp(preg
, pattern
, cflags
)) {
270 apr_pool_cleanup_register(p
, (void *) preg
, regex_cleanup
,
271 apr_pool_cleanup_null
);
276 AP_DECLARE(void) ap_pregfree(apr_pool_t
*p
, ap_regex_t
*reg
)
279 apr_pool_cleanup_kill(p
, (void *) reg
, regex_cleanup
);
283 * Similar to standard strstr() but we ignore case in this version.
284 * Based on the strstr() implementation further below.
286 AP_DECLARE(char *) ap_strcasestr(const char *s1
, const char *s2
)
294 for ( ; (*s1
!= '\0') && (apr_tolower(*s1
) != apr_tolower(*s2
)); s1
++);
298 /* found first character of s2, see if the rest matches */
301 for (++p1
, ++p2
; apr_tolower(*p1
) == apr_tolower(*p2
); ++p1
, ++p2
) {
303 /* both strings ended together */
308 /* second string ended, a match */
311 /* didn't find a match here, try starting at next character in s1 */
318 * Returns an offsetted pointer in bigstring immediately after
319 * prefix. Returns bigstring if bigstring doesn't start with
320 * prefix or if prefix is longer than bigstring while still matching.
321 * NOTE: pointer returned is relative to bigstring, so we
322 * can use standard pointer comparisons in the calling function
323 * (eg: test if ap_stripprefix(a,b) == a)
325 AP_DECLARE(const char *) ap_stripprefix(const char *bigstring
,
334 while (*p1
&& *prefix
) {
335 if (*p1
++ != *prefix
++)
341 /* hit the end of bigstring! */
345 /* This function substitutes for $0-$9, filling in regular expression
346 * submatches. Pass it the same nmatch and pmatch arguments that you
347 * passed ap_regexec(). pmatch should not be greater than the maximum number
348 * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t.
350 * input should be the string with the $-expressions, source should be the
351 * string that was matched against.
353 * It returns the substituted string, or NULL on error.
355 * Parts of this code are based on Henry Spencer's regsub(), from his
356 * AT&T V8 regexp package.
359 AP_DECLARE(char *) ap_pregsub(apr_pool_t
*p
, const char *input
,
360 const char *source
, size_t nmatch
,
361 ap_regmatch_t pmatch
[])
363 const char *src
= input
;
372 return apr_pstrdup(p
, src
);
374 /* First pass, find the size */
378 while ((c
= *src
++) != '\0') {
381 else if (c
== '$' && apr_isdigit(*src
))
386 if (no
> 9) { /* Ordinary character. */
387 if (c
== '\\' && (*src
== '$' || *src
== '&'))
391 else if (no
< nmatch
&& pmatch
[no
].rm_so
< pmatch
[no
].rm_eo
) {
392 len
+= pmatch
[no
].rm_eo
- pmatch
[no
].rm_so
;
397 dest
= dst
= apr_pcalloc(p
, len
+ 1);
399 /* Now actually fill in the string */
403 while ((c
= *src
++) != '\0') {
406 else if (c
== '$' && apr_isdigit(*src
))
411 if (no
> 9) { /* Ordinary character. */
412 if (c
== '\\' && (*src
== '$' || *src
== '&'))
416 else if (no
< nmatch
&& pmatch
[no
].rm_so
< pmatch
[no
].rm_eo
) {
417 len
= pmatch
[no
].rm_eo
- pmatch
[no
].rm_so
;
418 memcpy(dst
, source
+ pmatch
[no
].rm_so
, len
);
429 * Parse .. so we don't compromise security
431 AP_DECLARE(void) ap_getparents(char *name
)
436 /* Four paseses, as per RFC 1808 */
437 /* a) remove ./ path segments */
438 for (next
= name
; *next
&& (*next
!= '.'); next
++) {
441 l
= w
= first_dot
= next
- name
;
442 while (name
[l
] != '\0') {
443 if (name
[l
] == '.' && IS_SLASH(name
[l
+ 1])
444 && (l
== 0 || IS_SLASH(name
[l
- 1])))
447 name
[w
++] = name
[l
++];
450 /* b) remove trailing . path, segment */
451 if (w
== 1 && name
[0] == '.')
453 else if (w
> 1 && name
[w
- 1] == '.' && IS_SLASH(name
[w
- 2]))
457 /* c) remove all xx/../ segments. (including leading ../ and /../) */
460 while (name
[l
] != '\0') {
461 if (name
[l
] == '.' && name
[l
+ 1] == '.' && IS_SLASH(name
[l
+ 2])
462 && (l
== 0 || IS_SLASH(name
[l
- 1]))) {
463 register int m
= l
+ 3, n
;
467 while (l
>= 0 && !IS_SLASH(name
[l
]))
474 while ((name
[n
] = name
[m
]))
481 /* d) remove trailing xx/.. segment. */
482 if (l
== 2 && name
[0] == '.' && name
[1] == '.')
484 else if (l
> 2 && name
[l
- 1] == '.' && name
[l
- 2] == '.'
485 && IS_SLASH(name
[l
- 3])) {
488 while (l
>= 0 && !IS_SLASH(name
[l
]))
498 AP_DECLARE(void) ap_no2slash(char *name
)
504 #ifdef HAVE_UNC_PATHS
505 /* Check for UNC names. Leave leading two slashes. */
506 if (s
[0] == '/' && s
[1] == '/')
511 if ((*d
++ = *s
) == '/') {
525 * copy at most n leading directories of s into d
526 * d should be at least as large as s plus 1 extra byte
528 * the return value is the ever useful pointer to the trailing \0 of d
530 * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments,
531 * so that if n == 0, "/" is returned in d with n == 1
532 * and s == "e:/test.html", "e:/" is returned in d
533 * *** See also directory_walk in modules/http/http_request.c
536 * /a/b, 0 ==> / (true for all platforms)
545 * c:/a/b 3 ==> c:/a/b
546 * c:/a/b 4 ==> c:/a/b
548 AP_DECLARE(char *) ap_make_dirstr_prefix(char *d
, const char *s
, int n
)
557 if (*s
== '\0' || (*s
== '/' && (--n
) == 0)) {
569 * return the parent directory name including trailing / of the file s
571 AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t
*p
, const char *s
)
573 const char *last_slash
= ap_strrchr_c(s
, '/');
577 if (last_slash
== NULL
) {
578 return apr_pstrdup(p
, "");
580 l
= (last_slash
- s
) + 1;
581 d
= apr_pstrmemdup(p
, s
, l
);
587 AP_DECLARE(int) ap_count_dirs(const char *path
)
591 for (x
= 0, n
= 0; path
[x
]; x
++)
597 AP_DECLARE(char *) ap_getword_nc(apr_pool_t
*atrans
, char **line
, char stop
)
599 return ap_getword(atrans
, (const char **) line
, stop
);
602 AP_DECLARE(char *) ap_getword(apr_pool_t
*atrans
, const char **line
, char stop
)
604 const char *pos
= *line
;
608 while ((*pos
!= stop
) && *pos
) {
613 res
= apr_pstrmemdup(atrans
, *line
, len
);
616 while (*pos
== stop
) {
625 AP_DECLARE(char *) ap_getword_white_nc(apr_pool_t
*atrans
, char **line
)
627 return ap_getword_white(atrans
, (const char **) line
);
630 AP_DECLARE(char *) ap_getword_white(apr_pool_t
*atrans
, const char **line
)
632 const char *pos
= *line
;
636 while (!apr_isspace(*pos
) && *pos
) {
641 res
= apr_pstrmemdup(atrans
, *line
, len
);
643 while (apr_isspace(*pos
)) {
652 AP_DECLARE(char *) ap_getword_nulls_nc(apr_pool_t
*atrans
, char **line
,
655 return ap_getword_nulls(atrans
, (const char **) line
, stop
);
658 AP_DECLARE(char *) ap_getword_nulls(apr_pool_t
*atrans
, const char **line
,
661 const char *pos
= ap_strchr_c(*line
, stop
);
665 res
= apr_pstrdup(atrans
, *line
);
666 *line
+= strlen(*line
);
670 res
= apr_pstrndup(atrans
, *line
, pos
- *line
);
679 /* Get a word, (new) config-file style --- quoted strings and backslashes
683 static char *substring_conf(apr_pool_t
*p
, const char *start
, int len
,
686 char *result
= apr_palloc(p
, len
+ 2);
690 for (i
= 0; i
< len
; ++i
) {
691 if (start
[i
] == '\\' && (start
[i
+ 1] == '\\'
692 || (quote
&& start
[i
+ 1] == quote
)))
693 *resp
++ = start
[++i
];
699 #if RESOLVE_ENV_PER_TOKEN
700 return (char *)ap_resolve_env(p
,result
);
706 AP_DECLARE(char *) ap_getword_conf_nc(apr_pool_t
*p
, char **line
)
708 return ap_getword_conf(p
, (const char **) line
);
711 AP_DECLARE(char *) ap_getword_conf(apr_pool_t
*p
, const char **line
)
713 const char *str
= *line
, *strend
;
717 while (*str
&& apr_isspace(*str
))
725 if ((quote
= *str
) == '"' || quote
== '\'') {
727 while (*strend
&& *strend
!= quote
) {
728 if (*strend
== '\\' && strend
[1] &&
729 (strend
[1] == quote
|| strend
[1] == '\\')) {
736 res
= substring_conf(p
, str
+ 1, strend
- str
- 1, quote
);
738 if (*strend
== quote
)
743 while (*strend
&& !apr_isspace(*strend
))
746 res
= substring_conf(p
, str
, strend
- str
, 0);
749 while (*strend
&& apr_isspace(*strend
))
755 /* Check a string for any ${ENV} environment variable
756 * construct and replace each them by the value of
757 * that environment variable, if it exists. If the
758 * environment value does not exist, leave the ${ENV}
759 * construct alone; it means something else.
761 AP_DECLARE(const char *) ap_resolve_env(apr_pool_t
*p
, const char * word
)
763 # define SMALL_EXPANSION 5
768 } *result
, *current
, sresult
[SMALL_EXPANSION
];
770 const char *s
, *e
, *ep
;
774 s
= ap_strchr_c(word
, '$');
779 /* well, actually something to do */
780 ep
= word
+ strlen(word
);
782 result
= current
= &(sresult
[spc
++]);
783 current
->next
= NULL
;
784 current
->string
= word
;
785 current
->len
= s
- word
;
786 outlen
= current
->len
;
789 /* prepare next entry */
791 current
->next
= (spc
< SMALL_EXPANSION
)
793 : (struct sll
*)apr_palloc(p
,
794 sizeof(*current
->next
));
795 current
= current
->next
;
796 current
->next
= NULL
;
801 if (s
[1] == '{' && (e
= ap_strchr_c(s
, '}'))) {
802 word
= getenv(apr_pstrndup(p
, s
+2, e
-s
-2));
804 current
->string
= word
;
805 current
->len
= strlen(word
);
806 outlen
+= current
->len
;
810 current
->len
= e
- s
+ 1;
811 outlen
+= current
->len
;
816 current
->string
= s
++;
823 s
= ap_strchr_c(s
, '$');
824 current
->string
= word
;
825 current
->len
= s
? s
- word
: ep
- word
;
826 outlen
+= current
->len
;
830 /* assemble result */
831 res_buf
= cp
= apr_palloc(p
, outlen
+ 1);
834 memcpy(cp
, result
->string
, result
->len
);
837 result
= result
->next
;
839 res_buf
[outlen
] = '\0';
844 AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t
*cfp
)
847 ap_log_error(APLOG_MARK
, APLOG_DEBUG
, 0, NULL
,
848 "Done with config file %s", cfp
->name
);
850 return (cfp
->close
== NULL
) ? 0 : cfp
->close(cfp
->param
);
853 static apr_status_t
cfg_close(void *param
)
855 apr_file_t
*cfp
= (apr_file_t
*) param
;
856 return (apr_file_close(cfp
));
859 static int cfg_getch(void *param
)
862 apr_file_t
*cfp
= (apr_file_t
*) param
;
863 if (apr_file_getc(&ch
, cfp
) == APR_SUCCESS
)
868 static void *cfg_getstr(void *buf
, size_t bufsiz
, void *param
)
870 apr_file_t
*cfp
= (apr_file_t
*) param
;
872 rv
= apr_file_gets(buf
, bufsiz
, cfp
);
873 if (rv
== APR_SUCCESS
) {
879 /* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */
880 AP_DECLARE(apr_status_t
) ap_pcfg_openfile(ap_configfile_t
**ret_cfg
,
881 apr_pool_t
*p
, const char *name
)
883 ap_configfile_t
*new_cfg
;
884 apr_file_t
*file
= NULL
;
892 ap_log_error(APLOG_MARK
, APLOG_ERR
, 0, NULL
,
893 "Internal error: pcfg_openfile() called with NULL filename");
897 status
= apr_file_open(&file
, name
, APR_READ
| APR_BUFFERED
,
900 ap_log_error(APLOG_MARK
, APLOG_DEBUG
, 0, NULL
,
901 "Opening config file %s (%s)",
902 name
, (status
!= APR_SUCCESS
) ?
903 apr_strerror(status
, buf
, sizeof(buf
)) : "successful");
905 if (status
!= APR_SUCCESS
)
908 status
= apr_file_info_get(&finfo
, APR_FINFO_TYPE
, file
);
909 if (status
!= APR_SUCCESS
)
912 if (finfo
.filetype
!= APR_REG
&&
913 #if defined(WIN32) || defined(OS2) || defined(NETWARE)
914 strcasecmp(apr_filepath_name_get(name
), "nul") != 0) {
916 strcmp(name
, "/dev/null") != 0) {
917 #endif /* WIN32 || OS2 */
918 ap_log_error(APLOG_MARK
, APLOG_ERR
, 0, NULL
,
919 "Access to file %s denied by server: not a regular file",
921 apr_file_close(file
);
926 /* Some twisted character [no pun intended] at MS decided that a
927 * zero width joiner as the lead wide character would be ideal for
928 * describing Unicode text files. This was further convoluted to
929 * another MSism that the same character mapped into utf-8, EF BB BF
930 * would signify utf-8 text files.
932 * Since MS configuration files are all protecting utf-8 encoded
933 * Unicode path, file and resource names, we already have the correct
934 * WinNT encoding. But at least eat the stupid three bytes up front.
937 unsigned char buf
[4];
939 status
= apr_file_read(file
, buf
, &len
);
940 if ((status
!= APR_SUCCESS
) || (len
< 3)
941 || memcmp(buf
, "\xEF\xBB\xBF", 3) != 0) {
943 apr_file_seek(file
, APR_SET
, &zero
);
948 new_cfg
= apr_palloc(p
, sizeof(*new_cfg
));
949 new_cfg
->param
= file
;
950 new_cfg
->name
= apr_pstrdup(p
, name
);
951 new_cfg
->getch
= (int (*)(void *)) cfg_getch
;
952 new_cfg
->getstr
= (void *(*)(void *, size_t, void *)) cfg_getstr
;
953 new_cfg
->close
= (int (*)(void *)) cfg_close
;
954 new_cfg
->line_number
= 0;
960 /* Allocate a ap_configfile_t handle with user defined functions and params */
961 AP_DECLARE(ap_configfile_t
*) ap_pcfg_open_custom(apr_pool_t
*p
,
964 int(*getch
)(void *param
),
965 void *(*getstr
) (void *buf
, size_t bufsiz
, void *param
),
966 int(*close_func
)(void *param
))
968 ap_configfile_t
*new_cfg
= apr_palloc(p
, sizeof(*new_cfg
));
970 ap_log_error(APLOG_MARK
, APLOG_DEBUG
, 0, NULL
,
971 "Opening config handler %s", descr
);
973 new_cfg
->param
= param
;
974 new_cfg
->name
= descr
;
975 new_cfg
->getch
= getch
;
976 new_cfg
->getstr
= getstr
;
977 new_cfg
->close
= close_func
;
978 new_cfg
->line_number
= 0;
982 /* Read one character from a configfile_t */
983 AP_DECLARE(int) ap_cfg_getc(ap_configfile_t
*cfp
)
985 register int ch
= cfp
->getch(cfp
->param
);
991 /* Read one line from open ap_configfile_t, strip LF, increase line number */
992 /* If custom handler does not define a getstr() function, read char by char */
993 AP_DECLARE(int) ap_cfg_getline(char *buf
, size_t bufsize
, ap_configfile_t
*cfp
)
995 /* If a "get string" function is defined, use it */
996 if (cfp
->getstr
!= NULL
) {
1000 size_t cbufsize
= bufsize
;
1004 if (cfp
->getstr(cbuf
, cbufsize
, cfp
->param
) == NULL
)
1008 * check for line continuation,
1009 * i.e. match [^\\]\\[\r]\n only
1012 while (cp
< cbuf
+cbufsize
&& *cp
!= '\0')
1014 if (cp
> cbuf
&& cp
[-1] == LF
) {
1016 if (cp
> cbuf
&& cp
[-1] == CR
)
1018 if (cp
> cbuf
&& cp
[-1] == '\\') {
1020 if (!(cp
> cbuf
&& cp
[-1] == '\\')) {
1022 * line continuation requested -
1023 * then remove backslash and continue
1025 cbufsize
-= (cp
-cbuf
);
1031 * no real continuation because escaped -
1032 * then just remove escape character
1034 for ( ; cp
< cbuf
+cbufsize
&& *cp
!= '\0'; cp
++)
1043 * Leading and trailing white space is eliminated completely
1046 while (apr_isspace(*src
))
1048 /* blast trailing whitespace */
1049 dst
= &src
[strlen(src
)];
1050 while (--dst
>= src
&& apr_isspace(*dst
))
1052 /* Zap leading whitespace by shifting */
1054 for (dst
= buf
; (*dst
++ = *src
++) != '\0'; )
1057 #ifdef DEBUG_CFG_LINES
1058 ap_log_error(APLOG_MARK
, APLOG_DEBUG
, 0, NULL
, "Read config: %s", buf
);
1062 /* No "get string" function defined; read character by character */
1064 register size_t i
= 0;
1067 /* skip leading whitespace */
1069 c
= cfp
->getch(cfp
->param
);
1070 } while (c
== '\t' || c
== ' ');
1076 /* too small, assume caller is crazy */
1081 if ((c
== '\t') || (c
== ' ')) {
1083 while ((c
== '\t') || (c
== ' '))
1084 c
= cfp
->getch(cfp
->param
);
1087 /* silently ignore CR (_assume_ that a LF follows) */
1088 c
= cfp
->getch(cfp
->param
);
1091 /* increase line number and return on LF */
1094 if (c
== EOF
|| c
== 0x4 || c
== LF
|| i
>= (bufsize
- 2)) {
1096 * check for line continuation
1098 if (i
> 0 && buf
[i
-1] == '\\') {
1100 if (!(i
> 0 && buf
[i
-1] == '\\')) {
1101 /* line is continued */
1102 c
= cfp
->getch(cfp
->param
);
1105 /* else nothing needs be done because
1106 * then the backslash is escaped and
1107 * we just strip to a single one
1110 /* blast trailing whitespace */
1111 while (i
> 0 && apr_isspace(buf
[i
- 1]))
1114 #ifdef DEBUG_CFG_LINES
1115 ap_log_error(APLOG_MARK
, APLOG_DEBUG
, 0, NULL
,
1116 "Read config: %s", buf
);
1122 c
= cfp
->getch(cfp
->param
);
1127 /* Size an HTTP header field list item, as separated by a comma.
1128 * The return value is a pointer to the beginning of the non-empty list item
1129 * within the original string (or NULL if there is none) and the address
1130 * of field is shifted to the next non-comma, non-whitespace character.
1131 * len is the length of the item excluding any beginning whitespace.
1133 AP_DECLARE(const char *) ap_size_list_item(const char **field
, int *len
)
1135 const unsigned char *ptr
= (const unsigned char *)*field
;
1136 const unsigned char *token
;
1137 int in_qpair
, in_qstr
, in_com
;
1139 /* Find first non-comma, non-whitespace byte */
1141 while (*ptr
== ',' || apr_isspace(*ptr
))
1146 /* Find the end of this item, skipping over dead bits */
1148 for (in_qpair
= in_qstr
= in_com
= 0;
1149 *ptr
&& (in_qpair
|| in_qstr
|| in_com
|| *ptr
!= ',');
1157 case '\\': in_qpair
= 1; /* quoted-pair */
1159 case '"' : if (!in_com
) /* quoted string delim */
1162 case '(' : if (!in_qstr
) /* comment (may nest) */
1165 case ')' : if (in_com
) /* end comment */
1173 if ((*len
= (ptr
- token
)) == 0) {
1174 *field
= (const char *)ptr
;
1178 /* Advance field pointer to the next non-comma, non-white byte */
1180 while (*ptr
== ',' || apr_isspace(*ptr
))
1183 *field
= (const char *)ptr
;
1184 return (const char *)token
;
1187 /* Retrieve an HTTP header field list item, as separated by a comma,
1188 * while stripping insignificant whitespace and lowercasing anything not in
1189 * a quoted string or comment. The return value is a new string containing
1190 * the converted list item (or NULL if none) and the address pointed to by
1191 * field is shifted to the next non-comma, non-whitespace.
1193 AP_DECLARE(char *) ap_get_list_item(apr_pool_t
*p
, const char **field
)
1195 const char *tok_start
;
1196 const unsigned char *ptr
;
1199 int addspace
= 0, in_qpair
= 0, in_qstr
= 0, in_com
= 0, tok_len
= 0;
1201 /* Find the beginning and maximum length of the list item so that
1202 * we can allocate a buffer for the new string and reset the field.
1204 if ((tok_start
= ap_size_list_item(field
, &tok_len
)) == NULL
) {
1207 token
= apr_palloc(p
, tok_len
+ 1);
1209 /* Scan the token again, but this time copy only the good bytes.
1210 * We skip extra whitespace and any whitespace around a '=', '/',
1211 * or ';' and lowercase normal characters not within a comment,
1212 * quoted-string or quoted-pair.
1214 for (ptr
= (const unsigned char *)tok_start
, pos
= (unsigned char *)token
;
1215 *ptr
&& (in_qpair
|| in_qstr
|| in_com
|| *ptr
!= ',');
1224 case '\\': in_qpair
= 1;
1230 case '"' : if (!in_com
)
1237 case '(' : if (!in_qstr
)
1244 case ')' : if (in_com
)
1250 case '\t': if (addspace
)
1252 if (in_com
|| in_qstr
)
1259 case ';' : if (!(in_com
|| in_qstr
))
1263 default : if (addspace
== 1)
1265 *pos
++ = (in_com
|| in_qstr
) ? *ptr
1266 : apr_tolower(*ptr
);
1277 /* Find an item in canonical form (lowercase, no extra spaces) within
1278 * an HTTP field value list. Returns 1 if found, 0 if not found.
1279 * This would be much more efficient if we stored header fields as
1280 * an array of list items as they are received instead of a plain string.
1282 AP_DECLARE(int) ap_find_list_item(apr_pool_t
*p
, const char *line
,
1285 const unsigned char *pos
;
1286 const unsigned char *ptr
= (const unsigned char *)line
;
1287 int good
= 0, addspace
= 0, in_qpair
= 0, in_qstr
= 0, in_com
= 0;
1292 do { /* loop for each item in line's list */
1294 /* Find first non-comma, non-whitespace byte */
1296 while (*ptr
== ',' || apr_isspace(*ptr
))
1300 good
= 1; /* until proven otherwise for this item */
1302 break; /* no items left and nothing good found */
1304 /* We skip extra whitespace and any whitespace around a '=', '/',
1305 * or ';' and lowercase normal characters not within a comment,
1306 * quoted-string or quoted-pair.
1308 for (pos
= (const unsigned char *)tok
;
1309 *ptr
&& (in_qpair
|| in_qstr
|| in_com
|| *ptr
!= ',');
1315 good
= (*pos
++ == *ptr
);
1319 case '\\': in_qpair
= 1;
1321 good
= good
&& (*pos
++ == ' ');
1322 good
= good
&& (*pos
++ == *ptr
);
1325 case '"' : if (!in_com
)
1328 good
= good
&& (*pos
++ == ' ');
1329 good
= good
&& (*pos
++ == *ptr
);
1332 case '(' : if (!in_qstr
)
1335 good
= good
&& (*pos
++ == ' ');
1336 good
= good
&& (*pos
++ == *ptr
);
1339 case ')' : if (in_com
)
1341 good
= good
&& (*pos
++ == *ptr
);
1345 case '\t': if (addspace
|| !good
)
1347 if (in_com
|| in_qstr
)
1348 good
= (*pos
++ == *ptr
);
1354 case ';' : if (!(in_com
|| in_qstr
))
1356 good
= good
&& (*pos
++ == *ptr
);
1358 default : if (!good
)
1361 good
= (*pos
++ == ' ');
1362 if (in_com
|| in_qstr
)
1363 good
= good
&& (*pos
++ == *ptr
);
1365 good
= good
&& (*pos
++ == apr_tolower(*ptr
));
1372 good
= 0; /* not good if only a prefix was matched */
1374 } while (*ptr
&& !good
);
1380 /* Retrieve a token, spacing over it and returning a pointer to
1381 * the first non-white byte afterwards. Note that these tokens
1382 * are delimited by semis and commas; and can also be delimited
1383 * by whitespace at the caller's option.
1386 AP_DECLARE(char *) ap_get_token(apr_pool_t
*p
, const char **accept_line
,
1389 const char *ptr
= *accept_line
;
1390 const char *tok_start
;
1394 /* Find first non-white byte */
1396 while (*ptr
&& apr_isspace(*ptr
))
1401 /* find token end, skipping over quoted strings.
1402 * (comments are already gone).
1405 while (*ptr
&& (accept_white
|| !apr_isspace(*ptr
))
1406 && *ptr
!= ';' && *ptr
!= ',') {
1413 tok_len
= ptr
- tok_start
;
1414 token
= apr_pstrndup(p
, tok_start
, tok_len
);
1416 /* Advance accept_line pointer to the next non-white byte */
1418 while (*ptr
&& apr_isspace(*ptr
))
1426 /* find http tokens, see the definition of token from RFC2068 */
1427 AP_DECLARE(int) ap_find_token(apr_pool_t
*p
, const char *line
, const char *tok
)
1429 const unsigned char *start_token
;
1430 const unsigned char *s
;
1435 s
= (const unsigned char *)line
;
1437 /* find start of token, skip all stop characters, note NUL
1438 * isn't a token stop, so we don't need to test for it
1440 while (TEST_CHAR(*s
, T_HTTP_TOKEN_STOP
)) {
1447 /* find end of the token */
1448 while (*s
&& !TEST_CHAR(*s
, T_HTTP_TOKEN_STOP
)) {
1451 if (!strncasecmp((const char *)start_token
, (const char *)tok
,
1462 AP_DECLARE(int) ap_find_last_token(apr_pool_t
*p
, const char *line
,
1465 int llen
, tlen
, lidx
;
1470 llen
= strlen(line
);
1475 (lidx
> 0 && !(apr_isspace(line
[lidx
- 1]) || line
[lidx
- 1] == ',')))
1478 return (strncasecmp(&line
[lidx
], tok
, tlen
) == 0);
1481 AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t
*p
, const char *str
)
1485 const unsigned char *s
;
1487 cmd
= apr_palloc(p
, 2 * strlen(str
) + 1); /* Be safe */
1488 d
= (unsigned char *)cmd
;
1489 s
= (const unsigned char *)str
;
1492 #if defined(OS2) || defined(WIN32)
1494 * Newlines to Win32/OS2 CreateProcess() are ill advised.
1495 * Convert them to spaces since they are effectively white
1496 * space to most applications
1498 if (*s
== '\r' || *s
== '\n') {
1504 if (TEST_CHAR(*s
, T_ESCAPE_SHELL_CMD
)) {
1514 static char x2c(const char *what
)
1516 register char digit
;
1518 #if !APR_CHARSET_EBCDIC
1519 digit
= ((what
[0] >= 'A') ? ((what
[0] & 0xdf) - 'A') + 10
1522 digit
+= (what
[1] >= 'A' ? ((what
[1] & 0xdf) - 'A') + 10
1524 #else /*APR_CHARSET_EBCDIC*/
1531 digit
= apr_xlate_conv_byte(ap_hdrs_from_ascii
,
1532 0xFF & strtol(xstr
, NULL
, 16));
1533 #endif /*APR_CHARSET_EBCDIC*/
1538 * Unescapes a URL, leaving reserved characters intact.
1539 * Returns 0 on success, non-zero on error
1541 * bad % escape returns HTTP_BAD_REQUEST
1543 * decoding %00 or a forbidden character returns HTTP_NOT_FOUND
1546 static int unescape_url(char *url
, const char *forbid
, const char *reserved
)
1548 register int badesc
, badpath
;
1553 /* Initial scan for first '%'. Don't bother writing values before
1555 y
= strchr(url
, '%');
1559 for (x
= y
; *y
; ++x
, ++y
) {
1564 if (!apr_isxdigit(*(y
+ 1)) || !apr_isxdigit(*(y
+ 2))) {
1570 decoded
= x2c(y
+ 1);
1571 if ((decoded
== '\0')
1572 || (forbid
&& ap_strchr_c(forbid
, decoded
))) {
1577 else if (reserved
&& ap_strchr_c(reserved
, decoded
)) {
1591 return HTTP_BAD_REQUEST
;
1594 return HTTP_NOT_FOUND
;
1600 AP_DECLARE(int) ap_unescape_url(char *url
)
1603 #ifdef CASE_BLIND_FILESYSTEM
1604 return unescape_url(url
, "/\\", NULL
);
1606 return unescape_url(url
, "/", NULL
);
1609 AP_DECLARE(int) ap_unescape_url_keep2f(char *url
)
1611 /* AllowEncodedSlashes (corrected) */
1612 return unescape_url(url
, NULL
, "/");
1615 /* IFDEF these out until they've been thought through.
1616 * Just a germ of an API extension for now
1618 AP_DECLARE(int) ap_unescape_url_proxy(char *url
)
1620 /* leave RFC1738 reserved characters intact, * so proxied URLs
1621 * don't get mangled. Where does that leave encoded '&' ?
1623 return unescape_url(url
, NULL
, "/;?");
1625 AP_DECLARE(int) ap_unescape_url_reserved(char *url
, const char *reserved
)
1627 return unescape_url(url
, NULL
, reserved
);
1631 AP_DECLARE(char *) ap_construct_server(apr_pool_t
*p
, const char *hostname
,
1632 apr_port_t port
, const request_rec
*r
)
1634 if (ap_is_default_port(port
, r
)) {
1635 return apr_pstrdup(p
, hostname
);
1638 return apr_psprintf(p
, "%s:%u", hostname
, port
);
1642 AP_DECLARE(int) ap_unescape_all(char *url
)
1644 return unescape_url(url
, NULL
, NULL
);
1647 /* c2x takes an unsigned, and expects the caller has guaranteed that
1648 * 0 <= what < 256... which usually means that you have to cast to
1649 * unsigned char first, because (unsigned)(char)(x) first goes through
1650 * signed extension to an int before the unsigned cast.
1652 * The reason for this assumption is to assist gcc code generation --
1653 * the unsigned char -> unsigned extension is already done earlier in
1654 * both uses of this code, so there's no need to waste time doing it
1657 static const char c2x_table
[] = "0123456789abcdef";
1659 static APR_INLINE
unsigned char *c2x(unsigned what
, unsigned char prefix
,
1660 unsigned char *where
)
1662 #if APR_CHARSET_EBCDIC
1663 what
= apr_xlate_conv_byte(ap_hdrs_to_ascii
, (unsigned char)what
);
1664 #endif /*APR_CHARSET_EBCDIC*/
1666 *where
++ = c2x_table
[what
>> 4];
1667 *where
++ = c2x_table
[what
& 0xf];
1672 * escape_path_segment() escapes a path segment, as defined in RFC 1808. This
1673 * routine is (should be) OS independent.
1675 * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all
1676 * cases if a ':' occurs before the first '/' in the URL, the URL should be
1677 * prefixed with "./" (or the ':' escaped). In the case of Unix, this means
1678 * leaving '/' alone, but otherwise doing what escape_path_segment() does. For
1679 * efficiency reasons, we don't use escape_path_segment(), which is provided for
1680 * reference. Again, RFC 1808 is where this stuff is defined.
1682 * If partial is set, os_escape_path() assumes that the path will be appended to
1683 * something with a '/' in it (and thus does not prefix "./").
1686 AP_DECLARE(char *) ap_escape_path_segment_buffer(char *copy
, const char *segment
)
1688 const unsigned char *s
= (const unsigned char *)segment
;
1689 unsigned char *d
= (unsigned char *)copy
;
1693 if (TEST_CHAR(c
, T_ESCAPE_PATH_SEGMENT
)) {
1705 AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t
*p
, const char *segment
)
1707 return ap_escape_path_segment_buffer(apr_palloc(p
, 3 * strlen(segment
) + 1), segment
);
1710 AP_DECLARE(char *) ap_os_escape_path(apr_pool_t
*p
, const char *path
, int partial
)
1712 char *copy
= apr_palloc(p
, 3 * strlen(path
) + 3);
1713 const unsigned char *s
= (const unsigned char *)path
;
1714 unsigned char *d
= (unsigned char *)copy
;
1718 const char *colon
= ap_strchr_c(path
, ':');
1719 const char *slash
= ap_strchr_c(path
, '/');
1721 if (colon
&& (!slash
|| colon
< slash
)) {
1727 if (TEST_CHAR(c
, T_OS_ESCAPE_PATH
)) {
1739 /* ap_escape_uri is now a macro for os_escape_path */
1741 AP_DECLARE(char *) ap_escape_html2(apr_pool_t
*p
, const char *s
, int toasc
)
1746 /* first, count the number of extra characters */
1747 for (i
= 0, j
= 0; s
[i
] != '\0'; i
++)
1748 if (s
[i
] == '<' || s
[i
] == '>')
1750 else if (s
[i
] == '&')
1752 else if (s
[i
] == '"')
1754 else if (toasc
&& !apr_isascii(s
[i
]))
1758 return apr_pstrmemdup(p
, s
, i
);
1760 x
= apr_palloc(p
, i
+ j
+ 1);
1761 for (i
= 0, j
= 0; s
[i
] != '\0'; i
++, j
++)
1763 memcpy(&x
[j
], "<", 4);
1766 else if (s
[i
] == '>') {
1767 memcpy(&x
[j
], ">", 4);
1770 else if (s
[i
] == '&') {
1771 memcpy(&x
[j
], "&", 5);
1774 else if (s
[i
] == '"') {
1775 memcpy(&x
[j
], """, 6);
1778 else if (toasc
&& !apr_isascii(s
[i
])) {
1779 char *esc
= apr_psprintf(p
, "&#%3.3d;", (unsigned char)s
[i
]);
1780 memcpy(&x
[j
], esc
, 6);
1789 AP_DECLARE(char *) ap_escape_logitem(apr_pool_t
*p
, const char *str
)
1793 const unsigned char *s
;
1799 ret
= apr_palloc(p
, 4 * strlen(str
) + 1); /* Be safe */
1800 d
= (unsigned char *)ret
;
1801 s
= (const unsigned char *)str
;
1804 if (TEST_CHAR(*s
, T_ESCAPE_LOGITEM
)) {
1840 AP_DECLARE(apr_size_t
) ap_escape_errorlog_item(char *dest
, const char *source
,
1843 unsigned char *d
, *ep
;
1844 const unsigned char *s
;
1846 if (!source
|| !buflen
) { /* be safe */
1850 d
= (unsigned char *)dest
;
1851 s
= (const unsigned char *)source
;
1852 ep
= d
+ buflen
- 1;
1854 for (; d
< ep
&& *s
; ++s
) {
1856 if (TEST_CHAR(*s
, T_ESCAPE_LOGITEM
)) {
1882 case '"': /* no need for this in error log */
1887 ep
= --d
; /* break the for loop as well */
1900 return (d
- (unsigned char *)dest
);
1903 AP_DECLARE(int) ap_is_directory(apr_pool_t
*p
, const char *path
)
1907 if (apr_stat(&finfo
, path
, APR_FINFO_TYPE
, p
) != APR_SUCCESS
)
1908 return 0; /* in error condition, just return no */
1910 return (finfo
.filetype
== APR_DIR
);
1913 AP_DECLARE(int) ap_is_rdirectory(apr_pool_t
*p
, const char *path
)
1917 if (apr_stat(&finfo
, path
, APR_FINFO_LINK
| APR_FINFO_TYPE
, p
) != APR_SUCCESS
)
1918 return 0; /* in error condition, just return no */
1920 return (finfo
.filetype
== APR_DIR
);
1923 AP_DECLARE(char *) ap_make_full_path(apr_pool_t
*a
, const char *src1
,
1926 apr_size_t len1
, len2
;
1929 len1
= strlen(src1
);
1930 len2
= strlen(src2
);
1931 /* allocate +3 for '/' delimiter, trailing NULL and overallocate
1932 * one extra byte to allow the caller to add a trailing '/'
1934 path
= (char *)apr_palloc(a
, len1
+ len2
+ 3);
1937 memcpy(path
+ 1, src2
, len2
+ 1);
1941 memcpy(path
, src1
, len1
);
1943 if (next
[-1] != '/') {
1946 memcpy(next
, src2
, len2
+ 1);
1952 * Check for an absoluteURI syntax (see section 3.2 in RFC2068).
1954 AP_DECLARE(int) ap_is_url(const char *u
)
1958 for (x
= 0; u
[x
] != ':'; x
++) {
1960 ((!apr_isalpha(u
[x
])) && (!apr_isdigit(u
[x
])) &&
1961 (u
[x
] != '+') && (u
[x
] != '-') && (u
[x
] != '.'))) {
1966 return (x
? 1 : 0); /* If the first character is ':', it's broken, too */
1969 AP_DECLARE(int) ap_ind(const char *s
, char c
)
1971 const char *p
= ap_strchr_c(s
, c
);
1978 AP_DECLARE(int) ap_rind(const char *s
, char c
)
1980 const char *p
= ap_strrchr_c(s
, c
);
1987 AP_DECLARE(void) ap_str_tolower(char *str
)
1990 *str
= apr_tolower(*str
);
1996 * We must return a FQDN
1998 char *ap_get_local_host(apr_pool_t
*a
)
2000 #ifndef MAXHOSTNAMELEN
2001 #define MAXHOSTNAMELEN 256
2003 char str
[MAXHOSTNAMELEN
+ 1];
2004 char *server_hostname
= NULL
;
2005 apr_sockaddr_t
*sockaddr
;
2008 if (apr_gethostname(str
, sizeof(str
) - 1, a
) != APR_SUCCESS
) {
2009 ap_log_perror(APLOG_MARK
, APLOG_STARTUP
| APLOG_WARNING
, 0, a
,
2010 "%s: apr_gethostname() failed to determine ServerName",
2013 str
[sizeof(str
) - 1] = '\0';
2014 if (apr_sockaddr_info_get(&sockaddr
, str
, APR_UNSPEC
, 0, 0, a
) == APR_SUCCESS
) {
2015 if ( (apr_getnameinfo(&hostname
, sockaddr
, 0) == APR_SUCCESS
) &&
2016 (ap_strchr_c(hostname
, '.')) ) {
2017 server_hostname
= apr_pstrdup(a
, hostname
);
2018 return server_hostname
;
2019 } else if (ap_strchr_c(str
, '.')) {
2020 server_hostname
= apr_pstrdup(a
, str
);
2022 apr_sockaddr_ip_get(&hostname
, sockaddr
);
2023 server_hostname
= apr_pstrdup(a
, hostname
);
2026 ap_log_perror(APLOG_MARK
, APLOG_STARTUP
| APLOG_WARNING
, 0, a
,
2027 "%s: apr_sockaddr_info_get() failed for %s",
2028 ap_server_argv0
, str
);
2032 if (!server_hostname
)
2033 server_hostname
= apr_pstrdup(a
, "127.0.0.1");
2035 ap_log_perror(APLOG_MARK
, APLOG_ALERT
|APLOG_STARTUP
, 0, a
,
2036 "%s: Could not reliably determine the server's fully qualified "
2037 "domain name, using %s for ServerName",
2038 ap_server_argv0
, server_hostname
);
2040 return server_hostname
;
2043 /* simple 'pool' alloc()ing glue to apr_base64.c
2045 AP_DECLARE(char *) ap_pbase64decode(apr_pool_t
*p
, const char *bufcoded
)
2050 decoded
= (char *) apr_palloc(p
, 1 + apr_base64_decode_len(bufcoded
));
2051 l
= apr_base64_decode(decoded
, bufcoded
);
2052 decoded
[l
] = '\0'; /* make binary sequence into string */
2057 AP_DECLARE(char *) ap_pbase64encode(apr_pool_t
*p
, char *string
)
2060 int l
= strlen(string
);
2062 encoded
= (char *) apr_palloc(p
, 1 + apr_base64_encode_len(l
));
2063 l
= apr_base64_encode(encoded
, string
, l
);
2064 encoded
[l
] = '\0'; /* make binary sequence into string */
2069 /* we want to downcase the type/subtype for comparison purposes
2070 * but nothing else because ;parameter=foo values are case sensitive.
2071 * XXX: in truth we want to downcase parameter names... but really,
2072 * apache has never handled parameters and such correctly. You
2073 * also need to compress spaces and such to be able to compare
2076 AP_DECLARE(void) ap_content_type_tolower(char *str
)
2080 semi
= strchr(str
, ';');
2085 ap_str_tolower(str
);
2093 * Given a string, replace any bare " with \" .
2095 AP_DECLARE(char *) ap_escape_quotes(apr_pool_t
*p
, const char *instring
)
2098 const char *inchr
= instring
;
2099 char *outchr
, *outstring
;
2102 * Look through the input string, jogging the length of the output
2103 * string up by an extra byte each time we find an unescaped ".
2105 while (*inchr
!= '\0') {
2107 if (*inchr
== '"') {
2111 * If we find a slosh, and it's not the last byte in the string,
2112 * it's escaping something - advance past both bytes.
2114 if ((*inchr
== '\\') && (inchr
[1] != '\0')) {
2120 outstring
= apr_palloc(p
, newlen
+ 1);
2124 * Now copy the input string to the output string, inserting a slosh
2125 * in front of every " that doesn't already have one.
2127 while (*inchr
!= '\0') {
2128 if ((*inchr
== '\\') && (inchr
[1] != '\0')) {
2129 *outchr
++ = *inchr
++;
2130 *outchr
++ = *inchr
++;
2132 if (*inchr
== '"') {
2135 if (*inchr
!= '\0') {
2136 *outchr
++ = *inchr
++;
2144 * Given a string, append the PID deliminated by delim.
2145 * Usually used to create a pid-appended filepath name
2146 * (eg: /a/b/foo -> /a/b/foo.6726). A function, and not
2147 * a macro, to avoid unistd.h dependency
2149 AP_DECLARE(char *) ap_append_pid(apr_pool_t
*p
, const char *string
,
2152 return apr_psprintf(p
, "%s%s%" APR_PID_T_FMT
, string
,
2158 * Parse a given timeout parameter string into an apr_interval_time_t value.
2159 * The unit of the time interval is given as postfix string to the numeric
2160 * string. Currently the following units are understood:
2167 * If no unit is contained in the given timeout parameter the default_time_unit
2168 * will be used instead.
2169 * @param timeout_parameter The string containing the timeout parameter.
2170 * @param timeout The timeout value to be returned.
2171 * @param default_time_unit The default time unit to use if none is specified
2172 * in timeout_parameter.
2173 * @return Status value indicating whether the parsing was successful or not.
2175 AP_DECLARE(apr_status_t
) ap_timeout_parameter_parse(
2176 const char *timeout_parameter
,
2177 apr_interval_time_t
*timeout
,
2178 const char *default_time_unit
)
2181 const char *time_str
;
2184 tout
= apr_strtoi64(timeout_parameter
, &endp
, 10);
2188 if (!endp
|| !*endp
) {
2189 time_str
= default_time_unit
;
2195 switch (*time_str
) {
2196 /* Time is in seconds */
2198 *timeout
= (apr_interval_time_t
) apr_time_from_sec(tout
);
2201 /* Time is in hours */
2202 *timeout
= (apr_interval_time_t
) apr_time_from_sec(tout
* 3600);
2205 switch (*(++time_str
)) {
2206 /* Time is in miliseconds */
2208 *timeout
= (apr_interval_time_t
) tout
* 1000;
2210 /* Time is in minutes */
2212 *timeout
= (apr_interval_time_t
) apr_time_from_sec(tout
* 60);
2215 return APR_EGENERAL
;
2219 return APR_EGENERAL
;