2 * UCW Library -- Table printer types
4 * (c) 2014 Robert Kessl <robert.kessl@economia.cz>
8 #include <ucw/table-types.h>
9 #include <ucw/fastbuf.h>
10 #include <ucw/table.h>
17 // FIXME: I seriously doubt there is any good reason for keeping
18 // these types separated from the generic xtype machinery. There
19 // is nothing special in them, which would be tightly connected
20 // to the table printer. Especially as they are already tested
21 // by xtypes-test.c. --mj
25 static const struct unit_definition xt_size_units[] = {
26 [XT_SIZE_UNIT_BYTE] = { "", 1LLU, 1 },
27 [XT_SIZE_UNIT_KILOBYTE] = { "KB", 1024LLU, 1 },
28 [XT_SIZE_UNIT_MEGABYTE] = { "MB", 1024LLU * 1024LLU, 1 },
29 [XT_SIZE_UNIT_GIGABYTE] = { "GB", 1024LLU * 1024LLU * 1024LLU, 1 },
30 [XT_SIZE_UNIT_TERABYTE] = { "TB", 1024LLU * 1024LLU * 1024LLU * 1024LLU, 1 },
34 static enum size_units xt_size_auto_units(u64 sz)
36 if(sz >= xt_size_units[XT_SIZE_UNIT_TERABYTE].num) {
37 return XT_SIZE_UNIT_TERABYTE;
38 } else if(sz >= xt_size_units[XT_SIZE_UNIT_GIGABYTE].num) {
39 return XT_SIZE_UNIT_GIGABYTE;
40 } else if(sz >= xt_size_units[XT_SIZE_UNIT_MEGABYTE].num) {
41 return XT_SIZE_UNIT_MEGABYTE;
42 } else if(sz >= xt_size_units[XT_SIZE_UNIT_KILOBYTE].num) {
43 return XT_SIZE_UNIT_KILOBYTE;
46 return XT_SIZE_UNIT_BYTE;
49 static const char *xt_size_format(void *src, u32 fmt, struct mempool *pool)
51 u64 curr_val = *(u64*) src;
54 if(fmt & XT_SIZE_FMT_FIXED_UNIT) {
55 out_units = fmt & ~XT_SIZE_FMT_FIXED_UNIT;
59 return mp_printf(pool, "%"PRIu64, curr_val);
60 case XTYPE_FMT_PRETTY:
61 out_units = XT_SIZE_UNIT_AUTO;
63 case XTYPE_FMT_DEFAULT:
65 out_units = XT_SIZE_UNIT_BYTE;
70 if(out_units == XT_SIZE_UNIT_AUTO) {
71 out_units = xt_size_auto_units(curr_val);
73 ASSERT(out_units < ARRAY_SIZE(xt_size_units));
75 curr_val = curr_val / xt_size_units[out_units].num;
76 return mp_printf(pool, "%"PRIu64"%s", curr_val, xt_size_units[out_units].unit);
79 static const char *xt_size_fmt_parse(const char *opt_str, u32 *dest, struct mempool *pool)
81 if(strlen(opt_str) == 0 || strcmp(opt_str, "B") == 0 || strcmp(opt_str, "Bytes") == 0) {
82 *dest = XT_SIZE_FMT_UNIT(XT_SIZE_UNIT_BYTE);
86 if(strcmp(opt_str, "auto") == 0) {
87 *dest = XT_SIZE_FMT_UNIT(XT_SIZE_UNIT_AUTO);
91 int unit_idx = xtype_unit_parser(opt_str, xt_size_units);
93 return mp_printf(pool, "Unknown option '%s'", opt_str);
96 *dest = XT_SIZE_FMT_UNIT(unit_idx);
100 static const char *xt_size_parse(const char *str, void *dest, struct mempool *pool)
103 char *units_start = NULL;
104 // FIXME: Use str_to_u64() here to avoid troubles with strtoul()
105 // FIXME: Besides, who promises that u64 fits in unsigned long int?
106 u64 parsed = strtoul(str, &units_start, 10);
107 if(str == units_start) {
108 return mp_printf(pool, "Invalid value of size: '%s'.", str);
111 if(errno == EINVAL) {
112 return "Error occured during parsing of size.";
114 if(errno == ERANGE) {
115 return "Error: size value either too large or too small.";
118 if(*units_start == 0) {
119 *(u64*) dest = (u64) parsed;
123 int unit_idx = xtype_unit_parser(units_start, xt_size_units);
125 return mp_printf(pool, "Invalid units: '%s'.", units_start);
128 // FIXME: Detect overflow?
129 *(u64*) dest = parsed * xt_size_units[unit_idx].num;
133 TABLE_COL_BODY(size, u64)
135 const struct xtype xt_size = {
138 .parse = xt_size_parse,
139 .format = xt_size_format,
140 .parse_fmt = xt_size_fmt_parse
145 #define FORMAT_TIME_SIZE 20 // Minimum buffer size
147 static const char *xt_timestamp_format(void *src, u32 fmt, struct mempool *pool)
149 char formatted_time_buf[FORMAT_TIME_SIZE] = { 0 };
151 u64 tmp_time_u64 = *(u64*)src;
152 time_t tmp_time = (time_t) tmp_time_u64;
153 struct tm t = *gmtime(&tmp_time);
155 case XTYPE_FMT_DEFAULT:
157 sprintf(formatted_time_buf, "%"PRIu64, tmp_time_u64);
159 case XTYPE_FMT_PRETTY:
160 strftime(formatted_time_buf, FORMAT_TIME_SIZE, "%F %T", &t);
167 return mp_strdup(pool, formatted_time_buf);
170 static const char *xt_timestamp_fmt_parse(const char *opt_str, u32 *dest, struct mempool *pool)
172 if(strcasecmp(opt_str, "timestamp") == 0 || strcasecmp(opt_str, "epoch") == 0) {
173 *dest = XT_TIMESTAMP_FMT_EPOCH;
175 } else if(strcasecmp(opt_str, "datetime") == 0) {
176 *dest = XT_TIMESTAMP_FMT_DATETIME;
180 return mp_printf(pool, "Invalid column format option: '%s'.", opt_str);
183 static const char *xt_timestamp_parse(const char *str, void *dest, struct mempool *pool)
186 char *parse_end = NULL;
187 // FIXME: Again, why strtoul()?
188 u64 parsed = strtoul(str, &parse_end, 10);
189 if(str == parse_end) {
190 return mp_printf(pool, "Invalid value of timestamp: '%s'.", str);
192 if(errno == EINVAL) {
193 return "Error occured during parsing of size.";
196 if(errno == ERANGE) {
197 return "Error: size value either too large or too small.";
200 if(*parse_end == 0) {
201 *(u64*) dest = (u64) parsed;
205 struct tm parsed_time;
206 parse_end = strptime(str, "%F %T", &parsed_time);
207 if(parse_end == NULL) {
208 return mp_printf(pool, "Invalid value of timestamp: '%s'.", str);
210 if(*parse_end != 0) {
211 return mp_printf(pool, "Invalid value of timestamp: '%s'.", str);
214 time_t tmp_time = mktime(&parsed_time);
215 *(u64*)dest = (u64) tmp_time;
220 TABLE_COL_BODY(timestamp, u64)
222 const struct xtype xt_timestamp = {
225 .parse = xt_timestamp_parse,
226 .format = xt_timestamp_format,
227 .parse_fmt = xt_timestamp_fmt_parse