Skip to content

Commit

Permalink
Add printf() formatting function
Browse files Browse the repository at this point in the history
It accepts arbitrary number of arguments, the first argument is
the format string just like in C. The rest are data that must
be the same type as described in the format string.

Fixed printing datetime in new style formats with brackets.

Adapted the unit test ocrpt_expr_format_test to exercise printf()
and show the datetime fix.

Signed-off-by: Zoltán Böszörményi <[email protected]>
  • Loading branch information
zboszor committed May 8, 2022
1 parent 5d1357b commit b180a97
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 139 deletions.
194 changes: 106 additions & 88 deletions libsrc/formatting.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,12 @@ static void ocrpt_formatstring_money_length_prec(const char *fmt, int32_t *lengt
if (fmt[i] != '}') \
FORMAT_ERROR;

#define COPY_FORMAT(start, end) \
#define COPY_FORMAT(start, end, extra) \
do { \
ocrpt_mem_string_append_len(str, fmt + start, end - start); \
ocrpt_mem_string_append_c(str, '\0'); \
*out_type = type; \
*advance = end - start + 4; \
*advance = end - start + extra; \
} while (0)

#define SWITCH_TO_LITERAL(x) \
Expand Down Expand Up @@ -449,7 +449,7 @@ static ocrpt_string *ocrpt_get_next_format_string(opencreport *o, const char *fm
type = OCRPT_FORMAT_DATETIME;
if (fmt[adv + 2] == '{') {
BRACKETED_FORMAT;
COPY_FORMAT(adv + 3, i);
COPY_FORMAT(adv + 3, i - 6, 4);
return str;
} else
RETURN_END_LITERAL(type, 2);
Expand Down Expand Up @@ -522,7 +522,7 @@ static ocrpt_string *ocrpt_get_next_format_string(opencreport *o, const char *fm
type = OCRPT_FORMAT_NUMBER;
if (fmt[adv + 2] == '{') {
BRACKETED_FORMAT;
COPY_FORMAT(adv + 3, i);
COPY_FORMAT(adv + 3, i, 4);

ocrpt_string *str1;
enum ocrpt_formatstring_type type1;
Expand Down Expand Up @@ -576,7 +576,7 @@ static ocrpt_string *ocrpt_get_next_format_string(opencreport *o, const char *fm
type = OCRPT_FORMAT_STRING;
if (fmt[adv + 2] == '{') {
BRACKETED_FORMAT;
COPY_FORMAT(adv + 3, i);
COPY_FORMAT(adv + 3, i, 4);

ocrpt_string *str1;
enum ocrpt_formatstring_type type1;
Expand Down Expand Up @@ -697,31 +697,14 @@ void ocrpt_utf8backward(const char *s, int l, int *l2, int blen, int *blen2) {
*blen2 = i;
}

void ocrpt_format_string(opencreport *o, ocrpt_expr *e, const char *formatstring, int32_t formatlen, ocrpt_result *data) {
enum ocrpt_formatstring_type types[2] = { OCRPT_FORMAT_NONE, OCRPT_FORMAT_LITERAL };
int32_t advance, type_idx;
void ocrpt_format_string(opencreport *o, ocrpt_expr *e, const char *formatstring, int32_t formatlen, ocrpt_expr **expr, int32_t n_expr) {
int32_t i, advance;
locale_t locale;

/* Use the specified locale, so that thousand separators, etc. work. */
locale = uselocale(o->locale);

switch (data->type) {
case OCRPT_RESULT_STRING:
types[0] = OCRPT_FORMAT_STRING;
break;
case OCRPT_RESULT_NUMBER:
types[0] = OCRPT_FORMAT_NUMBER;
break;
case OCRPT_RESULT_DATETIME:
types[0] = OCRPT_FORMAT_DATETIME;
break;
case OCRPT_RESULT_ERROR:
ocrpt_expr_make_error_result(o, e, data->string->str);
return;
}

size_t len = (data->type == OCRPT_RESULT_STRING ? data->string->len : 16);
ocrpt_string *string = ocrpt_mem_string_resize(e->result[o->residx]->string, len);
ocrpt_string *string = ocrpt_mem_string_resize(e->result[o->residx]->string, 16);
if (string) {
if (!e->result[o->residx]->string) {
e->result[o->residx]->string = string;
Expand All @@ -731,85 +714,120 @@ void ocrpt_format_string(opencreport *o, ocrpt_expr *e, const char *formatstring
}

advance = 0;
type_idx = 0;
while (advance < formatlen && formatstring[advance]) {
ocrpt_string *tmp;
char *result;
enum ocrpt_formatstring_type type;
int32_t adv, length;
ssize_t len;
bool error, lpadded;

length = -1;
lpadded = 0;
tmp = ocrpt_get_next_format_string(o, formatstring + advance, types[type_idx], &type, &adv, &error, &length, &lpadded);

if (error) {
ocrpt_expr_make_error_result(o, e, tmp->str);
ocrpt_mem_string_free(tmp, true);
for (i = 0; i < n_expr; i++) {
ocrpt_result *data = expr[i]->result[o->residx];
enum ocrpt_formatstring_type types[2] = { OCRPT_FORMAT_NONE, OCRPT_FORMAT_LITERAL };
int32_t type_idx;
bool data_handled = false;

switch (data->type) {
case OCRPT_RESULT_STRING:
types[0] = OCRPT_FORMAT_STRING;
break;
case OCRPT_RESULT_NUMBER:
types[0] = OCRPT_FORMAT_NUMBER;
break;
case OCRPT_RESULT_DATETIME:
types[0] = OCRPT_FORMAT_DATETIME;
break;
case OCRPT_RESULT_ERROR:
ocrpt_expr_make_error_result(o, e, data->string->str);
return;
}

if (types[type_idx] == type || (types[type_idx] == OCRPT_FORMAT_NUMBER && type == OCRPT_FORMAT_MONEY))
type_idx++;

switch (type) {
case OCRPT_FORMAT_LITERAL:
ocrpt_mem_string_append(string, tmp->str);
break;
case OCRPT_FORMAT_NUMBER:
if (mpfr_asprintf(&result, tmp->str, data->number) >= 0) {
ocrpt_mem_string_append(string, result);
mpfr_free_str(result);
type_idx = 0;
while (advance < formatlen && formatstring[advance]) {
ocrpt_string *tmp;
char *result;
enum ocrpt_formatstring_type type;
int32_t adv, length;
ssize_t len;
bool error, lpadded;

length = -1;
lpadded = 0;
tmp = ocrpt_get_next_format_string(o, formatstring + advance, types[type_idx], &type, &adv, &error, &length, &lpadded);

if (error) {
ocrpt_expr_make_error_result(o, e, tmp->str);
ocrpt_mem_string_free(tmp, true);
return;
}
break;
case OCRPT_FORMAT_MONEY:
len = ocrpt_mpfr_strfmon(o, NULL, 0, tmp->str, data->number);
if (len >= 0) {
result = ocrpt_mem_malloc(len + 1);
ocrpt_mpfr_strfmon(o, result, len, tmp->str, data->number);
result[len] = 0;
ocrpt_mem_string_append(string, result);
ocrpt_mem_free(result);

if (types[type_idx] == type || (types[type_idx] == OCRPT_FORMAT_NUMBER && type == OCRPT_FORMAT_MONEY)) {
type_idx++;
data_handled = true;
}
break;
case OCRPT_FORMAT_DATETIME:
char dt[256];

strftime_l(dt, sizeof(dt), tmp->str, &data->datetime, o->locale);
ocrpt_mem_string_append(string, dt);
break;
case OCRPT_FORMAT_STRING:
int32_t blen = data->string->len;
switch (type) {
case OCRPT_FORMAT_LITERAL:
if (!data_handled || i == (n_expr - 1))
ocrpt_mem_string_append(string, tmp->str);
else
goto end_inner_loop;
break;
case OCRPT_FORMAT_NUMBER:
if (mpfr_asprintf(&result, tmp->str, data->number) >= 0) {
ocrpt_mem_string_append(string, result);
mpfr_free_str(result);
}
data_handled = true;
break;
case OCRPT_FORMAT_MONEY:
len = ocrpt_mpfr_strfmon(o, NULL, 0, tmp->str, data->number);
if (len >= 0) {
result = ocrpt_mem_malloc(len + 1);
ocrpt_mpfr_strfmon(o, result, len, tmp->str, data->number);
result[len] = 0;
ocrpt_mem_string_append(string, result);
ocrpt_mem_free(result);
}
data_handled = true;
break;
case OCRPT_FORMAT_DATETIME:
char dt[256];

strftime_l(dt, sizeof(dt), tmp->str, &data->datetime, o->locale);
ocrpt_mem_string_append(string, dt);
data_handled = true;
break;
case OCRPT_FORMAT_STRING:
int32_t blen = data->string->len;

if (length > 0) {
int32_t slen;
ocrpt_utf8forward(data->string->str, length, &slen, data->string->len, &blen);
if (length > 0) {
int32_t slen;
ocrpt_utf8forward(data->string->str, length, &slen, data->string->len, &blen);

if (lpadded) {
char *padstr;
int32_t padlen;
if (lpadded) {
char *padstr;
int32_t padlen;

padlen = length - slen;
padstr = ocrpt_mem_malloc(padlen + 1);
memset(padstr, ' ', padlen);
padstr[padlen] = 0;
padlen = length - slen;
padstr = ocrpt_mem_malloc(padlen + 1);
memset(padstr, ' ', padlen);
padstr[padlen] = 0;

ocrpt_mem_string_append(string, padstr);
ocrpt_mem_string_append(string, padstr);

ocrpt_mem_free(padstr);
ocrpt_mem_free(padstr);
}
}

ocrpt_mem_string_append_len(string, data->string->str, blen);
data_handled = true;
break;
case OCRPT_FORMAT_NONE:
break;
}

ocrpt_mem_string_append_len(string, data->string->str, blen);
break;
case OCRPT_FORMAT_NONE:
break;
}
end_inner_loop:
ocrpt_mem_string_free(tmp, true);

advance += adv;
advance += adv;

ocrpt_mem_string_free(tmp, true);
if (data_handled && i != (n_expr - 1))
break;
}
}

uselocale(locale);
Expand Down
2 changes: 1 addition & 1 deletion libsrc/formatting.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ struct ocrpt_format_string_element_t {
void ocrpt_utf8forward(const char *s, int l, int *l2, int blen, int *blen2);
void ocrpt_utf8backward(const char *s, int l, int *l2, int blen, int *blen2);

void ocrpt_format_string(opencreport *o, ocrpt_expr *e, const char *formatstring, int32_t formatlen, ocrpt_result *data);
void ocrpt_format_string(opencreport *o, ocrpt_expr *e, const char *formatstring, int32_t formatlen, ocrpt_expr **data, int32_t n_expr);

#endif
37 changes: 35 additions & 2 deletions libsrc/functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -3645,7 +3645,7 @@ OCRPT_STATIC_FUNCTION(ocrpt_format) {
} else
formatlen = strlen(formatstring);

ocrpt_format_string(o, e, formatstring, formatlen, e->ops[0]->result[o->residx]);
ocrpt_format_string(o, e, formatstring, formatlen, e->ops, 1);
}

OCRPT_STATIC_FUNCTION(ocrpt_dtosf) {
Expand Down Expand Up @@ -3711,7 +3711,39 @@ OCRPT_STATIC_FUNCTION(ocrpt_dtosf) {
} else
formatlen = strlen(formatstring);

ocrpt_format_string(o, e, formatstring, formatlen, e->ops[0]->result[o->residx]);
ocrpt_format_string(o, e, formatstring, formatlen, e->ops, 1);
}

OCRPT_STATIC_FUNCTION(ocrpt_printf) {
int32_t i;

if (e->n_ops < 2) {
ocrpt_expr_make_error_result(o, e, "invalid operand(s)");
return;
}

for (i = 0; i < e->n_ops; i++) {
if (!e->ops[i]->result[o->residx]) {
ocrpt_expr_make_error_result(o, e, "invalid operand(s)");
return;
}
}

for (i = 0; i < e->n_ops; i++) {
if (e->ops[i]->result[o->residx]->type == OCRPT_RESULT_ERROR) {
ocrpt_expr_make_error_result(o, e, e->ops[i]->result[o->residx]->string->str);
return;
}
}

if (e->ops[0]->result[o->residx]->type != OCRPT_RESULT_STRING) {
ocrpt_expr_make_error_result(o, e, "invalid operand(s)");
return;
}

ocrpt_expr_init_result(o, e, OCRPT_RESULT_STRING);

ocrpt_format_string(o, e, e->ops[0]->result[o->residx]->string->str, e->ops[0]->result[o->residx]->string->len, &e->ops[1], e->n_ops - 1);
}

/*
Expand Down Expand Up @@ -3782,6 +3814,7 @@ static const ocrpt_function ocrpt_functions[] = {
{ "nulls", ocrpt_nulls, 0, false, false, false, false },
{ "or", ocrpt_or, -1, true, true, false, false },
{ "pow", ocrpt_pow, 2, false, false, false, false },
{ "printf", ocrpt_printf, -1, false, false, false, false },
{ "proper", ocrpt_proper, 1, false, false, false, false },
{ "random", ocrpt_random, 0, false, false, false, true },
{ "remainder", ocrpt_remainder, 2, false, false, false, false },
Expand Down
Loading

0 comments on commit b180a97

Please sign in to comment.