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