]> git.tuebingen.mpg.de Git - adu.git/blob - select.c
577ff724bc211eb06ac787fcef82f590dd7d9c43
[adu.git] / select.c
1 /*
2  * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file select.c The select mode of adu. */
8
9 #include <dirent.h> /* readdir() */
10 #include "format.h"
11 #include "adu.h"
12 #include "gcc-compat.h"
13 #include "cmdline.h"
14 #include "fd.h"
15 #include "string.h"
16 #include "error.h"
17 #include "portable_io.h"
18
19 /** The decimal representation of an uint64_t never exceeds that size. */
20 #define FORMATED_VALUE_SIZE 25
21
22 #define GLOBAL_LIST_ATOMS \
23         ATOM(size, SIZE) \
24         ATOM(files, COUNT) \
25         ATOM(dirname, STRING) \
26
27 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
28 struct atom global_list_atoms[] = {
29         GLOBAL_LIST_ATOMS
30         {.name = NULL}
31 };
32 #undef ATOM
33 #define ATOM(x, y) gla_ ## x,
34 enum global_list_atoms {GLOBAL_LIST_ATOMS};
35 #undef ATOM
36
37 #define GLOBAL_SUMMARY_ATOMS \
38         ATOM(dirs, COUNT) \
39         ATOM(files, COUNT) \
40         ATOM(size, SIZE)
41
42 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
43 struct atom global_summary_atoms[] = {
44         GLOBAL_SUMMARY_ATOMS
45         {.name = NULL}
46 };
47 #undef ATOM
48 #define ATOM(x, y) gsa_ ## x,
49 enum global_summary_atoms {GLOBAL_SUMMARY_ATOMS};
50 #undef ATOM
51
52 #define USER_SUMMARY_ATOMS \
53         ATOM(pw_name, STRING) \
54         ATOM(uid, ID) \
55         ATOM(dirs, COUNT) \
56         ATOM(files, COUNT) \
57         ATOM(size, SIZE)
58
59 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
60 struct atom user_summary_atoms[] = {
61         USER_SUMMARY_ATOMS
62         {.name = NULL}
63 };
64 #undef ATOM
65 #define ATOM(x, y) usa_ ## x,
66 enum user_summary_atoms {USER_SUMMARY_ATOMS};
67 #undef ATOM
68
69
70
71 /* these get filled in by the select command. */
72 static char count_unit_buf[4] = "( )", size_unit_buf[4] = "( )";
73
74 struct global_list_info {
75         uint32_t count;
76         int ret;
77         int osl_errno;
78         struct format_info *fi;
79 };
80
81 struct global_summary_info {
82         /** Global dir count. */
83         uint64_t num_dirs;
84         /** Global files count. */
85         uint64_t num_files;
86         /** Global bytes count. */
87         uint64_t num_bytes;
88         int ret;
89         int osl_errno;
90 };
91
92 enum user_stats_flags {
93         USF_PRINT_DIRNAME = 1,
94         USF_PRINT_BYTES = 2,
95         USF_PRINT_FILES = 4,
96         USF_COMPUTE_SUMMARY = 8,
97 };
98
99 struct user_stats_info {
100         uint32_t count;
101         enum user_stats_flags flags;
102         int ret;
103         int osl_errno;
104         struct user_info *ui;
105 };
106
107 static const uint64_t size_unit_divisors[] = {
108         [size_unit_arg_b] = 1ULL,
109         [size_unit_arg_k] = 1024ULL,
110         [size_unit_arg_m] = 1024ULL * 1024ULL,
111         [size_unit_arg_g] = 1024ULL * 1024ULL * 1024ULL,
112         [size_unit_arg_t] = 1024ULL * 1024ULL * 1024ULL * 1024ULL,
113 };
114
115 static const uint64_t count_unit_divisors[] = {
116
117         [count_unit_arg_n] = 1ULL,
118         [count_unit_arg_k] = 1000ULL,
119         [count_unit_arg_m] = 1000ULL * 1000ULL,
120         [count_unit_arg_g] = 1000ULL * 1000ULL * 1000ULL,
121         [count_unit_arg_t] = 1000ULL * 1000ULL * 1000ULL * 1000ULL,
122 };
123
124 static const char size_unit_abbrevs[] = " BKMGT";
125 static const char count_unit_abbrevs[] = "  kmgt";
126 static enum enum_size_unit format_size_value(enum enum_size_unit unit,
127                 uint64_t value, int print_unit, char *result)
128 {
129         enum enum_size_unit u = unit;
130         char unit_buf[2] = "\0\0";
131
132         if (unit == size_unit_arg_h) /* human readable */
133                 for (u = size_unit_arg_b; u < size_unit_arg_t &&
134                                 value > size_unit_divisors[u + 1]; u++)
135                         ; /* nothing */
136         if (print_unit)
137                 unit_buf[0] = size_unit_abbrevs[u];
138         sprintf(result, "%llu%s",
139                 (long long unsigned)value / size_unit_divisors[u], unit_buf);
140         return u;
141 }
142
143 static enum enum_count_unit format_count_value(enum enum_count_unit unit,
144                 uint64_t value, int print_unit, char *result)
145 {
146         enum enum_count_unit u = unit;
147         char unit_buf[2] = "\0\0";
148
149         if (unit == count_unit_arg_h) /* human readable */
150                 for (u = count_unit_arg_n; u < count_unit_arg_t &&
151                                 value > count_unit_divisors[u + 1]; u++)
152                         ; /* nothing */
153         if (print_unit)
154                 unit_buf[0] = count_unit_abbrevs[u];
155         sprintf(result, "%llu%s",
156                 (long long unsigned)value / count_unit_divisors[u], unit_buf);
157         return u;
158 }
159
160 static FILE *output_file;
161
162 __printf_1_2 static int output(const char const *fmt, ...)
163 {
164         va_list argp;
165         int ret;
166
167         va_start(argp, fmt);
168         ret = vfprintf(output_file, fmt, argp);
169         va_end(argp);
170         return ret < 0? -E_OUTPUT : 1;
171 }
172
173 static int get_dir_name_by_number(uint64_t *dirnum, char **name)
174 {
175         char *result = NULL, *tmp;
176         struct osl_row *row;
177         uint64_t val = *dirnum;
178         struct osl_object obj;
179         int ret;
180         char *pfx;
181
182 again:
183         obj.data = &val;
184         obj.size = sizeof(val);
185         ret = osl(osl_get_row(dir_table, DT_NUM, &obj, &row));
186         if (ret < 0)
187                 goto out;
188         ret = osl(osl_get_object(dir_table, row, DT_PARENT_NUM, &obj));
189         if (ret < 0)
190                 goto out;
191         val = *(uint64_t *)obj.data;
192         ret = osl(osl_get_object(dir_table, row, DT_NAME, &obj));
193         if (ret < 0)
194                 goto out;
195         pfx = (select_conf.print_base_dir_given || val)? (char *)obj.data :  ".";
196         tmp = make_message("%s/%s", pfx, result? result : "");
197         free(result);
198         result = tmp;
199         if (val)
200                 goto again;
201 out:
202         if (ret < 0) {
203                 free(result);
204                 *name = NULL;
205         } else {
206                 assert(result);
207                 *name = result;
208         }
209         return ret;
210 }
211
212 static int get_dir_name_of_row(struct osl_row *dir_table_row, char **name)
213 {
214         struct osl_object obj;
215         int ret;
216
217         *name = NULL;
218         ret = osl(osl_get_object(dir_table, dir_table_row, DT_NUM, &obj));
219         if (ret < 0)
220                 return ret;
221         return get_dir_name_by_number((uint64_t *)obj.data, name);
222 }
223
224 static int get_num_files_of_row(struct osl_row *row, uint64_t *num_files)
225 {
226         struct osl_object obj;
227         int ret = osl(osl_get_object(dir_table, row, DT_FILES, &obj));
228         if (ret < 0)
229                 return ret;
230         *num_files = *(uint64_t *)obj.data;
231         return 1;
232 }
233
234 static int get_num_bytes_of_row(struct osl_row *row, uint64_t *num_bytes)
235 {
236         struct osl_object obj;
237         int ret = osl(osl_get_object(dir_table, row, DT_BYTES, &obj));
238         if (ret < 0)
239                 return ret;
240         *num_bytes = *(uint64_t *)obj.data;
241         return 1;
242 }
243
244
245 static int user_stats_loop_function(struct osl_row *row, void *data)
246 {
247         struct user_stats_info *usi = data;
248         struct osl_object obj;
249         int ret, summary = usi->flags & USF_COMPUTE_SUMMARY;
250         char formated_value[FORMATED_VALUE_SIZE];
251
252         check_signals();
253         if (!usi->count && !summary) {
254                 ret = -E_LOOP_COMPLETE;
255                 goto err;
256         }
257         if (summary || (usi->count && (usi->flags & USF_PRINT_FILES))) {
258                 uint64_t files;
259                 ret = osl(osl_get_object(usi->ui->table, row, UT_FILES, &obj));
260                 if (ret < 0)
261                         goto err;
262                 files = *(uint64_t *)obj.data;
263                 if (usi->count && (usi->flags & USF_PRINT_FILES)) {
264                         format_count_value(select_conf.count_unit_arg, files,
265                                 select_conf.count_unit_arg == count_unit_arg_h,
266                                 formated_value);
267                         ret = output("\t%s%s", formated_value,
268                                 (usi->flags & (USF_PRINT_BYTES | USF_PRINT_DIRNAME))?
269                                         "\t" : "\n");
270                         if (ret < 0)
271                                 goto err;
272                 }
273                 if (summary)
274                         usi->ui->files += files;
275         }
276         if (summary || (usi->count && (usi->flags & USF_PRINT_BYTES))) {
277                 uint64_t bytes;
278                 ret = osl(osl_get_object(usi->ui->table, row, UT_BYTES, &obj));
279                 if (ret < 0)
280                         goto err;
281                 bytes = *(uint64_t *)obj.data;
282                 if (usi->count && (usi->flags & USF_PRINT_BYTES)) {
283                         format_size_value(select_conf.size_unit_arg, bytes,
284                                 select_conf.size_unit_arg == size_unit_arg_h,
285                                 formated_value);
286                         ret = output("%s%s%s",
287                                 (usi->flags & USF_PRINT_FILES)? "" : "\t",
288                                 formated_value,
289                                 usi->flags & USF_PRINT_DIRNAME?  "\t" : "\n"
290                         );
291                         if (ret < 0)
292                                 goto err;
293                 }
294                 if (summary) {
295                         usi->ui->bytes += bytes;
296                         usi->ui->dirs++;
297                 }
298
299         }
300         if (usi->count && (usi->flags & USF_PRINT_DIRNAME)) {
301                 char *dirname;
302                 ret = osl(osl_get_object(usi->ui->table, row, UT_DIR_NUM, &obj));
303                 if (ret < 0)
304                         goto err;
305                 ret = get_dir_name_by_number((uint64_t *)obj.data, &dirname);
306                 if (ret < 0)
307                         goto err;
308                 ret = output("%s%s\n",
309                         (usi->flags & (USF_PRINT_BYTES | USF_PRINT_FILES))? "" : "\t",
310                         dirname);
311                 free(dirname);
312                 if (ret < 0)
313                         goto err;
314         }
315         if (usi->count > 0)
316                 usi->count--;
317         return 1;
318 err:
319         usi->ret = ret;
320         usi->osl_errno = (ret == -E_OSL)? osl_errno : 0;
321         return -1;
322 }
323
324 static int check_loop_return(int ret, int loop_ret, int loop_osl_errno)
325 {
326         if (ret >= 0)
327                 return ret;
328         assert(ret == -E_OSL);
329         if (osl_errno != E_OSL_LOOP)
330                 /* error not caused by loop function returning negative. */
331                 return ret;
332         assert(loop_ret < 0);
333         if (loop_ret == -E_LOOP_COMPLETE) /* no error */
334                 return 1;
335         if (loop_ret == -E_OSL) { /* osl error in loop function */
336                 assert(loop_osl_errno);
337                 osl_errno = loop_osl_errno;
338         }
339         return loop_ret;
340 }
341
342 static int adu_loop_reverse(struct osl_table *t, unsigned col_num, void *private_data,
343                 osl_rbtree_loop_func *func, int *loop_ret, int *loop_osl_errno)
344 {
345         int ret = osl(osl_rbtree_loop_reverse(t, col_num, private_data, func));
346         return check_loop_return(ret, *loop_ret, *loop_osl_errno);
347 }
348
349 static int global_summary_loop_function(struct osl_row *row, void *data)
350 {
351         struct global_summary_info *gsi = data;
352         int ret;
353         uint64_t num;
354
355         ret = get_num_files_of_row(row, &num);
356         if (ret < 0)
357                 goto err;
358         gsi->num_files += num;
359
360         ret = get_num_bytes_of_row(row, &num);
361         if (ret < 0)
362                 goto err;
363         gsi->num_bytes += num;
364         gsi->num_dirs++;
365         return 1;
366 err:
367         gsi->ret = ret;
368         gsi->osl_errno = (ret == -E_OSL)? osl_errno : 0;
369         return -1;
370 }
371
372 static int print_global_summary(struct format_info *fi)
373 {
374         int ret;
375         char *buf;
376         struct global_summary_info gsi = {.num_dirs = 0};
377
378         union atom_value values[] = {
379                 [gsa_dirs] = {.num_value = 0ULL},
380                 [gsa_files] = {.num_value =  0ULL},
381                 [gsa_size] = {.num_value =  0ULL}
382         };
383
384         ret = adu_loop_reverse(dir_table, DT_BYTES, &gsi,
385                 global_summary_loop_function, &gsi.ret, &gsi.osl_errno);
386         if (ret < 0)
387                 return ret;
388         values[gsa_dirs].num_value = (long long unsigned)gsi.num_dirs;
389         values[gsa_files].num_value = (long long unsigned)gsi.num_files;
390         values[gsa_size].num_value = (long long unsigned)gsi.num_bytes;
391         if (!select_conf.no_headers_given) {
392                 ret = output("Global summary\n");
393                 if (ret < 0)
394                         return ret;
395         }
396         buf = format_items(fi, values);
397         ret = output("%s", buf);
398         free(buf);
399         return ret;
400 }
401
402 static int print_user_summary_line(struct user_info *ui, __a_unused void *data)
403 {
404         struct format_info *fi = data;
405         union atom_value values[] = {
406                 [usa_pw_name] = {.string_value = ui->pw_name?
407                         ui->pw_name : "?"},
408                 [usa_uid] = {.num_value = (long long unsigned)ui->uid},
409                 [usa_dirs] = {.num_value = (long long unsigned)ui->dirs},
410                 [usa_files] = {.num_value =  (long long unsigned)ui->files},
411                 [usa_size] = {.num_value =  (long long unsigned)ui->bytes}
412         };
413         char *buf = format_items(fi, values);
414         int ret = output("%s", buf);
415
416         free(buf);
417         return ret;
418 }
419
420 static int name_comp(const void *a, const void *b)
421 {
422         char *x = ((struct user_info *)a)->pw_name;
423         char *y = ((struct user_info *)b)->pw_name;
424
425         if (!x)
426                 return 1;
427         if (!y)
428                 return -1;
429         return strcmp(x, y);
430 }
431
432 static int uid_comp(const void *a, const void *b)
433 {
434         return -NUM_COMPARE(((struct user_info *)a)->uid,
435                 ((struct user_info *)b)->uid);
436 }
437
438 static int dir_count_comp(const void *a, const void *b)
439 {
440         return NUM_COMPARE(((struct user_info *)a)->dirs,
441                 ((struct user_info *)b)->dirs);
442 }
443
444 static int file_count_comp(const void *a, const void *b)
445 {
446         return NUM_COMPARE(((struct user_info *)a)->files,
447                 ((struct user_info *)b)->files);
448 }
449
450 static int size_comp(const void *a, const void *b)
451 {
452         return NUM_COMPARE(((struct user_info *)a)->bytes,
453                 ((struct user_info *)b)->bytes);
454 }
455
456 /*
457  * The comparators for sorting the user summary.
458  *
459  * This is an array of pointers to functions taking two constant void *
460  * pointers and returning an int.
461  */
462 static int (*summary_comparators[])(const void *, const void *) = {
463         [user_summary_sort_arg_name] = name_comp,
464         [user_summary_sort_arg_uid] = uid_comp,
465         [user_summary_sort_arg_dir_count] = dir_count_comp,
466         [user_summary_sort_arg_file_count] = file_count_comp,
467         [user_summary_sort_arg_size] = size_comp,
468 };
469
470 static int print_user_summary(struct format_info *fi)
471 {
472         if (!select_conf.no_headers_given) {
473                 int ret = output("User summary\n");
474                 if (ret < 0)
475                         return ret;
476         }
477         sort_hash_table(summary_comparators[select_conf.user_summary_sort_arg]);
478         return for_each_admissible_user(print_user_summary_line, fi);
479 }
480
481 static int print_user_list(struct user_info *ui, __a_unused void *data)
482 {
483         int ret;
484         struct user_stats_info usi;
485         enum enum_user_list ula = select_conf.user_list_arg;
486         int print_size_list = (ula == user_list_arg_size
487                 || ula == user_list_arg_both);
488
489         if (print_size_list) {
490                 usi.count = select_conf.limit_arg;
491                 usi.ui = ui;
492                 usi.flags = USF_PRINT_DIRNAME | USF_PRINT_BYTES | USF_COMPUTE_SUMMARY;
493                 if (!select_conf.no_headers_given) {
494                         ret = output("%s (uid %u), by size%s:\n",
495                                 ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid,
496                                 size_unit_buf);
497                         if (ret < 0)
498                                 return ret;
499                 }
500                 ret = adu_loop_reverse(ui->table, UT_BYTES, &usi, user_stats_loop_function,
501                         &usi.ret, &usi.osl_errno);
502                 if (ret < 0)
503                         return ret;
504                 ret = output("\n");
505                 if (ret < 0)
506                         return ret;
507         }
508         if (ula == user_list_arg_file_count || ula == user_list_arg_both) {
509                 if (!select_conf.no_headers_given) {
510                         ret = output("%s (uid %u), by file count%s:\n",
511                                 ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid,
512                                 count_unit_buf);
513                         if (ret < 0)
514                                 return ret;
515                 }
516                 usi.count = select_conf.limit_arg,
517                 usi.ui = ui;
518                 usi.flags = USF_PRINT_DIRNAME | USF_PRINT_FILES;
519                 ret = adu_loop_reverse(ui->table, UT_FILES, &usi, user_stats_loop_function,
520                         &usi.ret, &usi.osl_errno);
521                 if (ret < 0)
522                         return ret;
523                 ret = output("\n");
524                 if (ret < 0)
525                         return ret;
526         }
527         if (ula == user_list_arg_none && !select_conf.no_user_summary_given) {
528                 usi.count = select_conf.limit_arg;
529                 usi.ui = ui;
530                 usi.flags = USF_COMPUTE_SUMMARY;
531                 ret = adu_loop_reverse(ui->table, UT_FILES, &usi, user_stats_loop_function,
532                         &usi.ret, &usi.osl_errno);
533                 if (ret < 0)
534                         return ret;
535         }
536         return 1;
537 }
538
539 static int print_user_lists(void)
540 {
541         return for_each_admissible_user(print_user_list, NULL);
542 }
543
544 static int global_list_loop_function(struct osl_row *row, void *data)
545 {
546         struct global_list_info *gli = data;
547         union atom_value values[] = {
548                 [gla_size] = {.num_value = 0ULL},
549                 [gla_files] = {.num_value =  0ULL},
550                 [gla_dirname] = {.string_value = NULL}
551         };
552         uint64_t num_files, num_bytes;
553         char *dirname, *buf;
554         int ret;
555
556         check_signals();
557         ret = -E_LOOP_COMPLETE;
558         if (!gli->count)
559                 goto err;
560
561         ret = get_num_files_of_row(row, &num_files);
562         if (ret < 0)
563                 goto err;
564         values[gla_files].num_value = (long long unsigned)num_files;
565
566         ret = get_num_bytes_of_row(row, &num_bytes);
567         if (ret < 0)
568                 goto err;
569         values[gla_size].num_value = (long long unsigned)num_bytes;
570
571         ret = get_dir_name_of_row(row, &dirname);
572         if (ret < 0)
573                 goto err;
574         values[gla_dirname].string_value = dirname;
575
576         buf = format_items(gli->fi, values);
577         free(dirname);
578         ret = output("%s", buf);
579         free(buf);
580         if (gli->count > 0)
581                 gli->count--;
582         return ret;
583 err:
584         gli->ret = ret;
585         gli->osl_errno = (ret == -E_OSL)? osl_errno : 0;
586         return -1;
587 }
588
589 static int print_global_list(struct format_info *fi)
590 {
591         int ret;
592         enum dir_table_columns sort_column = DT_BYTES;
593         struct global_list_info gli = {
594                 .fi = fi,
595                 .count = select_conf.limit_arg
596         };
597
598         if (!select_conf.no_headers_given) {
599                 ret = output("Global list\n");
600                 if (ret < 0)
601                         return ret;
602         }
603         if (select_conf.sort_arg == sort_arg_files)
604                 sort_column = DT_FILES;
605         return adu_loop_reverse(dir_table, sort_column, &gli,
606                 global_list_loop_function, &gli.ret, &gli.osl_errno);
607 }
608
609 static int print_statistics(struct select_format_info *sli)
610 {
611         int ret;
612
613         switch (select_conf.select_mode_arg) {
614                 case select_mode_arg_global_list:
615                         ret = print_global_list(sli->global_list_fi);
616                         free_format_info(sli->global_list_fi);
617                         return ret;
618                 case select_mode_arg_global_summary:
619                         ret = print_global_summary(sli->global_summary_fi);
620                         free_format_info(sli->global_summary_fi);
621                         return ret;
622                 case select_mode_arg_user_list:
623                         ret = print_user_lists();
624                         return ret;
625                 case select_mode_arg_user_summary:
626                         ret = print_user_summary(sli->user_summary_fi);
627                         free_format_info(sli->user_summary_fi);
628                         return ret;
629         };
630         ERROR_LOG("bad select mode\n");
631         return ERRNO_TO_ERROR(-EINVAL);
632 }
633
634 static int read_uid_file(struct uid_range *admissible_uids)
635 {
636         size_t size;
637         uint32_t n;
638         char *filename = get_uid_list_name(), *map;
639         int ret = mmap_full_file(filename, O_RDONLY, (void **)&map, &size, NULL);
640         unsigned bits;
641
642         if (ret < 0) {
643                 INFO_LOG("failed to map %s\n", filename);
644                 free(filename);
645                 return ret;
646         }
647         num_uids = size / 4;
648         INFO_LOG("found %u uids in %s\n", (unsigned)num_uids, filename);
649         free(filename);
650         /*
651          * Compute number of hash table bits. The hash table size must be a
652          * power of two and larger than the number of uids.
653          */
654         bits = 2;
655         while (1 << bits < num_uids)
656                 bits++;
657         create_hash_table(bits);
658         for (n = 0; n < num_uids; n++) {
659                 uint32_t uid = read_u32(map + n * sizeof(uid));
660                 ret = search_uid(uid, admissible_uids, OPEN_USER_TABLE, NULL);
661                 if (ret < 0)
662                         goto out;
663         }
664 out:
665         adu_munmap(map, size);
666         return ret;
667 }
668
669 int run_select_query(struct uid_range *admissible_uids,
670                 struct select_format_info *sfi)
671 {
672         int ret;
673
674         if (select_conf.output_given && strcmp(select_conf.output_arg, "-")) {
675                 output_file = fopen(select_conf.output_arg, "w");
676                 if (!output_file)
677                         return -ERRNO_TO_ERROR(errno);
678         } else
679                 output_file = stdout;
680
681         if (select_conf.count_unit_arg != count_unit_arg_h)
682                 count_unit_buf[1] = count_unit_abbrevs[select_conf.count_unit_arg];
683         else
684                 count_unit_buf[0] = '\0';
685         if (select_conf.size_unit_arg != size_unit_arg_h)
686                 size_unit_buf[1] = size_unit_abbrevs[select_conf.size_unit_arg];
687         else
688                 size_unit_buf[0] = '\0';
689
690         ret = open_dir_table(0);
691         if (ret < 0)
692                 goto out;
693         check_signals();
694         ret = read_uid_file(admissible_uids);
695         if (ret < 0)
696                 goto out;
697         check_signals();
698         ret = print_statistics(sfi);
699 out:
700         close_all_tables();
701         if (output_file != stdout)
702                 fclose(output_file);
703         return ret;
704 }
705
706 /* return: < 0: error, >0: OK, == 0: help given */
707 int parse_select_options(char *string, struct select_cmdline_parser_params *params,
708                 struct uid_range **admissible_uids, struct select_format_info *sfi)
709 {
710         int ret;
711         const char **line;
712
713         if (conf.select_options_given) {
714                 int argc;
715                 char **argv;
716
717                 ret = create_argv(string, &argv);
718                 if (ret < 0)
719                         return ret;
720                 argc = ret;
721                 ret = select_cmdline_parser_ext(argc, argv, &select_conf, params);
722                 free_argv(argv);
723                 if (ret)
724                         return -E_SYNTAX;
725                 if (select_conf.help_given || select_conf.detailed_help_given)
726                         goto help;
727
728         }
729         ret = parse_uid_arg(select_conf.uid_arg, admissible_uids);
730         if (ret < 0)
731                 return ret;
732         ret = parse_format_string(select_conf.user_summary_format_arg,
733                 user_summary_atoms, &sfi->user_summary_fi);
734         if (ret < 0)
735                 return ret;
736         ret = parse_format_string(select_conf.global_summary_format_arg,
737                 global_summary_atoms, &sfi->global_summary_fi);
738         if (ret < 0)
739                 goto global_summary_err;
740         ret = parse_format_string(select_conf.global_list_format_arg,
741                 global_list_atoms, &sfi->global_list_fi);
742         if (ret < 0)
743                 goto global_list_err;
744         return 1;
745 global_list_err:
746         free_format_info(sfi->global_summary_fi);
747 global_summary_err:
748         free_format_info(sfi->user_summary_fi);
749         return ret;
750 help:
751         line = select_conf.detailed_help_given?
752                 select_args_info_detailed_help : select_args_info_help;
753         if (!output_file)
754                 output_file = stdout;
755         for (; *line; line++) {
756                 ret = output("%s\n", *line);
757                 if (ret < 0)
758                         return ret;
759         }
760         return 0;
761 }
762
763 int com_select(void)
764 {
765         struct uid_range *admissible_uids = NULL;
766         struct select_format_info sfi;
767         int ret;
768         struct select_cmdline_parser_params params = {
769                 .override = 1,
770                 .initialize = 1,
771                 .check_required = 1,
772                 .check_ambiguity = 1,
773                 .print_errors = 1
774         };
775
776         select_cmdline_parser_init(&select_conf);
777         ret = parse_select_options(conf.select_options_arg, &params,
778                 &admissible_uids, &sfi);
779         if (ret <= 0) /* do not run query if help was given */
780                 return ret;
781         return run_select_query(admissible_uids, &sfi);
782 }