1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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: Felipe Pena <felipe@php.net> |
16 | Authors: Joe Watkins <joe.watkins@live.co.uk> |
17 | Authors: Bob Weinand <bwoebi@php.net> |
18 +----------------------------------------------------------------------+
19 */
20
21 #include "zend.h"
22 #include "php.h"
23 #include "spprintf.h"
24 #include "phpdbg.h"
25 #include "phpdbg_io.h"
26 #include "phpdbg_eol.h"
27 #include "ext/standard/html.h"
28
29 #ifdef _WIN32
30 # include "win32/time.h"
31 #endif
32
33 ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
34
35 /* copied from php-src/main/snprintf.c and slightly modified */
36 /*
37 * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
38 *
39 * XXX: this is a magic number; do not decrease it
40 * Emax = 1023
41 * NDIG = 320
42 * NUM_BUF_SIZE >= strlen("-") + Emax + strlrn(".") + NDIG + strlen("E+1023") + 1;
43 */
44 #define NUM_BUF_SIZE 2048
45
46 /*
47 * Descriptor for buffer area
48 */
49 struct buf_area {
50 char *buf_end;
51 char *nextb; /* pointer to next byte to read/write */
52 };
53
54 typedef struct buf_area buffy;
55
56 /*
57 * The INS_CHAR macro inserts a character in the buffer and writes
58 * the buffer back to disk if necessary
59 * It uses the char pointers sp and bep:
60 * sp points to the next available character in the buffer
61 * bep points to the end-of-buffer+1
62 * While using this macro, note that the nextb pointer is NOT updated.
63 *
64 * NOTE: Evaluation of the c argument should not have any side-effects
65 */
66 #define INS_CHAR(c, sp, bep, cc) \
67 { \
68 if (sp < bep) \
69 { \
70 *sp++ = c; \
71 } \
72 cc++; \
73 }
74
75 #define NUM( c ) ( c - '0' )
76
77 #define STR_TO_DEC( str, num ) \
78 num = NUM( *str++ ) ; \
79 while ( isdigit((int)*str ) ) \
80 { \
81 num *= 10 ; \
82 num += NUM( *str++ ) ; \
83 }
84
85 /*
86 * This macro does zero padding so that the precision
87 * requirement is satisfied. The padding is done by
88 * adding '0's to the left of the string that is going
89 * to be printed.
90 */
91 #define FIX_PRECISION( adjust, precision, s, s_len ) \
92 if ( adjust ) \
93 while ( s_len < precision ) \
94 { \
95 *--s = '0' ; \
96 s_len++ ; \
97 }
98
99 /*
100 * Macro that does padding. The padding is done by printing
101 * the character ch.
102 */
103 #define PAD( width, len, ch ) do \
104 { \
105 INS_CHAR( ch, sp, bep, cc ) ; \
106 width-- ; \
107 } \
108 while ( width > len )
109
110 /*
111 * Prefix the character ch to the string str
112 * Increase length
113 * Set the has_prefix flag
114 */
115 #define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES
116
117 #include <locale.h>
118 #define LCONV_DECIMAL_POINT (*lconv->decimal_point)
119 #define NUL '\0'
120 #define S_NULL "(null)"
121 #define S_NULL_LEN 6
122 #define FLOAT_DIGITS 6
123
124 /*
125 * Do format conversion placing the output in buffer
126 */
format_converter(register buffy * odp,const char * fmt,zend_bool escape_xml,va_list ap)127 static int format_converter(register buffy *odp, const char *fmt, zend_bool escape_xml, va_list ap) {
128 char *sp;
129 char *bep;
130 int cc = 0;
131 int i;
132
133 char *s = NULL, *free_s = NULL;
134 size_t s_len;
135 zend_bool free_zcopy;
136 zval *zvp, zcopy;
137
138 int min_width = 0;
139 int precision = 0;
140 enum {
141 LEFT, RIGHT
142 } adjust;
143 char pad_char;
144 char prefix_char;
145
146 double fp_num;
147 wide_int i_num = (wide_int) 0;
148 u_wide_int ui_num;
149
150 char num_buf[NUM_BUF_SIZE];
151 char char_buf[2]; /* for printing %% and %<unknown> */
152
153 struct lconv *lconv = NULL;
154
155 /*
156 * Flag variables
157 */
158 length_modifier_e modifier;
159 boolean_e alternate_form;
160 boolean_e print_sign;
161 boolean_e print_blank;
162 boolean_e adjust_precision;
163 boolean_e adjust_width;
164 bool_int is_negative;
165
166 sp = odp->nextb;
167 bep = odp->buf_end;
168
169 while (*fmt) {
170 if (*fmt != '%') {
171 INS_CHAR(*fmt, sp, bep, cc);
172 } else {
173 /*
174 * Default variable settings
175 */
176 adjust = RIGHT;
177 alternate_form = print_sign = print_blank = NO;
178 pad_char = ' ';
179 prefix_char = NUL;
180 free_zcopy = 0;
181
182 fmt++;
183
184 /*
185 * Try to avoid checking for flags, width or precision
186 */
187 if (isascii((int)*fmt) && !islower((int)*fmt)) {
188 /*
189 * Recognize flags: -, #, BLANK, +
190 */
191 for (;; fmt++) {
192 if (*fmt == '-')
193 adjust = LEFT;
194 else if (*fmt == '+')
195 print_sign = YES;
196 else if (*fmt == '#')
197 alternate_form = YES;
198 else if (*fmt == ' ')
199 print_blank = YES;
200 else if (*fmt == '0')
201 pad_char = '0';
202 else
203 break;
204 }
205
206 /*
207 * Check if a width was specified
208 */
209 if (isdigit((int)*fmt)) {
210 STR_TO_DEC(fmt, min_width);
211 adjust_width = YES;
212 } else if (*fmt == '*') {
213 min_width = va_arg(ap, int);
214 fmt++;
215 adjust_width = YES;
216 if (min_width < 0) {
217 adjust = LEFT;
218 min_width = -min_width;
219 }
220 } else
221 adjust_width = NO;
222
223 /*
224 * Check if a precision was specified
225 */
226 if (*fmt == '.') {
227 adjust_precision = YES;
228 fmt++;
229 if (isdigit((int)*fmt)) {
230 STR_TO_DEC(fmt, precision);
231 } else if (*fmt == '*') {
232 precision = va_arg(ap, int);
233 fmt++;
234 if (precision < 0)
235 precision = 0;
236 } else
237 precision = 0;
238
239 if (precision > FORMAT_CONV_MAX_PRECISION && *fmt != 's' && *fmt != 'v' && *fmt != 'b') {
240 precision = FORMAT_CONV_MAX_PRECISION;
241 }
242 } else
243 adjust_precision = NO;
244 } else
245 adjust_precision = adjust_width = NO;
246
247 /*
248 * Modifier check
249 */
250 switch (*fmt) {
251 case 'L':
252 fmt++;
253 modifier = LM_LONG_DOUBLE;
254 break;
255 case 'I':
256 fmt++;
257 #if SIZEOF_LONG_LONG
258 if (*fmt == '6' && *(fmt+1) == '4') {
259 fmt += 2;
260 modifier = LM_LONG_LONG;
261 } else
262 #endif
263 if (*fmt == '3' && *(fmt+1) == '2') {
264 fmt += 2;
265 modifier = LM_LONG;
266 } else {
267 #ifdef _WIN64
268 modifier = LM_LONG_LONG;
269 #else
270 modifier = LM_LONG;
271 #endif
272 }
273 break;
274 case 'l':
275 fmt++;
276 #if SIZEOF_LONG_LONG
277 if (*fmt == 'l') {
278 fmt++;
279 modifier = LM_LONG_LONG;
280 } else
281 #endif
282 modifier = LM_LONG;
283 break;
284 case 'z':
285 fmt++;
286 modifier = LM_SIZE_T;
287 break;
288 case 'j':
289 fmt++;
290 #if SIZEOF_INTMAX_T
291 modifier = LM_INTMAX_T;
292 #else
293 modifier = LM_SIZE_T;
294 #endif
295 break;
296 case 't':
297 fmt++;
298 #if SIZEOF_PTRDIFF_T
299 modifier = LM_PTRDIFF_T;
300 #else
301 modifier = LM_SIZE_T;
302 #endif
303 break;
304 case 'h':
305 fmt++;
306 if (*fmt == 'h') {
307 fmt++;
308 }
309 /* these are promoted to int, so no break */
310 default:
311 modifier = LM_STD;
312 break;
313 }
314
315 /*
316 * Argument extraction and printing.
317 * First we determine the argument type.
318 * Then, we convert the argument to a string.
319 * On exit from the switch, s points to the string that
320 * must be printed, s_len has the length of the string
321 * The precision requirements, if any, are reflected in s_len.
322 *
323 * NOTE: pad_char may be set to '0' because of the 0 flag.
324 * It is reset to ' ' by non-numeric formats
325 */
326 switch (*fmt) {
327 case 'Z':
328 zvp = (zval *) va_arg(ap, zval *);
329 free_zcopy = zend_make_printable_zval(zvp, &zcopy);
330 if (free_zcopy) {
331 zvp = &zcopy;
332 }
333 s_len = Z_STRLEN_P(zvp);
334 s = Z_STRVAL_P(zvp);
335 if (adjust_precision && precision < s_len) {
336 s_len = precision;
337 }
338 break;
339 case 'u':
340 switch(modifier) {
341 default:
342 i_num = (wide_int) va_arg(ap, unsigned int);
343 break;
344 case LM_LONG_DOUBLE:
345 goto fmt_error;
346 case LM_LONG:
347 i_num = (wide_int) va_arg(ap, unsigned long int);
348 break;
349 case LM_SIZE_T:
350 i_num = (wide_int) va_arg(ap, size_t);
351 break;
352 #if SIZEOF_LONG_LONG
353 case LM_LONG_LONG:
354 i_num = (wide_int) va_arg(ap, u_wide_int);
355 break;
356 #endif
357 #if SIZEOF_INTMAX_T
358 case LM_INTMAX_T:
359 i_num = (wide_int) va_arg(ap, uintmax_t);
360 break;
361 #endif
362 #if SIZEOF_PTRDIFF_T
363 case LM_PTRDIFF_T:
364 i_num = (wide_int) va_arg(ap, ptrdiff_t);
365 break;
366 #endif
367 }
368 /*
369 * The rest also applies to other integer formats, so fall
370 * into that case.
371 */
372 case 'd':
373 case 'i':
374 /*
375 * Get the arg if we haven't already.
376 */
377 if ((*fmt) != 'u') {
378 switch(modifier) {
379 default:
380 i_num = (wide_int) va_arg(ap, int);
381 break;
382 case LM_LONG_DOUBLE:
383 goto fmt_error;
384 case LM_LONG:
385 i_num = (wide_int) va_arg(ap, long int);
386 break;
387 case LM_SIZE_T:
388 #if SIZEOF_SSIZE_T
389 i_num = (wide_int) va_arg(ap, ssize_t);
390 #else
391 i_num = (wide_int) va_arg(ap, size_t);
392 #endif
393 break;
394 #if SIZEOF_LONG_LONG
395 case LM_LONG_LONG:
396 i_num = (wide_int) va_arg(ap, wide_int);
397 break;
398 #endif
399 #if SIZEOF_INTMAX_T
400 case LM_INTMAX_T:
401 i_num = (wide_int) va_arg(ap, intmax_t);
402 break;
403 #endif
404 #if SIZEOF_PTRDIFF_T
405 case LM_PTRDIFF_T:
406 i_num = (wide_int) va_arg(ap, ptrdiff_t);
407 break;
408 #endif
409 }
410 }
411 s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
412 &num_buf[NUM_BUF_SIZE], &s_len);
413 FIX_PRECISION(adjust_precision, precision, s, s_len);
414
415 if (*fmt != 'u') {
416 if (is_negative) {
417 prefix_char = '-';
418 } else if (print_sign) {
419 prefix_char = '+';
420 } else if (print_blank) {
421 prefix_char = ' ';
422 }
423 }
424 break;
425
426
427 case 'o':
428 switch(modifier) {
429 default:
430 ui_num = (u_wide_int) va_arg(ap, unsigned int);
431 break;
432 case LM_LONG_DOUBLE:
433 goto fmt_error;
434 case LM_LONG:
435 ui_num = (u_wide_int) va_arg(ap, unsigned long int);
436 break;
437 case LM_SIZE_T:
438 ui_num = (u_wide_int) va_arg(ap, size_t);
439 break;
440 #if SIZEOF_LONG_LONG
441 case LM_LONG_LONG:
442 ui_num = (u_wide_int) va_arg(ap, u_wide_int);
443 break;
444 #endif
445 #if SIZEOF_INTMAX_T
446 case LM_INTMAX_T:
447 ui_num = (u_wide_int) va_arg(ap, uintmax_t);
448 break;
449 #endif
450 #if SIZEOF_PTRDIFF_T
451 case LM_PTRDIFF_T:
452 ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
453 break;
454 #endif
455 }
456 s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
457 FIX_PRECISION(adjust_precision, precision, s, s_len);
458 if (alternate_form && *s != '0') {
459 *--s = '0';
460 s_len++;
461 }
462 break;
463
464
465 case 'x':
466 case 'X':
467 switch(modifier) {
468 default:
469 ui_num = (u_wide_int) va_arg(ap, unsigned int);
470 break;
471 case LM_LONG_DOUBLE:
472 goto fmt_error;
473 case LM_LONG:
474 ui_num = (u_wide_int) va_arg(ap, unsigned long int);
475 break;
476 case LM_SIZE_T:
477 ui_num = (u_wide_int) va_arg(ap, size_t);
478 break;
479 #if SIZEOF_LONG_LONG
480 case LM_LONG_LONG:
481 ui_num = (u_wide_int) va_arg(ap, u_wide_int);
482 break;
483 #endif
484 #if SIZEOF_INTMAX_T
485 case LM_INTMAX_T:
486 ui_num = (u_wide_int) va_arg(ap, uintmax_t);
487 break;
488 #endif
489 #if SIZEOF_PTRDIFF_T
490 case LM_PTRDIFF_T:
491 ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
492 break;
493 #endif
494 }
495 s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
496 FIX_PRECISION(adjust_precision, precision, s, s_len);
497 if (alternate_form && i_num != 0) {
498 *--s = *fmt; /* 'x' or 'X' */
499 *--s = '0';
500 s_len += 2;
501 }
502 break;
503
504
505 case 's':
506 case 'v':
507 s = va_arg(ap, char *);
508 if (s != NULL) {
509 if (adjust_precision) {
510 s_len = precision;
511 } else {
512 s_len = strlen(s);
513 }
514
515 if (escape_xml) {
516 /* added: support for xml escaping */
517
518 int old_slen = s_len, i = 0;
519 char *old_s = s, *s_ptr;
520 free_s = s_ptr = s = emalloc(old_slen * 6 + 1);
521 do {
522 if (old_s[i] == '&' || old_s[i] == '"' || old_s[i] == '<') {
523 *s_ptr++ = '&';
524 switch (old_s[i]) {
525 case '"':
526 s_len += 5;
527 *s_ptr++ = 'q';
528 *s_ptr++ = 'u';
529 *s_ptr++ = 'o';
530 *s_ptr++ = 't';
531 break;
532 case '<':
533 s_len += 3;
534 *s_ptr++ = 'l';
535 *s_ptr++ = 't';
536 break;
537 case '&':
538 s_len += 4;
539 *s_ptr++ = 'a';
540 *s_ptr++ = 'm';
541 *s_ptr++ = 'p';
542 break;
543 }
544 *s_ptr++ = ';';
545 } else {
546 *s_ptr++ = old_s[i];
547 }
548 } while (i++ < old_slen);
549 }
550 } else {
551 s = S_NULL;
552 s_len = S_NULL_LEN;
553 }
554 pad_char = ' ';
555 break;
556
557
558 case 'b':
559 if (escape_xml) {
560 s = PHPDBG_G(err_buf).xml;
561 } else {
562 s = PHPDBG_G(err_buf).msg;
563 }
564
565 if (s != NULL) {
566 if (escape_xml) {
567 s_len = PHPDBG_G(err_buf).xmllen;
568 } else {
569 s_len = PHPDBG_G(err_buf).msglen;
570 }
571
572 if (adjust_precision && precision != s_len) {
573 s_len = precision;
574 }
575 } else {
576 s = "";
577 s_len = 0;
578 }
579 pad_char = ' ';
580 break;
581
582
583 case 'r':
584 if (PHPDBG_G(req_id)) {
585 s_len = spprintf(&s, 0, "req=\"" ZEND_ULONG_FMT "\"", PHPDBG_G(req_id));
586 free_s = s;
587 } else {
588 s = "";
589 s_len = 0;
590 }
591 break;
592
593
594 case 'f':
595 case 'F':
596 case 'e':
597 case 'E':
598
599 switch(modifier) {
600 case LM_LONG_DOUBLE:
601 fp_num = (double) va_arg(ap, long double);
602 break;
603 case LM_STD:
604 fp_num = va_arg(ap, double);
605 break;
606 default:
607 goto fmt_error;
608 }
609
610 if (zend_isnan(fp_num)) {
611 s = "NAN";
612 s_len = 3;
613 } else if (zend_isinf(fp_num)) {
614 s = "INF";
615 s_len = 3;
616 } else {
617 if (!lconv) {
618 lconv = localeconv();
619 }
620
621 s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
622 (adjust_precision == NO) ? FLOAT_DIGITS : precision,
623 (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
624 &is_negative, &num_buf[1], &s_len);
625 if (is_negative)
626 prefix_char = '-';
627 else if (print_sign)
628 prefix_char = '+';
629 else if (print_blank)
630 prefix_char = ' ';
631 }
632 break;
633
634
635 case 'g':
636 case 'k':
637 case 'G':
638 case 'H':
639 switch(modifier) {
640 case LM_LONG_DOUBLE:
641 fp_num = (double) va_arg(ap, long double);
642 break;
643 case LM_STD:
644 fp_num = va_arg(ap, double);
645 break;
646 default:
647 goto fmt_error;
648 }
649
650 if (zend_isnan(fp_num)) {
651 s = "NAN";
652 s_len = 3;
653 break;
654 } else if (zend_isinf(fp_num)) {
655 if (fp_num > 0) {
656 s = "INF";
657 s_len = 3;
658 } else {
659 s = "-INF";
660 s_len = 4;
661 }
662 break;
663 }
664
665 if (adjust_precision == NO) {
666 precision = FLOAT_DIGITS;
667 } else if (precision == 0) {
668 precision = 1;
669 }
670 /*
671 * * We use &num_buf[ 1 ], so that we have room for the sign
672 */
673 if (!lconv) {
674 lconv = localeconv();
675 }
676
677 s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
678 if (*s == '-') {
679 prefix_char = *s++;
680 } else if (print_sign) {
681 prefix_char = '+';
682 } else if (print_blank) {
683 prefix_char = ' ';
684 }
685
686 s_len = strlen(s);
687
688 if (alternate_form && (strchr(s, '.')) == NULL) {
689 s[s_len++] = '.';
690 }
691 break;
692
693
694 case 'c':
695 char_buf[0] = (char) (va_arg(ap, int));
696 s = &char_buf[0];
697 s_len = 1;
698 pad_char = ' ';
699 break;
700
701
702 case '%':
703 char_buf[0] = '%';
704 s = &char_buf[0];
705 s_len = 1;
706 pad_char = ' ';
707 break;
708
709
710 case 'n':
711 *(va_arg(ap, int *)) = cc;
712 goto skip_output;
713
714 /*
715 * Always extract the argument as a "char *" pointer. We
716 * should be using "void *" but there are still machines
717 * that don't understand it.
718 * If the pointer size is equal to the size of an unsigned
719 * integer we convert the pointer to a hex number, otherwise
720 * we print "%p" to indicate that we don't handle "%p".
721 */
722 case 'p':
723 if (sizeof(char *) <= sizeof(u_wide_int)) {
724 ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
725 s = ap_php_conv_p2(ui_num, 4, 'x',
726 &num_buf[NUM_BUF_SIZE], &s_len);
727 if (ui_num != 0) {
728 *--s = 'x';
729 *--s = '0';
730 s_len += 2;
731 }
732 } else {
733 s = "%p";
734 s_len = 2;
735 }
736 pad_char = ' ';
737 break;
738
739
740 case NUL:
741 /*
742 * The last character of the format string was %.
743 * We ignore it.
744 */
745 continue;
746
747
748 fmt_error:
749 php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
750 /*
751 * The default case is for unrecognized %'s.
752 * We print %<char> to help the user identify what
753 * option is not understood.
754 * This is also useful in case the user wants to pass
755 * the output of format_converter to another function
756 * that understands some other %<char> (like syslog).
757 * Note that we can't point s inside fmt because the
758 * unknown <char> could be preceded by width etc.
759 */
760 default:
761 char_buf[0] = '%';
762 char_buf[1] = *fmt;
763 s = char_buf;
764 s_len = 2;
765 pad_char = ' ';
766 break;
767 }
768
769 if (prefix_char != NUL) {
770 *--s = prefix_char;
771 s_len++;
772 }
773 if (adjust_width && adjust == RIGHT && min_width > s_len) {
774 if (pad_char == '0' && prefix_char != NUL) {
775 INS_CHAR(*s, sp, bep, cc)
776 s++;
777 s_len--;
778 min_width--;
779 }
780 PAD(min_width, s_len, pad_char);
781 }
782 /*
783 * Print the string s.
784 */
785 for (i = s_len; i != 0; i--) {
786 INS_CHAR(*s, sp, bep, cc);
787 s++;
788 }
789
790 if (adjust_width && adjust == LEFT && min_width > s_len)
791 PAD(min_width, s_len, pad_char);
792 if (free_zcopy) {
793 zval_ptr_dtor_str(&zcopy);
794 }
795 }
796 skip_output:
797 if (free_s) {
798 efree(free_s);
799 free_s = NULL;
800 }
801
802 fmt++;
803 }
804 odp->nextb = sp;
805 return (cc);
806 }
807
strx_printv(int * ccp,char * buf,size_t len,const char * format,zend_bool escape_xml,va_list ap)808 static void strx_printv(int *ccp, char *buf, size_t len, const char *format, zend_bool escape_xml, va_list ap) {
809 buffy od;
810 int cc;
811
812 /*
813 * First initialize the descriptor
814 * Notice that if no length is given, we initialize buf_end to the
815 * highest possible address.
816 */
817 if (len == 0) {
818 od.buf_end = (char *) ~0;
819 od.nextb = (char *) ~0;
820 } else {
821 od.buf_end = &buf[len-1];
822 od.nextb = buf;
823 }
824
825 /*
826 * Do the conversion
827 */
828 cc = format_converter(&od, format, escape_xml, ap);
829 if (len != 0 && od.nextb <= od.buf_end) {
830 *(od.nextb) = '\0';
831 }
832 if (ccp) {
833 *ccp = cc;
834 }
835 }
836
phpdbg_xml_vsnprintf(char * buf,size_t len,const char * format,zend_bool escape_xml,va_list ap)837 static int phpdbg_xml_vsnprintf(char *buf, size_t len, const char *format, zend_bool escape_xml, va_list ap) {
838 int cc;
839
840 strx_printv(&cc, buf, len, format, escape_xml, ap);
841 return (cc);
842 }
843
phpdbg_xml_vasprintf(char ** buf,const char * format,zend_bool escape_xml,va_list ap)844 PHPDBG_API int phpdbg_xml_vasprintf(char **buf, const char *format, zend_bool escape_xml, va_list ap) {
845 va_list ap2;
846 int cc;
847
848 va_copy(ap2, ap);
849 cc = phpdbg_xml_vsnprintf(NULL, 0, format, escape_xml, ap2);
850 va_end(ap2);
851
852 *buf = NULL;
853
854 if (cc >= 0) {
855 *buf = emalloc(++cc);
856 if ((cc = phpdbg_xml_vsnprintf(*buf, cc, format, escape_xml, ap)) < 0) {
857 efree(*buf);
858 *buf = NULL;
859 }
860 }
861
862 return cc;
863 }
864 /* copy end */
865
_phpdbg_asprintf(char ** buf,const char * format,...)866 PHPDBG_API int _phpdbg_asprintf(char **buf, const char *format, ...) {
867 int ret;
868 va_list va;
869
870 va_start(va, format);
871 ret = phpdbg_xml_vasprintf(buf, format, 0, va);
872 va_end(va);
873
874 return ret;
875 }
876
phpdbg_encode_xml(char ** buf,char * msg,int msglen,int from,char * to)877 static int phpdbg_encode_xml(char **buf, char *msg, int msglen, int from, char *to) {
878 int i;
879 int tolen = to ? strlen(to) : 5;
880 char *tmp = *buf = emalloc(msglen * tolen);
881 for (i = 0; i++ < msglen; msg++) {
882 if (*msg == '&') {
883 memcpy(tmp, ZEND_STRL("&"));
884 tmp += sizeof("&") - 1;
885 } else if (*msg == '<') {
886 memcpy(tmp, ZEND_STRL("<"));
887 tmp += sizeof("<") - 1;
888 } else if (((int) *msg) == from) {
889 memcpy(tmp, to, tolen);
890 tmp += tolen;
891 } else {
892 *tmp++ = *msg;
893 }
894 }
895
896 {
897 int len = tmp - *buf;
898 *buf = erealloc(*buf, len + 1);
899 return len;
900 }
901 }
902
phpdbg_encode_ctrl_chars(char ** buf,int * buflen)903 static void phpdbg_encode_ctrl_chars(char **buf, int *buflen) {
904 char *tmp, *tmpptr;
905 int len;
906 int i;
907
908 tmp = tmpptr = emalloc(*buflen * 5);
909
910 for (i = 0; i < *buflen; i++) {
911 if ((*buf)[i] < 0x20) {
912 *tmpptr++ = '&';
913 *tmpptr++ = '#';
914 if ((unsigned int) ((*buf)[i]) > 9) {
915 *tmpptr++ = ((*buf)[i] / 10) + '0';
916 }
917 *tmpptr++ = ((*buf)[i] % 10) + '0';
918 *tmpptr++ = ';';
919 } else {
920 *tmpptr++ = (*buf)[i];
921 }
922 }
923
924 len = tmpptr - tmp;
925
926 efree(*buf);
927 *buf = erealloc(tmp, len + 1);
928 *buflen = len;
929 }
930
phpdbg_process_print(int fd,int type,const char * tag,const char * msg,int msglen,const char * xml,int xmllen)931 static int phpdbg_process_print(int fd, int type, const char *tag, const char *msg, int msglen, const char *xml, int xmllen) {
932 char *msgout = NULL, *buf;
933 int msgoutlen, xmloutlen, buflen;
934 const char *severity;
935
936 if ((PHPDBG_G(flags) & PHPDBG_WRITE_XML) && PHPDBG_G(in_script_xml) && PHPDBG_G(in_script_xml) != type) {
937 phpdbg_mixed_write(fd, ZEND_STRL("</stream>"));
938 PHPDBG_G(in_script_xml) = 0;
939 }
940
941 switch (type) {
942 case P_ERROR:
943 severity = "error";
944 if (!PHPDBG_G(last_was_newline)) {
945 if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
946 phpdbg_mixed_write(fd, ZEND_STRL("<phpdbg>\n" "</phpdbg>"));
947 } else {
948 phpdbg_mixed_write(fd, ZEND_STRL("\n"));
949 }
950 PHPDBG_G(last_was_newline) = 1;
951 }
952 if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
953 msgoutlen = phpdbg_asprintf(&msgout, "\033[%sm[%.*s]\033[0m\n", PHPDBG_G(colors)[PHPDBG_COLOR_ERROR]->code, msglen, msg);
954 } else {
955 msgoutlen = phpdbg_asprintf(&msgout, "[%.*s]\n", msglen, msg);
956 }
957 break;
958
959 case P_NOTICE:
960 severity = "notice";
961 if (!PHPDBG_G(last_was_newline)) {
962 if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
963 phpdbg_mixed_write(fd, ZEND_STRL("<phpdbg>\n" "</phpdbg>"));
964 } else {
965 phpdbg_mixed_write(fd, ZEND_STRL("\n"));
966 }
967 PHPDBG_G(last_was_newline) = 1;
968 }
969 if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
970 msgoutlen = phpdbg_asprintf(&msgout, "\033[%sm[%.*s]\033[0m\n", PHPDBG_G(colors)[PHPDBG_COLOR_NOTICE]->code, msglen, msg);
971 } else {
972 msgoutlen = phpdbg_asprintf(&msgout, "[%.*s]\n", msglen, msg);
973 }
974 break;
975
976 case P_WRITELN:
977 severity = "normal";
978 if (msg) {
979 msgoutlen = phpdbg_asprintf(&msgout, "%.*s\n", msglen, msg);
980 } else {
981 msgoutlen = 1;
982 msgout = estrdup("\n");
983 }
984 PHPDBG_G(last_was_newline) = 1;
985 break;
986
987 case P_WRITE:
988 severity = "normal";
989 if (msg) {
990 msgout = estrndup(msg, msglen);
991 msgoutlen = msglen;
992 PHPDBG_G(last_was_newline) = msg[msglen - 1] == '\n';
993 } else {
994 msgoutlen = 0;
995 msgout = estrdup("");
996 }
997 break;
998
999 case P_STDOUT:
1000 case P_STDERR:
1001 if (msg) {
1002 PHPDBG_G(last_was_newline) = msg[msglen - 1] == '\n';
1003 if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
1004 zend_string *encoded;
1005
1006 if (PHPDBG_G(in_script_xml) != type) {
1007 char *stream_buf;
1008 int stream_buflen = phpdbg_asprintf(&stream_buf, "<stream type=\"%s\">", type == P_STDERR ? "stderr" : "stdout");
1009 phpdbg_mixed_write(fd, stream_buf, stream_buflen);
1010 efree(stream_buf);
1011 PHPDBG_G(in_script_xml) = type;
1012 }
1013 encoded = php_escape_html_entities((unsigned char *) msg, msglen, 0, ENT_NOQUOTES, PG(internal_encoding) && PG(internal_encoding)[0] ? PG(internal_encoding) : (SG(default_charset) ? SG(default_charset) : "UTF-8"));
1014 buflen = ZSTR_LEN(encoded);
1015 memcpy(buf = emalloc(buflen + 1), ZSTR_VAL(encoded), buflen);
1016 phpdbg_encode_ctrl_chars(&buf, &buflen);
1017 phpdbg_mixed_write(fd, buf, buflen);
1018 efree(buf);
1019 } else {
1020 phpdbg_mixed_write(fd, msg, msglen);
1021 }
1022 }
1023 return msglen;
1024
1025 /* no formatting on logging output */
1026 case P_LOG:
1027 severity = "log";
1028 if (msg) {
1029 struct timeval tp;
1030 if (gettimeofday(&tp, NULL) == SUCCESS) {
1031 msgoutlen = phpdbg_asprintf(&msgout, "[%ld %.8F]: %.*s\n", tp.tv_sec, tp.tv_usec / 1000000., msglen, msg);
1032 } else {
1033 msgoutlen = FAILURE;
1034 }
1035 }
1036 break;
1037 EMPTY_SWITCH_DEFAULT_CASE()
1038 }
1039
1040 if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
1041 char *xmlout;
1042
1043 if (PHPDBG_G(req_id)) {
1044 char *xmlbuf = NULL;
1045 xmllen = phpdbg_asprintf(&xmlbuf, "req=\"" ZEND_ULONG_FMT "\" %.*s", PHPDBG_G(req_id), xmllen, xml);
1046 xml = xmlbuf;
1047 }
1048 if (msgout) {
1049 buflen = phpdbg_encode_xml(&buf, msgout, msgoutlen, '"', """);
1050 xmloutlen = phpdbg_asprintf(&xmlout, "<%s severity=\"%s\" %.*s msgout=\"%.*s\" />", tag, severity, xmllen, xml, buflen, buf);
1051
1052 efree(buf);
1053 } else {
1054 xmloutlen = phpdbg_asprintf(&xmlout, "<%s severity=\"%s\" %.*s msgout=\"\" />", tag, severity, xmllen, xml);
1055 }
1056
1057 phpdbg_encode_ctrl_chars(&xmlout, &xmloutlen);
1058 phpdbg_eol_convert(&xmlout, &xmloutlen);
1059 phpdbg_mixed_write(fd, xmlout, xmloutlen);
1060 efree(xmlout);
1061 } else if (msgout) {
1062 phpdbg_eol_convert(&msgout, &msgoutlen);
1063 phpdbg_mixed_write(fd, msgout, msgoutlen);
1064 }
1065
1066 if (PHPDBG_G(req_id) && (PHPDBG_G(flags) & PHPDBG_WRITE_XML)) {
1067 efree((char *) xml);
1068 }
1069
1070 if (msgout) {
1071 efree(msgout);
1072 }
1073
1074 return msgout ? msgoutlen : xmloutlen;
1075 } /* }}} */
1076
phpdbg_vprint(int type,int fd,const char * tag,const char * xmlfmt,const char * strfmt,va_list args)1077 PHPDBG_API int phpdbg_vprint(int type, int fd, const char *tag, const char *xmlfmt, const char *strfmt, va_list args) {
1078 char *msg = NULL, *xml = NULL;
1079 int msglen = 0, xmllen = 0;
1080 int len;
1081 va_list argcpy;
1082
1083 if (strfmt != NULL && strlen(strfmt) > 0L) {
1084 va_copy(argcpy, args);
1085 msglen = phpdbg_xml_vasprintf(&msg, strfmt, 0, argcpy);
1086 va_end(argcpy);
1087 }
1088
1089 if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
1090 if (xmlfmt != NULL && strlen(xmlfmt) > 0L) {
1091 va_copy(argcpy, args);
1092 xmllen = phpdbg_xml_vasprintf(&xml, xmlfmt, 1, argcpy);
1093 va_end(argcpy);
1094 } else {
1095 xml = estrdup("");
1096 }
1097 }
1098
1099 if (PHPDBG_G(err_buf).active && type != P_STDOUT && type != P_STDERR) {
1100 phpdbg_free_err_buf();
1101
1102 PHPDBG_G(err_buf).type = type;
1103 PHPDBG_G(err_buf).fd = fd;
1104 PHPDBG_G(err_buf).tag = estrdup(tag);
1105 PHPDBG_G(err_buf).msg = msg;
1106 PHPDBG_G(err_buf).msglen = msglen;
1107 if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
1108 PHPDBG_G(err_buf).xml = xml;
1109 PHPDBG_G(err_buf).xmllen = xmllen;
1110 }
1111
1112 return msglen;
1113 }
1114
1115 len = phpdbg_process_print(fd, type, tag, msg, msglen, xml, xmllen);
1116
1117 if (msg) {
1118 efree(msg);
1119 }
1120
1121 if (xml) {
1122 efree(xml);
1123 }
1124
1125 return len;
1126 }
1127
phpdbg_free_err_buf(void)1128 PHPDBG_API void phpdbg_free_err_buf(void) {
1129 if (PHPDBG_G(err_buf).type == 0) {
1130 return;
1131 }
1132
1133 PHPDBG_G(err_buf).type = 0;
1134
1135 efree(PHPDBG_G(err_buf).tag);
1136 efree(PHPDBG_G(err_buf).msg);
1137 if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
1138 efree(PHPDBG_G(err_buf).xml);
1139 }
1140 }
1141
phpdbg_activate_err_buf(zend_bool active)1142 PHPDBG_API void phpdbg_activate_err_buf(zend_bool active) {
1143 PHPDBG_G(err_buf).active = active;
1144 }
1145
phpdbg_output_err_buf(const char * tag,const char * xmlfmt,const char * strfmt,...)1146 PHPDBG_API int phpdbg_output_err_buf(const char *tag, const char *xmlfmt, const char *strfmt, ...) {
1147 int len;
1148 va_list args;
1149 int errbuf_active = PHPDBG_G(err_buf).active;
1150
1151 if (PHPDBG_G(flags) & PHPDBG_DISCARD_OUTPUT) {
1152 return 0;
1153 }
1154
1155 PHPDBG_G(err_buf).active = 0;
1156
1157 va_start(args, strfmt);
1158 len = phpdbg_vprint(PHPDBG_G(err_buf).type, PHPDBG_G(err_buf).fd, tag ? tag : PHPDBG_G(err_buf).tag, xmlfmt, strfmt, args);
1159 va_end(args);
1160
1161 PHPDBG_G(err_buf).active = errbuf_active;
1162 phpdbg_free_err_buf();
1163
1164 return len;
1165 }
1166
phpdbg_print(int type,int fd,const char * tag,const char * xmlfmt,const char * strfmt,...)1167 PHPDBG_API int phpdbg_print(int type, int fd, const char *tag, const char *xmlfmt, const char *strfmt, ...) {
1168 va_list args;
1169 int len;
1170
1171 if (PHPDBG_G(flags) & PHPDBG_DISCARD_OUTPUT) {
1172 return 0;
1173 }
1174
1175 va_start(args, strfmt);
1176 len = phpdbg_vprint(type, fd, tag, xmlfmt, strfmt, args);
1177 va_end(args);
1178
1179 return len;
1180 }
1181
phpdbg_xml_internal(int fd,const char * fmt,...)1182 PHPDBG_API int phpdbg_xml_internal(int fd, const char *fmt, ...) {
1183 int len = 0;
1184
1185 if (PHPDBG_G(flags) & PHPDBG_DISCARD_OUTPUT) {
1186 return 0;
1187 }
1188
1189 if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
1190 va_list args;
1191 char *buffer;
1192 int buflen;
1193
1194 va_start(args, fmt);
1195 buflen = phpdbg_xml_vasprintf(&buffer, fmt, 1, args);
1196 va_end(args);
1197
1198 phpdbg_encode_ctrl_chars(&buffer, &buflen);
1199
1200 if (PHPDBG_G(in_script_xml)) {
1201 phpdbg_mixed_write(fd, ZEND_STRL("</stream>"));
1202 PHPDBG_G(in_script_xml) = 0;
1203 }
1204
1205 len = phpdbg_mixed_write(fd, buffer, buflen);
1206 efree(buffer);
1207 }
1208
1209 return len;
1210 }
1211
phpdbg_log_internal(int fd,const char * fmt,...)1212 PHPDBG_API int phpdbg_log_internal(int fd, const char *fmt, ...) {
1213 va_list args;
1214 char *buffer;
1215 int buflen;
1216 int len = 0;
1217
1218 va_start(args, fmt);
1219 buflen = phpdbg_xml_vasprintf(&buffer, fmt, 0, args);
1220 va_end(args);
1221
1222 len = phpdbg_mixed_write(fd, buffer, buflen);
1223 efree(buffer);
1224
1225 return len;
1226 }
1227
phpdbg_out_internal(int fd,const char * fmt,...)1228 PHPDBG_API int phpdbg_out_internal(int fd, const char *fmt, ...) {
1229 va_list args;
1230 char *buffer;
1231 int buflen;
1232 int len = 0;
1233
1234 if (PHPDBG_G(flags) & PHPDBG_DISCARD_OUTPUT) {
1235 return 0;
1236 }
1237
1238 va_start(args, fmt);
1239 buflen = phpdbg_xml_vasprintf(&buffer, fmt, 0, args);
1240 va_end(args);
1241
1242 if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
1243 char *msg;
1244 int msglen;
1245
1246 msglen = phpdbg_encode_xml(&msg, buffer, buflen, 256, NULL);
1247 phpdbg_encode_ctrl_chars(&msg, &msglen);
1248 phpdbg_eol_convert(&msg, &msglen);
1249
1250 if (PHPDBG_G(in_script_xml)) {
1251 phpdbg_mixed_write(fd, ZEND_STRL("</stream>"));
1252 PHPDBG_G(in_script_xml) = 0;
1253 }
1254
1255 phpdbg_mixed_write(fd, ZEND_STRL("<phpdbg>"));
1256 len = phpdbg_mixed_write(fd, msg, msglen);
1257 phpdbg_mixed_write(fd, ZEND_STRL("</phpdbg>"));
1258 } else {
1259 phpdbg_eol_convert(&buffer, &buflen);
1260 len = phpdbg_mixed_write(fd, buffer, buflen);
1261 }
1262
1263 efree(buffer);
1264 return len;
1265 }
1266
1267
phpdbg_rlog_internal(int fd,const char * fmt,...)1268 PHPDBG_API int phpdbg_rlog_internal(int fd, const char *fmt, ...) { /* {{{ */
1269 int rc = 0;
1270
1271 va_list args;
1272 struct timeval tp;
1273
1274 va_start(args, fmt);
1275 if (gettimeofday(&tp, NULL) == SUCCESS) {
1276 char friendly[100];
1277 char *format = NULL, *buffer = NULL, *outbuf = NULL;
1278 const time_t tt = tp.tv_sec;
1279
1280 #ifdef PHP_WIN32
1281 strftime(friendly, 100, "%a %b %d %H.%%04d %Y", localtime(&tt));
1282 #else
1283 strftime(friendly, 100, "%a %b %d %T.%%04d %Y", localtime(&tt));
1284 #endif
1285 phpdbg_asprintf(&buffer, friendly, tp.tv_usec/1000);
1286 phpdbg_asprintf(&format, "[%s]: %s\n", buffer, fmt);
1287 rc = phpdbg_xml_vasprintf(&outbuf, format, 0, args);
1288
1289 if (outbuf) {
1290 rc = phpdbg_mixed_write(fd, outbuf, rc);
1291 efree(outbuf);
1292 }
1293
1294 efree(format);
1295 efree(buffer);
1296 }
1297 va_end(args);
1298
1299 return rc;
1300 } /* }}} */
1301