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