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