push
This commit is contained in:
690
format_print.c
Normal file
690
format_print.c
Normal file
@@ -0,0 +1,690 @@
|
||||
/*
|
||||
* Copyright 2008-2013 Various Authors
|
||||
* Copyright 2004 Timo Hirvonen
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "format_print.h"
|
||||
#include "expr.h"
|
||||
#include "glob.h"
|
||||
#include "utils.h"
|
||||
#include "options.h"
|
||||
#include "uchar.h"
|
||||
#include "xmalloc.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
static int width;
|
||||
static int align_left;
|
||||
static int pad;
|
||||
static bool width_is_exact;
|
||||
|
||||
static GBUF(cond_buffer);
|
||||
static GBUF(l_str);
|
||||
static GBUF(m_str);
|
||||
static GBUF(r_str);
|
||||
static struct fp_len str_len = {0, 0, 0};
|
||||
static int *len = &str_len.llen;
|
||||
static struct gbuf* str = &l_str;
|
||||
|
||||
static void stack_print(char *stack, int stack_len)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
gbuf_grow(str, width ? width : stack_len);
|
||||
char* buf = str->buffer + str->len;
|
||||
|
||||
if (width) {
|
||||
if (align_left) {
|
||||
while (i < width && stack_len)
|
||||
buf[i++] = stack[--stack_len];
|
||||
while (i < width)
|
||||
buf[i++] = pad;
|
||||
} else {
|
||||
int pad_len;
|
||||
|
||||
if (stack_len > width)
|
||||
stack_len = width;
|
||||
pad_len = width - stack_len;
|
||||
while (i < pad_len)
|
||||
buf[i++] = pad;
|
||||
while (i < width)
|
||||
buf[i++] = stack[--stack_len];
|
||||
}
|
||||
} else {
|
||||
while (stack_len)
|
||||
buf[i++] = stack[--stack_len];
|
||||
}
|
||||
gbuf_used(str, i);
|
||||
*len += i;
|
||||
}
|
||||
|
||||
static void print_num(int num)
|
||||
{
|
||||
char stack[20];
|
||||
int i, p;
|
||||
|
||||
if (num < 0) {
|
||||
if (width == 0)
|
||||
width = 1;
|
||||
for (i = 0; i < width; i++)
|
||||
gbuf_add_ch(str, '?');
|
||||
*len += width;
|
||||
return;
|
||||
}
|
||||
p = 0;
|
||||
do {
|
||||
stack[p++] = num % 10 + '0';
|
||||
num /= 10;
|
||||
} while (num);
|
||||
|
||||
stack_print(stack, p);
|
||||
}
|
||||
|
||||
#define DBL_MAX_LEN (20)
|
||||
|
||||
static void print_double(double num)
|
||||
{
|
||||
char stack[DBL_MAX_LEN], b[DBL_MAX_LEN];
|
||||
int i, p = 0;
|
||||
|
||||
i = snprintf(b, DBL_MAX_LEN, "%g", num) - 1;
|
||||
while (i >= 0) {
|
||||
stack[p++] = b[i];
|
||||
i--;
|
||||
}
|
||||
stack_print(stack, p);
|
||||
}
|
||||
|
||||
/* print '{,-}{h:,}mm:ss' */
|
||||
static void print_time(int t)
|
||||
{
|
||||
int h, m, s;
|
||||
char stack[32];
|
||||
int neg = 0;
|
||||
int p = 0;
|
||||
|
||||
if (t < 0) {
|
||||
neg = 1;
|
||||
t *= -1;
|
||||
}
|
||||
h = t / 3600;
|
||||
t = t % 3600;
|
||||
m = t / 60;
|
||||
s = t % 60;
|
||||
|
||||
/* put all chars to stack in reverse order ;) */
|
||||
stack[p++] = s % 10 + '0';
|
||||
stack[p++] = s / 10 + '0';
|
||||
stack[p++] = ':';
|
||||
stack[p++] = m % 10 + '0';
|
||||
if (m / 10 || h || time_show_leading_zero)
|
||||
stack[p++] = m / 10 + '0';
|
||||
if (h) {
|
||||
stack[p++] = ':';
|
||||
do {
|
||||
stack[p++] = h % 10 + '0';
|
||||
h /= 10;
|
||||
} while (h);
|
||||
}
|
||||
if (neg)
|
||||
stack[p++] = '-';
|
||||
|
||||
stack_print(stack, p);
|
||||
}
|
||||
|
||||
static void print_str(const char *src)
|
||||
{
|
||||
int str_width = u_str_width(src);
|
||||
|
||||
if (width && width_is_exact) {
|
||||
*len += width;
|
||||
if (align_left) {
|
||||
gbuf_add_ustr(str, src, &width);
|
||||
gbuf_set(str, ' ', width);
|
||||
} else {
|
||||
int s = 0;
|
||||
int ws_len = width - str_width;
|
||||
if (ws_len < 0) {
|
||||
int skip = -ws_len;
|
||||
int clipped_mark_len = min_u(u_str_width(clipped_text_internal), width);
|
||||
skip += clipped_mark_len;
|
||||
gbuf_add_ustr(str, clipped_text_internal, &clipped_mark_len);
|
||||
s = u_skip_chars(src, &skip, true);
|
||||
/* pad if a wide character caused us to skip too much */
|
||||
ws_len = -skip;
|
||||
|
||||
}
|
||||
gbuf_set(str, ' ', ws_len);
|
||||
gbuf_add_ustr(str, src + s, &width);
|
||||
}
|
||||
} else {
|
||||
if (!width)
|
||||
width = str_width;
|
||||
*len += width;
|
||||
gbuf_add_ustr(str, src, &width);
|
||||
*len -= width;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int strnequal(const char *a, const char *b, size_t b_len)
|
||||
{
|
||||
return a && (strlen(a) == b_len) && (memcmp(a, b, b_len) == 0);
|
||||
}
|
||||
|
||||
static const struct format_option *find_fopt(const struct format_option *fopts, const char *key)
|
||||
{
|
||||
const struct format_option *fo;
|
||||
char ch = strlen(key) == 1 ? *key : 0;
|
||||
for (fo = fopts; fo->type != 0; fo++) {
|
||||
if ((ch != 0 && fo->ch == ch) || strnequal(fo->str, key, strlen(key))) {
|
||||
return fo;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *str_val(const char *key, const struct format_option *fopts, char *buf)
|
||||
{
|
||||
const struct format_option *fo;
|
||||
const struct cmus_opt *opt;
|
||||
const char *val = NULL;
|
||||
|
||||
fo = find_fopt(fopts, key);
|
||||
if (fo && !fo->empty) {
|
||||
if (fo->type == FO_STR)
|
||||
val = fo->fo_str;
|
||||
} else {
|
||||
opt = option_find_silent(key);
|
||||
if (opt) {
|
||||
opt->get(opt->data, buf, OPTION_MAX_SIZE);
|
||||
val = buf;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static int int_val(const char *key, const struct format_option *fopts, char *buf)
|
||||
{
|
||||
const struct format_option *fo;
|
||||
int val = -1;
|
||||
|
||||
fo = find_fopt(fopts, key);
|
||||
if (fo && !fo->empty) {
|
||||
if (fo->type == FO_INT)
|
||||
val = fo->fo_int;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static int format_eval_cond(struct expr* expr, const struct format_option *fopts)
|
||||
{
|
||||
if (!expr)
|
||||
return -1;
|
||||
enum expr_type type = expr->type;
|
||||
const char *key;
|
||||
const struct format_option *fo;
|
||||
const struct cmus_opt *opt;
|
||||
char buf[OPTION_MAX_SIZE];
|
||||
|
||||
if (expr->left) {
|
||||
int left = format_eval_cond(expr->left, fopts);
|
||||
|
||||
if (type == EXPR_AND)
|
||||
return left && format_eval_cond(expr->right, fopts);
|
||||
if (type == EXPR_OR)
|
||||
return left || format_eval_cond(expr->right, fopts);
|
||||
/* EXPR_NOT */
|
||||
return !left;
|
||||
}
|
||||
|
||||
key = expr->key;
|
||||
if (type == EXPR_STR) {
|
||||
const char *val = str_val(key, fopts, buf);
|
||||
int res;
|
||||
|
||||
if (!val)
|
||||
val = "";
|
||||
res = glob_match(&expr->estr.glob_head, val);
|
||||
if (expr->estr.op == SOP_EQ)
|
||||
return res;
|
||||
return !res;
|
||||
} else if (type == EXPR_INT) {
|
||||
int val = int_val(key, fopts, buf);
|
||||
int res = val - expr->eint.val;
|
||||
if (val == -1 || expr->eint.val == -1) {
|
||||
switch (expr->eid.op) {
|
||||
case KOP_EQ:
|
||||
return res == 0;
|
||||
case KOP_NE:
|
||||
return res != 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return expr_op_to_bool(res, expr->eint.op);
|
||||
} else if (type == EXPR_ID) {
|
||||
int a = 0, b = 0;
|
||||
const char *sa, *sb;
|
||||
int res = 0;
|
||||
if ((sa = str_val(key, fopts, buf)) && (sb = str_val(expr->eid.key, fopts, buf))) {
|
||||
res = strcmp(sa, sb);
|
||||
return expr_op_to_bool(res, expr->eid.op);
|
||||
} else {
|
||||
a = int_val(key, fopts, buf);
|
||||
b = int_val(expr->eid.key, fopts, buf);
|
||||
res = a - b;
|
||||
if (a == -1 || b == -1) {
|
||||
switch (expr->eid.op) {
|
||||
case KOP_EQ:
|
||||
return res == 0;
|
||||
case KOP_NE:
|
||||
return res != 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return expr_op_to_bool(res, expr->eid.op);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
if (strcmp(key, "stream") == 0) {
|
||||
fo = find_fopt(fopts, "filename");
|
||||
return fo && is_http_url(fo->fo_str);
|
||||
}
|
||||
fo = find_fopt(fopts, key);
|
||||
if (fo)
|
||||
return !fo->empty;
|
||||
opt = option_find_silent(key);
|
||||
if (opt) {
|
||||
opt->get(opt->data, buf, OPTION_MAX_SIZE);
|
||||
if (strcmp(buf, "false") != 0 && strlen(buf) != 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct expr *format_parse_cond(const char* format, int size)
|
||||
{
|
||||
gbuf_clear(&cond_buffer);
|
||||
gbuf_add_bytes(&cond_buffer, format, size);
|
||||
return expr_parse_i(cond_buffer.buffer, "condition contains control characters", 0);
|
||||
}
|
||||
|
||||
static uchar format_skip_cond_expr(const char *format, int *s)
|
||||
{
|
||||
uchar r = 0;
|
||||
while (format[*s]) {
|
||||
uchar u = u_get_char(format, s);
|
||||
if (u == '}' || u == '?') {
|
||||
return u;
|
||||
}
|
||||
if (u != '%') {
|
||||
continue;
|
||||
}
|
||||
u = u_get_char(format, s);
|
||||
if (u == '%' || u == '?' || u == '!' || u == '=') {
|
||||
continue;
|
||||
}
|
||||
if (u == '-') {
|
||||
u = u_get_char(format, s);
|
||||
}
|
||||
if (u == '.')
|
||||
u = u_get_char(format, s);
|
||||
while (isdigit(u)) {
|
||||
u = u_get_char(format, s);
|
||||
}
|
||||
if (u == '{') {
|
||||
unsigned level = 1;
|
||||
while (level) {
|
||||
u = u_get_char(format, s);
|
||||
if (u == 0)
|
||||
return 0;
|
||||
if (u == '}')
|
||||
--level;
|
||||
if (u != '%')
|
||||
continue;
|
||||
u = u_get_char(format, s);
|
||||
if (u == '%' || u == '?' || u == '!' || u == '=')
|
||||
continue;
|
||||
if (u == '-')
|
||||
u = u_get_char(format, s);
|
||||
if (u == '.')
|
||||
u = u_get_char(format, s);
|
||||
while (isdigit(u))
|
||||
u = u_get_char(format, s);
|
||||
if (u == 0)
|
||||
return 0;
|
||||
if (u == '{')
|
||||
++level;
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int format_read_cond(const char *format, int *s, int *a, int *b, int *end)
|
||||
{
|
||||
uchar t = format_skip_cond_expr(format, s);
|
||||
if (t != '?')
|
||||
return 1;
|
||||
*a = *s - 1;
|
||||
t = format_skip_cond_expr(format, s);
|
||||
if (t == 0)
|
||||
return 1;
|
||||
if (t == '?') {
|
||||
*b = *s - 1;
|
||||
t = format_skip_cond_expr(format, s);
|
||||
if (t != '}')
|
||||
return 1;
|
||||
}
|
||||
*end = *s - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void format_parse(int str_width, const char *format, const struct format_option *fopts, int f_size);
|
||||
|
||||
static void format_parse_if(int str_width, const char *format, const struct format_option *fopts, int *s)
|
||||
{
|
||||
int cond_pos = *s, then_pos = -1, else_pos = -1, end_pos = -1, cond_res = -1;
|
||||
BUG_ON(format_read_cond(format, s, &then_pos, &else_pos, &end_pos) != 0);
|
||||
|
||||
struct expr *cond = format_parse_cond(format + cond_pos, then_pos - cond_pos);
|
||||
cond_res = format_eval_cond(cond, fopts);
|
||||
if (cond)
|
||||
expr_free(cond);
|
||||
|
||||
BUG_ON(cond_res < 0);
|
||||
if (cond_res) {
|
||||
format_parse(str_width, format + then_pos + 1, fopts,
|
||||
(else_pos > 0 ? else_pos : end_pos) - then_pos - 1);
|
||||
} else if (else_pos > 0) {
|
||||
format_parse(str_width, format + else_pos + 1, fopts, end_pos - else_pos - 1);
|
||||
}
|
||||
|
||||
*s = end_pos + 1;
|
||||
}
|
||||
|
||||
static void format_parse(int str_width, const char *format, const struct format_option *fopts, int f_size)
|
||||
{
|
||||
int s = 0;
|
||||
|
||||
while (s < f_size) {
|
||||
const struct format_option *fo;
|
||||
int long_len = 0;
|
||||
const char *long_begin = NULL;
|
||||
uchar u;
|
||||
|
||||
u = u_get_char(format, &s);
|
||||
if (u != '%') {
|
||||
gbuf_add_uchar(str, u);
|
||||
(*len) += u_char_width(u);
|
||||
continue;
|
||||
}
|
||||
u = u_get_char(format, &s);
|
||||
if (u == '%' || u == '?') {
|
||||
gbuf_add_ch(str, u);
|
||||
++(*len);
|
||||
continue;
|
||||
}
|
||||
if (u == '!') {
|
||||
/* middle (priority) text starts */
|
||||
str = &m_str;
|
||||
len = &str_len.mlen;
|
||||
continue;
|
||||
}
|
||||
if (u == '=') {
|
||||
/* right aligned text starts */
|
||||
str = &r_str;
|
||||
len = &str_len.rlen;
|
||||
continue;
|
||||
}
|
||||
align_left = 0;
|
||||
if (u == '-') {
|
||||
align_left = 1;
|
||||
u = u_get_char(format, &s);
|
||||
}
|
||||
width_is_exact = true;
|
||||
if (u == '.') {
|
||||
width_is_exact = false;
|
||||
u = u_get_char(format, &s);
|
||||
}
|
||||
pad = ' ';
|
||||
if (u == '0') {
|
||||
pad = '0';
|
||||
u = u_get_char(format, &s);
|
||||
}
|
||||
width = 0;
|
||||
while (isdigit(u)) {
|
||||
/* minimum length of this field */
|
||||
width *= 10;
|
||||
width += u - '0';
|
||||
u = u_get_char(format, &s);
|
||||
}
|
||||
if (u == '%') {
|
||||
width = (width * str_width) / 100.0 + 0.5;
|
||||
u = u_get_char(format, &s);
|
||||
}
|
||||
if (u == '{') {
|
||||
long_begin = format + s;
|
||||
if (*long_begin == '?') {
|
||||
++s;
|
||||
format_parse_if(str_width, format, fopts, &s);
|
||||
BUG_ON(s > f_size);
|
||||
continue;
|
||||
}
|
||||
while (1) {
|
||||
BUG_ON(s >= f_size);
|
||||
u = u_get_char(format, &s);
|
||||
if (u == '}')
|
||||
break;
|
||||
long_len++;
|
||||
}
|
||||
}
|
||||
for (fo = fopts; ; fo++) {
|
||||
BUG_ON(fo->type == 0);
|
||||
if (long_len ? strnequal(fo->str, long_begin, long_len)
|
||||
: (fo->ch == u)) {
|
||||
|
||||
int type = fo->type;
|
||||
|
||||
if (fo->empty) {
|
||||
gbuf_set(str, ' ', width);
|
||||
*len += width;
|
||||
} else if (type == FO_STR) {
|
||||
print_str(fo->fo_str);
|
||||
} else if (type == FO_INT) {
|
||||
print_num(fo->fo_int);
|
||||
} else if (type == FO_TIME) {
|
||||
print_time(fo->fo_time);
|
||||
} else if (type == FO_DOUBLE) {
|
||||
print_double(fo->fo_double);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void format_read(int str_width, const char *format, const struct format_option *fopts)
|
||||
{
|
||||
gbuf_clear(&l_str);
|
||||
gbuf_clear(&m_str);
|
||||
gbuf_clear(&r_str);
|
||||
str_len.llen = 0;
|
||||
str_len.mlen = 0;
|
||||
str_len.rlen = 0;
|
||||
str = &l_str;
|
||||
len = &str_len.llen;
|
||||
format_parse(str_width, format, fopts, strlen(format));
|
||||
}
|
||||
|
||||
static void format_write(struct gbuf *buf, int str_width)
|
||||
{
|
||||
if (str_width == 0)
|
||||
str_width = str_len.llen + str_len.mlen + str_len.rlen + (str_len.rlen > 0);
|
||||
|
||||
/* NOTE: any invalid UTF-8 bytes have already been converted to <xx>
|
||||
* (ASCII) where x is hex digit
|
||||
*/
|
||||
|
||||
if (str_len.llen + str_len.mlen + str_len.rlen <= str_width) {
|
||||
/* all fit */
|
||||
int ws_len = str_width - (str_len.llen + str_len.mlen + str_len.rlen);
|
||||
|
||||
gbuf_add_bytes(buf, l_str.buffer, l_str.len);
|
||||
gbuf_add_bytes(buf, m_str.buffer, m_str.len);
|
||||
gbuf_set(buf, ' ', ws_len);
|
||||
gbuf_add_bytes(buf, r_str.buffer, r_str.len);
|
||||
} else {
|
||||
/* keep first character since it's almost always padding */
|
||||
int clipped_mark_len = min_u(u_str_width(clipped_text_internal) + 1, str_width);
|
||||
int r_space = str_width - clipped_mark_len;
|
||||
int r_width = min_i(r_space, str_len.rlen);
|
||||
int m_space = r_space - r_width;
|
||||
int m_width = min_i(m_space, str_len.mlen);
|
||||
int l_space = m_space - m_width;
|
||||
int l_width = l_space + clipped_mark_len;
|
||||
int r_idx = 0, ws_pad = 0;
|
||||
|
||||
gbuf_add_ustr(buf, l_str.buffer, &l_width);
|
||||
ws_pad += l_width;
|
||||
gbuf_add_ustr(buf, m_str.buffer, &m_width);
|
||||
ws_pad += m_width;
|
||||
|
||||
int r_skip = str_len.rlen - r_width;
|
||||
r_idx = u_skip_chars(r_str.buffer, &r_skip, true);
|
||||
ws_pad += -r_skip;
|
||||
gbuf_set(buf, ' ', ws_pad);
|
||||
gbuf_add_bytes(buf, r_str.buffer + r_idx, r_str.len - r_idx);
|
||||
}
|
||||
}
|
||||
|
||||
struct fp_len format_print(struct gbuf *buf, int str_width, const char *format, const struct format_option *fopts)
|
||||
{
|
||||
format_read(str_width, format, fopts);
|
||||
|
||||
#if DEBUG > 1
|
||||
if (str_len.llen > 0) {
|
||||
int ul = u_str_width(l_str.buffer);
|
||||
if (ul != str_len.llen)
|
||||
d_print("L %d != %d: size=%zu '%s'\n", ul, str_len.llen, l_str.len, l_str.buffer);
|
||||
}
|
||||
|
||||
if (str_len.rlen > 0) {
|
||||
int ul = u_str_width(r_str.buffer);
|
||||
if (ul != str_len.rlen)
|
||||
d_print("R %d != %d: size=%zu '%s'\n", ul, str_len.rlen, r_str.len, r_str.buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
format_write(buf, str_width);
|
||||
return str_len;
|
||||
}
|
||||
|
||||
static int format_valid_sub(const char *format, const struct format_option *fopts, int f_size);
|
||||
|
||||
static int format_valid_if(const char *format, const struct format_option *fopts, int *s)
|
||||
{
|
||||
int cond_pos = *s, then_pos = -1, else_pos = -1, end_pos = -1;
|
||||
if (format_read_cond(format, s, &then_pos, &else_pos, &end_pos) != 0)
|
||||
return 0;
|
||||
|
||||
struct expr *cond = format_parse_cond(format + cond_pos, then_pos - cond_pos);
|
||||
if (cond == NULL)
|
||||
return 0;
|
||||
expr_free(cond);
|
||||
|
||||
if (!format_valid_sub(format + then_pos + 1, fopts,
|
||||
(else_pos > 0 ? else_pos : end_pos) - then_pos - 1))
|
||||
return 0;
|
||||
if (else_pos > 0)
|
||||
if (!format_valid_sub(format + else_pos + 1, fopts, end_pos - else_pos - 1))
|
||||
return 0;
|
||||
|
||||
*s = end_pos + 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int format_valid_sub(const char *format, const struct format_option *fopts, int f_size)
|
||||
{
|
||||
int s = 0;
|
||||
|
||||
while (s < f_size) {
|
||||
uchar u;
|
||||
|
||||
u = u_get_char(format, &s);
|
||||
if (u == '%') {
|
||||
int pad_zero = 0, long_len = 0;
|
||||
const struct format_option *fo;
|
||||
const char *long_begin = NULL;
|
||||
|
||||
u = u_get_char(format, &s);
|
||||
if (u == '%' || u == '?' || u == '!' || u == '=')
|
||||
continue;
|
||||
if (u == '-')
|
||||
u = u_get_char(format, &s);
|
||||
if (u == '.')
|
||||
u = u_get_char(format, &s);
|
||||
if (u == '0') {
|
||||
pad_zero = 1;
|
||||
u = u_get_char(format, &s);
|
||||
}
|
||||
while (isdigit(u))
|
||||
u = u_get_char(format, &s);
|
||||
if (u == '%')
|
||||
u = u_get_char(format, &s);
|
||||
if (u == '{') {
|
||||
long_begin = format + s;
|
||||
if (*long_begin == '?') {
|
||||
++s;
|
||||
if (!format_valid_if(format, fopts, &s))
|
||||
return 0;
|
||||
if (s > f_size)
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (s >= f_size)
|
||||
return 0;
|
||||
u = u_get_char(format, &s);
|
||||
if (u == '}')
|
||||
break;
|
||||
long_len++;
|
||||
}
|
||||
}
|
||||
for (fo = fopts; fo->type; fo++) {
|
||||
if (long_len ? strnequal(fo->str, long_begin, long_len)
|
||||
: (fo->ch == u)) {
|
||||
if (pad_zero && !fo->pad_zero)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! fo->type)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int format_valid(const char *format, const struct format_option *fopts)
|
||||
{
|
||||
return format_valid_sub(format, fopts, strlen(format));
|
||||
}
|
||||
Reference in New Issue
Block a user