Skip to content

Bug fix 61038 - pack/unpack implementation fix #58

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ PHP 5.4 UPGRADE NOTES
4. Changes to existing functions
================================

- pack/unpack will now implement format character Z: NULL-padded string
Z: will strip everything after the first null on unpack, a: does not strip,
A: will strip any trailing whitespace on unpack.

- array_combine now returns array() instead of FALSE when two empty arrays are
provided as parameters.

Expand Down
63 changes: 56 additions & 7 deletions ext/standard/pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ static void php_pack(zval **val, int size, int *map, char *output)
/* }}} */

/* pack() idea stolen from Perl (implemented formats behave the same as there)
* Implemented formats are A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, @.
* Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, @.
*/
/* {{{ proto string pack(string format, mixed arg1 [, mixed arg2 [, mixed ...]])
Takes one or more arguments and packs them into a binary string according to the format argument */
Expand Down Expand Up @@ -170,6 +170,7 @@ PHP_FUNCTION(pack)
/* Always uses one arg */
case 'a':
case 'A':
case 'Z':
case 'h':
case 'H':
if (currentarg >= num_args) {
Expand Down Expand Up @@ -250,6 +251,7 @@ PHP_FUNCTION(pack)

case 'a':
case 'A':
case 'Z':
case 'c':
case 'C':
case 'x':
Expand Down Expand Up @@ -315,7 +317,8 @@ PHP_FUNCTION(pack)
switch ((int) code) {
case 'a':
case 'A':
memset(&output[outputpos], (code == 'a') ? '\0' : ' ', arg);
case 'Z':
memset(&output[outputpos], (code == 'a' || code == 'Z') ? '\0' : ' ', arg);
val = argv[currentarg++];
if (Z_ISREF_PP(val)) {
SEPARATE_ZVAL(val);
Expand Down Expand Up @@ -511,7 +514,7 @@ static long php_unpack(char *data, int size, int issigned, int *map)
* chars1, chars2, and ints.
* Numeric pack types will return numbers, a and A will return strings,
* f and d will return doubles.
* Implemented formats are A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, @.
* Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, f, d, x, X, @.
*/
/* {{{ proto array unpack(string format, string input)
Unpack binary string into named array elements according to format argument */
Expand Down Expand Up @@ -586,6 +589,7 @@ PHP_FUNCTION(unpack)

case 'a':
case 'A':
case 'Z':
size = arg;
arg = 1;
break;
Expand Down Expand Up @@ -662,9 +666,24 @@ PHP_FUNCTION(unpack)

if ((inputpos + size) <= inputlen) {
switch ((int) type) {
case 'a':
case 'a': {
/* a will not strip any trailing whitespace or null padding */
char pad = ' ';
int len = inputlen - inputpos; /* Remaining string */

/* If size was given take minimum of len and size */
if ((size >= 0) && (len > size)) {
len = size;
}

size = len;

add_assoc_stringl(return_value, n, &input[inputpos], len, 1);
break;
}
case 'A': {
char pad = (type == 'a') ? '\0' : ' ';
/* A will strip any trailing whitespace */
char padn = '\0'; char pads = ' '; char padt = '\t'; char padc = '\r'; char padl = '\n';
int len = inputlen - inputpos; /* Remaining string */

/* If size was given take minimum of len and size */
Expand All @@ -674,15 +693,45 @@ PHP_FUNCTION(unpack)

size = len;

/* Remove padding chars from unpacked data */
/* Remove trailing white space and nulls chars from unpacked data */
while (--len >= 0) {
if (input[inputpos + len] != pad)
if (input[inputpos + len] != padn
&& input[inputpos + len] != pads
&& input[inputpos + len] != padt
&& input[inputpos + len] != padc
&& input[inputpos + len] != padl
)
break;
}

add_assoc_stringl(return_value, n, &input[inputpos], len + 1, 1);
break;
}
/* New option added for Z to remain in-line with the Perl implementation */
case 'Z': {
/* Z will strip everything after the first null character */
char pad = '\0';
int len = inputlen - inputpos; /* Remaining string */

/* If size was given take minimum of len and size */
if ((size >= 0) && (len > size)) {
len = size;
}

size = len;

/* Remove everything after the first null */
int s = 0;
while (s++ <= len) {
if (input[inputpos + s] == pad)
break;
}
len = s;

add_assoc_stringl(return_value, n, &input[inputpos], len, 1);
break;
}


case 'h':
case 'H': {
Expand Down
37 changes: 37 additions & 0 deletions ext/standard/tests/strings/bug61038.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
BugFix #61038
--FILE--
<?php
var_dump(unpack("Z4", pack("Z4", "foo")));
var_dump(unpack("a4", pack("a4", "foo")));
var_dump(unpack("A4", pack("A4", "foo")));
var_dump(unpack("a9", pack("a*", "foo\x00bar\x00 ")));
var_dump(unpack("A9", pack("a*", "foo\x00bar\x00 ")));
var_dump(unpack("Z9", pack("a*", "foo\x00bar\x00 ")));
?>
--EXPECTF--
array(1) {
[1]=>
string(3) "foo"
}
array(1) {
[1]=>
string(4) "foo%c"
}
array(1) {
[1]=>
string(3) "foo"
}
array(1) {
[1]=>
string(9) "foo%cbar%c "
}
array(1) {
[1]=>
string(7) "foo%cbar"
}
array(1) {
[1]=>
string(3) "foo"
}

4 changes: 2 additions & 2 deletions ext/standard/tests/strings/unpack_error.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var_dump(unpack("I", pack("I", 65534), $extra_arg));

echo "\n-- Testing unpack() function with invalid format character --\n";
$extra_arg = 10;
var_dump(unpack("Z", pack("I", 65534)));
var_dump(unpack("G", pack("I", 65534)));
?>
===DONE===
--EXPECTF--
Expand All @@ -37,6 +37,6 @@ NULL

-- Testing unpack() function with invalid format character --

Warning: unpack(): Invalid format type Z in %s on line %d
Warning: unpack(): Invalid format type G in %s on line %d
bool(false)
===DONE===