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