r5:
[php5-apc.git] / apc.c
1 /*
2   +----------------------------------------------------------------------+
3   | APC                                                                  |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2006 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 2007/11/29 22:15:53 shire 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 /* {{{ filesystem functions */
274
275 #ifdef PHP_WIN32
276 int apc_win32_stat(const char *path, struct stat *buf TSRMLS_DC)
277 {
278     char rpath[MAXPATHLEN];
279     BY_HANDLE_FILE_INFORMATION fi;
280     HANDLE f;
281     
282     if (VCWD_STAT(path, buf)) {
283         return -1;
284     }
285
286     VCWD_REALPATH(path, rpath);
287     f = CreateFile(rpath, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_READONLY, NULL);
288     GetFileInformationByHandle(f, &fi);
289     buf->st_ino = (ino_t)fi.nFileIndexLow;
290     CloseHandle (f);
291     return 0;
292 }
293 #endif
294
295 int apc_search_paths(const char* filename, const char* path, apc_fileinfo_t* fileinfo)
296 {
297     char** paths;
298     char *exec_fname;
299     int exec_fname_length;
300     int found = 0;
301     int i;
302     TSRMLS_FETCH();
303
304     assert(filename && fileinfo);
305
306     if (IS_ABSOLUTE_PATH(filename, strlen(filename)) && apc_stat(filename, &fileinfo->st_buf) == 0) {
307         strncpy(fileinfo->fullpath, filename, MAXPATHLEN);
308         return 0;
309     }
310
311     paths = apc_tokenize(path, DEFAULT_DIR_SEPARATOR);
312     if (!paths)
313         return -1;
314
315     /* for each directory in paths, look for filename inside */
316     for (i = 0; paths[i]; i++) {
317         snprintf(fileinfo->fullpath, sizeof(fileinfo->fullpath), "%s%c%s", paths[i], DEFAULT_SLASH, filename);
318         if (apc_stat(fileinfo->fullpath, &fileinfo->st_buf) == 0) {
319             found = 1;
320             break;
321         }
322     }
323
324     /* check in path of the calling scripts' current working directory */
325     /* modified from main/streams/plain_wrapper.c */
326     if(!found && zend_is_executing(TSRMLS_C)) {
327         exec_fname = zend_get_executed_filename(TSRMLS_C);
328         exec_fname_length = strlen(exec_fname);
329         while((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
330         if((exec_fname && exec_fname[0] != '[') && exec_fname_length > 0) {
331             /* not: [no active file] or no path */
332             memcpy(fileinfo->fullpath, exec_fname, exec_fname_length);
333             fileinfo->fullpath[exec_fname_length] = DEFAULT_SLASH;
334             strcpy(fileinfo->fullpath +exec_fname_length +1, filename);
335             /* apc_wprint("filename: %s, exec_fname: %s, fileinfo->fullpath: %s", filename, exec_fname, fileinfo->fullpath); */
336             if (apc_stat(fileinfo->fullpath, &fileinfo->st_buf) == 0) {
337                 found = 1;
338             }
339         }
340     }
341     
342     /* free the value returned by apc_tokenize */
343     for (i = 0; paths[i]; i++) {
344         apc_efree(paths[i]);
345     }
346     apc_efree(paths);
347
348     return found ? 0 : -1;
349 }
350
351 /* }}} */
352
353 /* {{{ regular expression wrapper functions */
354
355 typedef struct {
356     regex_t *reg;
357     unsigned char type;
358 } apc_regex;
359
360 void* apc_regex_compile_array(char* patterns[])
361 {
362     apc_regex** regs;
363     int npat;
364     int i;
365
366     if (!patterns)
367         return NULL;
368
369     /* count the number of patterns in patterns */
370     for (npat = 0; patterns[npat] != NULL; npat++) {}
371
372     if (npat == 0)
373         return NULL;
374
375     /* allocate the array of compiled expressions */
376     regs = (apc_regex**) apc_emalloc(sizeof(apc_regex*) * (npat + 1));
377     for (i = 0; i <= npat; i++) {
378         regs[i] = (apc_regex *) apc_emalloc(sizeof(apc_regex));
379         regs[i]->reg = NULL;
380         regs[i]->type = APC_NEGATIVE_MATCH;
381     }
382
383     /* compile the expressions */
384     for (i = 0; i < npat; i++) {
385         char *pattern = patterns[i];
386         if(pattern[0]=='+') { regs[i]->type = APC_POSITIVE_MATCH; pattern = patterns[i]+sizeof(char); }
387         else if(pattern[0]=='-') { regs[i]->type = APC_NEGATIVE_MATCH; pattern = patterns[i]+sizeof(char); }
388
389         regs[i]->reg = (regex_t*) apc_emalloc(sizeof(regex_t));
390
391         if (regcomp(regs[i]->reg, pattern, REG_EXTENDED | REG_NOSUB) != 0) {
392             apc_wprint("apc_regex_compile_array: invalid expression '%s'",
393                        pattern);
394
395             apc_regex_destroy_array(regs);
396
397             return NULL;
398         }
399     }
400
401     return (void*) regs;
402 }
403
404 void apc_regex_destroy_array(void* p)
405 {
406     if (p != NULL) {
407         apc_regex** regs = (apc_regex**) p;
408         int i;
409
410         for (i = 0; regs[i]->reg != NULL; i++) {
411             regfree(regs[i]->reg);
412             apc_efree(regs[i]->reg);
413             apc_efree(regs[i]);
414         }
415         apc_efree(regs);
416     }
417 }
418
419 int apc_regex_match_array(void* p, const char* input)
420 {
421     apc_regex** regs;
422     int i;
423
424     if (!p)
425         return 0;
426
427     regs = (apc_regex**) p;
428     for (i = 0; regs[i]->reg != NULL; i++)
429         if (regexec(regs[i]->reg, input, 0, NULL, 0) == 0)
430             return (int)(regs[i]->type);
431
432     return 0;
433 }
434
435 /* }}} */
436
437 /* {{{ crc32 implementation */
438
439 /* this table was generated by crc32gen() */
440 static unsigned int crc32tab[] = {
441     /*   0 */  0x00000000, 0x3b83984b, 0x77073096, 0x4c84a8dd, 
442     /*   4 */  0xee0e612c, 0xd58df967, 0x990951ba, 0xa28ac9f1, 
443     /*   8 */  0x076dc419, 0x3cee5c52, 0x706af48f, 0x4be96cc4, 
444     /*  12 */  0xe963a535, 0xd2e03d7e, 0x9e6495a3, 0xa5e70de8, 
445     /*  16 */  0x0edb8832, 0x35581079, 0x79dcb8a4, 0x425f20ef, 
446     /*  20 */  0xe0d5e91e, 0xdb567155, 0x97d2d988, 0xac5141c3, 
447     /*  24 */  0x09b64c2b, 0x3235d460, 0x7eb17cbd, 0x4532e4f6, 
448     /*  28 */  0xe7b82d07, 0xdc3bb54c, 0x90bf1d91, 0xab3c85da, 
449     /*  32 */  0x1db71064, 0x2634882f, 0x6ab020f2, 0x5133b8b9, 
450     /*  36 */  0xf3b97148, 0xc83ae903, 0x84be41de, 0xbf3dd995, 
451     /*  40 */  0x1adad47d, 0x21594c36, 0x6ddde4eb, 0x565e7ca0, 
452     /*  44 */  0xf4d4b551, 0xcf572d1a, 0x83d385c7, 0xb8501d8c, 
453     /*  48 */  0x136c9856, 0x28ef001d, 0x646ba8c0, 0x5fe8308b, 
454     /*  52 */  0xfd62f97a, 0xc6e16131, 0x8a65c9ec, 0xb1e651a7, 
455     /*  56 */  0x14015c4f, 0x2f82c404, 0x63066cd9, 0x5885f492, 
456     /*  60 */  0xfa0f3d63, 0xc18ca528, 0x8d080df5, 0xb68b95be, 
457     /*  64 */  0x3b6e20c8, 0x00edb883, 0x4c69105e, 0x77ea8815, 
458     /*  68 */  0xd56041e4, 0xeee3d9af, 0xa2677172, 0x99e4e939, 
459     /*  72 */  0x3c03e4d1, 0x07807c9a, 0x4b04d447, 0x70874c0c, 
460     /*  76 */  0xd20d85fd, 0xe98e1db6, 0xa50ab56b, 0x9e892d20, 
461     /*  80 */  0x35b5a8fa, 0x0e3630b1, 0x42b2986c, 0x79310027, 
462     /*  84 */  0xdbbbc9d6, 0xe038519d, 0xacbcf940, 0x973f610b, 
463     /*  88 */  0x32d86ce3, 0x095bf4a8, 0x45df5c75, 0x7e5cc43e, 
464     /*  92 */  0xdcd60dcf, 0xe7559584, 0xabd13d59, 0x9052a512, 
465     /*  96 */  0x26d930ac, 0x1d5aa8e7, 0x51de003a, 0x6a5d9871, 
466     /* 100 */  0xc8d75180, 0xf354c9cb, 0xbfd06116, 0x8453f95d, 
467     /* 104 */  0x21b4f4b5, 0x1a376cfe, 0x56b3c423, 0x6d305c68, 
468     /* 108 */  0xcfba9599, 0xf4390dd2, 0xb8bda50f, 0x833e3d44, 
469     /* 112 */  0x2802b89e, 0x138120d5, 0x5f058808, 0x64861043, 
470     /* 116 */  0xc60cd9b2, 0xfd8f41f9, 0xb10be924, 0x8a88716f, 
471     /* 120 */  0x2f6f7c87, 0x14ece4cc, 0x58684c11, 0x63ebd45a, 
472     /* 124 */  0xc1611dab, 0xfae285e0, 0xb6662d3d, 0x8de5b576, 
473     /* 128 */  0x76dc4190, 0x4d5fd9db, 0x01db7106, 0x3a58e94d, 
474     /* 132 */  0x98d220bc, 0xa351b8f7, 0xefd5102a, 0xd4568861, 
475     /* 136 */  0x71b18589, 0x4a321dc2, 0x06b6b51f, 0x3d352d54, 
476     /* 140 */  0x9fbfe4a5, 0xa43c7cee, 0xe8b8d433, 0xd33b4c78, 
477     /* 144 */  0x7807c9a2, 0x438451e9, 0x0f00f934, 0x3483617f, 
478     /* 148 */  0x9609a88e, 0xad8a30c5, 0xe10e9818, 0xda8d0053, 
479     /* 152 */  0x7f6a0dbb, 0x44e995f0, 0x086d3d2d, 0x33eea566, 
480     /* 156 */  0x91646c97, 0xaae7f4dc, 0xe6635c01, 0xdde0c44a, 
481     /* 160 */  0x6b6b51f4, 0x50e8c9bf, 0x1c6c6162, 0x27eff929, 
482     /* 164 */  0x856530d8, 0xbee6a893, 0xf262004e, 0xc9e19805, 
483     /* 168 */  0x6c0695ed, 0x57850da6, 0x1b01a57b, 0x20823d30, 
484     /* 172 */  0x8208f4c1, 0xb98b6c8a, 0xf50fc457, 0xce8c5c1c, 
485     /* 176 */  0x65b0d9c6, 0x5e33418d, 0x12b7e950, 0x2934711b, 
486     /* 180 */  0x8bbeb8ea, 0xb03d20a1, 0xfcb9887c, 0xc73a1037, 
487     /* 184 */  0x62dd1ddf, 0x595e8594, 0x15da2d49, 0x2e59b502, 
488     /* 188 */  0x8cd37cf3, 0xb750e4b8, 0xfbd44c65, 0xc057d42e, 
489     /* 192 */  0x4db26158, 0x7631f913, 0x3ab551ce, 0x0136c985, 
490     /* 196 */  0xa3bc0074, 0x983f983f, 0xd4bb30e2, 0xef38a8a9, 
491     /* 200 */  0x4adfa541, 0x715c3d0a, 0x3dd895d7, 0x065b0d9c, 
492     /* 204 */  0xa4d1c46d, 0x9f525c26, 0xd3d6f4fb, 0xe8556cb0, 
493     /* 208 */  0x4369e96a, 0x78ea7121, 0x346ed9fc, 0x0fed41b7, 
494     /* 212 */  0xad678846, 0x96e4100d, 0xda60b8d0, 0xe1e3209b, 
495     /* 216 */  0x44042d73, 0x7f87b538, 0x33031de5, 0x088085ae, 
496     /* 220 */  0xaa0a4c5f, 0x9189d414, 0xdd0d7cc9, 0xe68ee482, 
497     /* 224 */  0x5005713c, 0x6b86e977, 0x270241aa, 0x1c81d9e1, 
498     /* 228 */  0xbe0b1010, 0x8588885b, 0xc90c2086, 0xf28fb8cd, 
499     /* 232 */  0x5768b525, 0x6ceb2d6e, 0x206f85b3, 0x1bec1df8, 
500     /* 236 */  0xb966d409, 0x82e54c42, 0xce61e49f, 0xf5e27cd4, 
501     /* 240 */  0x5edef90e, 0x655d6145, 0x29d9c998, 0x125a51d3, 
502     /* 244 */  0xb0d09822, 0x8b530069, 0xc7d7a8b4, 0xfc5430ff, 
503     /* 248 */  0x59b33d17, 0x6230a55c, 0x2eb40d81, 0x153795ca, 
504     /* 252 */  0xb7bd5c3b, 0x8c3ec470, 0xc0ba6cad, 0xfb39f4e6, 
505 };
506
507 unsigned int apc_crc32(const char* buf, int len)
508 {
509     int i;
510     int k;
511     unsigned int crc;
512
513     /* preconditioning */
514     crc = 0xFFFFFFFF;
515     
516     for (i = 0; i < len; i++) {
517         k = (crc ^ buf[i]) & 0x000000FF;
518         crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[k];
519     }
520
521     /* postconditioning */
522     return ~crc;
523 }
524
525 /* crc32gen: generate the nth (0..255) crc32 table value */
526 #if 0
527 static unsigned long crc32gen(int n)
528 {
529     int i;
530     unsigned long crc;
531     
532     crc = n;
533     for (i = 8; i >= 0; i--) {
534         if (crc & 1) {
535             crc = (crc >> 1) ^ 0xEDB88320;
536         }
537         else {
538             crc >>= 1;
539         }
540     }
541     return crc;
542 }
543 #endif
544
545 /* }}} */
546
547 /*
548  * Local variables:
549  * tab-width: 4
550  * c-basic-offset: 4
551  * End:
552  * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
553  * vim<600: expandtab sw=4 ts=4 sts=4
554  */