New PHP5 APC - version 3.0.19, using PHP5 5.2.0-8+etch11,
[php5-apc.git] / apc.c
1 /*
2   +----------------------------------------------------------------------+
3   | APC                                                                  |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2008 The PHP Group                                     |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Daniel Cowgill <dcowgill@communityconnect.com>              |
16   |          George Schlossnagle <george@omniti.com>                     |
17   |          Rasmus Lerdorf <rasmus@php.net>                             |
18   |          Arun C. Murthy <arunc@yahoo-inc.com>                        |
19   |          Gopal Vijayaraghavan <gopalv@yahoo-inc.com>                 |
20   +----------------------------------------------------------------------+
21
22    This software was contributed to PHP by Community Connect Inc. in 2002
23    and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
24    Future revisions and derivatives of this source code must acknowledge
25    Community Connect Inc. as the original contributor of this module by
26    leaving this note intact in the source code.
27
28    All other licensing and usage conditions are those of the PHP Group.
29
30  */
31
32 /* $Id: apc.c,v 3.18.2.3 2008/05/13 15:48:48 gopalv Exp $ */
33
34 #include "apc.h"
35 #include <regex.h>      /* for POSIX regular expressions */
36 #include "php.h"
37
38 #define NELEMS(a) (sizeof(a)/sizeof((a)[0]))
39
40 /* {{{ memory allocation wrappers */
41
42 void* apc_emalloc(size_t n)
43 {
44     void* p = malloc(n);
45     if (p == NULL) {
46         apc_eprint("apc_emalloc: malloc failed to allocate %u bytes:", n);
47     }
48     return p;
49 }
50
51 void* apc_erealloc(void* p, size_t n)
52 {
53     p = realloc(p, n);
54     if (p == NULL) {
55         apc_eprint("apc_erealloc: realloc failed to allocate %u bytes:", n);
56     }
57     return p;
58 }
59
60 void apc_efree(void* p)
61 {
62     if (p == NULL) {
63         apc_eprint("apc_efree: attempt to free null pointer");
64     }
65     free(p);
66 }
67
68 char* apc_estrdup(const char* s)
69 {
70     int len;
71     char* dup;
72
73     if (s == NULL) {
74         return NULL;
75     }
76     len = strlen(s);
77     dup = (char*) malloc(len+1);
78     if (dup == NULL) {
79         apc_eprint("apc_estrdup: malloc failed to allocate %u bytes:", len+1);
80     }
81     memcpy(dup, s, len);
82     dup[len] = '\0';
83     return dup;
84 }
85
86 void* apc_xstrdup(const char* s, apc_malloc_t f)
87 {
88     return s != NULL ? apc_xmemcpy(s, strlen(s)+1, f) : NULL;
89 }
90
91 void* apc_xmemcpy(const void* p, size_t n, apc_malloc_t f)
92 {
93     void* q;
94
95     if (p != NULL && (q = f(n)) != NULL) {
96         memcpy(q, p, n);
97         return q;
98     }
99     return NULL;
100 }
101
102 /* }}} */
103
104 /* {{{ console display functions */
105
106 static void my_log(int level, const char* fmt, va_list args)
107 {
108     static const char* level_strings[] = {
109         "apc-debug",
110         "apc-notice",
111         "apc-warning",
112         "apc-error"
113     };
114     static const int num_levels = NELEMS(level_strings);
115
116     time_t now;
117     char* buf;          /* for ctime */
118
119     fflush(stdout);
120
121     if (level < 0)
122         level = 0;
123     else if (level >= num_levels)
124         level = num_levels-1;
125     
126     now = time(0);
127     buf = ctime(&now);  /* TODO: replace with reentrant impl */
128     buf[24] = '\0';
129
130     fprintf(stderr, "[%s] [%s] ", buf, level_strings[level]);
131     vfprintf(stderr, fmt, args);
132
133     if (fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') {
134         fprintf(stderr, " %s", strerror(errno));
135     }
136     fprintf(stderr, "\n");
137
138     if (level == APC_ERROR) {
139         exit(2);
140     }
141 }
142
143 void apc_log(int level, const char* fmt, ...)
144 {
145     va_list args;
146     va_start(args, fmt);
147     my_log(level, fmt, args);
148     va_end(args);
149 }
150
151 void apc_eprint(const char* fmt, ...)
152 {
153     va_list args;
154     va_start(args, fmt);
155     my_log(APC_ERROR, fmt, args);
156     va_end(args);
157 }
158
159 void apc_wprint(const char* fmt, ...)
160 {
161     va_list args;
162     va_start(args, fmt);
163     my_log(APC_WARNING, fmt, args);
164     va_end(args);
165 }
166
167 void apc_nprint(const char* fmt, ...)
168 {
169     va_list args;
170     va_start(args, fmt);
171     my_log(APC_NOTICE, fmt, args);
172     va_end(args);
173 }
174
175 void apc_dprint(const char* fmt, ...)
176 {
177 #ifdef APC_DEBUG
178     va_list args;
179     va_start(args, fmt);
180     my_log(APC_DBG, fmt, args);
181     va_end(args);
182 #endif
183 }
184
185 /* }}} */
186
187 /* {{{ string and text manipulation */
188
189 char* apc_append(const char* s, const char* t)
190 {
191     int slen;
192     int tlen;
193     char* p;
194
195     slen = strlen(s);
196     tlen = strlen(t);
197
198     p = (char*) apc_emalloc((slen + tlen + 1) * sizeof(char));
199     memcpy(p, s, slen);
200     memcpy(p + slen, t, tlen + 1);
201
202     return p;
203 }
204
205 char* apc_substr(const char* s, int start, int length)
206 {
207     char* substr;
208     int src_len = strlen(s);
209
210     /* bring start into range */
211     if (start < 0) {
212         start = 0;
213     }
214     else if (start >= src_len) {
215         start = src_len - 1;
216     }
217
218     /* bring length into range */
219     if (length < 0 || src_len - start < length) {
220         length = src_len - start;
221     }
222
223     /* create the substring */
224     substr = apc_xmemcpy(s + start, length + 1, apc_emalloc);
225     substr[length] = '\0';
226     return substr;
227 }
228
229 char** apc_tokenize(const char* s, char delim)
230 {
231     char** tokens;      /* array of tokens, NULL terminated */
232     int size;           /* size of tokens array */
233     int n;              /* index of next token in tokens array */
234     int cur;            /* current position in input string */
235     int end;            /* final legal position in input string */
236     int next;           /* position of next delimiter in input */
237     
238     if (!s) {
239         return NULL;
240     }
241
242     size = 2;
243     n    = 0;
244     cur  = 0;
245     end  = strlen(s) - 1;
246     
247     tokens = (char**) apc_emalloc(size * sizeof(char*));
248     tokens[n] = NULL;
249
250     while (cur <= end) {
251         /* search for the next delimiter */
252         char* p = strchr(s + cur, delim);
253         next = p ? p-s : end+1;
254
255         /* resize token array if necessary */
256         if (n == size-1) {
257             size *= 2;
258             tokens = (char**) apc_erealloc(tokens, size * sizeof(char*));
259         }
260
261         /* save the current token */
262         tokens[n] = apc_substr(s, cur, next-cur);
263
264         tokens[++n] = NULL;
265         cur = next + 1;
266     }
267
268     return tokens;
269 }
270
271 /* }}} */
272
273 /* similar to php_stream_stat_path */
274 #ifdef ZEND_ENGINE_2
275 #define APC_URL_STAT(wrapper, filename, pstatbuf) \
276     ((wrapper)->wops->url_stat((wrapper), (filename), PHP_STREAM_URL_STAT_QUIET, (pstatbuf), NULL TSRMLS_CC))
277 #else
278 #define APC_URL_STAT(wrapper, filename, pstatbuf) \
279     ((wrapper)->wops->url_stat((wrapper), (filename), (pstatbuf) TSRMLS_CC))
280 #endif
281
282 int apc_search_paths(const char* filename, const char* path, apc_fileinfo_t* fileinfo)
283 {
284     char** paths;
285     char *exec_fname;
286     int exec_fname_length;
287     int found = 0;
288     int i;
289     php_stream_wrapper *wrapper = NULL;
290     char *path_for_open = NULL;
291
292     TSRMLS_FETCH();
293
294     assert(filename && fileinfo);
295
296
297     wrapper = php_stream_locate_url_wrapper(filename, &path_for_open, 0 TSRMLS_CC);
298
299     if(!wrapper || !wrapper->wops || !wrapper->wops->url_stat) {
300         return -1;
301     }
302
303 #ifdef ZEND_ENGINE_2
304     if(wrapper != &php_plain_files_wrapper) {
305         if(APC_URL_STAT(wrapper, path_for_open, &fileinfo->st_buf) == 0) {
306             strncpy(fileinfo->fullpath, path_for_open, MAXPATHLEN);
307             return 0;
308         }
309                 return -1; /* cannot stat */
310     }
311 #endif
312
313     if (IS_ABSOLUTE_PATH(path_for_open, strlen(path_for_open)) && 
314             APC_URL_STAT(wrapper, path_for_open, &fileinfo->st_buf) == 0) {
315         strncpy(fileinfo->fullpath, path_for_open, MAXPATHLEN);
316         return 0;
317     }
318
319     paths = apc_tokenize(path, DEFAULT_DIR_SEPARATOR);
320     if (!paths)
321         return -1;
322
323     /* for each directory in paths, look for filename inside */
324     for (i = 0; paths[i]; i++) {
325         snprintf(fileinfo->fullpath, sizeof(fileinfo->fullpath), "%s%c%s", paths[i], DEFAULT_SLASH, path_for_open);
326         if (APC_URL_STAT(wrapper, fileinfo->fullpath, &fileinfo->st_buf) == 0) {
327             found = 1;
328             break;
329         }
330     }
331
332     /* check in path of the calling scripts' current working directory */
333     /* modified from main/streams/plain_wrapper.c */
334     if(!found && zend_is_executing(TSRMLS_C)) {
335         exec_fname = zend_get_executed_filename(TSRMLS_C);
336         exec_fname_length = strlen(exec_fname);
337         while((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
338         if((exec_fname && exec_fname[0] != '[') && exec_fname_length > 0) {
339             /* not: [no active file] or no path */
340             memcpy(fileinfo->fullpath, exec_fname, exec_fname_length);
341             fileinfo->fullpath[exec_fname_length] = DEFAULT_SLASH;
342             strlcpy(fileinfo->fullpath +exec_fname_length +1, path_for_open,sizeof(fileinfo->fullpath)-exec_fname_length-1);
343             /* apc_wprint("filename: %s, exec_fname: %s, fileinfo->fullpath: %s", path_for_open, exec_fname, fileinfo->fullpath); */
344             if (APC_URL_STAT(wrapper, fileinfo->fullpath, &fileinfo->st_buf) == 0) {
345                 found = 1;
346             }
347         }
348     }
349     
350     /* free the value returned by apc_tokenize */
351     for (i = 0; paths[i]; i++) {
352         apc_efree(paths[i]);
353     }
354     apc_efree(paths);
355
356     return found ? 0 : -1;
357 }
358
359 /* }}} */
360
361 /* {{{ regular expression wrapper functions */
362
363 typedef struct {
364     regex_t *reg;
365     unsigned char type;
366 } apc_regex;
367
368 void* apc_regex_compile_array(char* patterns[])
369 {
370     apc_regex** regs;
371     int npat;
372     int i;
373
374     if (!patterns)
375         return NULL;
376
377     /* count the number of patterns in patterns */
378     for (npat = 0; patterns[npat] != NULL; npat++) {}
379
380     if (npat == 0)
381         return NULL;
382
383     /* allocate the array of compiled expressions */
384     regs = (apc_regex**) apc_emalloc(sizeof(apc_regex*) * (npat + 1));
385     for (i = 0; i <= npat; i++) {
386         regs[i] = (apc_regex *) apc_emalloc(sizeof(apc_regex));
387         regs[i]->reg = NULL;
388         regs[i]->type = APC_NEGATIVE_MATCH;
389     }
390
391     /* compile the expressions */
392     for (i = 0; i < npat; i++) {
393         char *pattern = patterns[i];
394         if(pattern[0]=='+') { regs[i]->type = APC_POSITIVE_MATCH; pattern = patterns[i]+sizeof(char); }
395         else if(pattern[0]=='-') { regs[i]->type = APC_NEGATIVE_MATCH; pattern = patterns[i]+sizeof(char); }
396
397         regs[i]->reg = (regex_t*) apc_emalloc(sizeof(regex_t));
398
399         if (regcomp(regs[i]->reg, pattern, REG_EXTENDED | REG_NOSUB) != 0) {
400             apc_wprint("apc_regex_compile_array: invalid expression '%s'",
401                        pattern);
402
403             apc_regex_destroy_array(regs);
404
405             return NULL;
406         }
407     }
408
409     return (void*) regs;
410 }
411
412 void apc_regex_destroy_array(void* p)
413 {
414     if (p != NULL) {
415         apc_regex** regs = (apc_regex**) p;
416         int i;
417
418         for (i = 0; regs[i]->reg != NULL; i++) {
419             regfree(regs[i]->reg);
420             apc_efree(regs[i]->reg);
421             apc_efree(regs[i]);
422         }
423         apc_efree(regs);
424     }
425 }
426
427 int apc_regex_match_array(void* p, const char* input)
428 {
429     apc_regex** regs;
430     int i;
431
432     if (!p)
433         return 0;
434
435     regs = (apc_regex**) p;
436     for (i = 0; regs[i]->reg != NULL; i++)
437         if (regexec(regs[i]->reg, input, 0, NULL, 0) == 0)
438             return (int)(regs[i]->type);
439
440     return 0;
441 }
442
443 /* }}} */
444
445 /* {{{ crc32 implementation */
446
447 /* this table was generated by crc32gen() */
448 static unsigned int crc32tab[] = {
449     /*   0 */  0x00000000, 0x3b83984b, 0x77073096, 0x4c84a8dd, 
450     /*   4 */  0xee0e612c, 0xd58df967, 0x990951ba, 0xa28ac9f1, 
451     /*   8 */  0x076dc419, 0x3cee5c52, 0x706af48f, 0x4be96cc4, 
452     /*  12 */  0xe963a535, 0xd2e03d7e, 0x9e6495a3, 0xa5e70de8, 
453     /*  16 */  0x0edb8832, 0x35581079, 0x79dcb8a4, 0x425f20ef, 
454     /*  20 */  0xe0d5e91e, 0xdb567155, 0x97d2d988, 0xac5141c3, 
455     /*  24 */  0x09b64c2b, 0x3235d460, 0x7eb17cbd, 0x4532e4f6, 
456     /*  28 */  0xe7b82d07, 0xdc3bb54c, 0x90bf1d91, 0xab3c85da, 
457     /*  32 */  0x1db71064, 0x2634882f, 0x6ab020f2, 0x5133b8b9, 
458     /*  36 */  0xf3b97148, 0xc83ae903, 0x84be41de, 0xbf3dd995, 
459     /*  40 */  0x1adad47d, 0x21594c36, 0x6ddde4eb, 0x565e7ca0, 
460     /*  44 */  0xf4d4b551, 0xcf572d1a, 0x83d385c7, 0xb8501d8c, 
461     /*  48 */  0x136c9856, 0x28ef001d, 0x646ba8c0, 0x5fe8308b, 
462     /*  52 */  0xfd62f97a, 0xc6e16131, 0x8a65c9ec, 0xb1e651a7, 
463     /*  56 */  0x14015c4f, 0x2f82c404, 0x63066cd9, 0x5885f492, 
464     /*  60 */  0xfa0f3d63, 0xc18ca528, 0x8d080df5, 0xb68b95be, 
465     /*  64 */  0x3b6e20c8, 0x00edb883, 0x4c69105e, 0x77ea8815, 
466     /*  68 */  0xd56041e4, 0xeee3d9af, 0xa2677172, 0x99e4e939, 
467     /*  72 */  0x3c03e4d1, 0x07807c9a, 0x4b04d447, 0x70874c0c, 
468     /*  76 */  0xd20d85fd, 0xe98e1db6, 0xa50ab56b, 0x9e892d20, 
469     /*  80 */  0x35b5a8fa, 0x0e3630b1, 0x42b2986c, 0x79310027, 
470     /*  84 */  0xdbbbc9d6, 0xe038519d, 0xacbcf940, 0x973f610b, 
471     /*  88 */  0x32d86ce3, 0x095bf4a8, 0x45df5c75, 0x7e5cc43e, 
472     /*  92 */  0xdcd60dcf, 0xe7559584, 0xabd13d59, 0x9052a512, 
473     /*  96 */  0x26d930ac, 0x1d5aa8e7, 0x51de003a, 0x6a5d9871, 
474     /* 100 */  0xc8d75180, 0xf354c9cb, 0xbfd06116, 0x8453f95d, 
475     /* 104 */  0x21b4f4b5, 0x1a376cfe, 0x56b3c423, 0x6d305c68, 
476     /* 108 */  0xcfba9599, 0xf4390dd2, 0xb8bda50f, 0x833e3d44, 
477     /* 112 */  0x2802b89e, 0x138120d5, 0x5f058808, 0x64861043, 
478     /* 116 */  0xc60cd9b2, 0xfd8f41f9, 0xb10be924, 0x8a88716f, 
479     /* 120 */  0x2f6f7c87, 0x14ece4cc, 0x58684c11, 0x63ebd45a, 
480     /* 124 */  0xc1611dab, 0xfae285e0, 0xb6662d3d, 0x8de5b576, 
481     /* 128 */  0x76dc4190, 0x4d5fd9db, 0x01db7106, 0x3a58e94d, 
482     /* 132 */  0x98d220bc, 0xa351b8f7, 0xefd5102a, 0xd4568861, 
483     /* 136 */  0x71b18589, 0x4a321dc2, 0x06b6b51f, 0x3d352d54, 
484     /* 140 */  0x9fbfe4a5, 0xa43c7cee, 0xe8b8d433, 0xd33b4c78, 
485     /* 144 */  0x7807c9a2, 0x438451e9, 0x0f00f934, 0x3483617f, 
486     /* 148 */  0x9609a88e, 0xad8a30c5, 0xe10e9818, 0xda8d0053, 
487     /* 152 */  0x7f6a0dbb, 0x44e995f0, 0x086d3d2d, 0x33eea566, 
488     /* 156 */  0x91646c97, 0xaae7f4dc, 0xe6635c01, 0xdde0c44a, 
489     /* 160 */  0x6b6b51f4, 0x50e8c9bf, 0x1c6c6162, 0x27eff929, 
490     /* 164 */  0x856530d8, 0xbee6a893, 0xf262004e, 0xc9e19805, 
491     /* 168 */  0x6c0695ed, 0x57850da6, 0x1b01a57b, 0x20823d30, 
492     /* 172 */  0x8208f4c1, 0xb98b6c8a, 0xf50fc457, 0xce8c5c1c, 
493     /* 176 */  0x65b0d9c6, 0x5e33418d, 0x12b7e950, 0x2934711b, 
494     /* 180 */  0x8bbeb8ea, 0xb03d20a1, 0xfcb9887c, 0xc73a1037, 
495     /* 184 */  0x62dd1ddf, 0x595e8594, 0x15da2d49, 0x2e59b502, 
496     /* 188 */  0x8cd37cf3, 0xb750e4b8, 0xfbd44c65, 0xc057d42e, 
497     /* 192 */  0x4db26158, 0x7631f913, 0x3ab551ce, 0x0136c985, 
498     /* 196 */  0xa3bc0074, 0x983f983f, 0xd4bb30e2, 0xef38a8a9, 
499     /* 200 */  0x4adfa541, 0x715c3d0a, 0x3dd895d7, 0x065b0d9c, 
500     /* 204 */  0xa4d1c46d, 0x9f525c26, 0xd3d6f4fb, 0xe8556cb0, 
501     /* 208 */  0x4369e96a, 0x78ea7121, 0x346ed9fc, 0x0fed41b7, 
502     /* 212 */  0xad678846, 0x96e4100d, 0xda60b8d0, 0xe1e3209b, 
503     /* 216 */  0x44042d73, 0x7f87b538, 0x33031de5, 0x088085ae, 
504     /* 220 */  0xaa0a4c5f, 0x9189d414, 0xdd0d7cc9, 0xe68ee482, 
505     /* 224 */  0x5005713c, 0x6b86e977, 0x270241aa, 0x1c81d9e1, 
506     /* 228 */  0xbe0b1010, 0x8588885b, 0xc90c2086, 0xf28fb8cd, 
507     /* 232 */  0x5768b525, 0x6ceb2d6e, 0x206f85b3, 0x1bec1df8, 
508     /* 236 */  0xb966d409, 0x82e54c42, 0xce61e49f, 0xf5e27cd4, 
509     /* 240 */  0x5edef90e, 0x655d6145, 0x29d9c998, 0x125a51d3, 
510     /* 244 */  0xb0d09822, 0x8b530069, 0xc7d7a8b4, 0xfc5430ff, 
511     /* 248 */  0x59b33d17, 0x6230a55c, 0x2eb40d81, 0x153795ca, 
512     /* 252 */  0xb7bd5c3b, 0x8c3ec470, 0xc0ba6cad, 0xfb39f4e6, 
513 };
514
515 unsigned int apc_crc32(const char* buf, int len)
516 {
517     int i;
518     int k;
519     unsigned int crc;
520
521     /* preconditioning */
522     crc = 0xFFFFFFFF;
523     
524     for (i = 0; i < len; i++) {
525         k = (crc ^ buf[i]) & 0x000000FF;
526         crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[k];
527     }
528
529     /* postconditioning */
530     return ~crc;
531 }
532
533 /* crc32gen: generate the nth (0..255) crc32 table value */
534 #if 0
535 static unsigned long crc32gen(int n)
536 {
537     int i;
538     unsigned long crc;
539     
540     crc = n;
541     for (i = 8; i >= 0; i--) {
542         if (crc & 1) {
543             crc = (crc >> 1) ^ 0xEDB88320;
544         }
545         else {
546             crc >>= 1;
547         }
548     }
549     return crc;
550 }
551 #endif
552
553 /* }}} */
554
555 /*
556  * Local variables:
557  * tab-width: 4
558  * c-basic-offset: 4
559  * End:
560  * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
561  * vim<600: expandtab sw=4 ts=4 sts=4
562  */