root/ext/standard/pack.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_pack
  2. PHP_FUNCTION
  3. php_unpack
  4. PHP_FUNCTION
  5. PHP_MINIT_FUNCTION

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

/* [<][>][^][v][top][bottom][index][help] */