xref: /PHP-7.1/ext/standard/pack.c (revision 7f6387b5)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Chris Schneider <cschneid@relog.ch>                          |
16    +----------------------------------------------------------------------+
17  */
18 /* $Id$ */
19 
20 #include "php.h"
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #ifdef PHP_WIN32
29 #define O_RDONLY _O_RDONLY
30 #include "win32/param.h"
31 #elif defined(NETWARE)
32 #ifdef USE_WINSOCK
33 #include <novsock2.h>
34 #else
35 #include <sys/socket.h>
36 #endif
37 #include <sys/param.h>
38 #else
39 #include <sys/param.h>
40 #endif
41 #include "ext/standard/head.h"
42 #include "php_string.h"
43 #include "pack.h"
44 #if HAVE_PWD_H
45 #ifdef PHP_WIN32
46 #include "win32/pwd.h"
47 #else
48 #include <pwd.h>
49 #endif
50 #endif
51 #include "fsock.h"
52 #if HAVE_NETINET_IN_H
53 #include <netinet/in.h>
54 #endif
55 
56 #define INC_OUTPUTPOS(a,b) \
57 	if ((a) < 0 || ((INT_MAX - outputpos)/((int)b)) < (a)) { \
58 		efree(formatcodes);	\
59 		efree(formatargs);	\
60 		php_error_docref(NULL, E_WARNING, "Type %c: integer overflow in format string", code); \
61 		RETURN_FALSE; \
62 	} \
63 	outputpos += (a)*(b);
64 
65 /* Whether machine is little endian */
66 char machine_little_endian;
67 
68 /* Mapping of byte from char (8bit) to long for machine endian */
69 static int byte_map[1];
70 
71 /* Mappings of bytes from int (machine dependent) to int for machine endian */
72 static int int_map[sizeof(int)];
73 
74 /* Mappings of bytes from shorts (16bit) for all endian environments */
75 static int machine_endian_short_map[2];
76 static int big_endian_short_map[2];
77 static int little_endian_short_map[2];
78 
79 /* Mappings of bytes from longs (32bit) for all endian environments */
80 static int machine_endian_long_map[4];
81 static int big_endian_long_map[4];
82 static int little_endian_long_map[4];
83 
84 #if SIZEOF_ZEND_LONG > 4
85 /* Mappings of bytes from quads (64bit) for all endian environments */
86 static int machine_endian_longlong_map[8];
87 static int big_endian_longlong_map[8];
88 static int little_endian_longlong_map[8];
89 #endif
90 
91 /* {{{ php_pack
92  */
php_pack(zval * val,size_t size,int * map,char * output)93 static void php_pack(zval *val, size_t size, int *map, char *output)
94 {
95 	size_t i;
96 	char *v;
97 
98 	convert_to_long_ex(val);
99 	v = (char *) &Z_LVAL_P(val);
100 
101 	for (i = 0; i < size; i++) {
102 		*output++ = v[map[i]];
103 	}
104 }
105 /* }}} */
106 
107 /* {{{ php_pack_reverse_int32
108  */
php_pack_reverse_int32(uint32_t arg)109 static inline uint32_t php_pack_reverse_int32(uint32_t arg)
110 {
111     uint32_t result;
112     result = ((arg & 0xFF) << 24) | ((arg & 0xFF00) << 8) | ((arg >> 8) & 0xFF00) | ((arg >> 24) & 0xFF);
113 
114 	return result;
115 }
116 /* }}} */
117 
118 /* {{{ php_pack
119  */
php_pack_reverse_int64(uint64_t arg)120 static inline uint64_t php_pack_reverse_int64(uint64_t arg)
121 {
122 	union Swap64 {
123 		uint64_t i;
124 		uint32_t ul[2];
125 	} tmp, result;
126 	tmp.i = arg;
127 	result.ul[0] = php_pack_reverse_int32(tmp.ul[1]);
128 	result.ul[1] = php_pack_reverse_int32(tmp.ul[0]);
129 
130 	return result.i;
131 }
132 /* }}} */
133 
134 /* {{{ php_pack_copy_float
135  */
php_pack_copy_float(int is_little_endian,void * dst,float f)136 static void php_pack_copy_float(int is_little_endian, void * dst, float f)
137 {
138 	union Copy32 {
139 		float f;
140 		uint32_t i;
141 	} m;
142 	m.f = f;
143 
144 #ifdef WORDS_BIGENDIAN
145 	if (is_little_endian) {
146 		m.i = php_pack_reverse_int32(m.i);
147 	}
148 #else /* WORDS_BIGENDIAN */
149 	if (!is_little_endian) {
150 		m.i = php_pack_reverse_int32(m.i);
151 	}
152 #endif /* WORDS_BIGENDIAN */
153 
154 	memcpy(dst, &m.f, sizeof(float));
155 }
156 /* }}} */
157 
158 /* {{{ php_pack_copy_double
159  */
php_pack_copy_double(int is_little_endian,void * dst,double d)160 static void php_pack_copy_double(int is_little_endian, void * dst, double d)
161 {
162 	union Copy64 {
163 		double d;
164 		uint64_t i;
165 	} m;
166 	m.d = d;
167 
168 #ifdef WORDS_BIGENDIAN
169 	if (is_little_endian) {
170 		m.i = php_pack_reverse_int64(m.i);
171 	}
172 #else /* WORDS_BIGENDIAN */
173 	if (!is_little_endian) {
174 		m.i = php_pack_reverse_int64(m.i);
175 	}
176 #endif /* WORDS_BIGENDIAN */
177 
178 	memcpy(dst, &m.d, sizeof(double));
179 }
180 /* }}} */
181 
182 /* {{{ php_pack_parse_float
183  */
php_pack_parse_float(int is_little_endian,void * src)184 static float php_pack_parse_float(int is_little_endian, void * src)
185 {
186 	union Copy32 {
187 		float f;
188 		uint32_t i;
189 	} m;
190 	memcpy(&m.i, src, sizeof(float));
191 
192 #ifdef WORDS_BIGENDIAN
193 	if (is_little_endian) {
194 		m.i = php_pack_reverse_int32(m.i);
195 	}
196 #else /* WORDS_BIGENDIAN */
197 	if (!is_little_endian) {
198 		m.i = php_pack_reverse_int32(m.i);
199 	}
200 #endif /* WORDS_BIGENDIAN */
201 
202 	return m.f;
203 }
204 /* }}} */
205 
206 /* {{{ php_pack_parse_double
207  */
php_pack_parse_double(int is_little_endian,void * src)208 static double php_pack_parse_double(int is_little_endian, void * src)
209 {
210 	union Copy64 {
211 		double d;
212 		uint64_t i;
213 	} m;
214 	memcpy(&m.i, src, sizeof(double));
215 
216 #ifdef WORDS_BIGENDIAN
217 	if (is_little_endian) {
218 		m.i = php_pack_reverse_int64(m.i);
219 	}
220 #else /* WORDS_BIGENDIAN */
221 	if (!is_little_endian) {
222 		m.i = php_pack_reverse_int64(m.i);
223 	}
224 #endif /* WORDS_BIGENDIAN */
225 
226 	return m.d;
227 }
228 /* }}} */
229 
230 /* pack() idea stolen from Perl (implemented formats behave the same as there except J and P)
231  * 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, @.
232  * Added g, G for little endian float and big endian float, added e, E for little endian double and big endian double.
233  */
234 /* {{{ proto string pack(string format, mixed arg1 [, mixed arg2 [, mixed ...]])
235    Takes one or more arguments and packs them into a binary string according to the format argument */
PHP_FUNCTION(pack)236 PHP_FUNCTION(pack)
237 {
238 	zval *argv = NULL;
239 	int num_args = 0;
240 	size_t i;
241 	int currentarg;
242 	char *format;
243 	size_t formatlen;
244 	char *formatcodes;
245 	int *formatargs;
246 	size_t formatcount = 0;
247 	int outputpos = 0, outputsize = 0;
248 	zend_string *output;
249 
250 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s*", &format, &formatlen, &argv, &num_args) == FAILURE) {
251 		return;
252 	}
253 
254 	/* We have a maximum of <formatlen> format codes to deal with */
255 	formatcodes = safe_emalloc(formatlen, sizeof(*formatcodes), 0);
256 	formatargs = safe_emalloc(formatlen, sizeof(*formatargs), 0);
257 	currentarg = 0;
258 
259 	/* Preprocess format into formatcodes and formatargs */
260 	for (i = 0; i < formatlen; formatcount++) {
261 		char code = format[i++];
262 		int arg = 1;
263 
264 		/* Handle format arguments if any */
265 		if (i < formatlen) {
266 			char c = format[i];
267 
268 			if (c == '*') {
269 				arg = -1;
270 				i++;
271 			}
272 			else if (c >= '0' && c <= '9') {
273 				arg = atoi(&format[i]);
274 
275 				while (format[i] >= '0' && format[i] <= '9' && i < formatlen) {
276 					i++;
277 				}
278 			}
279 		}
280 
281 		/* Handle special arg '*' for all codes and check argv overflows */
282 		switch ((int) code) {
283 			/* Never uses any args */
284 			case 'x':
285 			case 'X':
286 			case '@':
287 				if (arg < 0) {
288 					php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", code);
289 					arg = 1;
290 				}
291 				break;
292 
293 			/* Always uses one arg */
294 			case 'a':
295 			case 'A':
296 			case 'Z':
297 			case 'h':
298 			case 'H':
299 				if (currentarg >= num_args) {
300 					efree(formatcodes);
301 					efree(formatargs);
302 					php_error_docref(NULL, E_WARNING, "Type %c: not enough arguments", code);
303 					RETURN_FALSE;
304 				}
305 
306 				if (arg < 0) {
307 					convert_to_string(&argv[currentarg]);
308 					arg = Z_STRLEN(argv[currentarg]);
309 					if (code == 'Z') {
310 						/* add one because Z is always NUL-terminated:
311 						 * pack("Z*", "aa") === "aa\0"
312 						 * pack("Z2", "aa") === "a\0" */
313 						arg++;
314 					}
315 				}
316 
317 				currentarg++;
318 				break;
319 
320 			/* Use as many args as specified */
321 			case 'q':
322 			case 'Q':
323 			case 'J':
324 			case 'P':
325 #if SIZEOF_ZEND_LONG < 8
326 					efree(formatcodes);
327 					efree(formatargs);
328 					php_error_docref(NULL, E_WARNING, "64-bit format codes are not available for 32-bit versions of PHP");
329 					RETURN_FALSE;
330 #endif
331 			case 'c':
332 			case 'C':
333 			case 's':
334 			case 'S':
335 			case 'i':
336 			case 'I':
337 			case 'l':
338 			case 'L':
339 			case 'n':
340 			case 'N':
341 			case 'v':
342 			case 'V':
343 			case 'f': /* float */
344 			case 'g': /* little endian float */
345 			case 'G': /* big endian float */
346 			case 'd': /* double */
347 			case 'e': /* little endian double */
348 			case 'E': /* big endian double */
349 				if (arg < 0) {
350 					arg = num_args - currentarg;
351 				}
352 
353 				currentarg += arg;
354 
355 				if (currentarg > num_args) {
356 					efree(formatcodes);
357 					efree(formatargs);
358 					php_error_docref(NULL, E_WARNING, "Type %c: too few arguments", code);
359 					RETURN_FALSE;
360 				}
361 				break;
362 
363 			default:
364 				efree(formatcodes);
365 				efree(formatargs);
366 				php_error_docref(NULL, E_WARNING, "Type %c: unknown format code", code);
367 				RETURN_FALSE;
368 		}
369 
370 		formatcodes[formatcount] = code;
371 		formatargs[formatcount] = arg;
372 	}
373 
374 	if (currentarg < num_args) {
375 		php_error_docref(NULL, E_WARNING, "%d arguments unused", (num_args - currentarg));
376 	}
377 
378 	/* Calculate output length and upper bound while processing*/
379 	for (i = 0; i < formatcount; i++) {
380 	    int code = (int) formatcodes[i];
381 		int arg = formatargs[i];
382 
383 		switch ((int) code) {
384 			case 'h':
385 			case 'H':
386 				INC_OUTPUTPOS((arg + (arg % 2)) / 2,1)	/* 4 bit per arg */
387 				break;
388 
389 			case 'a':
390 			case 'A':
391 			case 'Z':
392 			case 'c':
393 			case 'C':
394 			case 'x':
395 				INC_OUTPUTPOS(arg,1)		/* 8 bit per arg */
396 				break;
397 
398 			case 's':
399 			case 'S':
400 			case 'n':
401 			case 'v':
402 				INC_OUTPUTPOS(arg,2)		/* 16 bit per arg */
403 				break;
404 
405 			case 'i':
406 			case 'I':
407 				INC_OUTPUTPOS(arg,sizeof(int))
408 				break;
409 
410 			case 'l':
411 			case 'L':
412 			case 'N':
413 			case 'V':
414 				INC_OUTPUTPOS(arg,4)		/* 32 bit per arg */
415 				break;
416 
417 #if SIZEOF_ZEND_LONG > 4
418 			case 'q':
419 			case 'Q':
420 			case 'J':
421 			case 'P':
422 				INC_OUTPUTPOS(arg,8)		/* 32 bit per arg */
423 				break;
424 #endif
425 
426 			case 'f': /* float */
427 			case 'g': /* little endian float */
428 			case 'G': /* big endian float */
429 				INC_OUTPUTPOS(arg,sizeof(float))
430 				break;
431 
432 			case 'd': /* double */
433 			case 'e': /* little endian double */
434 			case 'E': /* big endian double */
435 				INC_OUTPUTPOS(arg,sizeof(double))
436 				break;
437 
438 			case 'X':
439 				outputpos -= arg;
440 
441 				if (outputpos < 0) {
442 					php_error_docref(NULL, E_WARNING, "Type %c: outside of string", code);
443 					outputpos = 0;
444 				}
445 				break;
446 
447 			case '@':
448 				outputpos = arg;
449 				break;
450 		}
451 
452 		if (outputsize < outputpos) {
453 			outputsize = outputpos;
454 		}
455 	}
456 
457 	output = zend_string_alloc(outputsize, 0);
458 	outputpos = 0;
459 	currentarg = 0;
460 
461 	/* Do actual packing */
462 	for (i = 0; i < formatcount; i++) {
463 	    int code = (int) formatcodes[i];
464 		int arg = formatargs[i];
465 
466 		switch ((int) code) {
467 			case 'a':
468 			case 'A':
469 			case 'Z': {
470 				size_t arg_cp = (code != 'Z') ? arg : MAX(0, arg - 1);
471 
472 				zend_string *str = zval_get_string(&argv[currentarg++]);
473 
474 				memset(&ZSTR_VAL(output)[outputpos], (code == 'a' || code == 'Z') ? '\0' : ' ', arg);
475 				memcpy(&ZSTR_VAL(output)[outputpos], ZSTR_VAL(str),
476 					   (ZSTR_LEN(str) < arg_cp) ? ZSTR_LEN(str) : arg_cp);
477 
478 				outputpos += arg;
479 				zend_string_release(str);
480 				break;
481 			}
482 
483 			case 'h':
484 			case 'H': {
485 				int nibbleshift = (code == 'h') ? 0 : 4;
486 				int first = 1;
487 
488 				zend_string *str = zval_get_string(&argv[currentarg++]);
489 				char *v = ZSTR_VAL(str);
490 
491 				outputpos--;
492 				if ((size_t)arg > ZSTR_LEN(str)) {
493 					php_error_docref(NULL, E_WARNING, "Type %c: not enough characters in string", code);
494 					arg = ZSTR_LEN(str);
495 				}
496 
497 				while (arg-- > 0) {
498 					char n = *v++;
499 
500 					if (n >= '0' && n <= '9') {
501 						n -= '0';
502 					} else if (n >= 'A' && n <= 'F') {
503 						n -= ('A' - 10);
504 					} else if (n >= 'a' && n <= 'f') {
505 						n -= ('a' - 10);
506 					} else {
507 						php_error_docref(NULL, E_WARNING, "Type %c: illegal hex digit %c", code, n);
508 						n = 0;
509 					}
510 
511 					if (first--) {
512 						ZSTR_VAL(output)[++outputpos] = 0;
513 					} else {
514 					  first = 1;
515 					}
516 
517 					ZSTR_VAL(output)[outputpos] |= (n << nibbleshift);
518 					nibbleshift = (nibbleshift + 4) & 7;
519 				}
520 
521 				outputpos++;
522 				zend_string_release(str);
523 				break;
524 			}
525 
526 			case 'c':
527 			case 'C':
528 				while (arg-- > 0) {
529 					php_pack(&argv[currentarg++], 1, byte_map, &ZSTR_VAL(output)[outputpos]);
530 					outputpos++;
531 				}
532 				break;
533 
534 			case 's':
535 			case 'S':
536 			case 'n':
537 			case 'v': {
538 				int *map = machine_endian_short_map;
539 
540 				if (code == 'n') {
541 					map = big_endian_short_map;
542 				} else if (code == 'v') {
543 					map = little_endian_short_map;
544 				}
545 
546 				while (arg-- > 0) {
547 					php_pack(&argv[currentarg++], 2, map, &ZSTR_VAL(output)[outputpos]);
548 					outputpos += 2;
549 				}
550 				break;
551 			}
552 
553 			case 'i':
554 			case 'I':
555 				while (arg-- > 0) {
556 					php_pack(&argv[currentarg++], sizeof(int), int_map, &ZSTR_VAL(output)[outputpos]);
557 					outputpos += sizeof(int);
558 				}
559 				break;
560 
561 			case 'l':
562 			case 'L':
563 			case 'N':
564 			case 'V': {
565 				int *map = machine_endian_long_map;
566 
567 				if (code == 'N') {
568 					map = big_endian_long_map;
569 				} else if (code == 'V') {
570 					map = little_endian_long_map;
571 				}
572 
573 				while (arg-- > 0) {
574 					php_pack(&argv[currentarg++], 4, map, &ZSTR_VAL(output)[outputpos]);
575 					outputpos += 4;
576 				}
577 				break;
578 			}
579 
580 #if SIZEOF_ZEND_LONG > 4
581 			case 'q':
582 			case 'Q':
583 			case 'J':
584 			case 'P': {
585 				int *map = machine_endian_longlong_map;
586 
587 				if (code == 'J') {
588 					map = big_endian_longlong_map;
589 				} else if (code == 'P') {
590 					map = little_endian_longlong_map;
591 				}
592 
593 				while (arg-- > 0) {
594 					php_pack(&argv[currentarg++], 8, map, &ZSTR_VAL(output)[outputpos]);
595 					outputpos += 8;
596 				}
597 				break;
598 			}
599 #endif
600 
601 			case 'f': {
602 				while (arg-- > 0) {
603 					float v = (float) zval_get_double(&argv[currentarg++]);
604 					memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v));
605 					outputpos += sizeof(v);
606 				}
607 				break;
608 			}
609 
610 			case 'g': {
611 				/* pack little endian float */
612 				while (arg-- > 0) {
613 					float v = (float) zval_get_double(&argv[currentarg++]);
614 					php_pack_copy_float(1, &ZSTR_VAL(output)[outputpos], v);
615 					outputpos += sizeof(v);
616 				}
617 
618 				break;
619 			}
620 			case 'G': {
621 				/* pack big endian float */
622 				while (arg-- > 0) {
623 					float v = (float) zval_get_double(&argv[currentarg++]);
624 					php_pack_copy_float(0, &ZSTR_VAL(output)[outputpos], v);
625 					outputpos += sizeof(v);
626 				}
627 				break;
628 			}
629 
630 			case 'd': {
631 				while (arg-- > 0) {
632 					double v = (double) zval_get_double(&argv[currentarg++]);
633 					memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v));
634 					outputpos += sizeof(v);
635 				}
636 				break;
637 			}
638 
639 			case 'e': {
640 				/* pack little endian double */
641 				while (arg-- > 0) {
642 					double v = (double) zval_get_double(&argv[currentarg++]);
643 					php_pack_copy_double(1, &ZSTR_VAL(output)[outputpos], v);
644 					outputpos += sizeof(v);
645 				}
646 				break;
647 			}
648 
649 			case 'E': {
650 				/* pack big endian double */
651 				while (arg-- > 0) {
652 					double v = (double) zval_get_double(&argv[currentarg++]);
653 					php_pack_copy_double(0, &ZSTR_VAL(output)[outputpos], v);
654 					outputpos += sizeof(v);
655 				}
656 				break;
657 			}
658 
659 			case 'x':
660 				memset(&ZSTR_VAL(output)[outputpos], '\0', arg);
661 				outputpos += arg;
662 				break;
663 
664 			case 'X':
665 				outputpos -= arg;
666 
667 				if (outputpos < 0) {
668 					outputpos = 0;
669 				}
670 				break;
671 
672 			case '@':
673 				if (arg > outputpos) {
674 					memset(&ZSTR_VAL(output)[outputpos], '\0', arg - outputpos);
675 				}
676 				outputpos = arg;
677 				break;
678 		}
679 	}
680 
681 	efree(formatcodes);
682 	efree(formatargs);
683 	ZSTR_VAL(output)[outputpos] = '\0';
684 	ZSTR_LEN(output) = outputpos;
685 	RETURN_NEW_STR(output);
686 }
687 /* }}} */
688 
689 /* {{{ php_unpack
690  */
php_unpack(char * data,size_t size,int issigned,int * map)691 static zend_long php_unpack(char *data, size_t size, int issigned, int *map)
692 {
693 	zend_long result;
694 	char *cresult = (char *) &result;
695 	size_t i;
696 
697 	result = issigned ? -1 : 0;
698 
699 	for (i = 0; i < size; i++) {
700 		cresult[map[i]] = *data++;
701 	}
702 
703 	return result;
704 }
705 /* }}} */
706 
707 /* unpack() is based on Perl's unpack(), but is modified a bit from there.
708  * Rather than depending on error-prone ordered lists or syntactically
709  * unpleasant pass-by-reference, we return an object with named parameters
710  * (like *_fetch_object()). Syntax is "f[repeat]name/...", where "f" is the
711  * formatter char (like pack()), "[repeat]" is the optional repeater argument,
712  * and "name" is the name of the variable to use.
713  * Example: "c2chars/nints" will return an object with fields
714  * chars1, chars2, and ints.
715  * Numeric pack types will return numbers, a and A will return strings,
716  * f and d will return doubles.
717  * 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, @.
718  * Added g, G for little endian float and big endian float, added e, E for little endian double and big endian double.
719  */
720 /* {{{ proto array unpack(string format, string input)
721    Unpack binary string into named array elements according to format argument */
PHP_FUNCTION(unpack)722 PHP_FUNCTION(unpack)
723 {
724 	char *format, *input;
725 	zend_string *formatarg, *inputarg;
726 	zend_long formatlen, inputpos, inputlen;
727 	int i;
728 	zend_long offset = 0;
729 
730 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|l", &formatarg,
731 		&inputarg, &offset) == FAILURE) {
732 		return;
733 	}
734 
735 	format = ZSTR_VAL(formatarg);
736 	formatlen = ZSTR_LEN(formatarg);
737 	input = ZSTR_VAL(inputarg);
738 	inputlen = ZSTR_LEN(inputarg);
739 	inputpos = 0;
740 
741 
742 	if (offset < 0 || offset > inputlen) {
743 		php_error_docref(NULL, E_WARNING, "Offset " ZEND_LONG_FMT " is out of input range" , offset);
744 		RETURN_FALSE;
745 	}
746 	input += offset;
747 	inputlen -= offset;
748 
749 	array_init(return_value);
750 
751 	while (formatlen-- > 0) {
752 		char type = *(format++);
753 		char c;
754 		int arg = 1, argb;
755 		char *name;
756 		int namelen;
757 		int size=0;
758 
759 		/* Handle format arguments if any */
760 		if (formatlen > 0) {
761 			c = *format;
762 
763 			if (c >= '0' && c <= '9') {
764 				arg = atoi(format);
765 
766 				while (formatlen > 0 && *format >= '0' && *format <= '9') {
767 					format++;
768 					formatlen--;
769 				}
770 			} else if (c == '*') {
771 				arg = -1;
772 				format++;
773 				formatlen--;
774 			}
775 		}
776 
777 		/* Get of new value in array */
778 		name = format;
779 		argb = arg;
780 
781 		while (formatlen > 0 && *format != '/') {
782 			formatlen--;
783 			format++;
784 		}
785 
786 		namelen = format - name;
787 
788 		if (namelen > 200)
789 			namelen = 200;
790 
791 		switch ((int) type) {
792 			/* Never use any input */
793 			case 'X':
794 				size = -1;
795 				if (arg < 0) {
796 					php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", type);
797 					arg = 1;
798 				}
799 				break;
800 
801 			case '@':
802 				size = 0;
803 				break;
804 
805 			case 'a':
806 			case 'A':
807 			case 'Z':
808 				size = arg;
809 				arg = 1;
810 				break;
811 
812 			case 'h':
813 			case 'H':
814 				size = (arg > 0) ? (arg + (arg % 2)) / 2 : arg;
815 				arg = 1;
816 				break;
817 
818 			/* Use 1 byte of input */
819 			case 'c':
820 			case 'C':
821 			case 'x':
822 				size = 1;
823 				break;
824 
825 			/* Use 2 bytes of input */
826 			case 's':
827 			case 'S':
828 			case 'n':
829 			case 'v':
830 				size = 2;
831 				break;
832 
833 			/* Use sizeof(int) bytes of input */
834 			case 'i':
835 			case 'I':
836 				size = sizeof(int);
837 				break;
838 
839 			/* Use 4 bytes of input */
840 			case 'l':
841 			case 'L':
842 			case 'N':
843 			case 'V':
844 				size = 4;
845 				break;
846 
847 			/* Use 8 bytes of input */
848 			case 'q':
849 			case 'Q':
850 			case 'J':
851 			case 'P':
852 #if SIZEOF_ZEND_LONG > 4
853 				size = 8;
854 				break;
855 #else
856 				php_error_docref(NULL, E_WARNING, "64-bit format codes are not available for 32-bit versions of PHP");
857 				zval_dtor(return_value);
858 				RETURN_FALSE;
859 #endif
860 
861 			/* Use sizeof(float) bytes of input */
862 			case 'f':
863 			case 'g':
864 			case 'G':
865 				size = sizeof(float);
866 				break;
867 
868 			/* Use sizeof(double) bytes of input */
869 			case 'd':
870 			case 'e':
871 			case 'E':
872 				size = sizeof(double);
873 				break;
874 
875 			default:
876 				php_error_docref(NULL, E_WARNING, "Invalid format type %c", type);
877 				zval_dtor(return_value);
878 				RETURN_FALSE;
879 				break;
880 		}
881 
882 		if (size != 0 && size != -1 && size < 0) {
883 			php_error_docref(NULL, E_WARNING, "Type %c: integer overflow", type);
884 			zval_dtor(return_value);
885 			RETURN_FALSE;
886 		}
887 
888 		/* Do actual unpacking */
889 		for (i = 0; i != arg; i++ ) {
890 			/* Space for name + number, safe as namelen is ensured <= 200 */
891 			char n[256];
892 
893 			if (arg != 1 || namelen == 0) {
894 				/* Need to add element number to name */
895 				snprintf(n, sizeof(n), "%.*s%d", namelen, name, i + 1);
896 			} else {
897 				/* Truncate name to next format code or end of string */
898 				snprintf(n, sizeof(n), "%.*s", namelen, name);
899 			}
900 
901 			if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) {
902 				php_error_docref(NULL, E_WARNING, "Type %c: integer overflow", type);
903 				zval_dtor(return_value);
904 				RETURN_FALSE;
905 			}
906 
907 			if ((inputpos + size) <= inputlen) {
908 				switch ((int) type) {
909 					case 'a': {
910 						/* a will not strip any trailing whitespace or null padding */
911 						zend_long len = inputlen - inputpos;	/* Remaining string */
912 
913 						/* If size was given take minimum of len and size */
914 						if ((size >= 0) && (len > size)) {
915 							len = size;
916 						}
917 
918 						size = len;
919 
920 						add_assoc_stringl(return_value, n, &input[inputpos], len);
921 						break;
922 					}
923 					case 'A': {
924 						/* A will strip any trailing whitespace */
925 						char padn = '\0'; char pads = ' '; char padt = '\t'; char padc = '\r'; char padl = '\n';
926 						zend_long len = inputlen - inputpos;	/* Remaining string */
927 
928 						/* If size was given take minimum of len and size */
929 						if ((size >= 0) && (len > size)) {
930 							len = size;
931 						}
932 
933 						size = len;
934 
935 						/* Remove trailing white space and nulls chars from unpacked data */
936 						while (--len >= 0) {
937 							if (input[inputpos + len] != padn
938 								&& input[inputpos + len] != pads
939 								&& input[inputpos + len] != padt
940 								&& input[inputpos + len] != padc
941 								&& input[inputpos + len] != padl
942 							)
943 								break;
944 						}
945 
946 						add_assoc_stringl(return_value, n, &input[inputpos], len + 1);
947 						break;
948 					}
949 					/* New option added for Z to remain in-line with the Perl implementation */
950 					case 'Z': {
951 						/* Z will strip everything after the first null character */
952 						char pad = '\0';
953 						zend_long s,
954 							 len = inputlen - inputpos;	/* Remaining string */
955 
956 						/* If size was given take minimum of len and size */
957 						if ((size >= 0) && (len > size)) {
958 							len = size;
959 						}
960 
961 						size = len;
962 
963 						/* Remove everything after the first null */
964 						for (s=0 ; s < len ; s++) {
965 							if (input[inputpos + s] == pad)
966 								break;
967 						}
968 						len = s;
969 
970 						add_assoc_stringl(return_value, n, &input[inputpos], len);
971 						break;
972 					}
973 
974 
975 					case 'h':
976 					case 'H': {
977 						zend_long len = (inputlen - inputpos) * 2;	/* Remaining */
978 						int nibbleshift = (type == 'h') ? 0 : 4;
979 						int first = 1;
980 						char *buf;
981 						zend_long ipos, opos;
982 
983 						/* If size was given take minimum of len and size */
984 						if (size >= 0 && len > (size * 2)) {
985 							len = size * 2;
986 						}
987 
988 						if (len > 0 && argb > 0) {
989 							len -= argb % 2;
990 						}
991 
992 						buf = emalloc(len + 1);
993 
994 						for (ipos = opos = 0; opos < len; opos++) {
995 							char cc = (input[inputpos + ipos] >> nibbleshift) & 0xf;
996 
997 							if (cc < 10) {
998 								cc += '0';
999 							} else {
1000 								cc += 'a' - 10;
1001 							}
1002 
1003 							buf[opos] = cc;
1004 							nibbleshift = (nibbleshift + 4) & 7;
1005 
1006 							if (first-- == 0) {
1007 								ipos++;
1008 								first = 1;
1009 							}
1010 						}
1011 
1012 						buf[len] = '\0';
1013 						add_assoc_stringl(return_value, n, buf, len);
1014 						efree(buf);
1015 						break;
1016 					}
1017 
1018 					case 'c':
1019 					case 'C': {
1020 						int issigned = (type == 'c') ? (input[inputpos] & 0x80) : 0;
1021 						zend_long v = php_unpack(&input[inputpos], 1, issigned, byte_map);
1022 						add_assoc_long(return_value, n, v);
1023 						break;
1024 					}
1025 
1026 					case 's':
1027 					case 'S':
1028 					case 'n':
1029 					case 'v': {
1030 						zend_long v;
1031 						int issigned = 0;
1032 						int *map = machine_endian_short_map;
1033 
1034 						if (type == 's') {
1035 							issigned = input[inputpos + (machine_little_endian ? 1 : 0)] & 0x80;
1036 						} else if (type == 'n') {
1037 							map = big_endian_short_map;
1038 						} else if (type == 'v') {
1039 							map = little_endian_short_map;
1040 						}
1041 
1042 						v = php_unpack(&input[inputpos], 2, issigned, map);
1043 						add_assoc_long(return_value, n, v);
1044 						break;
1045 					}
1046 
1047 					case 'i':
1048 					case 'I': {
1049 						zend_long v;
1050 						int issigned = 0;
1051 
1052 						if (type == 'i') {
1053 							issigned = input[inputpos + (machine_little_endian ? (sizeof(int) - 1) : 0)] & 0x80;
1054 						}
1055 
1056 						v = php_unpack(&input[inputpos], sizeof(int), issigned, int_map);
1057 						add_assoc_long(return_value, n, v);
1058 						break;
1059 					}
1060 
1061 					case 'l':
1062 					case 'L':
1063 					case 'N':
1064 					case 'V': {
1065 						int issigned = 0;
1066 						int *map = machine_endian_long_map;
1067 						zend_long v = 0;
1068 
1069 						if (type == 'l' || type == 'L') {
1070 							issigned = input[inputpos + (machine_little_endian ? 3 : 0)] & 0x80;
1071 						} else if (type == 'N') {
1072 							issigned = input[inputpos] & 0x80;
1073 							map = big_endian_long_map;
1074 						} else if (type == 'V') {
1075 							issigned = input[inputpos + 3] & 0x80;
1076 							map = little_endian_long_map;
1077 						}
1078 
1079 						if (SIZEOF_ZEND_LONG > 4 && issigned) {
1080 							v = ~INT_MAX;
1081 						}
1082 
1083 						v |= php_unpack(&input[inputpos], 4, issigned, map);
1084 						if (SIZEOF_ZEND_LONG > 4) {
1085  							if (type == 'l') {
1086 								v = (signed int) v;
1087 							} else {
1088 								v = (unsigned int) v;
1089 							}
1090 						}
1091 						add_assoc_long(return_value, n, v);
1092 						break;
1093 					}
1094 
1095 #if SIZEOF_ZEND_LONG > 4
1096 					case 'q':
1097 					case 'Q':
1098 					case 'J':
1099 					case 'P': {
1100 						int issigned = 0;
1101 						int *map = machine_endian_longlong_map;
1102 						zend_long v = 0;
1103 
1104 						if (type == 'q' || type == 'Q') {
1105 							issigned = input[inputpos + (machine_little_endian ? 7 : 0)] & 0x80;
1106 						} else if (type == 'J') {
1107 							issigned = input[inputpos] & 0x80;
1108 							map = big_endian_longlong_map;
1109 						} else if (type == 'P') {
1110 							issigned = input[inputpos + 7] & 0x80;
1111 							map = little_endian_longlong_map;
1112 						}
1113 
1114 						v = php_unpack(&input[inputpos], 8, issigned, map);
1115 
1116 						if (type == 'q') {
1117 							v = (zend_long) v;
1118 						} else {
1119 							v = (zend_ulong) v;
1120 						}
1121 
1122 						add_assoc_long(return_value, n, v);
1123 						break;
1124 					}
1125 #endif
1126 
1127 					case 'f': /* float */
1128 					case 'g': /* little endian float*/
1129 					case 'G': /* big endian float*/
1130 					{
1131 						float v;
1132 
1133 						if (type == 'g') {
1134 							v = php_pack_parse_float(1, &input[inputpos]);
1135 						} else if (type == 'G') {
1136 							v = php_pack_parse_float(0, &input[inputpos]);
1137 						} else {
1138 							memcpy(&v, &input[inputpos], sizeof(float));
1139 						}
1140 
1141 						add_assoc_double(return_value, n, (double)v);
1142 						break;
1143 					}
1144 
1145 
1146 					case 'd': /* double */
1147 					case 'e': /* little endian float */
1148 					case 'E': /* big endian float */
1149 					{
1150 						double v;
1151 						if (type == 'e') {
1152 							v = php_pack_parse_double(1, &input[inputpos]);
1153 						} else if (type == 'E') {
1154 							v = php_pack_parse_double(0, &input[inputpos]);
1155 						} else {
1156 							memcpy(&v, &input[inputpos], sizeof(double));
1157 						}
1158 						add_assoc_double(return_value, n, v);
1159 						break;
1160 					}
1161 
1162 					case 'x':
1163 						/* Do nothing with input, just skip it */
1164 						break;
1165 
1166 					case 'X':
1167 						if (inputpos < size) {
1168 							inputpos = -size;
1169 							i = arg - 1;		/* Break out of for loop */
1170 
1171 							if (arg >= 0) {
1172 								php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1173 							}
1174 						}
1175 						break;
1176 
1177 					case '@':
1178 						if (arg <= inputlen) {
1179 							inputpos = arg;
1180 						} else {
1181 							php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1182 						}
1183 
1184 						i = arg - 1;	/* Done, break out of for loop */
1185 						break;
1186 				}
1187 
1188 				inputpos += size;
1189 				if (inputpos < 0) {
1190 					if (size != -1) { /* only print warning if not working with * */
1191 						php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1192 					}
1193 					inputpos = 0;
1194 				}
1195 			} else if (arg < 0) {
1196 				/* Reached end of input for '*' repeater */
1197 				break;
1198 			} else {
1199 				php_error_docref(NULL, E_WARNING, "Type %c: not enough input, need %d, have " ZEND_LONG_FMT, type, size, inputlen - inputpos);
1200 				zval_dtor(return_value);
1201 				RETURN_FALSE;
1202 			}
1203 		}
1204 
1205 		if (formatlen > 0) {
1206 			formatlen--;	/* Skip '/' separator, does no harm if inputlen == 0 */
1207 			format++;
1208 		}
1209 	}
1210 }
1211 /* }}} */
1212 
1213 /* {{{ PHP_MINIT_FUNCTION
1214  */
PHP_MINIT_FUNCTION(pack)1215 PHP_MINIT_FUNCTION(pack)
1216 {
1217 	int machine_endian_check = 1;
1218 	int i;
1219 
1220 	machine_little_endian = ((char *)&machine_endian_check)[0];
1221 
1222 	if (machine_little_endian) {
1223 		/* Where to get lo to hi bytes from */
1224 		byte_map[0] = 0;
1225 
1226 		for (i = 0; i < (int)sizeof(int); i++) {
1227 			int_map[i] = i;
1228 		}
1229 
1230 		machine_endian_short_map[0] = 0;
1231 		machine_endian_short_map[1] = 1;
1232 		big_endian_short_map[0] = 1;
1233 		big_endian_short_map[1] = 0;
1234 		little_endian_short_map[0] = 0;
1235 		little_endian_short_map[1] = 1;
1236 
1237 		machine_endian_long_map[0] = 0;
1238 		machine_endian_long_map[1] = 1;
1239 		machine_endian_long_map[2] = 2;
1240 		machine_endian_long_map[3] = 3;
1241 		big_endian_long_map[0] = 3;
1242 		big_endian_long_map[1] = 2;
1243 		big_endian_long_map[2] = 1;
1244 		big_endian_long_map[3] = 0;
1245 		little_endian_long_map[0] = 0;
1246 		little_endian_long_map[1] = 1;
1247 		little_endian_long_map[2] = 2;
1248 		little_endian_long_map[3] = 3;
1249 
1250 #if SIZEOF_ZEND_LONG > 4
1251 		machine_endian_longlong_map[0] = 0;
1252 		machine_endian_longlong_map[1] = 1;
1253 		machine_endian_longlong_map[2] = 2;
1254 		machine_endian_longlong_map[3] = 3;
1255 		machine_endian_longlong_map[4] = 4;
1256 		machine_endian_longlong_map[5] = 5;
1257 		machine_endian_longlong_map[6] = 6;
1258 		machine_endian_longlong_map[7] = 7;
1259 		big_endian_longlong_map[0] = 7;
1260 		big_endian_longlong_map[1] = 6;
1261 		big_endian_longlong_map[2] = 5;
1262 		big_endian_longlong_map[3] = 4;
1263 		big_endian_longlong_map[4] = 3;
1264 		big_endian_longlong_map[5] = 2;
1265 		big_endian_longlong_map[6] = 1;
1266 		big_endian_longlong_map[7] = 0;
1267 		little_endian_longlong_map[0] = 0;
1268 		little_endian_longlong_map[1] = 1;
1269 		little_endian_longlong_map[2] = 2;
1270 		little_endian_longlong_map[3] = 3;
1271 		little_endian_longlong_map[4] = 4;
1272 		little_endian_longlong_map[5] = 5;
1273 		little_endian_longlong_map[6] = 6;
1274 		little_endian_longlong_map[7] = 7;
1275 #endif
1276 	}
1277 	else {
1278 		zval val;
1279 		int size = sizeof(Z_LVAL(val));
1280 		Z_LVAL(val)=0; /*silence a warning*/
1281 
1282 		/* Where to get hi to lo bytes from */
1283 		byte_map[0] = size - 1;
1284 
1285 		for (i = 0; i < (int)sizeof(int); i++) {
1286 			int_map[i] = size - (sizeof(int) - i);
1287 		}
1288 
1289 		machine_endian_short_map[0] = size - 2;
1290 		machine_endian_short_map[1] = size - 1;
1291 		big_endian_short_map[0] = size - 2;
1292 		big_endian_short_map[1] = size - 1;
1293 		little_endian_short_map[0] = size - 1;
1294 		little_endian_short_map[1] = size - 2;
1295 
1296 		machine_endian_long_map[0] = size - 4;
1297 		machine_endian_long_map[1] = size - 3;
1298 		machine_endian_long_map[2] = size - 2;
1299 		machine_endian_long_map[3] = size - 1;
1300 		big_endian_long_map[0] = size - 4;
1301 		big_endian_long_map[1] = size - 3;
1302 		big_endian_long_map[2] = size - 2;
1303 		big_endian_long_map[3] = size - 1;
1304 		little_endian_long_map[0] = size - 1;
1305 		little_endian_long_map[1] = size - 2;
1306 		little_endian_long_map[2] = size - 3;
1307 		little_endian_long_map[3] = size - 4;
1308 
1309 #if SIZEOF_ZEND_LONG > 4
1310 		machine_endian_longlong_map[0] = size - 8;
1311 		machine_endian_longlong_map[1] = size - 7;
1312 		machine_endian_longlong_map[2] = size - 6;
1313 		machine_endian_longlong_map[3] = size - 5;
1314 		machine_endian_longlong_map[4] = size - 4;
1315 		machine_endian_longlong_map[5] = size - 3;
1316 		machine_endian_longlong_map[6] = size - 2;
1317 		machine_endian_longlong_map[7] = size - 1;
1318 		big_endian_longlong_map[0] = size - 8;
1319 		big_endian_longlong_map[1] = size - 7;
1320 		big_endian_longlong_map[2] = size - 6;
1321 		big_endian_longlong_map[3] = size - 5;
1322 		big_endian_longlong_map[4] = size - 4;
1323 		big_endian_longlong_map[5] = size - 3;
1324 		big_endian_longlong_map[6] = size - 2;
1325 		big_endian_longlong_map[7] = size - 1;
1326 		little_endian_longlong_map[0] = size - 1;
1327 		little_endian_longlong_map[1] = size - 2;
1328 		little_endian_longlong_map[2] = size - 3;
1329 		little_endian_longlong_map[3] = size - 4;
1330 		little_endian_longlong_map[4] = size - 5;
1331 		little_endian_longlong_map[5] = size - 6;
1332 		little_endian_longlong_map[6] = size - 7;
1333 		little_endian_longlong_map[7] = size - 8;
1334 #endif
1335 	}
1336 
1337 	return SUCCESS;
1338 }
1339 /* }}} */
1340 
1341 /*
1342  * Local variables:
1343  * tab-width: 4
1344  * c-basic-offset: 4
1345  * End:
1346  * vim600: noet sw=4 ts=4 fdm=marker
1347  * vim<600: noet sw=4 ts=4
1348  */
1349