Skip to content

Commit

Permalink
Support length modifiers in printf formatting
Browse files Browse the repository at this point in the history
Handle all length modifiers and conversion specifiers as laid out in
printf's man page.

Fixes: #7147
  • Loading branch information
tautschnig committed Mar 19, 2024
1 parent 5100bb8 commit f3e1e32
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 27 deletions.
12 changes: 12 additions & 0 deletions regression/cbmc/printf3/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <assert.h>
#include <stdio.h>

int main()
{
printf("%ld", 10);
printf("%llu", 11);
printf("%hhd", 12);
printf("%lf", 10.0);
printf("%Lf", 11.0);
assert(0);
}
12 changes: 12 additions & 0 deletions regression/cbmc/printf3/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CORE no-new-smt
main.c
--trace
^10$
^11$
^12$
^10.0+$
^11.0+$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
169 changes: 142 additions & 27 deletions src/goto-programs/printf_formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ Author: Daniel Kroening, kroening@kroening.com

#include "printf_formatter.h"

#include <sstream>

#include <util/c_types.h>
#include <util/expr_util.h>
#include <util/format_constant.h>
#include <util/pointer_expr.h>
#include <util/simplify_expr.h>
#include <util/std_expr.h>

#include <sstream>

const exprt printf_formattert::make_type(
const exprt &src, const typet &dest)
{
Expand Down Expand Up @@ -66,6 +67,8 @@ void printf_formattert::process_format(std::ostream &out)
format_constant.min_width=0;
format_constant.zero_padding=false;

std::string length_modifier;

char ch=next();

if(ch=='0') // leading zeros
Expand Down Expand Up @@ -94,6 +97,56 @@ void printf_formattert::process_format(std::ostream &out)
}
}

switch(ch)
{
case 'h':
ch = next();
if(ch == 'h')
{
length_modifier = "hh";
ch = next();
}
else
{
length_modifier = "h";

Check warning on line 111 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L111

Added line #L111 was not covered by tests
}
break;

case 'l':
ch = next();
if(ch == 'l')
{
length_modifier = "ll";
ch = next();
}
else
{
length_modifier = "l";
}
break;

case 'q':
ch = next();
length_modifier = "ll";

Check warning on line 130 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L128-L130

Added lines #L128 - L130 were not covered by tests
break;

case 'L':
case 'j':
case 'z':
case 't':
length_modifier = ch;
ch = next();
break;

case 'Z':
ch = next();
length_modifier = "z";

Check warning on line 143 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L141-L143

Added lines #L141 - L143 were not covered by tests
break;

default:
break;
}

