1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 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 | Author: Chris Schneider <cschneid@relog.ch> |
16 +----------------------------------------------------------------------+
17 */
18 /* $Id$ */
19
20 #include "php.h"
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #ifdef PHP_WIN32
29 #define O_RDONLY _O_RDONLY
30 #include "win32/param.h"
31 #else
32 #include <sys/param.h>
33 #endif
34 #include "ext/standard/head.h"
35 #include "php_string.h"
36 #include "pack.h"
37 #if HAVE_PWD_H
38 #ifdef PHP_WIN32
39 #include "win32/pwd.h"
40 #else
41 #include <pwd.h>
42 #endif
43 #endif
44 #include "fsock.h"
45 #if HAVE_NETINET_IN_H
46 #include <netinet/in.h>
47 #endif
48
49 #define INC_OUTPUTPOS(a,b) \
50 if ((a) < 0 || ((INT_MAX - outputpos)/((int)b)) < (a)) { \
51 efree(formatcodes); \
52 efree(formatargs); \
53 php_error_docref(NULL, E_WARNING, "Type %c: integer overflow in format string", code); \
54 RETURN_FALSE; \
55 } \
56 outputpos += (a)*(b);
57
58 /* Whether machine is little endian */
59 char machine_little_endian;
60
61 /* Mapping of byte from char (8bit) to long for machine endian */
62 static int byte_map[1];
63
64 /* Mappings of bytes from int (machine dependent) to int for machine endian */
65 static int int_map[sizeof(int)];
66
67 /* Mappings of bytes from shorts (16bit) for all endian environments */
68 static int machine_endian_short_map[2];
69 static int big_endian_short_map[2];
70 static int little_endian_short_map[2];
71
72 /* Mappings of bytes from longs (32bit) for all endian environments */
73 static int machine_endian_long_map[4];
74 static int big_endian_long_map[4];
75 static int little_endian_long_map[4];
76
77 #if SIZEOF_ZEND_LONG > 4
78 /* Mappings of bytes from quads (64bit) for all endian environments */
79 static int machine_endian_longlong_map[8];
80 static int big_endian_longlong_map[8];
81 static int little_endian_longlong_map[8];
82 #endif
83
84 /* {{{ php_pack
85 */
php_pack(zval * val,size_t size,int * map,char * output)86 static void php_pack(zval *val, size_t size, int *map, char *output)
87 {
88 size_t i;
89 char *v;
90
91 convert_to_long_ex(val);
92 v = (char *) &Z_LVAL_P(val);
93
94 for (i = 0; i < size; i++) {
95 *output++ = v[map[i]];
96 }
97 }
98 /* }}} */
99
100 /* {{{ php_pack_reverse_int32
101 */
php_pack_reverse_int32(uint32_t arg)102 static inline uint32_t php_pack_reverse_int32(uint32_t arg)
103 {
104 uint32_t result;
105 result = ((arg & 0xFF) << 24) | ((arg & 0xFF00) << 8) | ((arg >> 8) & 0xFF00) | ((arg >> 24) & 0xFF);
106
107 return result;
108 }
109 /* }}} */
110
111 /* {{{ php_pack
112 */
php_pack_reverse_int64(uint64_t arg)113 static inline uint64_t php_pack_reverse_int64(uint64_t arg)
114 {
115 union Swap64 {
116 uint64_t i;
117 uint32_t ul[2];
118 } tmp, result;
119 tmp.i = arg;
120 result.ul[0] = php_pack_reverse_int32(tmp.ul[1]);
121 result.ul[1] = php_pack_reverse_int32(tmp.ul[0]);
122
123 return result.i;
124 }
125 /* }}} */
126
127 /* {{{ php_pack_copy_float
128 */
php_pack_copy_float(int is_little_endian,void * dst,float f)129 static void php_pack_copy_float(int is_little_endian, void * dst, float f)
130 {
131 union Copy32 {
132 float f;
133 uint32_t i;
134 } m;
135 m.f = f;
136
137 #ifdef WORDS_BIGENDIAN
138 if (is_little_endian) {
139 m.i = php_pack_reverse_int32(m.i);
140 }
141 #else /* WORDS_BIGENDIAN */
142 if (!is_little_endian) {
143 m.i = php_pack_reverse_int32(m.i);
144 }
145 #endif /* WORDS_BIGENDIAN */
146
147 memcpy(dst, &m.f, sizeof(float));
148 }
149 /* }}} */
150
151 /* {{{ php_pack_copy_double
152 */
php_pack_copy_double(int is_little_endian,void * dst,double d)153 static void php_pack_copy_double(int is_little_endian, void * dst, double d)
154 {
155 union Copy64 {
156 double d;
157 uint64_t i;
158 } m;
159 m.d = d;
160
161 #ifdef WORDS_BIGENDIAN
162 if (is_little_endian) {
163 m.i = php_pack_reverse_int64(m.i);
164 }
165 #else /* WORDS_BIGENDIAN */
166 if (!is_little_endian) {
167 m.i = php_pack_reverse_int64(m.i);
168 }
169 #endif /* WORDS_BIGENDIAN */
170
171 memcpy(dst, &m.d, sizeof(double));
172 }
173 /* }}} */
174
175 /* {{{ php_pack_parse_float
176 */
php_pack_parse_float(int is_little_endian,void * src)177 static float php_pack_parse_float(int is_little_endian, void * src)
178 {
179 union Copy32 {
180 float f;
181 uint32_t i;
182 } m;
183 memcpy(&m.i, src, sizeof(float));
184
185 #ifdef WORDS_BIGENDIAN
186 if (is_little_endian) {
187 m.i = php_pack_reverse_int32(m.i);
188 }
189 #else /* WORDS_BIGENDIAN */
190 if (!is_little_endian) {
191 m.i = php_pack_reverse_int32(m.i);
192 }
193 #endif /* WORDS_BIGENDIAN */
194
195 return m.f;
196 }
197 /* }}} */
198
199 /* {{{ php_pack_parse_double
200 */
php_pack_parse_double(int is_little_endian,void * src)201 static double php_pack_parse_double(int is_little_endian, void * src)
202 {
203 union Copy64 {
204 double d;
205 uint64_t i;
206 } m;
207 memcpy(&m.i, src, sizeof(double));
208
209 #ifdef WORDS_BIGENDIAN
210 if (is_little_endian) {
211 m.i = php_pack_reverse_int64(m.i);
212 }
213 #else /* WORDS_BIGENDIAN */
214 if (!is_little_endian) {
215 m.i = php_pack_reverse_int64(m.i);
216 }
217 #endif /* WORDS_BIGENDIAN */
218
219 return m.d;
220 }
221 /* }}} */
222
223 /* pack() idea stolen from Perl (implemented formats behave the same as there except J and P)
224 * Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, q, Q, J, P, f, d, x, X, @.
225 * Added g, G for little endian float and big endian float, added e, E for little endian double and big endian double.
226 */
227 /* {{{ proto string pack(string format, mixed arg1 [, mixed arg2 [, mixed ...]])
228 Takes one or more arguments and packs them into a binary string according to the format argument */
PHP_FUNCTION(pack)229 PHP_FUNCTION(pack)
230 {
231 zval *argv = NULL;
232 int num_args = 0;
233 size_t i;
234 int currentarg;
235 char *format;
236 size_t formatlen;
237 char *formatcodes;
238 int *formatargs;
239 size_t formatcount = 0;
240 int outputpos = 0, outputsize = 0;
241 zend_string *output;
242
243 ZEND_PARSE_PARAMETERS_START(1, -1)
244 Z_PARAM_STRING(format, formatlen)
245 Z_PARAM_VARIADIC('*', argv, num_args)
246 ZEND_PARSE_PARAMETERS_END();
247
248 /* We have a maximum of <formatlen> format codes to deal with */
249 formatcodes = safe_emalloc(formatlen, sizeof(*formatcodes), 0);
250 formatargs = safe_emalloc(formatlen, sizeof(*formatargs), 0);
251 currentarg = 0;
252
253 /* Preprocess format into formatcodes and formatargs */
254 for (i = 0; i < formatlen; formatcount++) {
255 char code = format[i++];
256 int arg = 1;
257
258 /* Handle format arguments if any */
259 if (i < formatlen) {
260 char c = format[i];
261
262 if (c == '*') {
263 arg = -1;
264 i++;
265 }
266 else if (c >= '0' && c <= '9') {
267 arg = atoi(&format[i]);
268
269 while (format[i] >= '0' && format[i] <= '9' && i < formatlen) {
270 i++;
271 }
272 }
273 }
274
275 /* Handle special arg '*' for all codes and check argv overflows */
276 switch ((int) code) {
277 /* Never uses any args */
278 case 'x':
279 case 'X':
280 case '@':
281 if (arg < 0) {
282 php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", code);
283 arg = 1;
284 }
285 break;
286
287 /* Always uses one arg */
288 case 'a':
289 case 'A':
290 case 'Z':
291 case 'h':
292 case 'H':
293 if (currentarg >= num_args) {
294 efree(formatcodes);
295 efree(formatargs);
296 php_error_docref(NULL, E_WARNING, "Type %c: not enough arguments", code);
297 RETURN_FALSE;
298 }
299
300 if (arg < 0) {
301 convert_to_string(&argv[currentarg]);
302 arg = Z_STRLEN(argv[currentarg]);
303 if (code == 'Z') {
304 /* add one because Z is always NUL-terminated:
305 * pack("Z*", "aa") === "aa\0"
306 * pack("Z2", "aa") === "a\0" */
307 arg++;
308 }
309 }
310
311 currentarg++;
312 break;
313
314 /* Use as many args as specified */
315 case 'q':
316 case 'Q':
317 case 'J':
318 case 'P':
319 #if SIZEOF_ZEND_LONG < 8
320 efree(formatcodes);
321 efree(formatargs);
322 php_error_docref(NULL, E_WARNING, "64-bit format codes are not available for 32-bit versions of PHP");
323 RETURN_FALSE;
324 #endif
325 case 'c':
326 case 'C':
327 case 's':
328 case 'S':
329 case 'i':
330 case 'I':
331 case 'l':
332 case 'L':
333 case 'n':
334 case 'N':
335 case 'v':
336 case 'V':
337 case 'f': /* float */
338 case 'g': /* little endian float */
339 case 'G': /* big endian float */
340 case 'd': /* double */
341 case 'e': /* little endian double */
342 case 'E': /* big endian double */
343 if (arg < 0) {
344 arg = num_args - currentarg;
345 }
346 if (currentarg > INT_MAX - arg) {
347 goto too_few_args;
348 }
349 currentarg += arg;
350
351 if (currentarg > num_args) {
352 too_few_args:
353 efree(formatcodes);
354 efree(formatargs);
355 php_error_docref(NULL, E_WARNING, "Type %c: too few arguments", code);
356 RETURN_FALSE;
357 }
358 break;
359
360 default:
361 efree(formatcodes);
362 efree(formatargs);
363 php_error_docref(NULL, E_WARNING, "Type %c: unknown format code", code);
364 RETURN_FALSE;
365 }
366
367 formatcodes[formatcount] = code;
368 formatargs[formatcount] = arg;
369 }
370
371 if (currentarg < num_args) {
372 php_error_docref(NULL, E_WARNING, "%d arguments unused", (num_args - currentarg));
373 }
374
375 /* Calculate output length and upper bound while processing*/
376 for (i = 0; i < formatcount; i++) {
377 int code = (int) formatcodes[i];
378 int arg = formatargs[i];
379
380 switch ((int) code) {
381 case 'h':
382 case 'H':
383 INC_OUTPUTPOS((arg + (arg % 2)) / 2,1) /* 4 bit per arg */
384 break;
385
386 case 'a':
387 case 'A':
388 case 'Z':
389 case 'c':
390 case 'C':
391 case 'x':
392 INC_OUTPUTPOS(arg,1) /* 8 bit per arg */
393 break;
394
395 case 's':
396 case 'S':
397 case 'n':
398 case 'v':
399 INC_OUTPUTPOS(arg,2) /* 16 bit per arg */
400 break;
401
402 case 'i':
403 case 'I':
404 INC_OUTPUTPOS(arg,sizeof(int))
405 break;
406
407 case 'l':
408 case 'L':
409 case 'N':
410 case 'V':
411 INC_OUTPUTPOS(arg,4) /* 32 bit per arg */
412 break;
413
414 #if SIZEOF_ZEND_LONG > 4
415 case 'q':
416 case 'Q':
417 case 'J':
418 case 'P':
419 INC_OUTPUTPOS(arg,8) /* 32 bit per arg */
420 break;
421 #endif
422
423 case 'f': /* float */
424 case 'g': /* little endian float */
425 case 'G': /* big endian float */
426 INC_OUTPUTPOS(arg,sizeof(float))
427 break;
428
429 case 'd': /* double */
430 case 'e': /* little endian double */
431 case 'E': /* big endian double */
432 INC_OUTPUTPOS(arg,sizeof(double))
433 break;
434
435 case 'X':
436 outputpos -= arg;
437
438 if (outputpos < 0) {
439 php_error_docref(NULL, E_WARNING, "Type %c: outside of string", code);
440 outputpos = 0;
441 }
442 break;
443
444 case '@':
445 outputpos = arg;
446 break;
447 }
448
449 if (outputsize < outputpos) {
450 outputsize = outputpos;
451 }
452 }
453
454 output = zend_string_alloc(outputsize, 0);
455 outputpos = 0;
456 currentarg = 0;
457
458 /* Do actual packing */
459 for (i = 0; i < formatcount; i++) {
460 int code = (int) formatcodes[i];
461 int arg = formatargs[i];
462
463 switch ((int) code) {
464 case 'a':
465 case 'A':
466 case 'Z': {
467 size_t arg_cp = (code != 'Z') ? arg : MAX(0, arg - 1);
468
469 zend_string *str = zval_get_string(&argv[currentarg++]);
470
471 memset(&ZSTR_VAL(output)[outputpos], (code == 'a' || code == 'Z') ? '\0' : ' ', arg);
472 memcpy(&ZSTR_VAL(output)[outputpos], ZSTR_VAL(str),
473 (ZSTR_LEN(str) < arg_cp) ? ZSTR_LEN(str) : arg_cp);
474
475 outputpos += arg;
476 zend_string_release(str);
477 break;
478 }
479
480 case 'h':
481 case 'H': {
482 int nibbleshift = (code == 'h') ? 0 : 4;
483 int first = 1;
484
485 zend_string *str = zval_get_string(&argv[currentarg++]);
486 char *v = ZSTR_VAL(str);
487
488 outputpos--;
489 if ((size_t)arg > ZSTR_LEN(str)) {
490 php_error_docref(NULL, E_WARNING, "Type %c: not enough characters in string", code);
491 arg = ZSTR_LEN(str);
492 }
493
494 while (arg-- > 0) {
495 char n = *v++;
496
497 if (n >= '0' && n <= '9') {
498 n -= '0';
499 } else if (n >= 'A' && n <= 'F') {
500 n -= ('A' - 10);
501 } else if (n >= 'a' && n <= 'f') {
502 n -= ('a' - 10);
503 } else {
504 php_error_docref(NULL, E_WARNING, "Type %c: illegal hex digit %c", code, n);
505 n = 0;
506 }
507
508 if (first--) {
509 ZSTR_VAL(output)[++outputpos] = 0;
510 } else {
511 first = 1;
512 }
513
514 ZSTR_VAL(output)[outputpos] |= (n << nibbleshift);
515 nibbleshift = (nibbleshift + 4) & 7;
516 }
517
518 outputpos++;
519 zend_string_release(str);
520 break;
521 }
522
523 case 'c':
524 case 'C':
525 while (arg-- > 0) {
526 php_pack(&argv[currentarg++], 1, byte_map, &ZSTR_VAL(output)[outputpos]);
527 outputpos++;
528 }
529 break;
530
531 case 's':
532 case 'S':
533 case 'n':
534 case 'v': {
535 int *map = machine_endian_short_map;
536
537 if (code == 'n') {
538 map = big_endian_short_map;
539 } else if (code == 'v') {
540 map = little_endian_short_map;
541 }
542
543 while (arg-- > 0) {
544 php_pack(&argv[currentarg++], 2, map, &ZSTR_VAL(output)[outputpos]);
545 outputpos += 2;
546 }
547 break;
548 }
549
550 case 'i':
551 case 'I':
552 while (arg-- > 0) {
553 php_pack(&argv[currentarg++], sizeof(int), int_map, &ZSTR_VAL(output)[outputpos]);
554 outputpos += sizeof(int);
555 }
556 break;
557
558 case 'l':
559 case 'L':
560 case 'N':
561 case 'V': {
562 int *map = machine_endian_long_map;
563
564 if (code == 'N') {
565 map = big_endian_long_map;
566 } else if (code == 'V') {
567 map = little_endian_long_map;
568 }
569
570 while (arg-- > 0) {
571 php_pack(&argv[currentarg++], 4, map, &ZSTR_VAL(output)[outputpos]);
572 outputpos += 4;
573 }
574 break;
575 }
576
577 #if SIZEOF_ZEND_LONG > 4
578 case 'q':
579 case 'Q':
580 case 'J':
581 case 'P': {
582 int *map = machine_endian_longlong_map;
583
584 if (code == 'J') {
585 map = big_endian_longlong_map;
586 } else if (code == 'P') {
587 map = little_endian_longlong_map;
588 }
589
590 while (arg-- > 0) {
591 php_pack(&argv[currentarg++], 8, map, &ZSTR_VAL(output)[outputpos]);
592 outputpos += 8;
593 }
594 break;
595 }
596 #endif
597
598 case 'f': {
599 while (arg-- > 0) {
600 float v = (float) zval_get_double(&argv[currentarg++]);
601 memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v));
602 outputpos += sizeof(v);
603 }
604 break;
605 }
606
607 case 'g': {
608 /* pack little endian float */
609 while (arg-- > 0) {
610 float v = (float) zval_get_double(&argv[currentarg++]);
611 php_pack_copy_float(1, &ZSTR_VAL(output)[outputpos], v);
612 outputpos += sizeof(v);
613 }
614
615 break;
616 }
617 case 'G': {
618 /* pack big endian float */
619 while (arg-- > 0) {
620 float v = (float) zval_get_double(&argv[currentarg++]);
621 php_pack_copy_float(0, &ZSTR_VAL(output)[outputpos], v);
622 outputpos += sizeof(v);
623 }
624 break;
625 }
626
627 case 'd': {
628 while (arg-- > 0) {
629 double v = (double) zval_get_double(&argv[currentarg++]);
630 memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v));
631 outputpos += sizeof(v);
632 }
633 break;
634 }
635
636 case 'e': {
637 /* pack little endian double */
638 while (arg-- > 0) {
639 double v = (double) zval_get_double(&argv[currentarg++]);
640 php_pack_copy_double(1, &ZSTR_VAL(output)[outputpos], v);
641 outputpos += sizeof(v);
642 }
643 break;
644 }
645
646 case 'E': {
647 /* pack big endian double */
648 while (arg-- > 0) {
649 double v = (double) zval_get_double(&argv[currentarg++]);
650 php_pack_copy_double(0, &ZSTR_VAL(output)[outputpos], v);
651 outputpos += sizeof(v);
652 }
653 break;
654 }
655
656 case 'x':
657 memset(&ZSTR_VAL(output)[outputpos], '\0', arg);
658 outputpos += arg;
659 break;
660
661 case 'X':
662 outputpos -= arg;
663
664 if (outputpos < 0) {
665 outputpos = 0;
666 }
667 break;
668
669 case '@':
670 if (arg > outputpos) {
671 memset(&ZSTR_VAL(output)[outputpos], '\0', arg - outputpos);
672 }
673 outputpos = arg;
674 break;
675 }
676 }
677
678 efree(formatcodes);
679 efree(formatargs);
680 ZSTR_VAL(output)[outputpos] = '\0';
681 ZSTR_LEN(output) = outputpos;
682 RETURN_NEW_STR(output);
683 }
684 /* }}} */
685
686 /* {{{ php_unpack
687 */
php_unpack(char * data,size_t size,int issigned,int * map)688 static zend_long php_unpack(char *data, size_t size, int issigned, int *map)
689 {
690 zend_long result;
691 char *cresult = (char *) &result;
692 size_t i;
693
694 result = issigned ? -1 : 0;
695
696 for (i = 0; i < size; i++) {
697 cresult[map[i]] = *data++;
698 }
699
700 return result;
701 }
702 /* }}} */
703
704 /* unpack() is based on Perl's unpack(), but is modified a bit from there.
705 * Rather than depending on error-prone ordered lists or syntactically
706 * unpleasant pass-by-reference, we return an object with named parameters
707 * (like *_fetch_object()). Syntax is "f[repeat]name/...", where "f" is the
708 * formatter char (like pack()), "[repeat]" is the optional repeater argument,
709 * and "name" is the name of the variable to use.
710 * Example: "c2chars/nints" will return an object with fields
711 * chars1, chars2, and ints.
712 * Numeric pack types will return numbers, a and A will return strings,
713 * f and d will return doubles.
714 * Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, q, Q, J, P, f, d, x, X, @.
715 * Added g, G for little endian float and big endian float, added e, E for little endian double and big endian double.
716 */
717 /* {{{ proto array unpack(string format, string input)
718 Unpack binary string into named array elements according to format argument */
PHP_FUNCTION(unpack)719 PHP_FUNCTION(unpack)
720 {
721 char *format, *input;
722 zend_string *formatarg, *inputarg;
723 zend_long formatlen, inputpos, inputlen;
724 int i;
725 zend_long offset = 0;
726
727 ZEND_PARSE_PARAMETERS_START(2, 3)
728 Z_PARAM_STR(formatarg)
729 Z_PARAM_STR(inputarg)
730 Z_PARAM_OPTIONAL
731 Z_PARAM_LONG(offset)
732 ZEND_PARSE_PARAMETERS_END();
733
734 format = ZSTR_VAL(formatarg);
735 formatlen = ZSTR_LEN(formatarg);
736 input = ZSTR_VAL(inputarg);
737 inputlen = ZSTR_LEN(inputarg);
738 inputpos = 0;
739
740
741 if (offset < 0 || offset > inputlen) {
742 php_error_docref(NULL, E_WARNING, "Offset " ZEND_LONG_FMT " is out of input range" , offset);
743 RETURN_FALSE;
744 }
745 input += offset;
746 inputlen -= offset;
747
748 array_init(return_value);
749
750 while (formatlen-- > 0) {
751 char type = *(format++);
752 char c;
753 int arg = 1, argb;
754 char *name;
755 int namelen;
756 int size=0;
757
758 /* Handle format arguments if any */
759 if (formatlen > 0) {
760 c = *format;
761
762 if (c >= '0' && c <= '9') {
763 arg = atoi(format);
764
765 while (formatlen > 0 && *format >= '0' && *format <= '9') {
766 format++;
767 formatlen--;
768 }
769 } else if (c == '*') {
770 arg = -1;
771 format++;
772 formatlen--;
773 }
774 }
775
776 /* Get of new value in array */
777 name = format;
778 argb = arg;
779
780 while (formatlen > 0 && *format != '/') {
781 formatlen--;
782 format++;
783 }
784
785 namelen = format - name;
786
787 if (namelen > 200)
788 namelen = 200;
789
790 switch ((int) type) {
791 /* Never use any input */
792 case 'X':
793 size = -1;
794 if (arg < 0) {
795 php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", type);
796 arg = 1;
797 }
798 break;
799
800 case '@':
801 size = 0;
802 break;
803
804 case 'a':
805 case 'A':
806 case 'Z':
807 size = arg;
808 arg = 1;
809 break;
810
811 case 'h':
812 case 'H':
813 size = (arg > 0) ? (arg + (arg % 2)) / 2 : arg;
814 arg = 1;
815 break;
816
817 /* Use 1 byte of input */
818 case 'c':
819 case 'C':
820 case 'x':
821 size = 1;
822 break;
823
824 /* Use 2 bytes of input */
825 case 's':
826 case 'S':
827 case 'n':
828 case 'v':
829 size = 2;
830 break;
831
832 /* Use sizeof(int) bytes of input */
833 case 'i':
834 case 'I':
835 size = sizeof(int);
836 break;
837
838 /* Use 4 bytes of input */
839 case 'l':
840 case 'L':
841 case 'N':
842 case 'V':
843 size = 4;
844 break;
845
846 /* Use 8 bytes of input */
847 case 'q':
848 case 'Q':
849 case 'J':
850 case 'P':
851 #if SIZEOF_ZEND_LONG > 4
852 size = 8;
853 break;
854 #else
855 php_error_docref(NULL, E_WARNING, "64-bit format codes are not available for 32-bit versions of PHP");
856 zval_dtor(return_value);
857 RETURN_FALSE;
858 #endif
859
860 /* Use sizeof(float) bytes of input */
861 case 'f':
862 case 'g':
863 case 'G':
864 size = sizeof(float);
865 break;
866
867 /* Use sizeof(double) bytes of input */
868 case 'd':
869 case 'e':
870 case 'E':
871 size = sizeof(double);
872 break;
873
874 default:
875 php_error_docref(NULL, E_WARNING, "Invalid format type %c", type);
876 zval_dtor(return_value);
877 RETURN_FALSE;
878 break;
879 }
880
881 if (size != 0 && size != -1 && size < 0) {
882 php_error_docref(NULL, E_WARNING, "Type %c: integer overflow", type);
883 zval_dtor(return_value);
884 RETURN_FALSE;
885 }
886
887 /* Do actual unpacking */
888 for (i = 0; i != arg; i++ ) {
889 /* Space for name + number, safe as namelen is ensured <= 200 */
890 char n[256];
891
892 if (arg != 1 || namelen == 0) {
893 /* Need to add element number to name */
894 snprintf(n, sizeof(n), "%.*s%d", namelen, name, i + 1);
895 } else {
896 /* Truncate name to next format code or end of string */
897 snprintf(n, sizeof(n), "%.*s", namelen, name);
898 }
899
900 if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) {
901 php_error_docref(NULL, E_WARNING, "Type %c: integer overflow", type);
902 zval_dtor(return_value);
903 RETURN_FALSE;
904 }
905
906 if ((inputpos + size) <= inputlen) {
907 switch ((int) type) {
908 case 'a': {
909 /* a will not strip any trailing whitespace or null padding */
910 zend_long len = inputlen - inputpos; /* Remaining string */
911
912 /* If size was given take minimum of len and size */
913 if ((size >= 0) && (len > size)) {
914 len = size;
915 }
916
917 size = len;
918
919 add_assoc_stringl(return_value, n, &input[inputpos], len);
920 break;
921 }
922 case 'A': {
923 /* A will strip any trailing whitespace */
924 char padn = '\0'; char pads = ' '; char padt = '\t'; char padc = '\r'; char padl = '\n';
925 zend_long len = inputlen - inputpos; /* Remaining string */
926
927 /* If size was given take minimum of len and size */
928 if ((size >= 0) && (len > size)) {
929 len = size;
930 }
931
932 size = len;
933
934 /* Remove trailing white space and nulls chars from unpacked data */
935 while (--len >= 0) {
936 if (input[inputpos + len] != padn
937 && input[inputpos + len] != pads
938 && input[inputpos + len] != padt
939 && input[inputpos + len] != padc
940 && input[inputpos + len] != padl
941 )
942 break;
943 }
944
945 add_assoc_stringl(return_value, n, &input[inputpos], len + 1);
946 break;
947 }
948 /* New option added for Z to remain in-line with the Perl implementation */
949 case 'Z': {
950 /* Z will strip everything after the first null character */
951 char pad = '\0';
952 zend_long s,
953 len = inputlen - inputpos; /* Remaining string */
954
955 /* If size was given take minimum of len and size */
956 if ((size >= 0) && (len > size)) {
957 len = size;
958 }
959
960 size = len;
961
962 /* Remove everything after the first null */
963 for (s=0 ; s < len ; s++) {
964 if (input[inputpos + s] == pad)
965 break;
966 }
967 len = s;
968
969 add_assoc_stringl(return_value, n, &input[inputpos], len);
970 break;
971 }
972
973
974 case 'h':
975 case 'H': {
976 zend_long len = (inputlen - inputpos) * 2; /* Remaining */
977 int nibbleshift = (type == 'h') ? 0 : 4;
978 int first = 1;
979 zend_string *buf;
980 zend_long ipos, opos;
981
982 /* If size was given take minimum of len and size */
983 if (size >= 0 && len > (size * 2)) {
984 len = size * 2;
985 }
986
987 if (len > 0 && argb > 0) {
988 len -= argb % 2;
989 }
990
991 buf = zend_string_alloc(len, 0);
992
993 for (ipos = opos = 0; opos < len; opos++) {
994 char cc = (input[inputpos + ipos] >> nibbleshift) & 0xf;
995
996 if (cc < 10) {
997 cc += '0';
998 } else {
999 cc += 'a' - 10;
1000 }
1001
1002 ZSTR_VAL(buf)[opos] = cc;
1003 nibbleshift = (nibbleshift + 4) & 7;
1004
1005 if (first-- == 0) {
1006 ipos++;
1007 first = 1;
1008 }
1009 }
1010
1011 ZSTR_VAL(buf)[len] = '\0';
1012 add_assoc_str(return_value, n, buf);
1013 break;
1014 }
1015
1016 case 'c':
1017 case 'C': {
1018 int issigned = (type == 'c') ? (input[inputpos] & 0x80) : 0;
1019 zend_long v = php_unpack(&input[inputpos], 1, issigned, byte_map);
1020 add_assoc_long(return_value, n, v);
1021 break;
1022 }
1023
1024 case 's':
1025 case 'S':
1026 case 'n':
1027 case 'v': {
1028 zend_long v;
1029 int issigned = 0;
1030 int *map = machine_endian_short_map;
1031
1032 if (type == 's') {
1033 issigned = input[inputpos + (machine_little_endian ? 1 : 0)] & 0x80;
1034 } else if (type == 'n') {
1035 map = big_endian_short_map;
1036 } else if (type == 'v') {
1037 map = little_endian_short_map;
1038 }
1039
1040 v = php_unpack(&input[inputpos], 2, issigned, map);
1041 add_assoc_long(return_value, n, v);
1042 break;
1043 }
1044
1045 case 'i':
1046 case 'I': {
1047 zend_long v;
1048 int issigned = 0;
1049
1050 if (type == 'i') {
1051 issigned = input[inputpos + (machine_little_endian ? (sizeof(int) - 1) : 0)] & 0x80;
1052 }
1053
1054 v = php_unpack(&input[inputpos], sizeof(int), issigned, int_map);
1055 add_assoc_long(return_value, n, v);
1056 break;
1057 }
1058
1059 case 'l':
1060 case 'L':
1061 case 'N':
1062 case 'V': {
1063 int issigned = 0;
1064 int *map = machine_endian_long_map;
1065 zend_long v = 0;
1066
1067 if (type == 'l' || type == 'L') {
1068 issigned = input[inputpos + (machine_little_endian ? 3 : 0)] & 0x80;
1069 } else if (type == 'N') {
1070 issigned = input[inputpos] & 0x80;
1071 map = big_endian_long_map;
1072 } else if (type == 'V') {
1073 issigned = input[inputpos + 3] & 0x80;
1074 map = little_endian_long_map;
1075 }
1076
1077 if (SIZEOF_ZEND_LONG > 4 && issigned) {
1078 v = ~INT_MAX;
1079 }
1080
1081 v |= php_unpack(&input[inputpos], 4, issigned, map);
1082 if (SIZEOF_ZEND_LONG > 4) {
1083 if (type == 'l') {
1084 v = (signed int) v;
1085 } else {
1086 v = (unsigned int) v;
1087 }
1088 }
1089 add_assoc_long(return_value, n, v);
1090 break;
1091 }
1092
1093 #if SIZEOF_ZEND_LONG > 4
1094 case 'q':
1095 case 'Q':
1096 case 'J':
1097 case 'P': {
1098 int issigned = 0;
1099 int *map = machine_endian_longlong_map;
1100 zend_long v = 0;
1101
1102 if (type == 'q' || type == 'Q') {
1103 issigned = input[inputpos + (machine_little_endian ? 7 : 0)] & 0x80;
1104 } else if (type == 'J') {
1105 issigned = input[inputpos] & 0x80;
1106 map = big_endian_longlong_map;
1107 } else if (type == 'P') {
1108 issigned = input[inputpos + 7] & 0x80;
1109 map = little_endian_longlong_map;
1110 }
1111
1112 v = php_unpack(&input[inputpos], 8, issigned, map);
1113
1114 if (type == 'q') {
1115 v = (zend_long) v;
1116 } else {
1117 v = (zend_ulong) v;
1118 }
1119
1120 add_assoc_long(return_value, n, v);
1121 break;
1122 }
1123 #endif
1124
1125 case 'f': /* float */
1126 case 'g': /* little endian float*/
1127 case 'G': /* big endian float*/
1128 {
1129 float v;
1130
1131 if (type == 'g') {
1132 v = php_pack_parse_float(1, &input[inputpos]);
1133 } else if (type == 'G') {
1134 v = php_pack_parse_float(0, &input[inputpos]);
1135 } else {
1136 memcpy(&v, &input[inputpos], sizeof(float));
1137 }
1138
1139 add_assoc_double(return_value, n, (double)v);
1140 break;
1141 }
1142
1143
1144 case 'd': /* double */
1145 case 'e': /* little endian float */
1146 case 'E': /* big endian float */
1147 {
1148 double v;
1149 if (type == 'e') {
1150 v = php_pack_parse_double(1, &input[inputpos]);
1151 } else if (type == 'E') {
1152 v = php_pack_parse_double(0, &input[inputpos]);
1153 } else {
1154 memcpy(&v, &input[inputpos], sizeof(double));
1155 }
1156 add_assoc_double(return_value, n, v);
1157 break;
1158 }
1159
1160 case 'x':
1161 /* Do nothing with input, just skip it */
1162 break;
1163
1164 case 'X':
1165 if (inputpos < size) {
1166 inputpos = -size;
1167 i = arg - 1; /* Break out of for loop */
1168
1169 if (arg >= 0) {
1170 php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1171 }
1172 }
1173 break;
1174
1175 case '@':
1176 if (arg <= inputlen) {
1177 inputpos = arg;
1178 } else {
1179 php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1180 }
1181
1182 i = arg - 1; /* Done, break out of for loop */
1183 break;
1184 }
1185
1186 inputpos += size;
1187 if (inputpos < 0) {
1188 if (size != -1) { /* only print warning if not working with * */
1189 php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1190 }
1191 inputpos = 0;
1192 }
1193 } else if (arg < 0) {
1194 /* Reached end of input for '*' repeater */
1195 break;
1196 } else {
1197 php_error_docref(NULL, E_WARNING, "Type %c: not enough input, need %d, have " ZEND_LONG_FMT, type, size, inputlen - inputpos);
1198 zval_dtor(return_value);
1199 RETURN_FALSE;
1200 }
1201 }
1202
1203 if (formatlen > 0) {
1204 formatlen--; /* Skip '/' separator, does no harm if inputlen == 0 */
1205 format++;
1206 }
1207 }
1208 }
1209 /* }}} */
1210
1211 /* {{{ PHP_MINIT_FUNCTION
1212 */
PHP_MINIT_FUNCTION(pack)1213 PHP_MINIT_FUNCTION(pack)
1214 {
1215 int machine_endian_check = 1;
1216 int i;
1217
1218 machine_little_endian = ((char *)&machine_endian_check)[0];
1219
1220 if (machine_little_endian) {
1221 /* Where to get lo to hi bytes from */
1222 byte_map[0] = 0;
1223
1224 for (i = 0; i < (int)sizeof(int); i++) {
1225 int_map[i] = i;
1226 }
1227
1228 machine_endian_short_map[0] = 0;
1229 machine_endian_short_map[1] = 1;
1230 big_endian_short_map[0] = 1;
1231 big_endian_short_map[1] = 0;
1232 little_endian_short_map[0] = 0;
1233 little_endian_short_map[1] = 1;
1234
1235 machine_endian_long_map[0] = 0;
1236 machine_endian_long_map[1] = 1;
1237 machine_endian_long_map[2] = 2;
1238 machine_endian_long_map[3] = 3;
1239 big_endian_long_map[0] = 3;
1240 big_endian_long_map[1] = 2;
1241 big_endian_long_map[2] = 1;
1242 big_endian_long_map[3] = 0;
1243 little_endian_long_map[0] = 0;
1244 little_endian_long_map[1] = 1;
1245 little_endian_long_map[2] = 2;
1246 little_endian_long_map[3] = 3;
1247
1248 #if SIZEOF_ZEND_LONG > 4
1249 machine_endian_longlong_map[0] = 0;
1250 machine_endian_longlong_map[1] = 1;
1251 machine_endian_longlong_map[2] = 2;
1252 machine_endian_longlong_map[3] = 3;
1253 machine_endian_longlong_map[4] = 4;
1254 machine_endian_longlong_map[5] = 5;
1255 machine_endian_longlong_map[6] = 6;
1256 machine_endian_longlong_map[7] = 7;
1257 big_endian_longlong_map[0] = 7;
1258 big_endian_longlong_map[1] = 6;
1259 big_endian_longlong_map[2] = 5;
1260 big_endian_longlong_map[3] = 4;
1261 big_endian_longlong_map[4] = 3;
1262 big_endian_longlong_map[5] = 2;
1263 big_endian_longlong_map[6] = 1;
1264 big_endian_longlong_map[7] = 0;
1265 little_endian_longlong_map[0] = 0;
1266 little_endian_longlong_map[1] = 1;
1267 little_endian_longlong_map[2] = 2;
1268 little_endian_longlong_map[3] = 3;
1269 little_endian_longlong_map[4] = 4;
1270 little_endian_longlong_map[5] = 5;
1271 little_endian_longlong_map[6] = 6;
1272 little_endian_longlong_map[7] = 7;
1273 #endif
1274 }
1275 else {
1276 zval val;
1277 int size = sizeof(Z_LVAL(val));
1278 Z_LVAL(val)=0; /*silence a warning*/
1279
1280 /* Where to get hi to lo bytes from */
1281 byte_map[0] = size - 1;
1282
1283 for (i = 0; i < (int)sizeof(int); i++) {
1284 int_map[i] = size - (sizeof(int) - i);
1285 }
1286
1287 machine_endian_short_map[0] = size - 2;
1288 machine_endian_short_map[1] = size - 1;
1289 big_endian_short_map[0] = size - 2;
1290 big_endian_short_map[1] = size - 1;
1291 little_endian_short_map[0] = size - 1;
1292 little_endian_short_map[1] = size - 2;
1293
1294 machine_endian_long_map[0] = size - 4;
1295 machine_endian_long_map[1] = size - 3;
1296 machine_endian_long_map[2] = size - 2;
1297 machine_endian_long_map[3] = size - 1;
1298 big_endian_long_map[0] = size - 4;
1299 big_endian_long_map[1] = size - 3;
1300 big_endian_long_map[2] = size - 2;
1301 big_endian_long_map[3] = size - 1;
1302 little_endian_long_map[0] = size - 1;
1303 little_endian_long_map[1] = size - 2;
1304 little_endian_long_map[2] = size - 3;
1305 little_endian_long_map[3] = size - 4;
1306
1307 #if SIZEOF_ZEND_LONG > 4
1308 machine_endian_longlong_map[0] = size - 8;
1309 machine_endian_longlong_map[1] = size - 7;
1310 machine_endian_longlong_map[2] = size - 6;
1311 machine_endian_longlong_map[3] = size - 5;
1312 machine_endian_longlong_map[4] = size - 4;
1313 machine_endian_longlong_map[5] = size - 3;
1314 machine_endian_longlong_map[6] = size - 2;
1315 machine_endian_longlong_map[7] = size - 1;
1316 big_endian_longlong_map[0] = size - 8;
1317 big_endian_longlong_map[1] = size - 7;
1318 big_endian_longlong_map[2] = size - 6;
1319 big_endian_longlong_map[3] = size - 5;
1320 big_endian_longlong_map[4] = size - 4;
1321 big_endian_longlong_map[5] = size - 3;
1322 big_endian_longlong_map[6] = size - 2;
1323 big_endian_longlong_map[7] = size - 1;
1324 little_endian_longlong_map[0] = size - 1;
1325 little_endian_longlong_map[1] = size - 2;
1326 little_endian_longlong_map[2] = size - 3;
1327 little_endian_longlong_map[3] = size - 4;
1328 little_endian_longlong_map[4] = size - 5;
1329 little_endian_longlong_map[5] = size - 6;
1330 little_endian_longlong_map[6] = size - 7;
1331 little_endian_longlong_map[7] = size - 8;
1332 #endif
1333 }
1334
1335 return SUCCESS;
1336 }
1337 /* }}} */
1338
1339 /*
1340 * Local variables:
1341 * tab-width: 4
1342 * c-basic-offset: 4
1343 * End:
1344 * vim600: noet sw=4 ts=4 fdm=marker
1345 * vim<600: noet sw=4 ts=4
1346 */
1347