root/eaccelerator/tags/0.9.5/eaccelerator.c

Revision 272, 86.9 kB (checked in by bart, 2 years ago)

* Don't free memory with efree that has been allocated from the eA shm cache.
This should fix A LOT of bugreports in Trac and on SF. It fixes #171 but
I think also some other bugs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /*
2    +----------------------------------------------------------------------+
3    | eAccelerator project                                                 |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 2004 - 2006 eAccelerator                               |
6    | http://eaccelerator.net                                                                                                            |
7    +----------------------------------------------------------------------+
8    | This program is free software; you can redistribute it and/or        |
9    | modify it under the terms of the GNU General Public License          |
10    | as published by the Free Software Foundation; either version 2       |
11    | of the License, or (at your option) any later version.               |
12    |                                                                      |
13    | This program is distributed in the hope that it will be useful,      |
14    | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
15    | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
16    | GNU General Public License for more details.                         |
17    |                                                                      |
18    | You should have received a copy of the GNU General Public License    |
19    | along with this program; if not, write to the Free Software          |
20    | Foundation, Inc., 59 Temple Place - Suite 330, Boston,               |
21    | MA  02111-1307, USA.                                                 |
22    |                                                                      |
23    | A copy is availble at http://www.gnu.org/copyleft/gpl.txt            |
24    +----------------------------------------------------------------------+
25    $Id$
26 */
27
28 #include "eaccelerator.h"
29 #include "eaccelerator_version.h"
30
31 #ifdef HAVE_EACCELERATOR
32
33 #include "opcodes.h"
34
35 #include "zend.h"
36 #include "zend_API.h"
37 #include "zend_extensions.h"
38
39 #include "debug.h"
40 #include "shm.h"
41 #include "session.h"
42 #include "content.h"
43 #include "cache.h"
44 #include "ea_store.h"
45 #include "ea_restore.h"
46 #include "ea_info.h"
47 #include "ea_dasm.h"
48
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #ifdef ZEND_WIN32
52 #  include "win32/time.h"
53 #  include <time.h>
54 #  include <sys/utime.h>
55 #else
56 #  include <sys/file.h>
57 #  include <sys/time.h>
58 #  include <utime.h>
59 #endif
60 #include <fcntl.h>
61
62 #ifndef O_BINARY
63 #  define O_BINARY 0
64 #endif
65
66 #include "php.h"
67 #include "php_ini.h"
68 #include "php_logos.h"
69 #include "main/fopen_wrappers.h"
70 #include "ext/standard/info.h"
71 #include "ext/standard/php_incomplete_class.h"
72 #include "ext/standard/md5.h"
73
74 #include "SAPI.h"
75
76 #define MAX_DUP_STR_LEN 256
77
78 /* Globals (different for each process/thread) */
79 ZEND_DECLARE_MODULE_GLOBALS(eaccelerator)
80
81 /* Globals (common for each process/thread) */
82 static long eaccelerator_shm_size = 0;
83 long eaccelerator_shm_max = 0;
84 static long eaccelerator_shm_ttl = 0;
85 static long eaccelerator_shm_prune_period = 0;
86 extern long eaccelerator_debug;
87 static zend_bool eaccelerator_check_mtime = 1;
88 zend_bool eaccelerator_scripts_shm_only = 0;
89
90 eaccelerator_mm* eaccelerator_mm_instance = NULL;
91 static int eaccelerator_is_zend_extension = 0;
92 static int eaccelerator_is_extension      = 0;
93 zend_extension* ZendOptimizer = NULL;
94
95 static HashTable eaccelerator_global_function_table;
96 static HashTable eaccelerator_global_class_table;
97
98 int binary_eaccelerator_version;
99 int binary_php_version;
100 int binary_zend_version;
101
102 #ifdef ZEND_ENGINE_2
103 /* pointer to the properties_info hashtable destructor */
104 extern dtor_func_t properties_info_dtor;
105 #endif
106
107 /* saved original functions */
108 static zend_op_array *(*mm_saved_zend_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
109
110 #ifdef DEBUG
111 static void (*mm_saved_zend_execute)(zend_op_array *op_array TSRMLS_DC);
112 #endif
113
114 /* external declarations */
115 PHPAPI void php_stripslashes(char *str, int *len TSRMLS_DC);
116
117 ZEND_DLEXPORT zend_op_array* eaccelerator_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC);
118
119 /******************************************************************************/
120 /* hash mm functions                                                          */
121 /******************************************************************************/
122
123 inline unsigned int hash_mm(const char *data, int len) {
124   unsigned int h;
125   const char *e = data + len;
126   for (h = 2166136261U; data < e; ) {
127     h *= 16777619;
128     h ^= *data++;
129   }
130   return h;
131 }
132
133 /* Find a script entry with the given hash key */
134 static mm_cache_entry* hash_find_mm(const char  *key,
135                                     struct stat *buf,
136                                     int         *nreloads,
137                                     time_t      ttl) {
138   unsigned int hv, slot;
139   mm_cache_entry *p, *q;
140
141 #ifdef EACCELERATOR_USE_INODE
142   hv = buf->st_dev + buf->st_ino;
143 #else
144   hv = hash_mm(key, strlen(key));
145 #endif
146   slot = hv & EA_HASH_MAX;
147
148   EACCELERATOR_LOCK_RW();
149   q = NULL;
150   p = eaccelerator_mm_instance->hash[slot];
151   while (p != NULL) {
152 #ifdef EACCELERATOR_USE_INODE
153     if (p->st_dev == buf->st_dev && p->st_ino == buf->st_ino) {
154       struct stat buf2;
155       if ((eaccelerator_check_mtime &&
156           (buf->st_mtime != p->mtime || buf->st_size != p->filesize)) ||
157           (strcmp(p->realfilename, key) != 0 &&
158            (stat(p->realfilename,&buf2) != 0 ||
159            buf2.st_dev != buf->st_dev ||
160            buf2.st_ino != buf->st_ino))) {
161 #else
162     if ((p->hv == hv) && (strcmp(p->realfilename, key) == 0)) {
163       if (eaccelerator_check_mtime &&
164           (buf->st_mtime != p->mtime || buf->st_size != p->filesize)) {
165 #endif
166         /* key is invalid. Remove it. */
167         *nreloads = p->nreloads+1;
168         if (q == NULL) {
169           eaccelerator_mm_instance->hash[slot] = p->next;
170         } else {
171           q->next = p->next;
172         }
173         eaccelerator_mm_instance->hash_cnt--;
174         if (p->use_cnt > 0) {
175           /* key is used by other process/thred. Shedule it to remove */
176           p->removed = 1;
177           p->next = eaccelerator_mm_instance->removed;
178           eaccelerator_mm_instance->removed = p;
179           eaccelerator_mm_instance->rem_cnt++;
180           EACCELERATOR_UNLOCK_RW();
181           return NULL;
182         } else {
183           /* key is unused. Remove it. */
184           eaccelerator_free_nolock(p);
185           EACCELERATOR_UNLOCK_RW();
186           return NULL;
187         }
188       } else {
189         /* key is valid */
190         p->nhits++;
191         p->use_cnt++;
192         p->ttl = ttl;
193         EACCELERATOR_UNLOCK_RW();
194         return p;
195       }
196     }
197     q = p;
198     p = p->next;
199   }
200   EACCELERATOR_UNLOCK_RW();
201   return NULL;
202 }
203
204 /* Add a new entry to the hashtable */
205 static void hash_add_mm(mm_cache_entry *x) {
206   mm_cache_entry *p,*q;
207   unsigned int slot;
208 #ifdef EACCELERATOR_USE_INODE
209   slot = (x->st_dev + x->st_ino) & EA_HASH_MAX;
210 #else
211   x->hv = hash_mm(x->realfilename, strlen(x->realfilename));
212   slot = x->hv & EA_HASH_MAX;
213 #endif
214
215   EACCELERATOR_LOCK_RW();
216   x->next = eaccelerator_mm_instance->hash[slot];
217   eaccelerator_mm_instance->hash[slot] = x;
218   eaccelerator_mm_instance->hash_cnt++;
219   q = x;
220   p = x->next;
221   while (p != NULL) {
222 #ifdef EACCELERATOR_USE_INODE
223     if ((p->st_dev == x->st_dev) && (p->st_ino == x->st_ino)) {
224 #else
225     if ((p->hv == x->hv) &&
226         (strcmp(p->realfilename, x->realfilename) == 0)) {
227 #endif
228       q->next = p->next;
229       eaccelerator_mm_instance->hash_cnt--;
230       eaccelerator_mm_instance->hash[slot]->nreloads += p->nreloads;
231       if (p->use_cnt > 0) {
232         /* key is used by other process/thred. Shedule it to remove */
233         p->removed = 1;
234         p->next = eaccelerator_mm_instance->removed;
235         eaccelerator_mm_instance->removed = p;
236         eaccelerator_mm_instance->rem_cnt++;
237         EACCELERATOR_UNLOCK_RW();
238         return;
239       } else {
240         /* key is unused. Remove it. */
241         eaccelerator_free_nolock(p);
242         EACCELERATOR_UNLOCK_RW();
243         return;
244       }
245     }
246     q = p;
247     p = p->next;
248   }
249   EACCELERATOR_UNLOCK_RW();
250 }
251
252 /* Initialise the shared memory */
253 static int init_mm(TSRMLS_D) {
254   pid_t  owner = getpid();
255   MM     *mm;
256   size_t total;
257   char   mm_path[MAXPATHLEN];
258
259 /*  if (getppid() != 1) return SUCCESS; */ /*???*/
260 #ifdef ZEND_WIN32
261     snprintf(mm_path, MAXPATHLEN, "%s.%s", EACCELERATOR_MM_FILE, sapi_module.name);
262 #else
263     snprintf(mm_path, MAXPATHLEN, "%s.%s%d", EACCELERATOR_MM_FILE, sapi_module.name, getpid());
264 #endif
265 /*  snprintf(mm_path, MAXPATHLEN, "%s.%s%d", EACCELERATOR_MM_FILE, sapi_module.name, geteuid());*/
266   if ((eaccelerator_mm_instance = (eaccelerator_mm*)mm_attach(eaccelerator_shm_size*1024*1024, mm_path)) != NULL) {
267 #ifdef ZTS
268     ea_mutex = tsrm_mutex_alloc();
269 #endif
270     return SUCCESS;
271   }
272   mm = mm_create(eaccelerator_shm_size*1024*1024, mm_path);
273   if (!mm) {
274     return FAILURE;
275   }
276 #ifdef ZEND_WIN32
277   DBG(ea_debug_printf, (EA_DEBUG, "init_mm [%d]\n", getpid()));
278 #else
279   DBG(ea_debug_printf, (EA_DEBUG, "init_mm [%d,%d]\n", getpid(), getppid()));
280 #endif
281 #ifdef ZTS
282   ea_mutex = tsrm_mutex_alloc();
283 #endif
284   total = mm_available(mm);
285   eaccelerator_mm_instance = mm_malloc_lock(mm, sizeof(*eaccelerator_mm_instance));
286   if (!eaccelerator_mm_instance) {
287     return FAILURE;
288   }
289   mm_set_attach(mm, eaccelerator_mm_instance);
290   memset(eaccelerator_mm_instance, 0, sizeof(*eaccelerator_mm_instance));
291   eaccelerator_mm_instance->owner = owner;
292   eaccelerator_mm_instance->mm    = mm;
293   eaccelerator_mm_instance->total = total;
294   eaccelerator_mm_instance->hash_cnt = 0;
295   eaccelerator_mm_instance->rem_cnt  = 0;
296   eaccelerator_mm_instance->enabled = 1;
297   eaccelerator_mm_instance->optimizer_enabled = 1;
298   eaccelerator_mm_instance->removed = NULL;
299   eaccelerator_mm_instance->locks = NULL;
300   eaccelerator_mm_instance->user_hash_cnt = 0;
301   eaccelerator_mm_instance->last_prune = time(0);
302   EACCELERATOR_PROTECT();
303   return SUCCESS;
304 }
305
306 /* Clean up the shared memory */
307 static void shutdown_mm(TSRMLS_D) {
308   if (eaccelerator_mm_instance) {
309 #ifdef ZEND_WIN32
310     if (eaccelerator_mm_instance->owner == getpid()) {
311 #else
312     if (getpgrp() == getpid()) {
313 #endif
314       MM *mm = eaccelerator_mm_instance->mm;
315 #ifdef ZEND_WIN32
316       DBG(ea_debug_printf, (EA_DEBUG, "shutdown_mm [%d]\n", getpid()));
317 #else
318       DBG(ea_debug_printf, (EA_DEBUG, "shutdown_mm [%d,%d]\n", getpid(), getppid()));
319 #endif
320 #ifdef ZTS
321       tsrm_mutex_free(ea_mutex);
322 #endif
323       if (mm) {
324         mm_destroy(mm);
325       }
326       eaccelerator_mm_instance = NULL;
327     }
328   }
329 }
330
331
332 static int encode_version(const char *s) {
333   unsigned int v1 = 0;
334   unsigned int v2 = 0;
335   unsigned int v3 = 0;
336   unsigned int c;
337   char m = '.';
338   sscanf(s, "%u.%u%c%u",&v1,&v2,&m,&v3);
339   switch (m) {
340     case  'a': c = 0; break;
341     case  'b': c = 1; break;
342     case  '.': c = 2; break;
343     case  's': c = 15; break;
344     default: c = 2;
345   }
346   return ((v1 & 0xf) << 20) |
347          ((v2 & 0xff) << 12) |
348          ((c & 0xf) << 8) |
349          (v3 & 0xff);
350 }
351
352 /* This function isn't used. So disable it for now
353 static void decode_version(char *version, int v) {
354   int t = (v & 0x000f00) >> 8;
355   char c;
356   switch (t) {
357     case  0: c = 'a'; break;
358     case  1: c = 'b'; break;
359     case  2: c = '.'; break;
360     case 15: c = 's'; break;
361     default: c = '.';
362   }
363   snprintf(version, 16, "%d.%d%c%d", (v & 0xf00000) >> 20,
364                                      (v & 0x0ff000) >> 12,
365                                      c,
366                                      (v & 0x0000ff));
367 }
368 */
369
370 static char num2hex[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
371
372 #ifdef EACCELERATOR_USE_INODE
373 static int eaccelerator_inode_key(char* s, dev_t dev, ino_t ino TSRMLS_DC) {
374   int n, i;
375   snprintf(s, MAXPATHLEN-1, "%s/", EAG(cache_dir));
376   n = strlen(s);
377   for (i = 1; i <= EACCELERATOR_HASH_LEVEL && n < MAXPATHLEN - 1; i++) {
378     s[n++] = num2hex[(ino >> (i*4)) & 0xf];
379     s[n++] = '/';
380   }
381   s[n] = 0;
382   strlcat(s, "eaccelerator-", MAXPATHLEN-1);
383   n += sizeof("eaccelerator-") - 1;
384   while (dev > 0) {
385     if (n >= MAXPATHLEN) return 0;
386     s[n++] = (dev % 10) +'0';
387     dev /= 10;
388   }
389   if (n >= MAXPATHLEN) return 0;
390   s[n++] = '.';
391   while (ino > 0) {
392     if (n >= MAXPATHLEN) return 0;
393     s[n++] = (ino % 10) +'0';
394     ino /= 10;
395   }
396   if (n >= MAXPATHLEN) return 0;
397   s[n++] = '\000';
398   return 1;
399 }
400 #endif
401
402 /* Function to create a hash key when filenames are used */
403 int eaccelerator_md5(char* s, const char* prefix, const char* key TSRMLS_DC) {
404 #if defined(PHP_MAJOR_VERSION) && defined(PHP_MINOR_VERSION) && \
405     ((PHP_MAJOR_VERSION > 4) || (PHP_MAJOR_VERSION == 4 && PHP_MINOR_VERSION > 1))
406   char md5str[33];
407   PHP_MD5_CTX context;
408   unsigned char digest[16];
409   int i;
410   int n;
411
412   md5str[0] = '\0';
413   PHP_MD5Init(&context);
414   PHP_MD5Update(&context, (unsigned char*)key, strlen(key));
415   PHP_MD5Final(digest, &context);
416   make_digest(md5str, digest);
417   snprintf(s, MAXPATHLEN-1, "%s/", EAG(cache_dir));
418   n = strlen(s);
419   for (i = 0; i < EACCELERATOR_HASH_LEVEL && n < MAXPATHLEN - 1; i++) {
420     s[n++] = md5str[i];
421     s[n++] = '/';
422   }
423   s[n] = 0;
424   snprintf(s, MAXPATHLEN-1, "%s%s%s", s, prefix, md5str);
425   return 1;
426 #else
427   zval retval;
428   zval md5;
429   zval param;
430   zval *params[1];
431
432   ZVAL_STRING(&md5, "md5", 0);
433   INIT_ZVAL(param);
434   params[0] = &param;
435   ZVAL_STRING(params[0], (char*)key, 0);
436   if (call_user_function(CG(function_table), (zval**)NULL, &md5, &retval, 1, params TSRMLS_CC) == SUCCESS &&
437       Z_TYPE(retval) == IS_STRING && Z_STRLEN(retval) == 32) {
438     strncpy(s, EAG(cache_dir), MAXPATHLEN-1);
439     strlcat(s, prefix, MAXPATHLEN);
440     strlcat(s, Z_STRVAL(retval), MAXPATHLEN);
441     zval_dtor(&retval);
442     return 1;
443   }
444   s[0] ='\0';
445 #endif
446   return 0;
447 }
448
449 /* Remove expired keys, content and scripts from the cache */
450 void eaccelerator_prune(time_t t) {
451   unsigned int i;
452
453   EACCELERATOR_LOCK_RW();
454   eaccelerator_mm_instance->last_prune = t;
455   for (i = 0; i < EA_HASH_SIZE; i++) {
456     mm_cache_entry **p = &eaccelerator_mm_instance->hash[i];
457     while (*p != NULL) {
458       struct stat buf;
459       if (((*p)->ttl != 0 && (*p)->ttl < t && (*p)->use_cnt <= 0) ||
460           stat((*p)->realfilename,&buf) != 0 ||
461 #ifdef EACCELERATOR_USE_INODE
462           (*p)->st_dev != buf.st_dev ||
463           (*p)->st_ino != buf.st_ino ||
464 #endif
465           (*p)->mtime != buf.st_mtime ||
466           (*p)->filesize != buf.st_size) {
467         mm_cache_entry *r = *p;
468         *p = (*p)->next;
469         eaccelerator_mm_instance->hash_cnt--;
470         eaccelerator_free_nolock(r);
471       } else {
472         p = &(*p)->next;
473       }
474     }
475   }
476   EACCELERATOR_UNLOCK_RW();
477 }
478
479 /* Allocate a new cache chunk */
480 void* eaccelerator_malloc2(size_t size TSRMLS_DC) {
481   void *p = NULL;
482   time_t t;
483
484   if (eaccelerator_gc(TSRMLS_C) > 0) {
485     p = eaccelerator_malloc(size);
486     if (p != NULL) {
487       return p;
488     }
489   }
490   if (eaccelerator_shm_prune_period > 0) {
491     t = time(0);
492     if (t - eaccelerator_mm_instance->last_prune > eaccelerator_shm_prune_period) {
493       eaccelerator_prune(t);
494       p = eaccelerator_malloc(size);
495     }
496   }
497   return p;
498 }
499
500 #define EACCELERATOR_CRC32(crc, ch)   (crc = (crc >> 8) ^ crc32tab[(crc ^ (ch)) & 0xff])
501
502 static const unsigned int crc32tab[256] = {
503   0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
504   0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
505   0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
506   0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
507   0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
508   0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
509   0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
510   0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
511   0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
512   0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
513   0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
514   0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
515   0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
516   0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
517   0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
518   0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
519   0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
520   0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
521   0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
522   0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
523   0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
524   0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
525   0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
526   0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
527   0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
528   0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
529   0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
530   0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
531   0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
532   0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
533   0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
534   0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
535   0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
536   0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
537   0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
538   0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
539   0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
540   0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
541   0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
542   0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
543   0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
544   0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
545   0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
546   0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
547   0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
548   0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
549   0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
550   0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
551   0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
552   0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
553   0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
554   0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
555   0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
556   0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
557   0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
558   0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
559   0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
560   0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
561   0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
562   0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
563   0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
564   0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
565   0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
566   0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
567 };
568
569 unsigned int eaccelerator_crc32(const char *p, size_t n) {
570   unsigned int crc = ~0;
571   for (; n--; ++p) {
572     EACCELERATOR_CRC32(crc, *p);
573   }
574   return ~crc;
575 }
576
577 void eaccelerator_fixup (mm_cache_entry * p TSRMLS_DC)
578 {
579   mm_fc_entry *q;
580
581   EAG (mem) = (char *) ((long) p - (long) p->next);
582   EAG (compress) = 1;
583   p->next = NULL;
584   FIXUP (p->op_array);
585   FIXUP (p->f_head);
586   FIXUP (p->c_head);
587   fixup_op_array (p->op_array TSRMLS_CC);
588   q = p->f_head;
589   while (q != NULL) {
590     FIXUP (q->fc);
591     fixup_op_array ((eaccelerator_op_array *) q->fc TSRMLS_CC);
592     FIXUP (q->next);
593     q = q->next;
594   }
595   q = p->c_head;
596   while (q != NULL) {
597     FIXUP (q->fc);
598     fixup_class_entry ((eaccelerator_class_entry *) q->fc TSRMLS_CC);
599     FIXUP (q->next);
600     q = q->next;
601   }
602 }
603
604 /******************************************************************************/
605 /* Cache file functions.                                                                                                                */
606 /******************************************************************************/
607
608 /* Retrieve a cache entry from the cache directory */
609 static mm_cache_entry* hash_find_file(const char  *key,
610                                       struct stat *buf TSRMLS_DC) {
611   int f;
612   char s[MAXPATHLEN];
613   mm_file_header hdr;
614   mm_cache_entry *p;
615   int use_shm = 1;
616
617 #ifdef EACCELERATOR_USE_INODE
618   struct stat buf2;
619
620   if (!eaccelerator_inode_key(s, buf->st_dev, buf->st_ino TSRMLS_CC)) {
621     return NULL;
622   }
623 #else
624   if (!eaccelerator_md5(s, "/eaccelerator-", key TSRMLS_CC)) {
625     return NULL;
626   }
627 #endif
628
629   if ((f = open(s, O_RDONLY | O_BINARY)) > 0) {
630     EACCELERATOR_FLOCK(f, LOCK_SH);
631     if (read(f, &hdr, sizeof(hdr)) != sizeof(hdr)) {
632       EACCELERATOR_FLOCK(f, LOCK_UN);
633       close(f);
634       return NULL;
635     }
636     if (strncmp(hdr.magic, EA_MAGIC, 8) != 0 ||
637         hdr.eaccelerator_version != binary_eaccelerator_version ||
638         hdr.zend_version != binary_zend_version ||
639         hdr.php_version != binary_php_version) {
640       EACCELERATOR_FLOCK(f, LOCK_UN);
641       close(f);
642       unlink(s);
643       return NULL;
644     }
645     p = eaccelerator_malloc(hdr.size);
646     if (p == NULL) {
647       p = eaccelerator_malloc2(hdr.size TSRMLS_CC);
648     }
649     if (p == NULL) {
650       p = emalloc(hdr.size);
651       use_shm = 0;
652     }
653     if (p == NULL) {
654       EACCELERATOR_FLOCK(f, LOCK_UN);
655       close(f);
656       return NULL;
657     }
658     if (read(f, p, hdr.size) != hdr.size ||
659         p->size != hdr.size ||
660         hdr.crc32 != eaccelerator_crc32((const char*)p,p->size)) {
661       EACCELERATOR_FLOCK(f, LOCK_UN);
662       close(f);
663       unlink(s);
664       if (use_shm) eaccelerator_free(p); else efree(p);
665       return NULL;
666     }
667     EACCELERATOR_FLOCK(f, LOCK_UN);
668     close(f);
669 #ifdef EACCELERATOR_USE_INODE
670     if (p->st_dev != buf->st_dev || p->st_ino != buf->st_ino) {
671 #else
672     if (strcmp(key,p->realfilename) != 0) {
673 #endif
674       if (use_shm) eaccelerator_free(p); else efree(p);
675       return NULL;
676     }
677     if ((eaccelerator_check_mtime &&
678         (buf->st_mtime != p->mtime || buf->st_size != p->filesize))
679 #ifdef EACCELERATOR_USE_INODE
680         ||
681         (strcmp(p->realfilename, key) != 0 &&
682          (stat(p->realfilename,&buf2) != 0 ||
683          buf2.st_dev != buf->st_dev ||
684          buf2.st_ino != buf->st_ino))
685 #endif
686        ) {
687       /* key is invalid. Remove it. */
688       if (use_shm) eaccelerator_free(p); else efree(p);
689       unlink(s);
690       return NULL;
691     }
692     eaccelerator_fixup(p TSRMLS_CC);
693     if (use_shm) {
694       p->nhits    = 1;
695       p->nreloads = 1;
696       p->use_cnt  = 1;
697       p->removed  = 0;
698       if (eaccelerator_shm_ttl > 0) {
699         p->ttl = time(0) + eaccelerator_shm_ttl;
700       } else {
701         p->ttl = 0;
702       }
703       hash_add_mm(p);
704     } else {
705       p->use_cnt  = 0;
706       p->removed  = 1;
707     }
708     return p;
709   }
710   return NULL;
711 }
712
713 /* Add a cache entry to the cache directory */
714 static int hash_add_file(mm_cache_entry *p TSRMLS_DC) {
715   int f;
716   int ret = 0;
717   char s[MAXPATHLEN];
718   mm_file_header hdr;
719
720 #ifdef EACCELERATOR_USE_INODE
721   if (!eaccelerator_inode_key(s, p->st_dev, p->st_ino TSRMLS_CC)) {
722     return 0;
723   }
724 #else
725   if (!eaccelerator_md5(s, "/eaccelerator-", p->realfilename TSRMLS_CC)) {
726     return 0;
727   }
728 #endif
729
730   unlink(s);
731   f = open(s, O_CREAT | O_WRONLY | O_EXCL | O_BINARY, S_IRUSR | S_IWUSR);
732   if (f > 0) {
733     EACCELERATOR_FLOCK(f, LOCK_EX);
734     strncpy(hdr.magic, EA_MAGIC, 8);
735     hdr.eaccelerator_version = binary_eaccelerator_version;
736     hdr.zend_version    = binary_zend_version;
737     hdr.php_version     = binary_php_version;
738     hdr.size  = p->size;
739     hdr.mtime = p->mtime;
740     p->next = p;
741     hdr.crc32 = eaccelerator_crc32((const char*)p,p->size);
742     ret = (write(f, &hdr, sizeof(hdr)) == sizeof(hdr));
743     if (ret) ret = (write(f, p, p->size) == p->size);
744     EACCELERATOR_FLOCK(f, LOCK_UN);
745     close(f);
746   } else {
747     ea_debug_log("EACCELERATOR: Open for write failed for \"%s\": %s\n", s, strerror(errno));
748   }
749   return ret;
750 }
751
752 /* Create a cache entry from the given op_array, functions and classes of a
753    script */
754 static mm_cache_entry *eaccelerator_store_int (char *key, int len,
755         zend_op_array * op_array, Bucket * f, Bucket * c TSRMLS_DC)
756 {
757   mm_cache_entry *p;
758   mm_fc_entry *fc;
759   mm_fc_entry *q;
760   char *x;
761
762   DBG(ea_debug_pad, (EA_DEBUG TSRMLS_CC));
763   DBG(ea_debug_printf, (EA_DEBUG, "[%d] eaccelerator_store_int: key='%s'\n",
764           getpid (), key));
765
766   EAG (compress) = 1;
767   zend_hash_init (&EAG (strings), 0, NULL, NULL, 0);
768   p = (mm_cache_entry *) EAG (mem);
769   EAG (mem) += offsetof (mm_cache_entry, realfilename) + len + 1;
770
771   p->nhits = 0;
772   p->use_cnt = 0;
773   p->removed = 0;
774   p->f_head = NULL;
775   p->c_head = NULL;
776   memcpy (p->realfilename, key, len + 1);
777   x = p->realfilename;
778   zend_hash_add (&EAG (strings), key, len + 1, &x, sizeof (char *), NULL);
779
780   q = NULL;
781   while (c != NULL) {
782     DBG(ea_debug_pad, (EA_DEBUG TSRMLS_CC));
783     DBG(ea_debug_printf, (EA_DEBUG,
784             "[%d] eaccelerator_store_int:     class hashkey=", getpid ()));
785     DBG(ea_debug_binary_print, (EA_DEBUG, c->arKey, c->nKeyLength));
786
787     EACCELERATOR_ALIGN (EAG (mem));
788     fc = (mm_fc_entry *) EAG (mem);
789     EAG (mem) += offsetof (mm_fc_entry, htabkey) + c->nKeyLength;
790     memcpy (fc->htabkey, c->arKey, c->nKeyLength);
791     fc->htablen = c->nKeyLength;
792     fc->next = NULL;
793 #ifdef ZEND_ENGINE_2
794     fc->fc = *(zend_class_entry **) c->pData;
795 #else
796     fc->fc = c->pData;
797 #endif
798     c = c->pListNext;
799     x = fc->htabkey;
800     zend_hash_add (&EAG (strings), fc->htabkey, fc->htablen, &x,
801             sizeof (char *), NULL);
802     if (q == NULL) {
803       p->c_head = fc;
804     } else {
805       q->next = fc;
806     }
807     q = fc;
808   }
809
810   q = NULL;
811   while (f != NULL) {
812       DBG(ea_debug_pad, (EA_DEBUG TSRMLS_CC));
813       DBG(ea_debug_printf, (EA_DEBUG,
814               "[%d] eaccelerator_store_int:     function hashkey='%s'\n", getpid (), f->arKey));
815
816       EACCELERATOR_ALIGN (EAG (mem));
817       fc = (mm_fc_entry *) EAG (mem);
818       EAG (mem) += offsetof (mm_fc_entry, htabkey) + f->nKeyLength;
819       memcpy (fc->htabkey, f->arKey, f->nKeyLength);
820       fc->htablen = f->nKeyLength;
821       fc->next = NULL;
822       fc->fc = f->pData;
823       f = f->pListNext;
824       x = fc->htabkey;
825       zend_hash_add (&EAG (strings), fc->htabkey, fc->htablen, &x,
826               sizeof (char *), NULL);
827       if (q == NULL) {
828           p->f_head = fc;
829       } else {
830           q->next = fc;
831       }
832       q = fc;
833   }
834
835   q = p->c_head;
836   while (q != NULL) {
837       q->fc = store_class_entry ((zend_class_entry *) q->fc TSRMLS_CC);
838       q = q->next;
839   }
840
841   q = p->f_head;
842   while (q != NULL) {
843       q->fc = store_op_array ((zend_op_array *) q->fc TSRMLS_CC);
844       q = q->next;
845   }
846   p->op_array = store_op_array (op_array TSRMLS_CC);
847
848   zend_hash_destroy (&EAG (strings));
849   return p;
850 }
851
852 /* called after succesful compilation, from eaccelerator_compile file */
853 /* Adds the data from the compilation of the script to the cache */
854 static int eaccelerator_store(char* key, struct stat *buf, int nreloads,
855                          zend_op_array* op_array,
856                          Bucket* f, Bucket *c TSRMLS_DC) {
857   mm_cache_entry *p;
858   int len = strlen(key);
859   int use_shm = 1;
860   int ret = 0;
861   int size = 0;
862
863   zend_try {
864     size = calc_size(key, op_array, f, c TSRMLS_CC);
865   } zend_catch {
866     size =  0;
867   } zend_end_try();
868   if (size == 0) {
869     return 0;
870   }
871   DBG(ea_debug_printf, (EA_DEBUG, "[%d] eaccelerator_store: calc_size returned %d, mm=%x", getpid(), size, eaccelerator_mm_instance->mm));
872   EACCELERATOR_UNPROTECT();
873   EAG(mem) = eaccelerator_malloc(size);
874   if (EAG(mem) == NULL) {
875     EAG(mem) = eaccelerator_malloc2(size TSRMLS_CC);
876   }
877   if (!EAG(mem) && !eaccelerator_scripts_shm_only) {
878     EACCELERATOR_PROTECT();
879     EAG(mem) = emalloc(size);
880     use_shm = 0;
881   }
882   if (EAG(mem)) {
883     memset(EAG(mem), 0, size);
884     p = eaccelerator_store_int(key, len, op_array, f, c TSRMLS_CC);
885     p->mtime    = buf->st_mtime;
886     p->filesize = buf->st_size;
887     p->size     = size;
888     p->nreloads = nreloads;
889 #ifdef EACCELERATOR_USE_INODE
890     p->st_dev   = buf->st_dev;
891     p->st_ino   = buf->st_ino;
892 #endif
893     if (use_shm) {
894       if (eaccelerator_shm_ttl > 0) {
895         p->ttl = time(0) + eaccelerator_shm_ttl;
896       } else {
897         p->ttl = 0;
898       }
899       if (!eaccelerator_scripts_shm_only) {
900         hash_add_file(p TSRMLS_CC);
901       }
902       hash_add_mm(p);
903       EACCELERATOR_PROTECT();
904       ret = 1;
905     } else {
906       ret =  hash_add_file(p TSRMLS_CC);
907       efree(p);
908     }
909   }
910   return ret;
911 }
912
913 /* Try to restore a file from the cache. If the file isn't found in memory, the
914    the disk cache is checked */
915 static zend_op_array* eaccelerator_restore(char *realname, struct stat *buf,
916                                       int *nreloads, time_t compile_time TSRMLS_DC) {
917   mm_cache_entry *p;
918   zend_op_array *op_array = NULL;
919
920   *nreloads = 1;
921   EACCELERATOR_UNPROTECT();
922   p = hash_find_mm(realname, buf, nreloads, ((eaccelerator_shm_ttl > 0)?(compile_time + eaccelerator_shm_ttl):0));
923   if (p == NULL && !eaccelerator_scripts_shm_only) {
924     p = hash_find_file(realname, buf TSRMLS_CC);
925   }
926   EACCELERATOR_PROTECT();
927   if (p != NULL && p->op_array != NULL) {
928     EAG(class_entry) = NULL;
929     op_array = restore_op_array(NULL, p->op_array TSRMLS_CC);
930     if (op_array != NULL) {
931       mm_fc_entry *e;
932       mm_used_entry *used = emalloc(sizeof(mm_used_entry));
933       used->entry  = p;
934       used->next   = (mm_used_entry*)EAG(used_entries);
935       EAG(used_entries) = (void*)used;
936       EAG(mem) = op_array->filename;
937             /* only restore the classes and functions when we restore this script
938              * for the first time.
939              */
940       if (!zend_hash_exists(&EAG(restored), p->realfilename, strlen(p->realfilename))) {
941               for (e = p->c_head; e!=NULL; e = e->next) {
942           restore_class(e TSRMLS_CC);
943         }
944         for (e = p->f_head; e!=NULL; e = e->next) {
945           restore_function(e TSRMLS_CC);
946         }
947                     zend_hash_add(&EAG(restored), p->realfilename, strlen(p->realfilename), NULL, 0, NULL); 
948             }
949             EAG(mem) = p->realfilename;
950     }
951   }
952   return op_array;
953 }
954
955 /*
956  * Only files matching user specified conditions should be cached.
957  *
958  * TODO - check the algorithm (fl)
959  */
960 static int match(const char* name, const char* pat) {
961   char p,k;
962   int ok, neg;
963  
964   while (1) {
965     p = *pat++;
966     if (p == '\0') {
967       return (*name == '\0');
968     } else if (p == '*') {
969       if (*pat == '\0') {
970         return 1;
971       }
972       do {
973         if (match(name, pat)) {
974           return 1;
975         }
976       } while (*name++ != '\0');
977       return 0;
978     } else if (p == '?') {
979       if (*name++ == '\0') {
980         return 0;
981       }
982     } else if (p == '[') {
983       ok = 0;
984       if ((k = *name++) == '\0') {
985         return 0;
986       }
987       if ((neg = (*pat == '!')) != '\0') {
988         ++pat;
989       }
990       while ((p = *pat++) != ']') {
991         if (*pat == '-') {
992           if (p <= k && k <= pat[1]) {
993             ok = 1;
994           }
995           pat += 2;
996         } else {
997           if (p == '\\') {
998             p = *pat++;
999             if (p == '\0') {
1000               p ='\\';
1001               pat--;
1002             }
1003           }
1004           if (p == k) {
1005             ok = 1;
1006           }
1007         }
1008       }
1009       if (ok == neg) {
1010         return 0;
1011       }
1012     } else {
1013       if (p == '\\') {
1014         p = *pat++;
1015         if (p == '\0') {
1016           p ='\\';
1017           pat--;
1018         }
1019       }
1020       if (*name++ != p) {
1021         return 0;
1022       }
1023     }
1024   }
1025   return (*name == '\0');
1026 }
1027
1028 /* Check if the file is ok to cache */
1029 static int eaccelerator_ok_to_cache(char *realname TSRMLS_DC) {
1030   mm_cond_entry *p;
1031   int ok;
1032   if (EAG(cond_list) == NULL) {
1033     return 1;
1034   }
1035
1036   /* if "realname" matches to any pattern started with "!" then ignore it */
1037   for (p = EAG(cond_list); p != NULL; p = p->next) {
1038     if (p->not && match(realname, p->str)) {
1039       return 0;
1040     }
1041   }
1042
1043   /* else if it matches to any pattern not started with "!" then accept it */
1044   ok = 1;
1045   for (p = EAG(cond_list); p != NULL; p = p->next) {
1046     if (!p->not) {
1047       ok = 0;
1048       if (match(realname, p->str)) {
1049         return 1;
1050       }
1051     }
1052   }
1053   return ok;
1054 }
1055
1056 #ifndef EACCELERATOR_USE_INODE
1057 static char* eaccelerator_realpath(const char* name, char* realname TSRMLS_DC) {
1058 /* ???TODO it is possibe to cache name->realname mapping to avoid lstat() calls */
1059 #if ZEND_MODULE_API_NO >= 20001222
1060   return VCWD_REALPATH(name, realname);
1061 #else
1062   return V_REALPATH(name, realname);
1063 #endif
1064 }
1065 #endif
1066
1067 static int eaccelerator_stat(zend_file_handle *file_handle,
1068                         char* realname, struct stat* buf TSRMLS_DC) {
1069 #ifdef EACCELERATOR_USE_INODE
1070 #ifndef ZEND_WIN32
1071   if (file_handle->type == ZEND_HANDLE_FP && file_handle->handle.fp != NULL) {
1072     if (fstat(fileno(file_handle->handle.fp), buf) == 0 &&
1073        S_ISREG(buf->st_mode)) {
1074       if (file_handle->opened_path != NULL) {
1075         strcpy(realname,file_handle->opened_path);
1076       }
1077       return 0;
1078     }
1079   } else
1080 #endif
1081   if (file_handle->opened_path != NULL) {
1082     if (stat(file_handle->opened_path, buf) == 0 &&
1083         S_ISREG(buf->st_mode)) {
1084        strcpy(realname,file_handle->opened_path);
1085        return 0;
1086     }
1087   } else if (PG(include_path) == NULL ||
1088              file_handle->filename[0] == '.' ||
1089              IS_SLASH(file_handle->filename[0]) ||
1090              IS_ABSOLUTE_PATH(file_handle->filename,strlen(file_handle->filename))) {
1091     if (stat(file_handle->filename, buf) == 0 &&
1092         S_ISREG(buf->st_mode)) {
1093        return 0;
1094     }
1095   } else {
1096     char* ptr = PG(include_path);
1097     char* end;
1098     int   len;
1099     char  tryname[MAXPATHLEN];
1100     int   filename_len = strlen(file_handle->filename);
1101
1102     while (ptr && *ptr) {
1103       end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
1104       if (end != NULL) {
1105         len = end-ptr;
1106         end++;
1107       } else {
1108         len = strlen(ptr);
1109         end = ptr+len;
1110       }
1111       if (len+filename_len+2 < MAXPATHLEN) {
1112         memcpy(tryname, ptr, len);
1113         tryname[len] = '/';
1114         memcpy(tryname+len+1, file_handle->filename, filename_len);
1115         tryname[len+filename_len+1] = '\0';
1116         if (stat(tryname, buf) == 0 &&
1117             S_ISREG(buf->st_mode)) {
1118           return 0;
1119         }
1120       }
1121       ptr = end;
1122     }
1123
1124         if (zend_is_executing(TSRMLS_C)) {
1125         int tryname_length;
1126                 strncpy(tryname, zend_get_executed_filename(TSRMLS_C), MAXPATHLEN);
1127                 tryname[MAXPATHLEN - 1] = 0;
1128                 tryname_length = strlen(tryname);
1129
1130                 while (tryname_length >= 0 && !IS_SLASH(tryname[tryname_length]))
1131                         tryname_length--;
1132                 if (tryname_length > 0 && tryname[0] != '[' // [no active file]
1133                         && tryname_length + filename_len + 1 < MAXPATHLEN)
1134                 {
1135                         strncpy(tryname + tryname_length + 1, file_handle->filename, filename_len + 1);
1136                         if (stat(tryname, buf) == 0 && S_ISREG(buf->st_mode))
1137                                 return 0;
1138                 }
1139         }
1140   }
1141   return -1;
1142 #else
1143   if (file_handle->opened_path != NULL) {
1144     strcpy(realname,file_handle->opened_path);
1145 #ifndef ZEND_WIN32
1146     if (file_handle->type == ZEND_HANDLE_FP && file_handle->handle.fp != NULL) {
1147       if (!eaccelerator_check_mtime) {
1148         return 0;
1149       } else if (fstat(fileno(file_handle->handle.fp), buf) == 0 &&
1150                  S_ISREG(buf->st_mode)) {
1151         return 0;
1152       } else {
1153         return -1;
1154       }
1155     } else {
1156       if (!eaccelerator_check_mtime) {
1157         return 0;
1158       } else if (stat(realname, buf) == 0 &&
1159                  S_ISREG(buf->st_mode)) {
1160         return 0;
1161       } else {
1162         return -1;
1163       }
1164     }
1165 #else
1166     if (!eaccelerator_check_mtime) {
1167       return 0;
1168     } else if (stat(realname, buf) == 0 &&
1169                S_ISREG(buf->st_mode)) {
1170       return 0;
1171     } else {
1172       return -1;
1173     }
1174 #endif
1175   } else if (file_handle->filename == NULL) {
1176     return -1;
1177   } else if (PG(include_path) == NULL ||
1178              file_handle->filename[0] == '.' ||
1179              IS_SLASH(file_handle->filename[0]) ||
1180              IS_ABSOLUTE_PATH(file_handle->filename,strlen(file_handle->filename))) {
1181     if (eaccelerator_realpath(file_handle->filename, realname TSRMLS_CC)) {
1182       if (!eaccelerator_check_mtime) {
1183         return 0;
1184       } else if (stat(realname, buf) == 0 &&
1185                  S_ISREG(buf->st_mode)) {
1186         return 0;
1187       } else {
1188         return -1;
1189       }
1190     }
1191   } else {
1192     char* ptr = PG(include_path);
1193     char* end;
1194     int   len;
1195     char  tryname[MAXPATHLEN];
1196     int   filename_len = strlen(file_handle->filename);
1197
1198     while (ptr && *ptr) {
1199       end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
1200       if (end != NULL) {
1201         len = end-ptr;
1202         end++;
1203       } else {
1204         len = strlen(ptr);