switch(ch)
{
case '%':
Expand All @@ -105,17 +158,23 @@ void printf_formattert::process_format(std::ostream &out)
format_constant.style=format_spect::stylet::SCIENTIFIC;
if(next_operand==operands.end())
break;
out << format_constant(
make_type(*(next_operand++), double_type()));
if(length_modifier == "L")
out << format_constant(make_type(*(next_operand++), long_double_type()));

Check warning on line 162 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L162

Added line #L162 was not covered by tests
else
out << format_constant(make_type(*(next_operand++), double_type()));
break;

case 'a': // TODO: hexadecimal output
case 'A': // TODO: hexadecimal output
case 'f':
case 'F':
format_constant.style=format_spect::stylet::DECIMAL;
if(next_operand==operands.end())
break;
out << format_constant(
make_type(*(next_operand++), double_type()));
if(length_modifier == "L")
out << format_constant(make_type(*(next_operand++), long_double_type()));
else
out << format_constant(make_type(*(next_operand++), double_type()));
break;

case 'g':
Expand All @@ -125,10 +184,14 @@ void printf_formattert::process_format(std::ostream &out)
format_constant.precision=1;
if(next_operand==operands.end())
break;
out << format_constant(
make_type(*(next_operand++), double_type()));
if(length_modifier == "L")
out << format_constant(make_type(*(next_operand++), long_double_type()));

Check warning on line 188 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L188

Added line #L188 was not covered by tests
else
out << format_constant(make_type(*(next_operand++), double_type()));
break;

case 'S':
length_modifier = 'l';

Check warning on line 194 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L193-L194

Added lines #L193 - L194 were not covered by tests
case 's':
{
if(next_operand==operands.end())
Expand Down Expand Up @@ -164,35 +227,87 @@ void printf_formattert::process_format(std::ostream &out)
break;

case 'd':
case 'i':
{
if(next_operand==operands.end())
break;
out << format_constant(
make_type(*(next_operand++), signed_int_type()));
typet conversion_type;
if(length_modifier == "hh")
conversion_type = signed_char_type();
else if(length_modifier == "h")
conversion_type = signed_short_int_type();

Check warning on line 238 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L238

Added line #L238 was not covered by tests
else if(length_modifier == "l")
conversion_type = signed_long_int_type();
else if(length_modifier == "ll")
conversion_type = signed_long_long_int_type();

Check warning on line 242 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L242

Added line #L242 was not covered by tests
else if(length_modifier == "j") // intmax_t
conversion_type = signed_long_long_int_type();

Check warning on line 244 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L244

Added line #L244 was not covered by tests
else if(length_modifier == "z")
conversion_type = signed_size_type();

Check warning on line 246 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L246

Added line #L246 was not covered by tests
else if(length_modifier == "t")
conversion_type = pointer_diff_type();

Check warning on line 248 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L248

Added line #L248 was not covered by tests
else
conversion_type = signed_int_type();
out << format_constant(make_type(*(next_operand++), conversion_type));
}
break;

case 'D':
if(next_operand==operands.end())
break;
out << format_constant(
make_type(*(next_operand++), signed_long_int_type()));
case 'o': // TODO: octal output
case 'x': // TODO: hexadecimal output
case 'X': // TODO: hexadecimal output
case 'u':
{
if(next_operand == operands.end())
break;
typet conversion_type;
if(length_modifier == "hh")
conversion_type = unsigned_char_type();

Check warning on line 264 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L264

Added line #L264 was not covered by tests
else if(length_modifier == "h")
conversion_type = unsigned_short_int_type();

Check warning on line 266 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L266

Added line #L266 was not covered by tests
else if(length_modifier == "l")
conversion_type = unsigned_long_int_type();

Check warning on line 268 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L268

Added line #L268 was not covered by tests
else if(length_modifier == "ll")
conversion_type = unsigned_long_long_int_type();
else if(length_modifier == "j") // intmax_t
conversion_type = unsigned_long_long_int_type();
else if(length_modifier == "z")
conversion_type = size_type();
else if(length_modifier == "t")
conversion_type = pointer_diff_type();

Check warning on line 276 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L271-L276

Added lines #L271 - L276 were not covered by tests
else
conversion_type = unsigned_int_type();

Check warning on line 278 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L278

Added line #L278 was not covered by tests
out << format_constant(make_type(*(next_operand++), conversion_type));
}
break;

case 'u':
if(next_operand==operands.end())
case 'C':
length_modifier = 'l';
case 'c':
if(next_operand == operands.end())

Check warning on line 286 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L283-L286

Added lines #L283 - L286 were not covered by tests
break;
if(length_modifier == "l")
out << format_constant(make_type(*(next_operand++), wchar_t_type()));

Check warning on line 289 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L288-L289

Added lines #L288 - L289 were not covered by tests
else
out << format_constant(
make_type(*(next_operand++), unsigned_char_type()));

Check warning on line 292 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L291-L292

Added lines #L291 - L292 were not covered by tests
break;
out << format_constant(
make_type(*(next_operand++), unsigned_int_type()));
break;

case 'U':
if(next_operand==operands.end())
case 'p':

Check warning on line 295 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L295

Added line #L295 was not covered by tests
// TODO: hexadecimal output
out << format_constant(make_type(*(next_operand++), size_type()));

Check warning on line 297 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L297

Added line #L297 was not covered by tests
break;
out << format_constant(
make_type(*(next_operand++), unsigned_long_int_type()));
break;

default:
out << '%' << ch;
case 'n':
if(next_operand == operands.end())

Check warning on line 301 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L300-L301

Added lines #L300 - L301 were not covered by tests
break;
// printf would store the number of characters written so far in the
// object pointed to by this operand. We do not implement this side-effect
// here, and just skip one operand.
++next_operand;
break;

default:
out << '%' << ch;

Check warning on line 310 in src/goto-programs/printf_formatter.cpp

View check run for this annotation

Codecov / codecov/patch

src/goto-programs/printf_formatter.cpp#L309-L310

Added lines #L309 - L310 were not covered by tests
}
}

Expand Down

0 comments on commit f3e1e32

Please sign in to comment.