1 | /* |
2 | +----------------------------------------------------------------------+ |
3 | | PHP Version 5 | |
4 | +----------------------------------------------------------------------+ |
5 | | Copyright (c) 1997-2015 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 | */ |
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 */ |
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 | */ |
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 */ |
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 | */ |
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 | |