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#include "apr.h" 18#include "apr_strings.h" 19#include "apr_private.h" 20#include "apr_lib.h" 21 22#if APR_HAVE_SYS_TYPES_H 23#include <sys/types.h> 24#endif 25#if APR_HAVE_STRING_H 26#include <string.h> 27#endif 28#if APR_HAVE_CTYPE_H 29#include <ctype.h> 30#endif 31 32/* 33 * Apache's "replacement" for the strncpy() function. We roll our 34 * own to implement these specific changes: 35 * (1) strncpy() doesn't always null terminate and we want it to. 36 * (2) strncpy() null fills, which is bogus, esp. when copy 8byte 37 * strings into 8k blocks. 38 * (3) Instead of returning the pointer to the beginning of 39 * the destination string, we return a pointer to the 40 * terminating '\0' to allow us to "check" for truncation 41 * 42 * apr_cpystrn() follows the same call structure as strncpy(). 43 */ 44 45APR_DECLARE(char *) apr_cpystrn(char *dst, const char *src, apr_size_t dst_size) 46{ 47 48 char *d, *end; 49 50 if (dst_size == 0) { 51 return (dst); 52 } 53 54 d = dst; 55 end = dst + dst_size - 1; 56 57 for (; d < end; ++d, ++src) { 58 if (!(*d = *src)) { 59 return (d); 60 } 61 } 62 63 *d = '\0'; /* always null terminate */ 64 65 return (d); 66} 67 68 69/* 70 * This function provides a way to parse a generic argument string 71 * into a standard argv[] form of argument list. It respects the 72 * usual "whitespace" and quoteing rules. In the future this could 73 * be expanded to include support for the apr_call_exec command line 74 * string processing (including converting '+' to ' ' and doing the 75 * url processing. It does not currently support this function. 76 * 77 * token_context: Context from which pool allocations will occur. 78 * arg_str: Input argument string for conversion to argv[]. 79 * argv_out: Output location. This is a pointer to an array 80 * of pointers to strings (ie. &(char *argv[]). 81 * This value will be allocated from the contexts 82 * pool and filled in with copies of the tokens 83 * found during parsing of the arg_str. 84 */ 85APR_DECLARE(apr_status_t) apr_tokenize_to_argv(const char *arg_str, 86 char ***argv_out, 87 apr_pool_t *token_context) 88{ 89 const char *cp; 90 const char *ct; 91 char *cleaned, *dirty; 92 int escaped; 93 int isquoted, numargs = 0, argnum; 94 95#define SKIP_WHITESPACE(cp) \ 96 for ( ; *cp == ' ' || *cp == '\t'; ) { \ 97 cp++; \ 98 }; 99 100#define CHECK_QUOTATION(cp,isquoted) \ 101 isquoted = 0; \ 102 if (*cp == '"') { \ 103 isquoted = 1; \ 104 cp++; \ 105 } \ 106 else if (*cp == '\'') { \ 107 isquoted = 2; \ 108 cp++; \ 109 } 110 111/* DETERMINE_NEXTSTRING: 112 * At exit, cp will point to one of the following: NULL, SPACE, TAB or QUOTE. 113 * NULL implies the argument string has been fully traversed. 114 */ 115#define DETERMINE_NEXTSTRING(cp,isquoted) \ 116 for ( ; *cp != '\0'; cp++) { \ 117 if ( (*cp == '\\' && (*(cp+1) == ' ' || *(cp+1) == '\t' || \ 118 *(cp+1) == '"' || *(cp+1) == '\''))) { \ 119 cp++; \ 120 continue; \ 121 } \ 122 if ( (!isquoted && (*cp == ' ' || *cp == '\t')) \ 123 || (isquoted == 1 && *cp == '"') \ 124 || (isquoted == 2 && *cp == '\'') ) { \ 125 break; \ 126 } \ 127 } 128 129/* REMOVE_ESCAPE_CHARS: 130 * Compresses the arg string to remove all of the '\' escape chars. 131 * The final argv strings should not have any extra escape chars in it. 132 */ 133#define REMOVE_ESCAPE_CHARS(cleaned, dirty, escaped) \ 134 escaped = 0; \ 135 while(*dirty) { \ 136 if (!escaped && *dirty == '\\') { \ 137 escaped = 1; \ 138 } \ 139 else { \ 140 escaped = 0; \ 141 *cleaned++ = *dirty; \ 142 } \ 143 ++dirty; \ 144 } \ 145 *cleaned = 0; /* last line of macro... */ 146 147 cp = arg_str; 148 SKIP_WHITESPACE(cp); 149 ct = cp; 150 151 /* This is ugly and expensive, but if anyone wants to figure a 152 * way to support any number of args without counting and 153 * allocating, please go ahead and change the code. 154 * 155 * Must account for the trailing NULL arg. 156 */ 157 numargs = 1; 158 while (*ct != '\0') { 159 CHECK_QUOTATION(ct, isquoted); 160 DETERMINE_NEXTSTRING(ct, isquoted); 161 if (*ct != '\0') { 162 ct++; 163 } 164 numargs++; 165 SKIP_WHITESPACE(ct); 166 } 167 *argv_out = apr_palloc(token_context, numargs * sizeof(char*)); 168 169 /* determine first argument */ 170 for (argnum = 0; argnum < (numargs-1); argnum++) { 171 SKIP_WHITESPACE(cp); 172 CHECK_QUOTATION(cp, isquoted); 173 ct = cp; 174 DETERMINE_NEXTSTRING(cp, isquoted); 175 cp++; 176 (*argv_out)[argnum] = apr_palloc(token_context, cp - ct); 177 apr_cpystrn((*argv_out)[argnum], ct, cp - ct); 178 cleaned = dirty = (*argv_out)[argnum]; 179 REMOVE_ESCAPE_CHARS(cleaned, dirty, escaped); 180 } 181 (*argv_out)[argnum] = NULL; 182 183 return APR_SUCCESS; 184} 185 186/* Filepath_name_get returns the final element of the pathname. 187 * Using the current platform's filename syntax. 188 * "/foo/bar/gum" -> "gum" 189 * "/foo/bar/gum/" -> "" 190 * "gum" -> "gum" 191 * "wi\\n32\\stuff" -> "stuff 192 * 193 * Corrected Win32 to accept "a/b\\stuff", "a:stuff" 194 */ 195 196APR_DECLARE(const char *) apr_filepath_name_get(const char *pathname) 197{ 198 const char path_separator = '/'; 199 const char *s = strrchr(pathname, path_separator); 200 201#ifdef WIN32 202 const char path_separator_win = '\\'; 203 const char drive_separator_win = ':'; 204 const char *s2 = strrchr(pathname, path_separator_win); 205 206 if (s2 > s) s = s2; 207 208 if (!s) s = strrchr(pathname, drive_separator_win); 209#endif 210 211 return s ? ++s : pathname; 212} 213 214/* length of dest assumed >= length of src 215 * collapse in place (src == dest) is legal. 216 * returns terminating null ptr to dest string. 217 */ 218APR_DECLARE(char *) apr_collapse_spaces(char *dest, const char *src) 219{ 220 while (*src) { 221 if (!apr_isspace(*src)) 222 *dest++ = *src; 223 ++src; 224 } 225 *dest = 0; 226 return (dest); 227} 228 229#if !APR_HAVE_STRDUP 230char *strdup(const char *str) 231{ 232 char *sdup; 233 size_t len = strlen(str) + 1; 234 235 sdup = (char *) malloc(len); 236 memcpy(sdup, str, len); 237 238 return sdup; 239} 240#endif 241 242/* The following two routines were donated for SVR4 by Andreas Vogel */ 243#if (!APR_HAVE_STRCASECMP && !APR_HAVE_STRICMP) 244int strcasecmp(const char *a, const char *b) 245{ 246 const char *p = a; 247 const char *q = b; 248 for (p = a, q = b; *p && *q; p++, q++) { 249 int diff = apr_tolower(*p) - apr_tolower(*q); 250 if (diff) 251 return diff; 252 } 253 if (*p) 254 return 1; /* p was longer than q */ 255 if (*q) 256 return -1; /* p was shorter than q */ 257 return 0; /* Exact match */ 258} 259 260#endif 261 262#if (!APR_HAVE_STRNCASECMP && !APR_HAVE_STRNICMP) 263int strncasecmp(const char *a, const char *b, size_t n) 264{ 265 const char *p = a; 266 const char *q = b; 267 268 for (p = a, q = b; /*NOTHING */ ; p++, q++) { 269 int diff; 270 if (p == a + n) 271 return 0; /* Match up to n characters */ 272 if (!(*p && *q)) 273 return *p - *q; 274 diff = apr_tolower(*p) - apr_tolower(*q); 275 if (diff) 276 return diff; 277 } 278 /*NOTREACHED */ 279} 280#endif 281 282/* The following routine was donated for UTS21 by dwd@bell-labs.com */ 283#if (!APR_HAVE_STRSTR) 284char *strstr(char *s1, char *s2) 285{ 286 char *p1, *p2; 287 if (*s2 == '\0') { 288 /* an empty s2 */ 289 return(s1); 290 } 291 while((s1 = strchr(s1, *s2)) != NULL) { 292 /* found first character of s2, see if the rest matches */ 293 p1 = s1; 294 p2 = s2; 295 while (*++p1 == *++p2) { 296 if (*p1 == '\0') { 297 /* both strings ended together */ 298 return(s1); 299 } 300 } 301 if (*p2 == '\0') { 302 /* second string ended, a match */ 303 break; 304 } 305 /* didn't find a match here, try starting at next character in s1 */ 306 s1++; 307 } 308 return(s1); 309} 310#endif 311 312