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