]> mj.ucw.cz Git - libucw.git/blob - ucw/xtypes-extra.c
Build: Added support for custom PKG_CONFIG_PATH in UCW::Configure::PkgConfig().
[libucw.git] / ucw / xtypes-extra.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/fastbuf.h>
9 #include <ucw/strtonum.h>
10 #include <ucw/table.h>
11 #include <time.h>
12 #include <stdio.h>
13 #include <inttypes.h>
14 #include <errno.h>
15
16 #include <ucw/xtypes-extra.h>
17
18 /** xt_size **/
19
20 static const struct unit_definition xt_size_units[] = {
21   [XT_SIZE_UNIT_BYTE] = { "", 1LLU, 1 },
22   [XT_SIZE_UNIT_KILOBYTE] = { "KB", 1024LLU, 1 },
23   [XT_SIZE_UNIT_MEGABYTE] = { "MB", 1024LLU * 1024LLU, 1 },
24   [XT_SIZE_UNIT_GIGABYTE] = { "GB", 1024LLU * 1024LLU * 1024LLU, 1 },
25   [XT_SIZE_UNIT_TERABYTE] = { "TB", 1024LLU * 1024LLU * 1024LLU * 1024LLU, 1 },
26   { 0, 0, 0 }
27 };
28
29 static enum size_units xt_size_auto_units(u64 sz)
30 {
31   if(sz >= xt_size_units[XT_SIZE_UNIT_TERABYTE].num) {
32     return XT_SIZE_UNIT_TERABYTE;
33   } else if(sz >= xt_size_units[XT_SIZE_UNIT_GIGABYTE].num) {
34     return XT_SIZE_UNIT_GIGABYTE;
35   } else if(sz >= xt_size_units[XT_SIZE_UNIT_MEGABYTE].num) {
36     return XT_SIZE_UNIT_MEGABYTE;
37   } else if(sz >= xt_size_units[XT_SIZE_UNIT_KILOBYTE].num) {
38     return XT_SIZE_UNIT_KILOBYTE;
39   }
40
41   return XT_SIZE_UNIT_BYTE;
42 }
43
44 static const char *xt_size_format(void *src, u32 fmt, struct mempool *pool)
45 {
46   u64 curr_val = *(u64*) src;
47   uint out_units;
48
49   if(fmt & XT_SIZE_FMT_FIXED_UNIT) {
50     out_units = fmt & ~XT_SIZE_FMT_FIXED_UNIT;
51   } else {
52     switch(fmt) {
53     case XTYPE_FMT_RAW:
54       return mp_printf(pool, "%"PRIu64, curr_val);
55     case XTYPE_FMT_PRETTY:
56       out_units = XT_SIZE_UNIT_AUTO;
57       break;
58     case XTYPE_FMT_DEFAULT:
59     default:
60       out_units = XT_SIZE_UNIT_BYTE;
61       break;
62     }
63   }
64
65   if(out_units == XT_SIZE_UNIT_AUTO) {
66     out_units = xt_size_auto_units(curr_val);
67   }
68   ASSERT(out_units < ARRAY_SIZE(xt_size_units));
69
70   curr_val = curr_val / xt_size_units[out_units].num;
71   return mp_printf(pool, "%"PRIu64"%s", curr_val, xt_size_units[out_units].unit);
72 }
73
74 static const char *xt_size_fmt_parse(const char *opt_str, u32 *dest, struct mempool *pool)
75 {
76   if(strlen(opt_str) == 0 || strcmp(opt_str, "B") == 0 || strcmp(opt_str, "Bytes") == 0) {
77     *dest = XT_SIZE_FMT_UNIT(XT_SIZE_UNIT_BYTE);
78     return NULL;
79   }
80
81   if(strcmp(opt_str, "auto") == 0) {
82     *dest = XT_SIZE_FMT_UNIT(XT_SIZE_UNIT_AUTO);
83     return NULL;
84   }
85
86   int unit_idx = xtype_unit_parser(opt_str, xt_size_units);
87   if(unit_idx == -1) {
88     return mp_printf(pool, "Unknown option '%s'", opt_str);
89   }
90
91   *dest = XT_SIZE_FMT_UNIT(unit_idx);
92   return NULL;
93 }
94
95 static const char *xt_size_parse(const char *str, void *dest, struct mempool *pool)
96 {
97   errno = 0;
98   const char *units_start = NULL;
99   u64 parsed;
100   const char *err = str_to_u64(&parsed, str, &units_start, 10 | STN_FLAGS);
101   if(err != NULL) {
102     return mp_printf(pool, "Invalid value of size: '%s'; number parser error: %s.", str, err);
103   }
104
105   if(*units_start == 0) {
106     *(u64*) dest = (u64) parsed;
107     return NULL;
108   }
109
110   int unit_idx = xtype_unit_parser(units_start, xt_size_units);
111   if(unit_idx == -1) {
112     return mp_printf(pool, "Invalid units: '%s'.", str);
113   }
114
115   // Overflow detection
116   u64 num = xt_size_units[unit_idx].num;
117   if(parsed && UINT64_MAX / parsed < num) {
118     return mp_printf(pool, "Size too large: '%s'.", str);
119   }
120
121   *(u64*) dest = parsed * xt_size_units[unit_idx].num;
122   return NULL;
123 }
124
125 TABLE_COL_BODY(size, u64)
126
127 const struct xtype xt_size = {
128   .size = sizeof(u64),
129   .name = "size",
130   .parse = xt_size_parse,
131   .format = xt_size_format,
132   .parse_fmt = xt_size_fmt_parse
133 };
134
135 /** xt_timestamp **/
136
137 #define FORMAT_TIME_SIZE 20     // Minimum buffer size
138
139 static const char *xt_timestamp_format(void *src, u32 fmt, struct mempool *pool)
140 {
141   char formatted_time_buf[FORMAT_TIME_SIZE] = { 0 };
142
143   u64 tmp_time_u64 = *(u64*)src;
144   time_t tmp_time = (time_t) tmp_time_u64;
145   struct tm t = *gmtime(&tmp_time);
146   switch (fmt) {
147   case XTYPE_FMT_DEFAULT:
148   case XTYPE_FMT_RAW:
149     sprintf(formatted_time_buf, "%"PRIu64, tmp_time_u64);
150     break;
151   case XTYPE_FMT_PRETTY:
152     strftime(formatted_time_buf, FORMAT_TIME_SIZE, "%F %T", &t);
153     break;
154   default:
155     ASSERT(0);
156     break;
157   }
158
159   return mp_strdup(pool, formatted_time_buf);
160 }
161
162 static const char *xt_timestamp_fmt_parse(const char *opt_str, u32 *dest, struct mempool *pool)
163 {
164   if(strcasecmp(opt_str, "timestamp") == 0 || strcasecmp(opt_str, "epoch") == 0) {
165     *dest = XT_TIMESTAMP_FMT_EPOCH;
166     return NULL;
167   } else if(strcasecmp(opt_str, "datetime") == 0) {
168     *dest = XT_TIMESTAMP_FMT_DATETIME;
169     return NULL;
170   }
171
172   return mp_printf(pool, "Invalid column format option: '%s'.", opt_str);
173 }
174
175 static const char *xt_timestamp_parse(const char *str, void *dest, struct mempool *pool)
176 {
177   errno = 0;
178   const char *parse_end = NULL;
179   u64 parsed;
180   const char *err = str_to_u64(&parsed, str, &parse_end, 10 | STN_FLAGS);
181   if(str == parse_end) {
182     return mp_printf(pool, "Invalid value of timestamp: '%s'; number parser error: %s.", str, err);
183   }
184
185   if(*parse_end == 0) {
186     *(u64*) dest = (u64) parsed;
187     return NULL;
188   }
189
190   struct tm parsed_time;
191   parse_end = strptime(str, "%F %T", &parsed_time);
192   if(parse_end == NULL) {
193     return mp_printf(pool, "Invalid value of timestamp: '%s'.", str);
194   }
195   if(*parse_end != 0) {
196     return mp_printf(pool, "Invalid value of timestamp: '%s'.", str);
197   }
198
199   time_t tmp_time = mktime(&parsed_time);
200   *(u64*)dest = (u64) tmp_time;
201
202   return NULL;
203 }
204
205 TABLE_COL_BODY(timestamp, u64)
206
207 const struct xtype xt_timestamp = {
208   .size = sizeof(u64),
209   .name = "timestamp",
210   .parse = xt_timestamp_parse,
211   .format = xt_timestamp_format,
212   .parse_fmt = xt_timestamp_fmt_parse
213 };