1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | #include "cli.h" |
16 | #include <stdio.h> |
17 | #include <stdlib.h> |
18 | #include <string.h> |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | const char *value_to_string (const s4_val_t *val) |
25 | { |
26 | static char buf[12]; |
27 | const char *ret; |
28 | int32_t i; |
29 | |
30 | if (!s4_val_get_str (val, &ret)) { |
31 | s4_val_get_int (val, &i); |
32 | sprintf (buf, "%i", i); |
33 | ret = buf; |
34 | } |
35 | |
36 | return ret; |
37 | } |
38 | |
39 | void print_list (list_t *l) |
40 | { |
41 | list_data_t *data; |
42 | GList *list = l->list; |
43 | int first = 1; |
44 | const char *sep; |
45 | const char *print_mode = get_var ("print_mode"); |
46 | |
47 | if (strcmp (print_mode, "compact") == 0) { |
48 | sep = ", "; |
49 | } else { |
50 | sep = ",\n"; |
51 | } |
52 | |
53 | printf ("["); |
54 | for (; list != NULL((void*)0); list = g_list_next (list)((list) ? (((GList *)(list))->next) : ((void*)0))) { |
55 | if (first) { |
56 | first = 0; |
57 | } else { |
58 | printf ("%s", sep); |
59 | } |
60 | data = list->data; |
61 | printf ("%s=%s (%s)", data->key, |
62 | value_to_string (data->val), |
63 | data->src); |
64 | } |
65 | printf ("]\n"); |
66 | } |
67 | |
68 | |
69 | static int columns_has_data (int count, const s4_result_t **cols) |
70 | { |
71 | int i; |
72 | for (i = 0; i < count; i++) { |
73 | if (cols[i] != NULL((void*)0)) |
74 | return 1; |
75 | } |
76 | |
77 | return 0; |
78 | } |
79 | |
80 | |
81 | static void print_row (int row, int column_width, int column_count, |
82 | const s4_result_t **columns, const char *print_format) |
83 | { |
84 | int i; |
85 | const s4_result_t *res; |
86 | char col_str[column_width]; |
87 | int compact = strcmp (print_format, "compact") == 0; |
88 | |
89 | printf ("\r|%5i ", row); |
90 | |
91 | do { |
92 | for (i = 0; i < column_count; i++) { |
| 19 | | Assuming 'i' is < 'column_count' | |
|
| 20 | | Loop condition is true. Entering loop body | |
|
93 | res = columns[i]; |
| 21 | | Assigned value is garbage or undefined |
|
94 | if (res != NULL((void*)0)) { |
95 | columns[i] = s4_result_next (res); |
96 | if (compact) { |
97 | snprintf (col_str, column_width + 1, "| %-*s", column_width, |
98 | value_to_string (s4_result_get_val (res))); |
99 | } else { |
100 | snprintf (col_str, column_width + 1, "| %s (%s) %*s", |
101 | value_to_string (s4_result_get_val (res)), |
102 | s4_result_get_src (res), |
103 | column_width, ""); |
104 | } |
105 | printf ("%s", col_str); |
106 | } else { |
107 | printf ("| %*s", column_width - 2, ""); |
108 | } |
109 | } |
110 | printf ("|\n| "); |
111 | } while (columns_has_data (column_count, columns)); |
112 | } |
113 | |
114 | |
115 | |
116 | |
117 | static const char *column_key (int col, const s4_resultset_t *set) |
118 | { |
119 | int row; |
120 | const char *ret = NULL((void*)0); |
121 | const s4_result_t *res; |
122 | |
123 | for (row = 0; row < s4_resultset_get_rowcount (set); row++) { |
124 | res = s4_resultset_get_result (set, row, col); |
125 | while (res != NULL((void*)0)) { |
126 | const char *key = s4_result_get_key (res); |
127 | if (ret != NULL((void*)0) && strcmp (ret, key)) { |
128 | return "_"; |
129 | } else if (ret == NULL((void*)0)) { |
130 | ret = key; |
131 | } |
132 | res = s4_result_next (res); |
133 | } |
134 | } |
135 | |
136 | return ret; |
137 | } |
138 | |
139 | |
140 | int find_column (const char *key, const s4_resultset_t *set) |
141 | { |
142 | int col; |
143 | |
144 | for (col = 0; col < s4_resultset_get_colcount (set); col++) { |
145 | const char *col_key = column_key (col, set); |
146 | if (col_key != NULL((void*)0) && strcmp (col_key, key) == 0) { |
147 | return col; |
148 | } |
149 | } |
150 | |
151 | return -1; |
152 | } |
153 | |
154 | |
155 | |
156 | |
157 | static int terminal_width (void) |
158 | { |
159 | const char *width = getenv ("COLUMNS"); |
160 | int ret; |
161 | |
162 | if (width == NULL((void*)0) || (ret = atoi (width)) == 0) { |
163 | return 80; |
164 | } |
165 | |
166 | return ret; |
167 | } |
168 | |
169 | |
170 | void print_result (const s4_resultset_t *set) |
171 | { |
172 | int col, row, total_width, col_width; |
173 | const s4_result_t **columns, *res; |
174 | const char *print_format = get_var ("print_mode"); |
175 | char *col_str; |
176 | |
177 | if (s4_resultset_get_rowcount (set) == 0) { |
| 8 | | Assuming the condition is false | |
|
| |
178 | printf ("No results\n"); |
179 | return; |
180 | } |
181 | |
182 | columns = malloc (sizeof (s4_result_t*) * s4_resultset_get_colcount (set)); |
183 | total_width = terminal_width () - 8; |
184 | col_width = total_width / s4_resultset_get_colcount (set); |
185 | col_str = malloc (col_width + 1); |
186 | |
187 | if (strcmp (print_format, "pretty") == 0 |
188 | || strcmp (print_format, "compact") == 0) { |
189 | |
190 | printf ("| row "); |
191 | for (col = 0; col < s4_resultset_get_colcount (set); col++) { |
| 10 | | Assuming the condition is false | |
|
| 11 | | Loop condition is false. Execution continues on line 196 | |
|
192 | snprintf (col_str, col_width + 1, "| %-*s", |
193 | col_width - 2, column_key (col, set)); |
194 | printf ("%s", col_str); |
195 | } |
196 | printf ("|\n|------|"); |
197 | for (col = 0; col < s4_resultset_get_colcount (set); col++) { |
| 12 | | Assuming the condition is false | |
|
| 13 | | Loop condition is false. Execution continues on line 203 | |
|
198 | for (row = 1; row < col_width; row++) { |
199 | putchar ('-'); |
200 | } |
201 | putchar ('|'); |
202 | } |
203 | putchar ('\n'); |
204 | |
205 | |
206 | for (row = 0; row < s4_resultset_get_rowcount (set); row++) { |
| 14 | | Assuming the condition is true | |
|
| 15 | | Loop condition is true. Entering loop body | |
|
207 | for (col = 0; col < s4_resultset_get_colcount (set); col++) { |
| 16 | | Assuming the condition is false | |
|
| 17 | | Loop condition is false. Execution continues on line 210 | |
|
208 | columns[col] = s4_resultset_get_result (set, row, col); |
209 | } |
210 | print_row (row, col_width, s4_resultset_get_colcount (set), |
| |
211 | columns, print_format); |
212 | } |
213 | } else { |
214 | |
215 | printf (" row | col | data\n"); |
216 | |
217 | |
218 | for (row = 0; row < s4_resultset_get_rowcount (set); row++) { |
219 | printf ("\r%5i ", row); |
220 | for (col = 0; col < s4_resultset_get_colcount (set); col++) { |
221 | printf ("| %5i ", col); |
222 | for (res = s4_resultset_get_result (set, row, col); |
223 | res != NULL((void*)0); |
224 | res = s4_result_next (res)) { |
225 | printf ("| %s=%s (%s)\n | ", s4_result_get_key (res), |
226 | value_to_string (s4_result_get_val (res)), |
227 | s4_result_get_src (res)); |
228 | } |
229 | printf ("\r "); |
230 | } |
231 | } |
232 | } |
233 | printf (" \r"); |
234 | |
235 | free (col_str); |
236 | free (columns); |
237 | } |
238 | |
239 | void print_cond (s4_condition_t *cond) |
240 | { |
241 | int i; |
242 | const char *operation; |
243 | s4_condition_t *operand; |
244 | |
245 | if (s4_cond_is_filter (cond)) { |
246 | switch (s4_cond_get_filter_type (cond)) { |
247 | case S4_FILTER_EQUAL: operation = "="; break; |
248 | case S4_FILTER_NOTEQUAL: operation = "!="; break; |
249 | case S4_FILTER_SMALLER: operation = "<"; break; |
250 | case S4_FILTER_GREATER: operation = ">"; break; |
251 | case S4_FILTER_SMALLEREQ: operation = "<="; break; |
252 | case S4_FILTER_GREATEREQ: operation = ">="; break; |
253 | case S4_FILTER_MATCH: operation = "~"; break; |
254 | case S4_FILTER_EXISTS: operation = "+"; break; |
255 | case S4_FILTER_TOKEN: operation = "^"; break; |
256 | default: operation = "unknown filter"; break; |
257 | } |
258 | if (s4_cond_get_key (cond) != NULL((void*)0)) { |
259 | printf ("%s %s", s4_cond_get_key (cond), operation); |
260 | } else { |
261 | printf ("%s", operation); |
262 | } |
263 | if (s4_cond_get_filter_type (cond) == S4_FILTER_MATCH) { |
264 | printf (" pattern"); |
265 | } else if (s4_cond_get_filter_type (cond) == S4_FILTER_TOKEN) { |
266 | printf (" %s", (const char *)s4_cond_get_funcdata (cond)); |
267 | } else if (s4_cond_get_filter_type (cond) != S4_FILTER_EXISTS) { |
268 | printf (" %s", value_to_string (s4_cond_get_funcdata (cond))); |
269 | } |
270 | } else { |
271 | switch (s4_cond_get_combiner_type (cond)) { |
272 | case S4_COMBINE_AND: operation = "&"; break; |
273 | case S4_COMBINE_NOT: operation = "!"; break; |
274 | case S4_COMBINE_OR: operation = "|"; break; |
275 | default: operation = "unknown combiner"; break; |
276 | } |
277 | |
278 | if (s4_cond_get_combiner_type (cond) == S4_COMBINE_NOT) { |
279 | printf ("!("); |
280 | } else { |
281 | printf ("("); |
282 | } |
283 | for (i = 0; (operand = s4_cond_get_operand (cond, i)) != NULL((void*)0); i++) { |
284 | if (i != 0) |
285 | printf (") %s (", operation); |
286 | print_cond (operand); |
287 | } |
288 | printf (")"); |
289 | } |
290 | } |
291 | |
292 | void print_fetch (s4_fetchspec_t *fetch) |
293 | { |
294 | int i; |
295 | const char *sep; |
296 | const char *print_mode = get_var ("print_mode"); |
297 | |
298 | if (strcmp (print_mode, "compact") == 0) { |
299 | sep = ", "; |
300 | } else { |
301 | sep = ",\n"; |
302 | } |
303 | printf ("("); |
304 | for (i = 0; i < s4_fetchspec_size (fetch); i++) { |
305 | if (i != 0) { |
306 | printf ("%s", sep); |
307 | } |
308 | printf ("%s", s4_fetchspec_get_key (fetch, i)); |
309 | } |
310 | printf (")\n"); |
311 | } |
312 | |
313 | void print_vars () |
314 | { |
315 | GHashTableIter iter; |
316 | char *str; |
317 | void *val; |
318 | |
319 | g_hash_table_iter_init (&iter, cond_table); |
320 | printf ("Cond table\n"); |
321 | while (g_hash_table_iter_next (&iter, (void**)&str, &val)) { |
| 1 | Loop condition is false. Execution continues on line 327 | |
|
322 | printf ("%s: ", str); |
323 | print_cond (val); |
324 | printf ("\n"); |
325 | } |
326 | |
327 | g_hash_table_iter_init (&iter, fetch_table); |
328 | printf ("Fetch table\n"); |
329 | while (g_hash_table_iter_next (&iter, (void**)&str, &val)) { |
| 2 | | Loop condition is false. Execution continues on line 333 | |
|
330 | printf ("%s: ", str); |
331 | print_fetch (val); |
332 | } |
333 | g_hash_table_iter_init (&iter, res_table); |
334 | printf ("Result table\n"); |
335 | while (g_hash_table_iter_next (&iter, (void**)&str, &val)) { |
| 3 | | Loop condition is true. Entering loop body | |
|
| 4 | | Loop condition is true. Entering loop body | |
|
| 5 | | Loop condition is true. Entering loop body | |
|
| 6 | | Loop condition is true. Entering loop body | |
|
336 | printf ("%s: ", str); |
337 | print_result (val); |
| |
338 | } |
339 | g_hash_table_iter_init (&iter, list_table); |
340 | printf ("List table\n"); |
341 | while (g_hash_table_iter_next (&iter, (void**)&str, &val)) { |
342 | printf ("%s: ", str); |
343 | print_list (val); |
344 | } |
345 | } |
346 | |
347 | void print_help (void) |
348 | { |
349 | printf("All statements must end with a semicolon\n\n" |
350 | "Statements with no value:\n" |
351 | ".add <list>, <list> - For every (key, val) from the first list it adds\n" |
352 | " the attributes (key, val, src) from the second list\n" |
353 | ".del <list>, <list> - For every (key, val) from the first list it deletes\n" |
354 | " the attributes (key, val, src) from the second list\n" |
355 | ".exit - Exit the program\n" |
356 | ".help - Prints this help\n" |
357 | ".set key value - Sets the option key to val\n" |
358 | ".set key - Shows the value of the key\n" |
359 | ".set - Shows the value of all keys\n" |
360 | ".vars - Prints all bound variables\n\n" |
361 | "?var = <cond> - Assigns cond to the condition variable var\n" |
362 | "%%var = <fetch> - Assigns fetch to the fetch variable var\n" |
363 | "@var = <result> - Assigns var to something returning result\n" |
364 | "$var = <list> - Assigns the list to the list variable var\n" |
365 | "#var = <pref> - Assigns the pref to the souce preference variable var\n\n" |
366 | "Conditions (<cond>):\n" |
367 | "?var - Returns the condition bound to var\n" |
368 | "!cond - Matches everything cond does not match\n" |
369 | "cond1 & cond2 - Matches if both cond1 and cond2 matches\n" |
370 | "cond1 | cond2 - Matches if cond1 or cond2 matches\n\n" |
371 | "Filter conditions\n" |
372 | "key = value - Matches all entries where key equals value\n" |
373 | "key ~ value - Matches all entries where key matches value\n" |
374 | "key < value - Matches all entries where key is smaller than value\n" |
375 | "key > value - Matches all entries where key is greater than value\n" |
376 | "key ^ token - Matches all entries where key has a token equal to token\n" |
377 | "key != value - Matches all entries where key does not equal value\n" |
378 | "key <= value - Matches all entries where key is smaller or equal to value\n" |
379 | "key >= value - Matches all entries where key is greater or equal to value\n" |
380 | "= value - Matches all entries where one or more keys equals value\n" |
381 | "~ value - Matches all entries where one or more keys matches value\n" |
382 | "< value - Matches all entries where one or more keys is smaller than value\n" |
383 | "> value - Matches all entries where one or more keys is greater than value\n" |
384 | "^ token - Matches all entries where one or more keys has token\n" |
385 | "!= value - Matches all entries where one or more keys does not equal value\n" |
386 | "<= value - Matches all entries where one or more keys is smaller or equal to value\n" |
387 | ">= value - Matches all entries where one or more keys is greater or equal to value\n" |
388 | "+key - Matches all entries that has key\n" |
389 | "+ - Matches everything\n" |
390 | "<pref> may be added after all filter conditions to use a source preference to only match\n" |
391 | "against the highest priority source in the source preference\n\n" |
392 | "Fetch specification (<fetch>):\n" |
393 | "%%var - Returns the fetch spec bound to var\n" |
394 | "(key1, ..., keyn) - Fetches keys 1 through n from matching entries\n" |
395 | "(key1 <pref>,...) - Fetches key1 using the source preference given\n" |
396 | "key - Fetches key from matching entries\n" |
397 | "key <pref> - Fetches key using the source preference given\n" |
398 | "_ - Fetches everything from matching entries\n\n" |
399 | "Results (<result>):\n" |
400 | ".query <fetch> <cond> - Queries the database, returns a result\n\n" |
401 | "@var - Returns the result bound to var\n\n" |
402 | "Lists (<list>):\n" |
403 | "$var - Returns the list bound to the variable var\n" |
404 | "<result>{<rng>,<rng>} - Creates a list of the columns given by {row,col}.\n" |
405 | "<result>{<rng>, key} - Creates a list of the column where the column key\n" |
406 | " equals key and the rows are in the range\n" |
407 | "[key val src, ...] - Creates a list\n" |
408 | "[key val, ...] - Creates a list where source is set to default_source\n\n" |
409 | "Ranges (<rng>):\n" |
410 | "start - stop - Creates a range from start to stop (inclusive)\n" |
411 | " - stop - Creates a range from 0 to stop\n" |
412 | "start - - Creates a range from start with no stop\n" |
413 | " - - Creates a range from 0 with no stop\n\n" |
414 | "Source preferences (<pref>):\n" |
415 | "#var - Returns the source preference bound to var\n" |
416 | ":src1:src2:...:srcn - Creates a new source preference where src1 has the highest priority,\n" |
417 | " src2 seconds highest and so on\n\n" |
418 | "Shorthand:\n" |
419 | ".q = .query\n" |
420 | ".a = .add\n" |
421 | ".d = .del\n" |
422 | ".v = .vars\n" |
423 | ".h = .help\n" |
424 | ".? = .help\n" |
425 | ".s = .set\n" |
426 | ".e = .exit\n" |
427 | ); |
428 | } |