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