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