xref: /PHP-5.5/ext/standard/pack.c (revision 9c0813fd)
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