1 /*
2 +----------------------------------------------------------------------+
3 | Xdebug |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2002-2021 Derick Rethans |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 1.01 of the Xdebug license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available at through the world-wide-web at |
10 | https://xdebug.org/license.php |
11 | If you did not receive a copy of the Xdebug license and are unable |
12 | to obtain it through the world-wide-web, please send a note to |
13 | derick@xdebug.org so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 */
16
17 #include <stdlib.h>
18 #include <string.h>
19 #ifndef WIN32
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/resource.h>
24 #include <sys/file.h>
25 #else
26 #define PATH_MAX MAX_PATH
27 #include <winsock2.h>
28 #include <io.h>
29 #include <process.h>
30 #endif
31 #include "php_xdebug.h"
32
33 #include "mm.h"
34 #include "crc32.h"
35 #include "str.h"
36
37 #include "lib_private.h"
38 #include "usefulstuff.h"
39 #include "log.h"
40
41 #include "ext/standard/php_lcg.h"
42 #include "ext/standard/flock_compat.h"
43 #include "main/php_ini.h"
44
45 #ifndef NAME_MAX
46 # ifdef _AIX
47 # include <unistd.h>
48 # define NAME_MAX pathconf("/dev/null",_PC_NAME_MAX)
49 # else
50 # define NAME_MAX (MAXNAMELEN-1)
51 # endif
52 #endif
53
ZEND_EXTERN_MODULE_GLOBALS(xdebug)54 ZEND_EXTERN_MODULE_GLOBALS(xdebug)
55
56 xdebug_arg *xdebug_arg_ctor(void)
57 {
58 xdebug_arg *tmp = /*(xdebug_arg*)*/ xdmalloc(sizeof(xdebug_arg));
59
60 tmp->args = NULL;
61 tmp->c = 0;
62
63 return tmp;
64 }
65
xdebug_arg_dtor(xdebug_arg * arg)66 void xdebug_arg_dtor(xdebug_arg *arg)
67 {
68 int i;
69
70 for (i = 0; i < arg->c; i++) {
71 xdfree(arg->args[i]);
72 }
73 if (arg->args) {
74 xdfree(arg->args);
75 }
76 xdfree(arg);
77 }
78
xdebug_join(const char * delim,xdebug_arg * args,int begin,int end)79 xdebug_str* xdebug_join(const char *delim, xdebug_arg *args, int begin, int end)
80 {
81 int i;
82 xdebug_str *ret = xdebug_str_new();
83
84 if (begin < 0) {
85 begin = 0;
86 }
87 if (end > args->c - 1) {
88 end = args->c - 1;
89 }
90 for (i = begin; i < end; i++) {
91 xdebug_str_add(ret, args->args[i], 0);
92 xdebug_str_add(ret, delim, 0);
93 }
94 xdebug_str_add(ret, args->args[end], 0);
95 return ret;
96 }
97
xdebug_explode(const char * delim,const char * str,xdebug_arg * args,int limit)98 void xdebug_explode(const char *delim, const char *str, xdebug_arg *args, int limit)
99 {
100 const char *p1, *p2, *endp;
101
102 endp = str + strlen(str);
103
104 p1 = str;
105 p2 = xdebug_memnstr(str, delim, strlen(delim), endp);
106
107 if (p2 == NULL) {
108 args->c++;
109 args->args = (char**) xdrealloc(args->args, sizeof(char*) * args->c);
110 args->args[args->c - 1] = (char*) xdmalloc(strlen(str) + 1);
111 memcpy(args->args[args->c - 1], p1, strlen(str));
112 args->args[args->c - 1][strlen(str)] = '\0';
113 } else {
114 do {
115 args->c++;
116 args->args = (char**) xdrealloc(args->args, sizeof(char*) * args->c);
117 args->args[args->c - 1] = (char*) xdmalloc(p2 - p1 + 1);
118 memcpy(args->args[args->c - 1], p1, p2 - p1);
119 args->args[args->c - 1][p2 - p1] = '\0';
120 p1 = p2 + strlen(delim);
121 } while ((p2 = xdebug_memnstr(p1, delim, strlen(delim), endp)) != NULL && (limit == -1 || --limit > 1));
122
123 if (p1 <= endp) {
124 args->c++;
125 args->args = (char**) xdrealloc(args->args, sizeof(char*) * args->c);
126 args->args[args->c - 1] = (char*) xdmalloc(endp - p1 + 1);
127 memcpy(args->args[args->c - 1], p1, endp - p1);
128 args->args[args->c - 1][endp - p1] = '\0';
129 }
130 }
131 }
132
xdebug_memnstr(const char * haystack,const char * needle,int needle_len,const char * end)133 const char* xdebug_memnstr(const char *haystack, const char *needle, int needle_len, const char *end)
134 {
135 const char *p = haystack;
136 char first = *needle;
137
138 /* let end point to the last character where needle may start */
139 end -= needle_len;
140
141 while (p <= end) {
142 while (*p != first)
143 if (++p > end)
144 return NULL;
145 if (memcmp(p, needle, needle_len) == 0)
146 return p;
147 p++;
148 }
149 return NULL;
150 }
151
xdebug_strrstr(const char * haystack,const char * needle)152 char* xdebug_strrstr(const char* haystack, const char* needle)
153 {
154 char *loc = NULL;
155 char *found = NULL;
156 size_t pos = 0;
157
158 while ((found = strstr(haystack + pos, needle)) != 0) {
159 loc = found;
160 pos = (found - haystack) + 1;
161 }
162
163 return loc;
164 }
165
xdebug_trim(const char * str)166 char *xdebug_trim(const char *str)
167 {
168 char *trimmed = NULL, *begin = (char *) str, *end = NULL;
169
170 /* trim leading space */
171 while (isspace((unsigned char) *begin)) {
172 ++begin;
173 }
174
175 /* All spaces */
176 if (*begin == '\0') {
177 return xdstrdup("");
178 }
179
180 /* trim trailing space */
181 end = begin + strlen(begin) - 1;
182 while (end > begin && isspace((unsigned char) *end)) {
183 --end;
184 }
185 end++;
186
187 trimmed = xdmalloc(end - begin + 1);
188 memcpy(trimmed, begin, (end - begin));
189 trimmed[(end - begin)] = '\0';
190
191 return trimmed;
192 }
193
194 /* not all versions of php export this */
xdebug_htoi(char * s)195 static int xdebug_htoi(char *s)
196 {
197 int value;
198 int c;
199
200 c = s[0];
201 if (isupper(c)) {
202 c = tolower(c);
203 }
204 value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
205
206 c = s[1];
207 if (isupper(c)) {
208 c = tolower(c);
209 }
210 value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
211
212 return value;
213 }
214
215 /* not all versions of php export this */
xdebug_raw_url_decode(char * str,int len)216 int xdebug_raw_url_decode(char *str, int len)
217 {
218 char *dest = str;
219 char *data = str;
220
221 while (len--) {
222 if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1)) && isxdigit((int) *(data + 2))) {
223 *dest = (char) xdebug_htoi(data + 1);
224 data += 2;
225 len -= 2;
226 } else {
227 *dest = *data;
228 }
229 data++;
230 dest++;
231 }
232 *dest = '\0';
233 return dest - str;
234 }
235
236 static unsigned char hexchars[] = "0123456789ABCDEF";
237
xdebug_raw_url_encode(char const * s,int len,int * new_length,int skip_slash)238 char *xdebug_raw_url_encode(char const *s, int len, int *new_length, int skip_slash)
239 {
240 register int x, y;
241 unsigned char *str;
242
243 str = (unsigned char *) xdmalloc(3 * len + 1);
244 for (x = 0, y = 0; len--; x++, y++) {
245 str[y] = (unsigned char) s[x];
246 if ((str[y] < '0' && str[y] != '-' && str[y] != '.' && (str[y] != '/' || !skip_slash)) ||
247 (str[y] < 'A' && str[y] > '9' && str[y] != ':') ||
248 (str[y] > 'Z' && str[y] < 'a' && str[y] != '_' && (str[y] != '\\' || !skip_slash)) ||
249 (str[y] > 'z'))
250 {
251 str[y++] = '%';
252 str[y++] = hexchars[(unsigned char) s[x] >> 4];
253 str[y] = hexchars[(unsigned char) s[x] & 15];
254 }
255 }
256 str[y] = '\0';
257 if (new_length) {
258 *new_length = y;
259 }
260 return ((char *) str);
261 }
262
263 /* fake URI's per IETF RFC 1738 and 2396 format */
xdebug_path_from_url(zend_string * fileurl)264 char *xdebug_path_from_url(zend_string *fileurl)
265 {
266 /* deal with file: url's */
267 char *dfp = NULL;
268 const char *fp = NULL, *efp = ZSTR_VAL(fileurl);
269 #ifdef PHP_WIN32
270 int l = 0;
271 int i;
272 #endif
273 char *tmp = NULL, *ret = NULL;
274
275 dfp = xdstrdup(efp);
276 fp = dfp;
277 xdebug_raw_url_decode(dfp, strlen(dfp));
278 tmp = strstr(fp, "file://");
279
280 if (tmp) {
281 fp = tmp + 7;
282 if (fp[0] == '/' && fp[2] == ':') {
283 fp++;
284 }
285 ret = xdstrdup(fp);
286 #ifdef PHP_WIN32
287 l = strlen(ret);
288 /* convert '/' to '\' */
289 for (i = 0; i < l; i++) {
290 if (ret[i] == '/') {
291 ret[i] = '\\';
292 }
293 }
294 #endif
295 } else {
296 ret = xdstrdup(ZSTR_VAL(fileurl));
297 }
298
299 free(dfp);
300 return ret;
301 }
302
303 /* fake URI's per IETF RFC 1738 and 2396 format */
xdebug_path_to_url(zend_string * fileurl)304 char *xdebug_path_to_url(zend_string *fileurl)
305 {
306 int l, i, new_len;
307 char *tmp = NULL;
308 char *encoded_fileurl;
309
310 /* encode the url */
311 encoded_fileurl = xdebug_raw_url_encode(ZSTR_VAL(fileurl), ZSTR_LEN(fileurl), &new_len, 1);
312
313 if (strncmp(ZSTR_VAL(fileurl), "phar://", 7) == 0) {
314 /* ignore, phar is cool */
315 tmp = xdstrdup(ZSTR_VAL(fileurl));
316 } else if (ZSTR_VAL(fileurl)[0] != '/' && ZSTR_VAL(fileurl)[0] != '\\' && ZSTR_VAL(fileurl)[1] != ':') {
317 /* convert relative paths */
318 cwd_state new_state;
319 char cwd[MAXPATHLEN];
320 char *result;
321
322 result = VCWD_GETCWD(cwd, MAXPATHLEN);
323 if (!result) {
324 cwd[0] = '\0';
325 }
326
327 new_state.cwd = estrdup(cwd);
328 new_state.cwd_length = strlen(cwd);
329
330 if (!virtual_file_ex(&new_state, ZSTR_VAL(fileurl), NULL, 1)) {
331 char *s = estrndup(new_state.cwd, new_state.cwd_length);
332 tmp = xdebug_sprintf("file://%s",s);
333 efree(s);
334 }
335 efree(new_state.cwd);
336
337 } else if (ZSTR_VAL(fileurl)[1] == '/' || ZSTR_VAL(fileurl)[1] == '\\') {
338 /* convert UNC paths (eg. \\server\sharepath) */
339 /* See https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows */
340 tmp = xdebug_sprintf("file:%s", encoded_fileurl);
341 } else if (ZSTR_VAL(fileurl)[0] == '/' || ZSTR_VAL(fileurl)[0] == '\\') {
342 /* convert *nix paths (eg. /path) */
343 tmp = xdebug_sprintf("file://%s", encoded_fileurl);
344 } else if (ZSTR_VAL(fileurl)[1] == ':') {
345 /* convert windows drive paths (eg. c:\path) */
346 tmp = xdebug_sprintf("file:///%s", encoded_fileurl);
347 } else {
348 /* no clue about it, use it raw */
349 tmp = xdstrdup(encoded_fileurl);
350 }
351 l = strlen(tmp);
352 /* convert '\' to '/' */
353 for (i = 0; i < l; i++) {
354 if (tmp[i] == '\\') {
355 tmp[i]='/';
356 }
357 }
358 xdfree(encoded_fileurl);
359 return tmp;
360 }
361
362 #ifndef PHP_WIN32
xdebug_open_file(char * fname,const char * mode,const char * extension,char ** new_fname)363 static FILE *xdebug_open_file(char *fname, const char *mode, const char *extension, char **new_fname)
364 {
365 FILE *fh;
366 char *tmp_fname;
367
368 if (extension) {
369 tmp_fname = xdebug_sprintf("%s.%s", fname, extension);
370 } else {
371 tmp_fname = xdstrdup(fname);
372 }
373 fh = fopen(tmp_fname, mode);
374 if (fh && new_fname) {
375 *new_fname = tmp_fname;
376 } else {
377 xdfree(tmp_fname);
378 }
379 return fh;
380 }
381
xdebug_open_file_with_random_ext(char * fname,const char * mode,const char * extension,char ** new_fname)382 static FILE *xdebug_open_file_with_random_ext(char *fname, const char *mode, const char *extension, char **new_fname)
383 {
384 FILE *fh;
385 char *tmp_fname;
386
387 if (extension) {
388 tmp_fname = xdebug_sprintf("%s.%06x.%s", fname, (long) (1000000 * php_combined_lcg()), extension);
389 } else {
390 tmp_fname = xdebug_sprintf("%s.%06x", fname, (long) (1000000 * php_combined_lcg()), extension);
391 }
392 fh = fopen(tmp_fname, mode);
393 if (fh && new_fname) {
394 *new_fname = tmp_fname;
395 } else {
396 xdfree(tmp_fname);
397 }
398 return fh;
399 }
400
xdebug_fopen(char * fname,const char * mode,const char * extension,char ** new_fname)401 FILE *xdebug_fopen(char *fname, const char *mode, const char *extension, char **new_fname)
402 {
403 int r;
404 FILE *fh;
405 struct stat buf;
406 char *tmp_fname = NULL;
407 int filename_len = 0;
408
409 /* We're not doing any tricks for append mode... as that has atomic writes
410 * anyway. And we ignore read mode as well. */
411 if (mode[0] == 'a' || mode[0] == 'r') {
412 return xdebug_open_file(fname, mode, extension, new_fname);
413 }
414
415 /* Make sure we don't open a file with a path that's too long */
416 filename_len += (fname ? strlen(fname) : 0); /* filename */
417 filename_len += (extension ? strlen(extension) : 0) + 1; /* extension (+ ".") */
418 filename_len += 8; /* possible random extension (+ ".") */
419 if (filename_len > NAME_MAX) {
420 fname[NAME_MAX - (extension ? strlen(extension) : 0 )] = '\0';
421 }
422
423 /* In write mode however we do have to do some stuff. */
424 /* 1. Check if the file exists */
425 if (extension) {
426 tmp_fname = xdebug_sprintf("%s.%s", fname, extension);
427 } else {
428 tmp_fname = xdstrdup(fname);
429 }
430 r = stat(tmp_fname, &buf);
431 /* We're not freeing "tmp_fname" as that is used in the freopen as well. */
432
433 if (r == -1) {
434 /* 2. Cool, the file doesn't exist so we can open it without probs now. */
435 fh = xdebug_open_file(fname, "w", extension, new_fname);
436 goto lock;
437 }
438
439 /* 3. It exists, check if we can open it. */
440 fh = xdebug_open_file(fname, "r+", extension, new_fname);
441 if (!fh) {
442 /* 4. If fh == null we couldn't even open the file, so open a new one with a new name */
443 fh = xdebug_open_file_with_random_ext(fname, "w", extension, new_fname);
444 goto lock;
445 }
446
447 /* 5. It exists and we can open it, check if we can exclusively lock it. */
448 r = flock(fileno(fh), LOCK_EX | LOCK_NB);
449 if (r == -1) {
450 if (errno == EWOULDBLOCK) {
451 fclose(fh);
452 /* 6. The file is in use, so we open one with a new name. */
453 fh = xdebug_open_file_with_random_ext(fname, "w", extension, new_fname);
454 goto lock;
455 }
456 }
457
458 /* 7. We established a lock, now we truncate and return the handle */
459 fh = freopen(tmp_fname, "w", fh);
460
461 lock: /* Yes yes, an evil goto label here!!! */
462 if (fh) {
463 /* 8. We have to lock again after the reopen as that basically closes
464 * the file and opens it again. There is a small race condition here...
465 */
466 flock(fileno(fh), LOCK_EX | LOCK_NB);
467 }
468 xdfree(tmp_fname);
469 return fh;
470 }
471 #else
xdebug_fopen(char * fname,const char * mode,const char * extension,char ** new_fname)472 FILE *xdebug_fopen(char *fname, const char *mode, const char *extension, char **new_fname)
473 {
474 char *tmp_fname;
475 FILE *ret;
476
477 if (extension) {
478 tmp_fname = xdebug_sprintf("%s.%s", fname, extension);
479 } else {
480 tmp_fname = xdstrdup(fname);
481 }
482 ret = fopen(tmp_fname, mode);
483 if (new_fname) {
484 *new_fname = tmp_fname;
485 } else {
486 xdfree(tmp_fname);
487 }
488 return ret;
489 }
490 #endif
491
492
xdebug_format_output_filename(char ** filename,char * format,char * script_name)493 int xdebug_format_output_filename(char **filename, char *format, char *script_name)
494 {
495 xdebug_str fname = XDEBUG_STR_INITIALIZER;
496 char cwd[128];
497
498 while (*format)
499 {
500 if (*format != '%') {
501 xdebug_str_addc(&fname, *format);
502 } else {
503 format++;
504 switch (*format)
505 {
506 case 'c': /* crc32 of the current working directory */
507 if (VCWD_GETCWD(cwd, 127)) {
508 xdebug_str_add_fmt(&fname, "%lu", xdebug_crc32(cwd, strlen(cwd)));
509 }
510 break;
511
512 case 'p': /* pid */
513 xdebug_str_add_fmt(&fname, ZEND_ULONG_FMT, xdebug_get_pid());
514 break;
515
516 case 'r': /* random number */
517 xdebug_str_add_fmt(&fname, "%06x", (long) (1000000 * php_combined_lcg()));
518 break;
519
520 case 's': { /* script fname */
521 char *char_ptr, *script_name_tmp;
522
523 /* we do not always have script_name available, so if we
524 * don't have it and this format specifier is used then we
525 * simple do nothing for this specifier */
526 if (!script_name) {
527 break;
528 }
529
530 /* create a copy to work on */
531 script_name_tmp = xdstrdup(script_name);
532
533 /* replace slashes, whitespace and colons with underscores */
534 while ((char_ptr = strpbrk(script_name_tmp, "/\\: ")) != NULL) {
535 char_ptr[0] = '_';
536 }
537 /* replace .php with _php */
538 char_ptr = strrchr(script_name_tmp, '.');
539 if (char_ptr) {
540 char_ptr[0] = '_';
541 }
542 xdebug_str_add(&fname, script_name_tmp, 0);
543 xdfree(script_name_tmp);
544 } break;
545
546 case 't': { /* timestamp (in seconds) */
547 xdebug_str_add_fmt(&fname, "%lu", xdebug_get_nanotime() / NANOS_IN_SEC);
548 } break;
549
550 case 'u': { /* timestamp (in microseconds) */
551 uint64_t nanotime = xdebug_get_nanotime();
552
553 xdebug_str_add_fmt(&fname, "%lu.%06d",
554 nanotime / NANOS_IN_SEC,
555 (nanotime % NANOS_IN_SEC) / NANOS_IN_MICROSEC
556 );
557 } break;
558
559 case 'H': /* $_SERVER['HTTP_HOST'] */
560 case 'U': /* $_SERVER['UNIQUE_ID'] */
561 case 'R': { /* $_SERVER['REQUEST_URI'] */
562 char *char_ptr, *strval;
563 zval *data = NULL;
564
565 if (Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY) {
566 switch (*format) {
567 case 'H':
568 data = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_HOST", sizeof("HTTP_HOST") - 1);
569 break;
570 case 'R':
571 data = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), "REQUEST_URI", sizeof("REQUEST_URI") - 1);
572 break;
573 case 'U':
574 data = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), "UNIQUE_ID", sizeof("UNIQUE_ID") - 1);
575 break;
576 }
577
578 if (data) {
579 strval = estrdup(Z_STRVAL_P(data));
580 /* replace slashes, dots, question marks, plus
581 * signs, ampersands, spaces and other evil chars
582 * with underscores */
583 while ((char_ptr = strpbrk(strval, "/\\.?&+:*\"<>| ")) != NULL) {
584 char_ptr[0] = '_';
585 }
586 xdebug_str_add(&fname, strval, 0);
587 efree(strval);
588 }
589 }
590 } break;
591
592 case 'S': { /* session id */
593 zval *data;
594 char *char_ptr, *strval;
595 char *sess_name;
596
597 sess_name = zend_ini_string((char*) "session.name", sizeof("session.name"), 0);
598
599 if (sess_name && Z_TYPE(PG(http_globals)[TRACK_VARS_COOKIE]) == IS_ARRAY &&
600 ((data = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_COOKIE]), sess_name, strlen(sess_name))) != NULL) &&
601 Z_STRLEN_P(data) < 100 /* Prevent any unrealistically long data being set as filename */
602 ) {
603 strval = estrdup(Z_STRVAL_P(data));
604 /* replace slashes, dots, question marks, plus signs,
605 * ampersands and spaces with underscores */
606 while ((char_ptr = strpbrk(strval, "/\\.?&+ ")) != NULL) {
607 char_ptr[0] = '_';
608 }
609 xdebug_str_add(&fname, strval, 0);
610 efree(strval);
611 }
612 } break;
613
614 case '%': /* literal % */
615 xdebug_str_addc(&fname, '%');
616 break;
617 }
618 }
619 format++;
620 }
621
622 *filename = fname.d;
623
624 return fname.l;
625 }
626
xdebug_format_file_link(char ** filename,const char * error_filename,int error_lineno)627 int xdebug_format_file_link(char **filename, const char *error_filename, int error_lineno)
628 {
629 xdebug_str fname = XDEBUG_STR_INITIALIZER;
630 char *format = XINI_LIB(file_link_format);
631
632 while (*format)
633 {
634 if (*format != '%') {
635 xdebug_str_addc(&fname, *format);
636 } else {
637 format++;
638 switch (*format)
639 {
640 case 'f': /* filename */
641 xdebug_str_add(&fname, error_filename, 0);
642 break;
643
644 case 'l': /* line number */
645 xdebug_str_add_fmt(&fname, "%d", error_lineno);
646 break;
647
648 case '%': /* literal % */
649 xdebug_str_addc(&fname, '%');
650 break;
651 }
652 }
653 format++;
654 }
655
656 *filename = fname.d;
657
658 return fname.l;
659 }
660
xdebug_format_filename(char ** formatted_name,const char * default_fmt,zend_string * filename)661 int xdebug_format_filename(char **formatted_name, const char *default_fmt, zend_string *filename)
662 {
663 xdebug_str fname = XDEBUG_STR_INITIALIZER;
664 char *name;
665 xdebug_str *parent, *ancester;
666 const char *full_filename = ZSTR_VAL(filename);
667 xdebug_arg *parts;
668 char *slash = xdebug_sprintf("%c", DEFAULT_SLASH);
669 char *fmt = XINI_LIB(filename_format);
670 const char *format = fmt && fmt[0] ? fmt : default_fmt; /* If the format is empty, we use the default */
671
672 /* Create pointers for the format chars */
673 parts = xdebug_arg_ctor();
674 xdebug_explode(slash, full_filename, parts, -1);
675 name = parts->args[parts->c - 1];
676 parent = parts->c > 1 ?
677 xdebug_join(slash, parts, parts->c - 2, parts->c - 1) :
678 xdebug_str_create_from_char(name);
679 ancester = parts->c > 2 ?
680 xdebug_join(slash, parts, parts->c - 3, parts->c - 1) :
681 xdebug_str_copy(parent);
682
683 while (*format)
684 {
685 if (*format != '%') {
686 xdebug_str_addc(&fname, *format);
687 } else {
688 format++;
689 switch (*format)
690 {
691 case 'n': /* filename */
692 xdebug_str_add(&fname, name, 0);
693 break;
694 case 'p': /* parent */
695 xdebug_str_add_str(&fname, parent);
696 break;
697 case 'a': /* ancester */
698 xdebug_str_add_str(&fname, ancester);
699 break;
700 case 'f': /* full path */
701 xdebug_str_add(&fname, full_filename, 0);
702 break;
703 case 's': /* slash */
704 xdebug_str_addc(&fname, DEFAULT_SLASH);
705 break;
706 case '%': /* literal % */
707 xdebug_str_addc(&fname, '%');
708 break;
709 }
710 }
711 format++;
712 }
713
714 xdfree(slash);
715 xdebug_str_free(ancester);
716 xdebug_str_free(parent);
717 xdebug_arg_dtor(parts);
718
719 *formatted_name = fname.d;
720
721 return fname.l;
722 }
723