]> mj.ucw.cz Git - libucw.git/blob - ucw/table-types.c
Merge remote-tracking branch 'origin/dev-table' into dev-table
[libucw.git] / ucw / table-types.c
1 /*
2  *      UCW Library -- Table printer types
3  *
4  *      (c) 2014 Robert Kessl <robert.kessl@economia.cz>
5  */
6
7 #include <ucw/lib.h>
8 #include <ucw/table-types.h>
9 #include <ucw/fastbuf.h>
10 #include <ucw/table.h>
11 #include <time.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <inttypes.h>
15 #include <errno.h>
16
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
22
23 /** xt_size **/
24
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 },
31   { 0, 0, 0 }
32 };
33
34 static enum size_units xt_size_auto_units(u64 sz)
35 {
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;
44   }
45
46   return XT_SIZE_UNIT_BYTE;
47 }
48
49 static const char *xt_size_format(void *src, u32 fmt, struct mempool *pool)
50 {
51   u64 curr_val = *(u64*) src;
52   uint out_units;
53
54   if(fmt & XT_SIZE_FMT_FIXED_UNIT) {
55     out_units = fmt & ~XT_SIZE_FMT_FIXED_UNIT;
56   } else {
57     switch(fmt) {
58     case XTYPE_FMT_RAW:
59       return mp_printf(pool, "%"PRIu64, curr_val);
60     case XTYPE_FMT_PRETTY:
61       out_units = XT_SIZE_UNIT_AUTO;
62       break;
63     case XTYPE_FMT_DEFAULT:
64     default:
65       out_units = XT_SIZE_UNIT_BYTE;
66       break;
67     }
68   }
69
70   if(out_units == XT_SIZE_UNIT_AUTO) {
71     out_units = xt_size_auto_units(curr_val);
72   }
73   ASSERT(out_units < ARRAY_SIZE(xt_size_units));
74
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);
77 }
78
79 static const char *xt_size_fmt_parse(const char *opt_str, u32 *dest, struct mempool *pool)
80 {
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);
83     return NULL;
84   }
85
86   if(strcmp(opt_str, "auto") == 0) {
87     *dest = XT_SIZE_FMT_UNIT(XT_SIZE_UNIT_AUTO);
88     return NULL;
89   }
90
91   int unit_idx = xtype_unit_parser(opt_str, xt_size_units);
92   if(unit_idx == -1) {
93     return mp_printf(pool, "Unknown option '%s'", opt_str);
94   }
95
96   *dest = XT_SIZE_FMT_UNIT(unit_idx);
97   return NULL;
98 }
99
100 static const char *xt_size_parse(const char *str, void *dest, struct mempool *pool)
101 {
102   errno = 0;
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);
109   }
110
111   if(errno == EINVAL) {
112     return "Error occured during parsing of size.";
113   }
114   if(errno == ERANGE) {
115     return "Error: size value either too large or too small.";
116   }
117
118   if(*units_start == 0) {
119     *(u64*) dest = (u64) parsed;
120     return NULL;
121   }
122
123   int unit_idx = xtype_unit_parser(units_start, xt_size_units);
124   if(unit_idx == -1) {
125     return mp_printf(pool, "Invalid units: '%s'.", units_start);
126   }
127
128   // FIXME: Detect overflow?
129   *(u64*) dest = parsed * xt_size_units[unit_idx].num;
130   return NULL;
131 }
132
133 TABLE_COL_BODY(size, u64)
134
135 const struct xtype xt_size = {
136   .size = sizeof(u64),
137   .name = "size",
138   .parse = xt_size_parse,
139   .format = xt_size_format,
140   .parse_fmt = xt_size_fmt_parse
141 };
142
143 /** xt_timestamp **/
144
145 #define FORMAT_TIME_SIZE 20     // Minimum buffer size
146
147 static const char *xt_timestamp_format(void *src, u32 fmt, struct mempool *pool)
148 {
149   char formatted_time_buf[FORMAT_TIME_SIZE] = { 0 };
150
151   u64 tmp_time_u64 = *(u64*)src;
152   time_t tmp_time = (time_t) tmp_time_u64;
153   struct tm t = *gmtime(&tmp_time);
154   switch (fmt) {
155   case XTYPE_FMT_DEFAULT:
156   case XTYPE_FMT_RAW:
157     sprintf(formatted_time_buf, "%"PRIu64, tmp_time_u64);
158     break;
159   case XTYPE_FMT_PRETTY:
160     strftime(formatted_time_buf, FORMAT_TIME_SIZE, "%F %T", &t);
161     break;
162   default:
163     ASSERT(0);
164     break;
165   }
166
167   return mp_strdup(pool, formatted_time_buf);
168 }
169
170 static const char *xt_timestamp_fmt_parse(const char *opt_str, u32 *dest, struct mempool *pool)
171 {
172   if(strcasecmp(opt_str, "timestamp") == 0 || strcasecmp(opt_str, "epoch") == 0) {
173     *dest = XT_TIMESTAMP_FMT_EPOCH;
174     return NULL;
175   } else if(strcasecmp(opt_str, "datetime") == 0) {
176     *dest = XT_TIMESTAMP_FMT_DATETIME;
177     return NULL;
178   }
179
180   return mp_printf(pool, "Invalid column format option: '%s'.", opt_str);
181 }
182
183 static const char *xt_timestamp_parse(const char *str, void *dest, struct mempool *pool)
184 {
185   errno = 0;
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);
191   }
192   if(errno == EINVAL) {
193     return "Error occured during parsing of size.";
194   }
195
196   if(errno == ERANGE) {
197     return "Error: size value either too large or too small.";
198   }
199
200   if(*parse_end == 0) {
201     *(u64*) dest = (u64) parsed;
202     return NULL;
203   }
204
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);
209   }
210   if(*parse_end != 0) {
211     return mp_printf(pool, "Invalid value of timestamp: '%s'.", str);
212   }
213
214   time_t tmp_time = mktime(&parsed_time);
215   *(u64*)dest = (u64) tmp_time;
216
217   return NULL;
218 }
219
220 TABLE_COL_BODY(timestamp, u64)
221
222 const struct xtype xt_timestamp = {
223   .size = sizeof(u64),
224   .name = "timestamp",
225   .parse = xt_timestamp_parse,
226   .format = xt_timestamp_format,
227   .parse_fmt = xt_timestamp_fmt_parse
228 };