xref: /PHP-5.3/ext/standard/pack.c (revision a2045ff3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 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 "safe_mode.h"
43 #include "php_string.h"
44 #include "pack.h"
45 #if HAVE_PWD_H
46 #ifdef PHP_WIN32
47 #include "win32/pwd.h"
48 #else
49 #include <pwd.h>
50 #endif
51 #endif
52 #include "fsock.h"
53 #if HAVE_NETINET_IN_H
54 #include <netinet/in.h>
55 #endif
56 
57 #define INC_OUTPUTPOS(a,b) \
58 	if ((a) < 0 || ((INT_MAX - outputpos)/((int)b)) < (a)) { \
59 		efree(argv);	\
60 		efree(formatcodes);	\
61 		efree(formatargs);	\
62 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: integer overflow in format string", code); \
63 		RETURN_FALSE; \
64 	} \
65 	outputpos += (a)*(b);
66 
67 /* Whether machine is little endian */
68 char machine_little_endian;
69 
70 /* Mapping of byte from char (8bit) to long for machine endian */
71 static int byte_map[1];
72 
73 /* Mappings of bytes from int (machine dependant) to int for machine endian */
74 static int int_map[sizeof(int)];
75 
76 /* Mappings of bytes from shorts (16bit) for all endian environments */
77 static int machine_endian_short_map[2];
78 static int big_endian_short_map[2];
79 static int little_endian_short_map[2];
80 
81 /* Mappings of bytes from longs (32bit) for all endian environments */
82 static int machine_endian_long_map[4];
83 static int big_endian_long_map[4];
84 static int little_endian_long_map[4];
85 
86 /* {{{ php_pack
87  */
php_pack(zval ** val,int size,int * map,char * output)88 static void php_pack(zval **val, int size, int *map, char *output)
89 {
90 	int i;
91 	char *v;
92 
93 	convert_to_long_ex(val);
94 	v = (char *) &Z_LVAL_PP(val);
95 
96 	for (i = 0; i < size; i++) {
97 		*output++ = v[map[i]];
98 	}
99 }
100 /* }}} */
101 
102 /* pack() idea stolen from Perl (implemented formats behave the same as there)
103  * Implemented formats are A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, @.
104  */
105 /* {{{ proto string pack(string format, mixed arg1 [, mixed arg2 [, mixed ...]])
106    Takes one or more arguments and packs them into a binary string according to the format argument */
PHP_FUNCTION(pack)107 PHP_FUNCTION(pack)
108 {
109 	zval ***argv = NULL;
110 	int num_args, i;
111 	int currentarg;
112 	char *format;
113 	int formatlen;
114 	char *formatcodes;
115 	int *formatargs;
116 	int formatcount = 0;
117 	int outputpos = 0, outputsize = 0;
118 	char *output;
119 
120 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &argv, &num_args) == FAILURE) {
121 		return;
122 	}
123 
124 	if (Z_ISREF_PP(argv[0])) {
125 		SEPARATE_ZVAL(argv[0]);
126 	}
127 	convert_to_string_ex(argv[0]);
128 
129 	format = Z_STRVAL_PP(argv[0]);
130 	formatlen = Z_STRLEN_PP(argv[0]);
131 
132 	/* We have a maximum of <formatlen> format codes to deal with */
133 	formatcodes = safe_emalloc(formatlen, sizeof(*formatcodes), 0);
134 	formatargs = safe_emalloc(formatlen, sizeof(*formatargs), 0);
135 	currentarg = 1;
136 
137 	/* Preprocess format into formatcodes and formatargs */
138 	for (i = 0; i < formatlen; formatcount++) {
139 		char code = format[i++];
140 		int arg = 1;
141 
142 		/* Handle format arguments if any */
143 		if (i < formatlen) {
144 			char c = format[i];
145 
146 			if (c == '*') {
147 				arg = -1;
148 				i++;
149 			}
150 			else if (c >= '0' && c <= '9') {
151 				arg = atoi(&format[i]);
152 
153 				while (format[i] >= '0' && format[i] <= '9' && i < formatlen) {
154 					i++;
155 				}
156 			}
157 		}
158 
159 		/* Handle special arg '*' for all codes and check argv overflows */
160 		switch ((int) code) {
161 			/* Never uses any args */
162 			case 'x':
163 			case 'X':
164 			case '@':
165 				if (arg < 0) {
166 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: '*' ignored", code);
167 					arg = 1;
168 				}
169 				break;
170 
171 			/* Always uses one arg */
172 			case 'a':
173 			case 'A':
174 			case 'h':
175 			case 'H':
176 				if (currentarg >= num_args) {
177 					efree(argv);
178 					efree(formatcodes);
179 					efree(formatargs);
180 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: not enough arguments", code);
181 					RETURN_FALSE;
182 				}
183 
184 				if (arg < 0) {
185 					if (Z_ISREF_PP(argv[currentarg])) {
186 						SEPARATE_ZVAL(argv[currentarg]);
187 					}
188 					convert_to_string_ex(argv[currentarg]);
189 					arg = Z_STRLEN_PP(argv[currentarg]);
190 				}
191 
192 				currentarg++;
193 				break;
194 
195 			/* Use as many args as specified */
196 			case 'c':
197 			case 'C':
198 			case 's':
199 			case 'S':
200 			case 'i':
201 			case 'I':
202 			case 'l':
203 			case 'L':
204 			case 'n':
205 			case 'N':
206 			case 'v':
207 			case 'V':
208 			case 'f':
209 			case 'd':
210 				if (arg < 0) {
211 					arg = num_args - currentarg;
212 				}
213 
214 				currentarg += arg;
215 
216 				if (currentarg > num_args) {
217 					efree(argv);
218 					efree(formatcodes);
219 					efree(formatargs);
220 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: too few arguments", code);
221 					RETURN_FALSE;
222 				}
223 				break;
224 
225 			default:
226 				efree(argv);
227 				efree(formatcodes);
228 				efree(formatargs);
229 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: unknown format code", code);
230 				RETURN_FALSE;
231 		}
232 
233 		formatcodes[formatcount] = code;
234 		formatargs[formatcount] = arg;
235 	}
236 
237 	if (currentarg < num_args) {
238 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%d arguments unused", (num_args - currentarg));
239 	}
240 
241 	/* Calculate output length and upper bound while processing*/
242 	for (i = 0; i < formatcount; i++) {
243 	    int code = (int) formatcodes[i];
244 		int arg = formatargs[i];
245 
246 		switch ((int) code) {
247 			case 'h':
248 			case 'H':
249 				INC_OUTPUTPOS((arg + (arg % 2)) / 2,1)	/* 4 bit per arg */
250 				break;
251 
252 			case 'a':
253 			case 'A':
254 			case 'c':
255 			case 'C':
256 			case 'x':
257 				INC_OUTPUTPOS(arg,1)		/* 8 bit per arg */
258 				break;
259 
260 			case 's':
261 			case 'S':
262 			case 'n':
263 			case 'v':
264 				INC_OUTPUTPOS(arg,2)		/* 16 bit per arg */
265 				break;
266 
267 			case 'i':
268 			case 'I':
269 				INC_OUTPUTPOS(arg,sizeof(int))
270 				break;
271 
272 			case 'l':
273 			case 'L':
274 			case 'N':
275 			case 'V':
276 				INC_OUTPUTPOS(arg,4)		/* 32 bit per arg */
277 				break;
278 
279 			case 'f':
280 				INC_OUTPUTPOS(arg,sizeof(float))
281 				break;
282 
283 			case 'd':
284 				INC_OUTPUTPOS(arg,sizeof(double))
285 				break;
286 
287 			case 'X':
288 				outputpos -= arg;
289 
290 				if (outputpos < 0) {
291 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: outside of string", code);
292 					outputpos = 0;
293 				}
294 				break;
295 
296 			case '@':
297 				outputpos = arg;
298 				break;
299 		}
300 
301 		if (outputsize < outputpos) {
302 			outputsize = outputpos;
303 		}
304 	}
305 
306 	output = emalloc(outputsize + 1);
307 	outputpos = 0;
308 	currentarg = 1;
309 
310 	/* Do actual packing */
311 	for (i = 0; i < formatcount; i++) {
312 	    int code = (int) formatcodes[i];
313 		int arg = formatargs[i];
314 		zval **val;
315 
316 		switch ((int) code) {
317 			case 'a':
318 			case 'A':
319 				memset(&output[outputpos], (code == 'a') ? '\0' : ' ', arg);
320 				val = argv[currentarg++];
321 				if (Z_ISREF_PP(val)) {
322 					SEPARATE_ZVAL(val);
323 				}
324 				convert_to_string_ex(val);
325 				memcpy(&output[outputpos], Z_STRVAL_PP(val),
326 					   (Z_STRLEN_PP(val) < arg) ? Z_STRLEN_PP(val) : arg);
327 				outputpos += arg;
328 				break;
329 
330 			case 'h':
331 			case 'H': {
332 				int nibbleshift = (code == 'h') ? 0 : 4;
333 				int first = 1;
334 				char *v;
335 
336 				val = argv[currentarg++];
337 				if (Z_ISREF_PP(val)) {
338 					SEPARATE_ZVAL(val);
339 				}
340 				convert_to_string_ex(val);
341 				v = Z_STRVAL_PP(val);
342 				outputpos--;
343 				if(arg > Z_STRLEN_PP(val)) {
344 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: not enough characters in string", code);
345 					arg = Z_STRLEN_PP(val);
346 				}
347 
348 				while (arg-- > 0) {
349 					char n = *v++;
350 
351 					if (n >= '0' && n <= '9') {
352 						n -= '0';
353 					} else if (n >= 'A' && n <= 'F') {
354 						n -= ('A' - 10);
355 					} else if (n >= 'a' && n <= 'f') {
356 						n -= ('a' - 10);
357 					} else {
358 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: illegal hex digit %c", code, n);
359 						n = 0;
360 					}
361 
362 					if (first--) {
363 						output[++outputpos] = 0;
364 					} else {
365 					  first = 1;
366 					}
367 
368 					output[outputpos] |= (n << nibbleshift);
369 					nibbleshift = (nibbleshift + 4) & 7;
370 				}
371 
372 				outputpos++;
373 				break;
374 			}
375 
376 			case 'c':
377 			case 'C':
378 				while (arg-- > 0) {
379 					php_pack(argv[currentarg++], 1, byte_map, &output[outputpos]);
380 					outputpos++;
381 				}
382 				break;
383 
384 			case 's':
385 			case 'S':
386 			case 'n':
387 			case 'v': {
388 				int *map = machine_endian_short_map;
389 
390 				if (code == 'n') {
391 					map = big_endian_short_map;
392 				} else if (code == 'v') {
393 					map = little_endian_short_map;
394 				}
395 
396 				while (arg-- > 0) {
397 					php_pack(argv[currentarg++], 2, map, &output[outputpos]);
398 					outputpos += 2;
399 				}
400 				break;
401 			}
402 
403 			case 'i':
404 			case 'I':
405 				while (arg-- > 0) {
406 					php_pack(argv[currentarg++], sizeof(int), int_map, &output[outputpos]);
407 					outputpos += sizeof(int);
408 				}
409 				break;
410 
411 			case 'l':
412 			case 'L':
413 			case 'N':
414 			case 'V': {
415 				int *map = machine_endian_long_map;
416 
417 				if (code == 'N') {
418 					map = big_endian_long_map;
419 				} else if (code == 'V') {
420 					map = little_endian_long_map;
421 				}
422 
423 				while (arg-- > 0) {
424 					php_pack(argv[currentarg++], 4, map, &output[outputpos]);
425 					outputpos += 4;
426 				}
427 				break;
428 			}
429 
430 			case 'f': {
431 				float v;
432 
433 				while (arg-- > 0) {
434 					val = argv[currentarg++];
435 					convert_to_double_ex(val);
436 					v = (float) Z_DVAL_PP(val);
437 					memcpy(&output[outputpos], &v, sizeof(v));
438 					outputpos += sizeof(v);
439 				}
440 				break;
441 			}
442 
443 			case 'd': {
444 				double v;
445 
446 				while (arg-- > 0) {
447 					val = argv[currentarg++];
448 					convert_to_double_ex(val);
449 					v = (double) Z_DVAL_PP(val);
450 					memcpy(&output[outputpos], &v, sizeof(v));
451 					outputpos += sizeof(v);
452 				}
453 				break;
454 			}
455 
456 			case 'x':
457 				memset(&output[outputpos], '\0', arg);
458 				outputpos += arg;
459 				break;
460 
461 			case 'X':
462 				outputpos -= arg;
463 
464 				if (outputpos < 0) {
465 					outputpos = 0;
466 				}
467 				break;
468 
469 			case '@':
470 				if (arg > outputpos) {
471 					memset(&output[outputpos], '\0', arg - outputpos);
472 				}
473 				outputpos = arg;
474 				break;
475 		}
476 	}
477 
478 	efree(argv);
479 	efree(formatcodes);
480 	efree(formatargs);
481 	output[outputpos] = '\0';
482 	RETVAL_STRINGL(output, outputpos, 1);
483 	efree(output);
484 }
485 /* }}} */
486 
487 /* {{{ php_unpack
488  */
php_unpack(char * data,int size,int issigned,int * map)489 static long php_unpack(char *data, int size, int issigned, int *map)
490 {
491 	long result;
492 	char *cresult = (char *) &result;
493 	int i;
494 
495 	result = issigned ? -1 : 0;
496 
497 	for (i = 0; i < size; i++) {
498 		cresult[map[i]] = *data++;
499 	}
500 
501 	return result;
502 }
503 /* }}} */
504 
505 /* unpack() is based on Perl's unpack(), but is modified a bit from there.
506  * Rather than depending on error-prone ordered lists or syntactically
507  * unpleasant pass-by-reference, we return an object with named paramters
508  * (like *_fetch_object()). Syntax is "f[repeat]name/...", where "f" is the
509  * formatter char (like pack()), "[repeat]" is the optional repeater argument,
510  * and "name" is the name of the variable to use.
511  * Example: "c2chars/nints" will return an object with fields
512  * chars1, chars2, and ints.
513  * Numeric pack types will return numbers, a and A will return strings,
514  * f and d will return doubles.
515  * Implemented formats are A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, @.
516  */
517 /* {{{ proto array unpack(string format, string input)
518    Unpack binary string into named array elements according to format argument */
PHP_FUNCTION(unpack)519 PHP_FUNCTION(unpack)
520 {
521 	char *format, *input, *formatarg, *inputarg;
522 	int formatlen, formatarg_len, inputarg_len;
523 	int inputpos, inputlen, i;
524 
525 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &formatarg, &formatarg_len,
526 		&inputarg, &inputarg_len) == FAILURE) {
527 		return;
528 	}
529 
530 	format = formatarg;
531 	formatlen = formatarg_len;
532 	input = inputarg;
533 	inputlen = inputarg_len;
534 	inputpos = 0;
535 
536 	array_init(return_value);
537 
538 	while (formatlen-- > 0) {
539 		char type = *(format++);
540 		char c;
541 		int arg = 1, argb;
542 		char *name;
543 		int namelen;
544 		int size=0;
545 
546 		/* Handle format arguments if any */
547 		if (formatlen > 0) {
548 			c = *format;
549 
550 			if (c >= '0' && c <= '9') {
551 				arg = atoi(format);
552 
553 				while (formatlen > 0 && *format >= '0' && *format <= '9') {
554 					format++;
555 					formatlen--;
556 				}
557 			} else if (c == '*') {
558 				arg = -1;
559 				format++;
560 				formatlen--;
561 			}
562 		}
563 
564 		/* Get of new value in array */
565 		name = format;
566 		argb = arg;
567 
568 		while (formatlen > 0 && *format != '/') {
569 			formatlen--;
570 			format++;
571 		}
572 
573 		namelen = format - name;
574 
575 		if (namelen > 200)
576 			namelen = 200;
577 
578 		switch ((int) type) {
579 			/* Never use any input */
580 			case 'X':
581 				size = -1;
582 				break;
583 
584 			case '@':
585 				size = 0;
586 				break;
587 
588 			case 'a':
589 			case 'A':
590 				size = arg;
591 				arg = 1;
592 				break;
593 
594 			case 'h':
595 			case 'H':
596 				size = (arg > 0) ? (arg + (arg % 2)) / 2 : arg;
597 				arg = 1;
598 				break;
599 
600 			/* Use 1 byte of input */
601 			case 'c':
602 			case 'C':
603 			case 'x':
604 				size = 1;
605 				break;
606 
607 			/* Use 2 bytes of input */
608 			case 's':
609 			case 'S':
610 			case 'n':
611 			case 'v':
612 				size = 2;
613 				break;
614 
615 			/* Use sizeof(int) bytes of input */
616 			case 'i':
617 			case 'I':
618 				size = sizeof(int);
619 				break;
620 
621 			/* Use 4 bytes of input */
622 			case 'l':
623 			case 'L':
624 			case 'N':
625 			case 'V':
626 				size = 4;
627 				break;
628 
629 			/* Use sizeof(float) bytes of input */
630 			case 'f':
631 				size = sizeof(float);
632 				break;
633 
634 			/* Use sizeof(double) bytes of input */
635 			case 'd':
636 				size = sizeof(double);
637 				break;
638 
639 			default:
640 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid format type %c", type);
641 				zval_dtor(return_value);
642 				RETURN_FALSE;
643 				break;
644 		}
645 
646 		/* Do actual unpacking */
647 		for (i = 0; i != arg; i++ ) {
648 			/* Space for name + number, safe as namelen is ensured <= 200 */
649 			char n[256];
650 
651 			if (arg != 1 || namelen == 0) {
652 				/* Need to add element number to name */
653 				snprintf(n, sizeof(n), "%.*s%d", namelen, name, i + 1);
654 			} else {
655 				/* Truncate name to next format code or end of string */
656 				snprintf(n, sizeof(n), "%.*s", namelen, name);
657 			}
658 
659 			if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) {
660 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: integer overflow", type);
661 				inputpos = 0;
662 			}
663 
664 			if ((inputpos + size) <= inputlen) {
665 				switch ((int) type) {
666 					case 'a':
667 					case 'A': {
668 						char pad = (type == 'a') ? '\0' : ' ';
669 						int len = inputlen - inputpos;	/* Remaining string */
670 
671 						/* If size was given take minimum of len and size */
672 						if ((size >= 0) && (len > size)) {
673 							len = size;
674 						}
675 
676 						size = len;
677 
678 						/* Remove padding chars from unpacked data */
679 						while (--len >= 0) {
680 							if (input[inputpos + len] != pad)
681 								break;
682 						}
683 
684 						add_assoc_stringl(return_value, n, &input[inputpos], len + 1, 1);
685 						break;
686 					}
687 
688 					case 'h':
689 					case 'H': {
690 						int len = (inputlen - inputpos) * 2;	/* Remaining */
691 						int nibbleshift = (type == 'h') ? 0 : 4;
692 						int first = 1;
693 						char *buf;
694 						int ipos, opos;
695 
696 						/* If size was given take minimum of len and size */
697 						if (size >= 0 && len > (size * 2)) {
698 							len = size * 2;
699 						}
700 
701 						if (argb > 0) {
702 							len -= argb % 2;
703 						}
704 
705 						buf = emalloc(len + 1);
706 
707 						for (ipos = opos = 0; opos < len; opos++) {
708 							char cc = (input[inputpos + ipos] >> nibbleshift) & 0xf;
709 
710 							if (cc < 10) {
711 								cc += '0';
712 							} else {
713 								cc += 'a' - 10;
714 							}
715 
716 							buf[opos] = cc;
717 							nibbleshift = (nibbleshift + 4) & 7;
718 
719 							if (first-- == 0) {
720 								ipos++;
721 								first = 1;
722 							}
723 						}
724 
725 						buf[len] = '\0';
726 						add_assoc_stringl(return_value, n, buf, len, 1);
727 						efree(buf);
728 						break;
729 					}
730 
731 					case 'c':
732 					case 'C': {
733 						int issigned = (type == 'c') ? (input[inputpos] & 0x80) : 0;
734 						long v = php_unpack(&input[inputpos], 1, issigned, byte_map);
735 						add_assoc_long(return_value, n, v);
736 						break;
737 					}
738 
739 					case 's':
740 					case 'S':
741 					case 'n':
742 					case 'v': {
743 						long v;
744 						int issigned = 0;
745 						int *map = machine_endian_short_map;
746 
747 						if (type == 's') {
748 							issigned = input[inputpos + (machine_little_endian ? 1 : 0)] & 0x80;
749 						} else if (type == 'n') {
750 							map = big_endian_short_map;
751 						} else if (type == 'v') {
752 							map = little_endian_short_map;
753 						}
754 
755 						v = php_unpack(&input[inputpos], 2, issigned, map);
756 						add_assoc_long(return_value, n, v);
757 						break;
758 					}
759 
760 					case 'i':
761 					case 'I': {
762 						long v;
763 						int issigned = 0;
764 
765 						if (type == 'i') {
766 							issigned = input[inputpos + (machine_little_endian ? (sizeof(int) - 1) : 0)] & 0x80;
767 						}
768 
769 						v = php_unpack(&input[inputpos], sizeof(int), issigned, int_map);
770 						add_assoc_long(return_value, n, v);
771 						break;
772 					}
773 
774 					case 'l':
775 					case 'L':
776 					case 'N':
777 					case 'V': {
778 						int issigned = 0;
779 						int *map = machine_endian_long_map;
780 						long v = 0;
781 
782 						if (type == 'l' || type == 'L') {
783 							issigned = input[inputpos + (machine_little_endian ? 3 : 0)] & 0x80;
784 						} else if (type == 'N') {
785 							issigned = input[inputpos] & 0x80;
786 							map = big_endian_long_map;
787 						} else if (type == 'V') {
788 							issigned = input[inputpos + 3] & 0x80;
789 							map = little_endian_long_map;
790 						}
791 
792 						if (sizeof(long) > 4 && issigned) {
793 							v = ~INT_MAX;
794 						}
795 
796 						v |= php_unpack(&input[inputpos], 4, issigned, map);
797 						if (sizeof(long) > 4) {
798  							if (type == 'l') {
799 								v = (signed int) v;
800 							} else {
801 								v = (unsigned int) v;
802 							}
803 						}
804 						add_assoc_long(return_value, n, v);
805 						break;
806 					}
807 
808 					case 'f': {
809 						float v;
810 
811 						memcpy(&v, &input[inputpos], sizeof(float));
812 						add_assoc_double(return_value, n, (double)v);
813 						break;
814 					}
815 
816 					case 'd': {
817 						double v;
818 
819 						memcpy(&v, &input[inputpos], sizeof(double));
820 						add_assoc_double(return_value, n, v);
821 						break;
822 					}
823 
824 					case 'x':
825 						/* Do nothing with input, just skip it */
826 						break;
827 
828 					case 'X':
829 						if (inputpos < size) {
830 							inputpos = -size;
831 							i = arg - 1;		/* Break out of for loop */
832 
833 							if (arg >= 0) {
834 								php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: outside of string", type);
835 							}
836 						}
837 						break;
838 
839 					case '@':
840 						if (arg <= inputlen) {
841 							inputpos = arg;
842 						} else {
843 							php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: outside of string", type);
844 						}
845 
846 						i = arg - 1;	/* Done, break out of for loop */
847 						break;
848 				}
849 
850 				inputpos += size;
851 				if (inputpos < 0) {
852 					if (size != -1) { /* only print warning if not working with * */
853 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: outside of string", type);
854 					}
855 					inputpos = 0;
856 				}
857 			} else if (arg < 0) {
858 				/* Reached end of input for '*' repeater */
859 				break;
860 			} else {
861 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type %c: not enough input, need %d, have %d", type, size, inputlen - inputpos);
862 				zval_dtor(return_value);
863 				RETURN_FALSE;
864 			}
865 		}
866 
867 		formatlen--;	/* Skip '/' separator, does no harm if inputlen == 0 */
868 		format++;
869 	}
870 }
871 /* }}} */
872 
873 /* {{{ PHP_MINIT_FUNCTION
874  */
PHP_MINIT_FUNCTION(pack)875 PHP_MINIT_FUNCTION(pack)
876 {
877 	int machine_endian_check = 1;
878 	int i;
879 
880 	machine_little_endian = ((char *)&machine_endian_check)[0];
881 
882 	if (machine_little_endian) {
883 		/* Where to get lo to hi bytes from */
884 		byte_map[0] = 0;
885 
886 		for (i = 0; i < (int)sizeof(int); i++) {
887 			int_map[i] = i;
888 		}
889 
890 		machine_endian_short_map[0] = 0;
891 		machine_endian_short_map[1] = 1;
892 		big_endian_short_map[0] = 1;
893 		big_endian_short_map[1] = 0;
894 		little_endian_short_map[0] = 0;
895 		little_endian_short_map[1] = 1;
896 
897 		machine_endian_long_map[0] = 0;
898 		machine_endian_long_map[1] = 1;
899 		machine_endian_long_map[2] = 2;
900 		machine_endian_long_map[3] = 3;
901 		big_endian_long_map[0] = 3;
902 		big_endian_long_map[1] = 2;
903 		big_endian_long_map[2] = 1;
904 		big_endian_long_map[3] = 0;
905 		little_endian_long_map[0] = 0;
906 		little_endian_long_map[1] = 1;
907 		little_endian_long_map[2] = 2;
908 		little_endian_long_map[3] = 3;
909 	}
910 	else {
911 		zval val;
912 		int size = sizeof(Z_LVAL(val));
913 		Z_LVAL(val)=0; /*silence a warning*/
914 
915 		/* Where to get hi to lo bytes from */
916 		byte_map[0] = size - 1;
917 
918 		for (i = 0; i < (int)sizeof(int); i++) {
919 			int_map[i] = size - (sizeof(int) - i);
920 		}
921 
922 		machine_endian_short_map[0] = size - 2;
923 		machine_endian_short_map[1] = size - 1;
924 		big_endian_short_map[0] = size - 2;
925 		big_endian_short_map[1] = size - 1;
926 		little_endian_short_map[0] = size - 1;
927 		little_endian_short_map[1] = size - 2;
928 
929 		machine_endian_long_map[0] = size - 4;
930 		machine_endian_long_map[1] = size - 3;
931 		machine_endian_long_map[2] = size - 2;
932 		machine_endian_long_map[3] = size - 1;
933 		big_endian_long_map[0] = size - 4;
934 		big_endian_long_map[1] = size - 3;
935 		big_endian_long_map[2] = size - 2;
936 		big_endian_long_map[3] = size - 1;
937 		little_endian_long_map[0] = size - 1;
938 		little_endian_long_map[1] = size - 2;
939 		little_endian_long_map[2] = size - 3;
940 		little_endian_long_map[3] = size - 4;
941 	}
942 
943 	return SUCCESS;
944 }
945 /* }}} */
946 
947 /*
948  * Local variables:
949  * tab-width: 4
950  * c-basic-offset: 4
951  * End:
952  * vim600: noet sw=4 ts=4 fdm=marker
953  * vim<600: noet sw=4 ts=4
954  */
955