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