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