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