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

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