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