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