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