Skip to content

Commit

Permalink
Ensure the correctness when using formatted output conversion
Browse files Browse the repository at this point in the history
The implementation of __format() produces incorrect results
when using field width and flag characters.

For example, consider the following printf calls, where the
prepended characters are placed in incorrect positions.

    printf("%#12x\n", 0xff00cde1) --> "0x  ff00cde1\n"
    printf("%#12o\n", 0x1000c)    --> "0     200014\n"
    printf("%12d\n", -3580)	  --> "0000000-3580\n"

Thus, this commit improves __format() to ensure correctness
and to make the results consistent with GCC, and adds a test
case to validate the changes.
  • Loading branch information
DrXiao committed Dec 2, 2024
1 parent a5e901b commit b310322
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 72 deletions.
115 changes: 45 additions & 70 deletions lib/c.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,37 +214,13 @@ int __format(char *buffer,
{
int bi = 0;
char pb[INT_BUF_LEN];
int pbi = 0;

if (alternate_form == 1) {
switch (base) {
case 8:
/* octal */
buffer[0] = '0';
bi = 1;
width -= 1;
break;
case 16:
/* hex */
buffer[0] = '0';
buffer[1] = 'x';
bi = 2;
width -= 2;
break;
default:
/* decimal */
/* do nothing */
break;
}
if (width < 0)
width = 0;
}
int pbi;

/* set to zeroes */
while (pbi < INT_BUF_LEN) {
for (pbi = 0; pbi < INT_BUF_LEN; pbi++)
pb[pbi] = '0';
pbi++;
}

pbi = 0;

switch (base) {
case 8:
Expand All @@ -261,53 +237,52 @@ int __format(char *buffer,
break;
}

while (width > INT_BUF_LEN) {
/* need to add extra padding */
if (zeropad == 1)
buffer[bi] = '0';
else
buffer[bi] = ' ';
bi++;
width--;
}
while (pb[pbi] == '0' && pbi < INT_BUF_LEN - 1)
pbi++;

/* no padding */
if (width == 0) {
int c = 0;
int started = 0;

/* output from first digit */
while (c < INT_BUF_LEN) {
if (pb[c] != '0')
started = 1;
if (started) {
buffer[bi] = pb[c];
bi++;
}
c++;
switch (base) {
case 8:
if (alternate_form) {
if (width && zeropad && pb[pbi] != '0') {
buffer[bi++] = '0';
width -= 1;
} else if (pb[pbi] != '0')
pb[--pbi] = '0';
}
/* special case - zero */
if (started == 0) {
buffer[bi] = '0';
bi++;
break;
case 10:
if (width && zeropad && pb[pbi] == '-') {
buffer[bi++] = '-';
pbi++;
width--;
}
} else {
/* padding */
int c = INT_BUF_LEN - width;
int started = 0;
while (c < INT_BUF_LEN) {
if (pb[c] != '0')
started = 1;
if (started)
buffer[bi] = pb[c];
else if (zeropad == 1)
buffer[bi] = '0';
else
buffer[bi] = ' ';
bi++;
c++;
break;
case 16:
if (alternate_form) {
if (width && zeropad && pb[pbi] != '0') {
buffer[bi++] = '0';
buffer[bi++] = 'x';
width -= 2;
} else if (pb[pbi] != '0') {
pb[--pbi] = 'x';
pb[--pbi] = '0';
}
}
break;
}

width -= (INT_BUF_LEN - pbi);
if (width < 0)
width = 0;

while (width) {
buffer[bi++] = zeropad ? '0' : ' ';
width--;
}

for (; pbi < INT_BUF_LEN; pbi++)
buffer[bi++] = pb[pbi];

return bi;
}

Expand Down
121 changes: 121 additions & 0 deletions tests/driver.sh
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,127 @@ int main() {
}
EOF

fmt_ans="0x0000000000000000000000ff00cde1
0xff00cde1
000000000000000000000000ff00cde1
ff00cde1
0xff00cde1
ff00cde1
0x00ff00cde1
0xff00cde1
0000ff00cde1
ff00cde1
00000000000000000000037700146741
037700146741
00000000000000000000037700146741
37700146741
037700146741
37700146741
037700146741
037700146741
037700146741
37700146741
-0000000000000000000000016724511
-16724511
-16724511
-00016724511
-16724511
0x0000000000000000000000fffff204
0xfffff204
000000000000000000000000fffff204
fffff204
0xfffff204
fffff204
0x00fffff204
0xfffff204
0000fffff204
fffff204
00000000000000000000037777771004
037777771004
00000000000000000000037777771004
37777771004
037777771004
37777771004
037777771004
037777771004
037777771004
37777771004
-0000000000000000000000000003580
-3580
-3580
-00000003580
-3580
0x00000000000000000000000001000c
0x1000c
0000000000000000000000000001000c
1000c
0x1000c
1000c
0x000001000c
0x1000c
00000001000c
1000c
00000000000000000000000000200014
0200014
00000000000000000000000000200014
200014
0200014
200014
000000200014
0200014
000000200014
200014
00000000000000000000000000065548
65548
65548
000000065548
65548
00000000000000000000000000000000
0
00000000000000000000000000000000
0
0
0
000000000000
0
000000000000
0
00000000000000000000000000000000
0
00000000000000000000000000000000
0
0
0
000000000000
0
000000000000
0
00000000000000000000000000000000
0
0
000000000000
0"

try_output 0 "$fmt_ans" << EOF
void printf_conversion(int num) {
printf("%#032x\n%#32x\n%032x\n%32x\n%#x\n%x\n", num, num, num, num, num, num);
printf("%#012x\n%#12x\n%012x\n%12x\n", num, num, num, num);
printf("%#032o\n%#32o\n%032o\n%32o\n%#o\n%o\n", num, num, num, num, num, num);
printf("%#012o\n%#12o\n%012o\n%12o\n", num, num, num, num);
printf("%032d\n%32d\n%d\n", num, num, num);
printf("%012d\n%12d\n", num, num);
}
int main() {
int a = 0xFF00CDE1, b = 0xFFFFF204, c = 65548, d = 0;
printf_conversion(a);
printf_conversion(b);
printf_conversion(c);
printf_conversion(d);
return 0;
}
EOF

try_ 0 << EOF
int main() {
return '\0';
Expand Down
2 changes: 1 addition & 1 deletion tests/snapshots/fib.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion tests/snapshots/hello.json

Large diffs are not rendered by default.

0 comments on commit b310322

Please sign in to comment.