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