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