]> git.tuebingen.mpg.de Git - adu.git/commitdiff
Merge commit 'fml/master'
authorAndre Noll <maan@systemlinux.org>
Tue, 1 Jul 2008 19:26:26 +0000 (21:26 +0200)
committerAndre Noll <maan@systemlinux.org>
Tue, 1 Jul 2008 19:26:26 +0000 (21:26 +0200)
Conflicts:

Makefile

12 files changed:
.gitignore
Makefile
adu.c
adu.ggo
adu.h
create.c
error.h
interactive.c [new file with mode: 0644]
select.c
select.ggo [new file with mode: 0644]
string.c
string.h

index 743f60ec00a543fe9082427154de469ee195781a..617d85d4041fab3b8e6d0b0dede60e11abbf4d16 100644 (file)
@@ -1,7 +1,7 @@
 *.[oa]
 foo*
 bar*
-cmdline.[ch]
+*cmdline.[ch]
 GPATH
 GRTAGS
 GSYMS
index 5e425da274858fd60a950d40bf24b40b08ce62fb..b595c682d81c3846a3e4f383ead5a8db9a36be03 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,12 @@
-objects := adu.o string.o cmdline.o fd.o select.o create.o
+objects := adu.o string.o cmdline.o fd.o select.o create.o interactive.o select.cmdline.o
 all: adu
+version := 0.0.3
 
 DEBUG_CPPFLAGS += -Wno-sign-compare -g -Wunused -Wundef -W
 DEBUG_CPPFLAGS += -Wredundant-decls
-#CPPFLAGS += -Os
+CPPFLAGS += -Os
 CPPFLAGS += -Wall
-#CPPFLAGS += -Wuninitialized
+CPPFLAGS += -Wuninitialized
 CPPFLAGS += -Wchar-subscripts
 CPPFLAGS += -Wformat-security
 CPPFLAGS += -Werror-implicit-function-declaration
@@ -14,6 +15,7 @@ CPPFLAGS += -Wunused-macros
 CPPFLAGS += -Wbad-function-cast
 CPPFLAGS += -D_LARGEFILE64_SOURCE
 CPPFLAGS += $(shell getconf LFS64_CFLAGS)
+CPPFLAGS += -DVERSION='"$(version)"'
 
 LDFLAGS += -D_LARGEFILE64_SOURCE
 LDFLAGS += $(shell getconf LFS64_LDFLAGS)
@@ -38,5 +40,18 @@ cmdline.c cmdline.h: adu.ggo
 %.o: %.c Makefile
        $(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) $<
 
+select.cmdline.c select.cmdline.h: select.ggo
+       gengetopt --string-parser \
+               --set-package=select \
+               --no-handle-help \
+               --no-handle-error \
+               --no-handle-version \
+               --arg-struct-name=select_args_info \
+               --file-name=$(subst .ggo,,$<).cmdline \
+               --func-name $(subst .ggo,,$<)_cmdline_parser < $<
+
+select.cmdline.o: select.cmdline.c select.cmdline.h
+       $(CC) -c $(CPPFLAGS) $<
+
 clean:
        rm -f *.o adu
diff --git a/adu.c b/adu.c
index db7a56438b992d3e47e6b691b6496f0334bd6254..02bfa302779ccdf5bc299e8a6a81f5b3c2fb8f60 100644 (file)
--- a/adu.c
+++ b/adu.c
@@ -8,6 +8,7 @@
 #include "string.h"
 #include "error.h"
 #include "portable_io.h"
+#include "select.cmdline.h"
 
 DEFINE_ERRLIST;
 int osl_errno;
@@ -18,6 +19,9 @@ static int signum;
 /** Command line and config file options. */
 struct gengetopt_args_info conf;
 
+/** Options passed to --select-options. */
+struct select_args_info select_conf;
+
 /** The number of different uids found so far. */
 uint32_t num_uids = 0;
 
@@ -48,12 +52,6 @@ static inline int ui_admissible(struct user_info *ui)
  */
 struct osl_table *dir_table = NULL;
 
-/**
- * The array of all uid ranges that were given at the command line.
- */
-struct uid_range *admissible_uids;
-
-
 /**
  * Compare the size of two directories
  *
@@ -166,61 +164,6 @@ static struct osl_column_description user_table_cols[] = {
        },
 };
 
-static int check_uid_arg(const char *arg, uint32_t *uid)
-{
-       const uint32_t max = ~0U;
-       /*
-        * we need an 64-bit int for string -> uid conversion because strtoll()
-        * returns a signed value.
-        */
-       int64_t val;
-       int ret = atoi64(arg, &val);
-
-       if (ret < 0)
-               return ret;
-       if (val < 0 || val > max)
-               return -ERRNO_TO_ERROR(EINVAL);
-       *uid = val;
-       return 1;
-}
-
-static int parse_uid_range(const char *orig_arg, struct uid_range *ur)
-{
-       int ret;
-       char *arg = adu_strdup(orig_arg), *p = strchr(arg, '-');
-
-       if (!p || p == arg) { /* -42 or 42 */
-               ret = check_uid_arg(p? p + 1 : arg, &ur->high);
-               if (ret < 0)
-                       goto out;
-               ur->low = p? 0 : ur->high;
-               ret = 1;
-               goto out;
-       }
-       /* 42- or 42-4711 */
-       *p = '\0';
-       p++;
-       ret = check_uid_arg(arg, &ur->low);
-       if (ret < 0)
-               goto out;
-       ur->high = ~0U;
-       if (*p) { /* 42-4711 */
-               ret = check_uid_arg(p, &ur->high);
-               if (ret < 0)
-                       goto out;
-       }
-       if (ur->low > ur->high)
-               ret = -ERRNO_TO_ERROR(EINVAL);
-out:
-       if (ret < 0)
-               ERROR_LOG("bad uid option: %s\n", orig_arg);
-       else
-               INFO_LOG("admissible uid range: %u - %u\n", ur->low,
-                       ur->high);
-       free(arg);
-       return ret;
-}
-
 /**
  * The log function.
  *
@@ -418,24 +361,25 @@ static uint32_t double_hash(uint32_t uid, uint32_t probe_num)
                % uid_hash_table_size;
 }
 
-static int uid_is_admissible(uint32_t uid)
+static int uid_is_admissible(uint32_t uid, struct uid_range *urs)
 {
-       int i;
-
-       for (i = 0; i < conf.uid_given; i++) {
-               struct uid_range *ur = admissible_uids + i;
+       struct uid_range *ur;
+       int ret = 1;
 
+       if (!urs) /* empty array means all uids are allowed */
+               return 1;
+       FOR_EACH_UID_RANGE(ur, urs)
                if (ur->low <= uid && ur->high >= uid)
-                       break;
-       }
-       i = !conf.uid_given || i < conf.uid_given;
+                       goto out;
+       ret = 0;
+out:
        DEBUG_LOG("uid %u is %sadmissible\n", (unsigned)uid,
-               i? "" : "not ");
-       return i;
+               ret? "" : "not ");
+       return ret;
 }
 
-int search_uid(uint32_t uid, enum search_uid_flags flags,
-               struct user_info **ui_ptr)
+int search_uid(uint32_t uid, struct uid_range *urs,
+               enum search_uid_flags flags, struct user_info **ui_ptr)
 {
        uint32_t p;
 
@@ -448,7 +392,7 @@ int search_uid(uint32_t uid, enum search_uid_flags flags,
                                return -E_BAD_UID;
                        ui->uid = uid;
                        ui->flags |= UI_FL_SLOT_USED;
-                       if (!uid_is_admissible(uid))
+                       if (!uid_is_admissible(uid, urs))
                                return 0;
                        ui->flags |= UI_FL_ADMISSIBLE;
                        ret = open_user_table(ui, flags & CREATE_USER_TABLE);
@@ -495,9 +439,6 @@ int open_dir_table(int create)
 
 static int check_args(void)
 {
-       int i, ret;
-
-
        if (conf.create_given && !conf.base_dir_given)
                return -E_SYNTAX;
 
@@ -514,19 +455,7 @@ static int check_args(void)
                        conf.base_dir_arg[len] = '\0';
                }
        }
-       if (!conf.uid_given)
-               return 0;
-       admissible_uids = adu_malloc(conf.uid_given * sizeof(*admissible_uids));
-       for (i = 0; i < conf.uid_given; i++) {
-               ret = parse_uid_range(conf.uid_arg[i], admissible_uids + i);
-               if (ret < 0)
-                       goto err;
-       }
        return 1;
-err:
-       free(admissible_uids);
-       admissible_uids = NULL;
-       return ret;
 }
 
 int main(int argc, char **argv)
@@ -550,12 +479,13 @@ int main(int argc, char **argv)
        ret = -E_SYNTAX;
        if (conf.select_given)
                ret = com_select();
-       else
+       else if (conf.create_given)
                ret = com_create();
+       else
+               ret = com_interactive();
        if (ret < 0)
                goto out;
 out:
-       free(admissible_uids);
        if (ret < 0) {
                ERROR_LOG("%s\n", adu_strerror(-ret));
                return -EXIT_FAILURE;
diff --git a/adu.ggo b/adu.ggo
index a46de259348a5adddbf91f451902a1b0d1e0b9f3..11a5d009d09a4bc4b50199569f9543fed63f52da 100644 (file)
--- a/adu.ggo
+++ b/adu.ggo
@@ -3,7 +3,6 @@
 # Licensed under the GPL v2. For licencing details see COPYING.
 
 package "adu"
-version "0.0.2"
 purpose "advanced disk usage
 
 adu creates a database containing disk usage statistics of a given
@@ -50,25 +49,6 @@ details="
        goes to stdout. Lower values mean more verbose logging.
 "
 
-option "uid" u
-#~~~~~~~~~~~~~
-"user id(s) to take into account"
-string typestr="uid_spec"
-optional
-multiple
-details="
-       An uid specifier may be a single number, or a range of uids.
-       Example:
-
-       --uid 42    # only consider uid 42
-       --uid 42-   # only consider uids greater or equal than 42
-       --uid 23-42 # only consider uids between 23 and 42, inclusively.
-
-       This option may be given multiple times. An uid is taken into
-       account if it satisfies at least one --uid option.
-"
-
-
 option "paths" p
 #~~~~~~~~~~~~~~~
 "files to take into account"
@@ -107,6 +87,14 @@ details="
        that directory.
 "
 
+groupoption "interactive" I
+#~~~~~~~~~~~~~~~~~~~~~~~~~~
+"activate interactive mode"
+group="mode"
+details="
+       In this mode, adu reads commands from stdin.
+"
+
 groupoption "select" S
 #~~~~~~~~~~~~~~~~~~~~~
 "query a database previously created with --create"
@@ -158,123 +146,12 @@ details="
        users. Decreasing the value causes adu to use slightly less memory.
 "
 
-
-##############################
-section "Options for --select"
-##############################
-
-option "limit" L
-#~~~~~~~~~~~~~~~
-"Limit output"
-int  typestr="num"
-default="-1"
-optional
-dependon="select"
-details="
-       Only print num lines of output. If negative (the default),
-       print all lines.
-"
-
-option "size-unit" -
-#~~~~~~~~~~~~~~~~~~~
-"select output format for sizes"
-enum typestr="format"
-values="h","b","k","m","g","t"
-default="h"
-optional
-dependon="select"
-details="
-       Print sizes in the given unit: human-readable, bytes,
-       kilobytes (2^10), megabytes (2^20), gigabytes (2^30), terabytes
-       (2^40). The default is \"h\", i.e. human-readable.
-"
-
-option "count-unit" -
-#~~~~~~~~~~~~~~~~~~~~
-"select output format for counted values"
-enum typestr="format"
-values="h","n","k","m","g","t"
-default="h"
-optional
-dependon="select"
-details="
-       Print the number of files/directories in the given unit:
-       human-readable, number, number/10^3, number/10^6, number/10^12,
-       number/10^15. The default is to print numbers in human-readable
-       format.
-"
-
-option "print-base-dir" -
-#~~~~~~~~~~~~~~~~~~~~~~~~
-"whether to include the base-dir in the output"
-flag off
-details="
-       If this flag is given, all directories printed are prefixed
-       with the base directory. The default is to print paths relative
-       to the base dir.
-"
-
-option "no-headers" -
-#~~~~~~~~~~~~~~~~~~~~
-"supress descriptions for listings/tables"
-flag off
-dependon="select"
-details="
-       This is mostly useful to feed the output of adu to scripts.
-"
-
-option "global-list" -
-#~~~~~~~~~~~~~~~~~~~~~
-"how to print global directory listings"
-enum typestr="which"
-values="size","file_count","both","none"
-default="both"
-optional
-dependon="select"
-details="
-       By default adu prints two global directory listings: The
-       first prints the directory names ordered by the sum of the
-       sizes of the contained files while the second listing prints
-       them sorted by the number of files. This option can be used
-       to print only one or neither of these two listings.
-"
-
-option "no-global-summary" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not print the summary line"
-flag off
-dependon="select"
-
-option "user-list" -
-#~~~~~~~~~~~~~~~~~~~
-"how to print per-user directory listings"
-enum typestr="which"
-values="size","file_count","both","none"
-default="both"
-optional
-dependon="select"
-details="
-       Similar to the global directory listings mentioned above,
-       adu can print two directory listings per user. This option
-       controls which of the these should be printed.
-"
-
-option "no-user-summary" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not print the user summary table"
-flag off
-dependon="select"
-
-
-option "user-summary-sort" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"how to sort the user-summary"
-enum typestr="col_spec"
-values="name","uid","dir_count","file_count","size"
-default="size"
+option "select-options" s
+#~~~~~~~~~~~~~~~~~~~~~~~~~
+"Options for select mode"
+string typestr="<options>"
 optional
 dependon="select"
 details="
-       It is enough to specify the first letter of the column specifier,
-       e.g. \"--user-summary-sort f\" sorts by file count.
+       Try --select-options \"-h\"
 "
diff --git a/adu.h b/adu.h
index debada6f1c46ec3d743372ebd8ff637470263dd8..2a15457b4b335bda8d28f219c8f100acfc6c5740 100644 (file)
--- a/adu.h
+++ b/adu.h
@@ -173,9 +173,12 @@ enum search_uid_flags {
        CREATE_USER_TABLE = 2,
 };
 
+#define FOR_EACH_UID_RANGE(ur, urs) for (ur = urs; ur->low <= ur->high; ur++)
+
 extern uint32_t num_uids;
 extern struct osl_table *dir_table;
 extern struct gengetopt_args_info conf;
+extern struct select_args_info select_conf;
 
 /* adu.c */
 __printf_2_3 void __log(int, const char*, ...);
@@ -184,14 +187,16 @@ void check_signals(void);
 void close_all_tables(void);
 char *get_uid_list_name(void);
 void create_hash_table(unsigned bits);
-int search_uid(uint32_t uid, enum search_uid_flags flags,
-               struct user_info **ui_ptr);
+int search_uid(uint32_t uid, struct uid_range *urs,
+               enum search_uid_flags flags, struct user_info **ui_ptr);
 int for_each_admissible_user(int (*func)(struct user_info *, void *),
                void *data);
 void sort_hash_table(int (*comp)(const void *, const void *));
 
 /* select.c */
+int run_select_query(struct uid_range *admissible_uids);
 int com_select(void);
 
 /* create.h */
 int com_create(void);
+int com_interactive(void);
index 47c715568d5e41e0f051eaf9530f920dcc167753..9b26f72fa95f99285a1b6ff65d691fa7c1db7eb5 100644 (file)
--- a/create.c
+++ b/create.c
@@ -30,8 +30,6 @@ static int write_uid(struct user_info *ui, void *data)
 static int write_uid_list(void)
 {
        char *buf, *p, *filename;
-       uint32_t count = 0;
-       struct user_info *ui;
        size_t size = num_uids * sizeof(uint32_t);
        int ret;
 
@@ -163,7 +161,7 @@ static int scan_dir(char *dirname, uint64_t *parent_dir_num)
                dir_size += size;
                dir_files++;
                uid = s.st_uid;
-               ret = search_uid(uid, CREATE_USER_TABLE | OPEN_USER_TABLE, &ui);
+               ret = search_uid(uid, NULL, CREATE_USER_TABLE | OPEN_USER_TABLE, &ui);
                if (ret < 0)
                        goto out;
                ui->bytes += size;
@@ -183,7 +181,7 @@ out:
        return ret;
 }
 
-int com_create()
+int com_create(void)
 {
        uint64_t zero = 0ULL;
        int ret;
diff --git a/error.h b/error.h
index 7c3f4aada972651ec60c9a862b609b12b842a7c6..895f9f89bdc68d0a1242afc237fe930fc3db1786 100644 (file)
--- a/error.h
+++ b/error.h
@@ -25,6 +25,7 @@
        _ERROR(MMAP, "mmap error") \
        _ERROR(OSL, "osl error") \
        _ERROR(SIGNAL_SIG_ERR, "signal() returned SIG_ERR") \
+       _ERROR(OUTPUT, "error writing output file") \
 
 
 /**
diff --git a/interactive.c b/interactive.c
new file mode 100644 (file)
index 0000000..494a4f8
--- /dev/null
@@ -0,0 +1,130 @@
+#include "adu.h"
+#include "string.h"
+#include "error.h"
+#include "cmdline.h"
+#include "select.cmdline.h"
+
+struct interactive_command {
+       const char *name;
+       int (*handler)(char *);
+       const char *desc;
+};
+
+static struct uid_range *admissible_uids;
+
+#define INTERACTIVE_COMMANDS \
+       INTERACTIVE_COMMAND(dump, "dump the current configuration") \
+       INTERACTIVE_COMMAND(set, "change the current configuration") \
+       INTERACTIVE_COMMAND(reset, "reset configuration to defaults") \
+       INTERACTIVE_COMMAND(help, "show list of commands and one-line descriptions") \
+       INTERACTIVE_COMMAND(run, "start the query according to the current options")
+
+
+#define INTERACTIVE_COMMAND(name, desc) \
+       static int icom_ ## name (char *line);
+
+INTERACTIVE_COMMANDS
+
+#undef INTERACTIVE_COMMAND
+
+#define INTERACTIVE_COMMAND(_name, _desc) \
+       { \
+       .name = #_name, \
+       .handler = icom_ ## _name, \
+       .desc = _desc \
+       },
+
+struct interactive_command icmds[] = {
+       INTERACTIVE_COMMANDS
+       {.name  = NULL}
+};
+
+#define FOR_EACH_COMMAND(c) for (c = icmds; c->name; c++)
+
+static int read_input_line(char *line, size_t size)
+{
+       return fgets(line, size, stdin)? 1 : -1;
+}
+
+static int icom_run(__a_unused char *line)
+{
+       return run_select_query(admissible_uids);
+}
+
+static int icom_help(__a_unused char *line)
+{
+       struct interactive_command *c;
+
+       FOR_EACH_COMMAND(c)
+               fprintf(stdout, "%s\t%s\n", c->name, c->desc);
+       return 1;
+}
+
+static int icom_reset(__a_unused char *line)
+{
+       select_cmdline_parser_init(&select_conf);
+       return 1;
+}
+
+static int icom_set(char *line)
+{
+       struct select_cmdline_parser_params params = {
+               .override = 1,
+               .initialize = 0,
+               .check_required = 1,
+               .check_ambiguity = 0,
+               .print_errors = 1
+       };
+       if (select_cmdline_parser_string_ext(line, &select_conf, "select",
+               &params))
+               return -E_SYNTAX;
+       return parse_uid_arg(select_conf.uid_arg, &admissible_uids);
+}
+
+static int icom_dump(__a_unused char *line)
+{
+       ERROR_LOG("dump: %s\n", select_conf.format_arg);
+       select_cmdline_parser_dump(stdout, &select_conf);
+       return 1;
+}
+
+static int exec_interactive_command(char *line)
+{
+       const char const *delim = "\t\n ";
+       int i;
+       char *cmd = adu_strdup(line + strspn(line, delim));
+       char *p = cmd + strcspn(cmd, delim);
+       int ret = -E_SYNTAX;
+
+       *p = '\0';
+       p++;
+       for (i = 0; icmds[i].name; i++) {
+               ERROR_LOG("name: %s, cmd: %s.\n", icmds[i].name, cmd);
+               if (strcmp(icmds[i].name, cmd))
+                       continue;
+               ERROR_LOG("exec cmd: %s, args: %s\n", cmd, p);
+               ret = icmds[i].handler(p);
+               break;
+       }
+       free(cmd);
+       return ret;
+}
+
+int com_interactive(void)
+{
+       char line[255];
+       int ret = 1;
+
+       select_cmdline_parser_init(&select_conf);
+       while (read_input_line(line, sizeof(line)) >= 0) {
+               size_t len = strlen(line);
+               if (!len)
+                       continue;
+               if (line[len - 1] == '\n')
+                       line[len - 1] = '\0';
+               ret = exec_interactive_command(line);
+               if (ret < 0)
+                       printf("%s\n", adu_strerror(-ret));
+       }
+       return ret;
+}
index 4c2f12cbc7ab7e6f7f69bfbd7809daf866f4959a..05302570ff5ba740731c3ba815c62b5f8da2e1fc 100644 (file)
--- a/select.c
+++ b/select.c
@@ -14,6 +14,7 @@
 #include "string.h"
 #include "error.h"
 #include "portable_io.h"
+#include "select.cmdline.h"
 
 /** Global dir count. */
 static uint64_t num_dirs;
@@ -76,7 +77,6 @@ static const uint64_t count_unit_divisors[] = {
 
 static const char size_unit_abbrevs[] = " BKMGT";
 static const char count_unit_abbrevs[] = "  kmgt";
-
 static enum enum_size_unit format_size_value(enum enum_size_unit unit,
                uint64_t value, int print_unit, char *result)
 {
@@ -111,6 +111,19 @@ static enum enum_count_unit format_count_value(enum enum_count_unit unit,
        return u;
 }
 
+static FILE *output_file;
+
+__printf_1_2 static int output(const char const *fmt, ...)
+{
+       va_list argp;
+       int ret;
+
+       va_start(argp, fmt);
+       ret = vfprintf(output_file, fmt, argp);
+       va_end(argp);
+       return ret < 0? -E_OUTPUT : 1;
+}
+
 static int get_dir_name_by_number(uint64_t *dirnum, char **name)
 {
        char *result = NULL, *tmp;
@@ -133,7 +146,7 @@ again:
        ret = osl(osl_get_object(dir_table, row, DT_NAME, &obj));
        if (ret < 0)
                goto out;
-       pfx = (conf.print_base_dir_given || val)? (char *)obj.data :  ".";
+       pfx = (select_conf.print_base_dir_given || val)? (char *)obj.data :  ".";
        tmp = make_message("%s/%s", pfx, result? result : "");
        free(result);
        result = tmp;
@@ -181,13 +194,14 @@ static int user_stats_loop_function(struct osl_row *row, void *data)
                        goto err;
                files = *(uint64_t *)obj.data;
                if (usi->count && (usi->flags & USF_PRINT_FILES)) {
-                       format_count_value(conf.count_unit_arg, files,
-                               conf.count_unit_arg == count_unit_arg_h,
+                       format_count_value(select_conf.count_unit_arg, files,
+                               select_conf.count_unit_arg == count_unit_arg_h,
                                formated_value);
-                       printf("\t%s%s", formated_value,
+                       ret = output("\t%s%s", formated_value,
                                (usi->flags & (USF_PRINT_BYTES | USF_PRINT_DIRNAME))?
-                                       "\t" : "\n"
-                       );
+                                       "\t" : "\n");
+                       if (ret < 0)
+                               goto err;
                }
                if (summary)
                        usi->ui->files += files;
@@ -199,14 +213,16 @@ static int user_stats_loop_function(struct osl_row *row, void *data)
                        goto err;
                bytes = *(uint64_t *)obj.data;
                if (usi->count && (usi->flags & USF_PRINT_BYTES)) {
-                       format_size_value(conf.size_unit_arg, bytes,
-                               conf.size_unit_arg == size_unit_arg_h,
+                       format_size_value(select_conf.size_unit_arg, bytes,
+                               select_conf.size_unit_arg == size_unit_arg_h,
                                formated_value);
-                       printf("%s%s%s",
+                       ret = output("%s%s%s",
                                (usi->flags & USF_PRINT_FILES)? "" : "\t",
                                formated_value,
                                usi->flags & USF_PRINT_DIRNAME?  "\t" : "\n"
                        );
+                       if (ret < 0)
+                               goto err;
                }
                if (summary) {
                        usi->ui->bytes += bytes;
@@ -222,10 +238,12 @@ static int user_stats_loop_function(struct osl_row *row, void *data)
                ret = get_dir_name_by_number((uint64_t *)obj.data, &dirname);
                if (ret < 0)
                        goto err;
-               printf("%s%s\n",
+               ret = output("%s%s\n",
                        (usi->flags & (USF_PRINT_BYTES | USF_PRINT_FILES))? "" : "\t",
                        dirname);
                free(dirname);
+               if (ret < 0)
+                       goto err;
        }
        if (usi->count > 0)
                usi->count--;
@@ -255,12 +273,14 @@ static int global_stats_loop_function(struct osl_row *row, void *data)
                        goto err;
                files = *(uint64_t *)obj.data;
                if (gsi->count && (gsi->flags & GSF_PRINT_FILES)) {
-                       format_count_value(conf.count_unit_arg, files,
-                               conf.count_unit_arg == count_unit_arg_h,
+                       format_count_value(select_conf.count_unit_arg, files,
+                               select_conf.count_unit_arg == count_unit_arg_h,
                                formated_value);
-                       printf("\t%s%s", formated_value,
+                       ret = output("\t%s%s", formated_value,
                                (gsi->flags & (GSF_PRINT_BYTES | GSF_PRINT_DIRNAME))?
                                "\t" : "\n");
+                       if (ret < 0)
+                               goto err;
                }
                if (summary)
                        num_files += files;
@@ -272,14 +292,16 @@ static int global_stats_loop_function(struct osl_row *row, void *data)
                        goto err;
                bytes = *(uint64_t *)obj.data;
                if (gsi->count && (gsi->flags & GSF_PRINT_BYTES)) {
-                       format_size_value(conf.size_unit_arg, bytes,
-                               conf.size_unit_arg == size_unit_arg_h,
+                       format_size_value(select_conf.size_unit_arg, bytes,
+                               select_conf.size_unit_arg == size_unit_arg_h,
                                formated_value);
-                       printf("%s%s%s",
+                       ret = output("%s%s%s",
                                (gsi->flags & GSF_PRINT_FILES)? "" : "\t",
                                formated_value,
                                (gsi->flags & GSF_PRINT_DIRNAME)? "\t" : "\n"
                        );
+                       if (ret < 0)
+                               goto err;
                }
                if (summary) {
                        num_bytes += bytes;
@@ -290,10 +312,12 @@ static int global_stats_loop_function(struct osl_row *row, void *data)
                ret = get_dir_name_of_row(row, &dirname);
                if (ret < 0)
                        goto err;
-               printf("%s%s\n",
+               ret = output("%s%s\n",
                        (gsi->flags & (GSF_PRINT_BYTES | GSF_PRINT_FILES))? "" : "\t",
                        dirname);
                free(dirname);
+               if (ret < 0)
+                       goto err;
        }
        if (gsi->count > 0)
                gsi->count--;
@@ -329,27 +353,31 @@ static int adu_loop_reverse(struct osl_table *t, unsigned col_num, void *private
        return check_loop_return(ret, *loop_ret, *loop_osl_errno);
 }
 
-static void print_global_summary(void)
+static int print_global_summary(void)
 {
        char d[FORMATED_VALUE_SIZE], f[FORMATED_VALUE_SIZE],
                s[FORMATED_VALUE_SIZE];
        enum enum_count_unit ud, uf;
        enum enum_size_unit us;
+       int ret;
 
-       if (conf.no_global_summary_given)
-               return;
-       ud = format_count_value(conf.count_unit_arg, num_dirs, 0, d);
-       uf = format_count_value(conf.count_unit_arg, num_files, 0, f);
-       us = format_size_value(conf.size_unit_arg, num_bytes, 0, s);
+       if (select_conf.no_global_summary_given)
+               return 1;
+       ud = format_count_value(select_conf.count_unit_arg, num_dirs, 0, d);
+       uf = format_count_value(select_conf.count_unit_arg, num_files, 0, f);
+       us = format_size_value(select_conf.size_unit_arg, num_bytes, 0, s);
 
-       if (!conf.no_headers_given)
-               printf("Global summary "
+       if (!select_conf.no_headers_given) {
+               ret = output("Global summary "
                        "(dirs(%c)/files(%c)/size(%c))\n",
                        count_unit_abbrevs[ud],
                        count_unit_abbrevs[uf],
                        size_unit_abbrevs[us]
                );
-       printf("\t%s\t%s\t%s\n\n", d, f, s);
+               if (ret < 0)
+                       return ret;
+       }
+       return output("\t%s\t%s\t%s\n\n", d, f, s);
 }
 
 static int print_user_summary_line(struct user_info *ui, __a_unused void *data)
@@ -358,23 +386,22 @@ static int print_user_summary_line(struct user_info *ui, __a_unused void *data)
                formated_file_count[FORMATED_VALUE_SIZE],
                formated_bytes[FORMATED_VALUE_SIZE ];
 
-       format_count_value(conf.count_unit_arg, ui->dirs,
-               conf.count_unit_arg == count_unit_arg_h,
+       format_count_value(select_conf.count_unit_arg, ui->dirs,
+               select_conf.count_unit_arg == count_unit_arg_h,
                formated_dir_count);
-       format_count_value(conf.count_unit_arg, ui->files,
-               conf.count_unit_arg == count_unit_arg_h,
+       format_count_value(select_conf.count_unit_arg, ui->files,
+               select_conf.count_unit_arg == count_unit_arg_h,
                formated_file_count);
-       format_size_value(conf.size_unit_arg, ui->bytes,
-               conf.size_unit_arg == size_unit_arg_h,
+       format_size_value(select_conf.size_unit_arg, ui->bytes,
+               select_conf.size_unit_arg == size_unit_arg_h,
                formated_bytes);
-       printf("\t%s\t%u\t%s\t%s\t%s\n",
+       return output("\t%s\t%u\t%s\t%s\t%s\n",
                ui->pw_name? ui->pw_name : "?",
                (unsigned)ui->uid,
                formated_dir_count,
                formated_file_count,
                formated_bytes
        );
-       return 1;
 }
 
 static int name_comp(const void *a, const void *b)
@@ -427,56 +454,69 @@ static int (*summary_comparators[])(const void *, const void *) = {
        [user_summary_sort_arg_size] = size_comp,
 };
 
-static void print_user_summary(void)
+static int print_user_summary(void)
 {
-       if (conf.no_user_summary_given)
-               return;
-       if (!conf.no_headers_given)
-               printf("User summary "
+       if (select_conf.no_user_summary_given)
+               return 1;
+       if (!select_conf.no_headers_given) {
+               int ret = output("User summary "
                        "(pw_name/uid/dirs%s/files%s/size%s):\n",
                        count_unit_buf, count_unit_buf, size_unit_buf);
-       sort_hash_table(summary_comparators[conf.user_summary_sort_arg]);
-       for_each_admissible_user(print_user_summary_line, NULL);
+               if (ret < 0)
+                       return ret;
+       }
+       sort_hash_table(summary_comparators[select_conf.user_summary_sort_arg]);
+       return for_each_admissible_user(print_user_summary_line, NULL);
 }
 
 static int print_user_list(struct user_info *ui, __a_unused void *data)
 {
        int ret;
        struct user_stats_info usi;
-       enum enum_user_list ula = conf.user_list_arg;
+       enum enum_user_list ula = select_conf.user_list_arg;
        int print_size_list = (ula == user_list_arg_size
                || ula == user_list_arg_both);
 
        if (print_size_list) {
-               usi.count = conf.limit_arg;
+               usi.count = select_conf.limit_arg;
                usi.ui = ui;
                usi.flags = USF_PRINT_DIRNAME | USF_PRINT_BYTES | USF_COMPUTE_SUMMARY;
-               if (!conf.no_headers_given)
-                       printf("%s (uid %u), by size%s:\n",
+               if (!select_conf.no_headers_given) {
+                       ret = output("%s (uid %u), by size%s:\n",
                                ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid,
                                size_unit_buf);
+                       if (ret < 0)
+                               return ret;
+               }
                ret = adu_loop_reverse(ui->table, UT_BYTES, &usi, user_stats_loop_function,
                        &usi.ret, &usi.osl_errno);
                if (ret < 0)
                        return ret;
-               printf("\n");
+               ret = output("\n");
+               if (ret < 0)
+                       return ret;
        }
        if (ula == user_list_arg_file_count || ula == user_list_arg_both) {
-               if (!conf.no_headers_given)
-                       printf("%s (uid %u), by file count%s:\n",
+               if (!select_conf.no_headers_given) {
+                       ret = output("%s (uid %u), by file count%s:\n",
                                ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid,
                                count_unit_buf);
-               usi.count = conf.limit_arg,
+                       if (ret < 0)
+                               return ret;
+               }
+               usi.count = select_conf.limit_arg,
                usi.ui = ui;
                usi.flags = USF_PRINT_DIRNAME | USF_PRINT_FILES;
                ret = adu_loop_reverse(ui->table, UT_FILES, &usi, user_stats_loop_function,
                        &usi.ret, &usi.osl_errno);
                if (ret < 0)
                        return ret;
-               printf("\n");
+               ret = output("\n");
+               if (ret < 0)
+                       return ret;
        }
-       if (ula == user_list_arg_none && !conf.no_user_summary_given) {
-               usi.count = conf.limit_arg;
+       if (ula == user_list_arg_none && !select_conf.no_user_summary_given) {
+               usi.count = select_conf.limit_arg;
                usi.ui = ui;
                usi.flags = USF_COMPUTE_SUMMARY;
                ret = adu_loop_reverse(ui->table, UT_FILES, &usi, user_stats_loop_function,
@@ -496,37 +536,47 @@ static int print_global_lists(void)
 {
        struct global_stats_info gsi;
        int ret;
-       enum enum_global_list gla = conf.global_list_arg;
+       enum enum_global_list gla = select_conf.global_list_arg;
        int print_size_list = (gla == global_list_arg_size
                || gla == global_list_arg_both);
 
        if (print_size_list) {
-               gsi.count = conf.limit_arg;
+               gsi.count = select_conf.limit_arg;
                gsi.flags = GSF_PRINT_DIRNAME | GSF_PRINT_BYTES | GSF_COMPUTE_SUMMARY;
-               if (!conf.no_headers_given)
-                       printf("By size%s:\n", size_unit_buf);
+               if (!select_conf.no_headers_given) {
+                       ret = output("By size%s:\n", size_unit_buf);
+                       if (ret < 0)
+                               return ret;
+               }
                ret = adu_loop_reverse(dir_table, DT_BYTES, &gsi,
                        global_stats_loop_function, &gsi.ret, &gsi.osl_errno);
                if (ret < 0)
                        return ret;
-               printf("\n");
+               ret = output("\n");
+               if (ret < 0)
+                       return ret;
        }
        if (gla == global_list_arg_file_count || gla == global_list_arg_both) {
-               gsi.count = conf.limit_arg;
+               gsi.count = select_conf.limit_arg;
                gsi.flags = GSF_PRINT_DIRNAME | GSF_PRINT_FILES;
                if (!print_size_list)
                        gsi.flags |= GSF_COMPUTE_SUMMARY;
-               if (!conf.no_headers_given)
-                       printf("By file count%s:\n", count_unit_buf);
+               if (!select_conf.no_headers_given) {
+                       ret = output("By file count%s:\n", count_unit_buf);
+                       if (ret < 0)
+                               return ret;
+               }
                ret = adu_loop_reverse(dir_table, DT_FILES, &gsi,
                        global_stats_loop_function, &gsi.ret, &gsi.osl_errno);
                if (ret < 0)
                        return ret;
-               printf("\n");
+               ret = output("\n");
+               if (ret < 0)
+                       return ret;
        }
-       if (gla == global_list_arg_none && !conf.no_global_summary_given) {
+       if (gla == global_list_arg_none && !select_conf.no_global_summary_given) {
                /* must compute summary */
-               gsi.count = conf.limit_arg;
+               gsi.count = select_conf.limit_arg;
                gsi.flags = GSF_COMPUTE_SUMMARY;
                ret = adu_loop_reverse(dir_table, DT_FILES, &gsi,
                        global_stats_loop_function, &gsi.ret, &gsi.osl_errno);
@@ -543,13 +593,19 @@ static int print_statistics(void)
        ret = print_global_lists();
        if (ret < 0)
                return ret;
-       print_global_summary();
-       print_user_lists();
-       print_user_summary();
+       ret = print_global_summary();
+       if (ret < 0)
+               return ret;
+       ret = print_user_lists();
+       if (ret < 0)
+               return ret;
+       ret = print_user_summary();
+       if (ret < 0)
+               return ret;
        return 1;
 }
 
-static int read_uid_file(void)
+static int read_uid_file(struct uid_range *admissible_uids)
 {
        size_t size;
        uint32_t n;
@@ -575,7 +631,7 @@ static int read_uid_file(void)
        create_hash_table(bits);
        for (n = 0; n < num_uids; n++) {
                uint32_t uid = read_u32(map + n * sizeof(uid));
-               ret = search_uid(uid, OPEN_USER_TABLE, NULL);
+               ret = search_uid(uid, admissible_uids, OPEN_USER_TABLE, NULL);
                if (ret < 0)
                        goto out;
        }
@@ -584,28 +640,62 @@ out:
        return ret;
 }
 
-int com_select(void)
+int run_select_query(struct uid_range *admissible_uids)
 {
        int ret;
 
-       if (conf.count_unit_arg != count_unit_arg_h)
-               count_unit_buf[1] = count_unit_abbrevs[conf.count_unit_arg];
+       if (select_conf.output_given && strcmp(select_conf.output_arg, "-")) {
+               output_file = fopen(select_conf.output_arg, "w");
+               if (!output_file)
+                       return -ERRNO_TO_ERROR(errno);
+       } else
+               output_file = stdout;
+
+       if (select_conf.count_unit_arg != count_unit_arg_h)
+               count_unit_buf[1] = count_unit_abbrevs[select_conf.count_unit_arg];
        else
                count_unit_buf[0] = '\0';
-       if (conf.size_unit_arg != size_unit_arg_h)
-               size_unit_buf[1] = size_unit_abbrevs[conf.size_unit_arg];
+       if (select_conf.size_unit_arg != size_unit_arg_h)
+               size_unit_buf[1] = size_unit_abbrevs[select_conf.size_unit_arg];
        else
                size_unit_buf[0] = '\0';
 
        ret = open_dir_table(0);
        if (ret < 0)
-               return ret;
+               goto out;
        check_signals();
-       ret = read_uid_file();
+       ret = read_uid_file(admissible_uids);
        if (ret < 0)
-               return ret;
+               goto out;
        check_signals();
        ret = print_statistics();
+out:
        close_all_tables();
+       if (output_file != stdout)
+               fclose(output_file);
        return ret;
 }
+
+int com_select(void)
+{
+       int ret;
+       struct uid_range *admissible_uids = NULL;
+
+       if (conf.select_options_given) {
+               struct select_cmdline_parser_params params = {
+                       .override = 1,
+                       .initialize = 1,
+                       .check_required = 1,
+                       .check_ambiguity = 1,
+                       .print_errors = 1
+               };
+
+               if (select_cmdline_parser_string_ext(conf.select_options_arg,
+                       &select_conf, "select", &params))
+                       return -E_SYNTAX;
+               ret = parse_uid_arg(select_conf.uid_arg, &admissible_uids);
+               if (ret < 0)
+                       return ret;
+       }
+       return run_select_query(admissible_uids);
+}
diff --git a/select.ggo b/select.ggo
new file mode 100644 (file)
index 0000000..f4d090b
--- /dev/null
@@ -0,0 +1,156 @@
+
+option "uid" u
+#~~~~~~~~~~~~~
+"user id(s) to take into account"
+string typestr="uid_spec"
+optional
+details="
+       An uid specifier may be a single number, or a range of uids.
+       Example:
+
+       --uid 42    # only consider uid 42
+       --uid 42-   # only consider uids greater or equal than 42
+       --uid 23-42 # only consider uids between 23 and 42, inclusively.
+       --uid 23-42,666-777,88 # consider uids 23-42, 666-777 and 88.
+"
+
+option "limit" L
+#~~~~~~~~~~~~~~~
+"Limit output"
+int  typestr="num"
+default="-1"
+optional
+details="
+       Only print num lines of output. If negative (the default),
+       print all lines.
+"
+
+option "no-headers" -
+#~~~~~~~~~~~~~~~~~~~~
+"supress descriptions for listings/tables"
+flag off
+details="
+       This is mostly useful to feed the output of adu to scripts.
+"
+
+option "sort" s
+#~~~~~~~~~~~~~~~
+"how to sort the output"
+enum typestr="<key>"
+values="sizes","files","unsorted"
+default="sizes"
+optional
+details="
+       Sort by file size, file count or unsorted.
+"
+
+option "format" f
+#~~~~~~~~~~~~~~~~~
+"how to format the output"
+string typestr="<format>"
+optional
+details="
+       %(basedir)      -- the path given to --base-dir during create
+       %(dir)          -- the name of the directory
+       %(dir_size)     -- the size of the sum of all regular files in this directory
+       %(num_files)    -- the number of regular files in this directory
+       %%              -- interpolates to %
+       %xx             -- interpolates to the character with hex code xx
+"
+
+option "output" o
+#~~~~~~~~~~~~~~~~
+"file to write output to"
+string typestr="<path>"
+optional
+default="-"
+details="
+       If empty, or not given, use stdout.
+"
+
+option "size-unit" -
+#~~~~~~~~~~~~~~~~~~~
+"select output format for sizes"
+enum typestr="format"
+values="h","b","k","m","g","t"
+default="h"
+optional
+details="
+       Print sizes in the given unit: human-readable, bytes,
+       kilobytes (2^10), megabytes (2^20), gigabytes (2^30), terabytes
+       (2^40). The default is \"h\", i.e. human-readable.
+"
+
+option "count-unit" -
+#~~~~~~~~~~~~~~~~~~~~
+"select output format for counted values"
+enum typestr="format"
+values="h","n","k","m","g","t"
+default="h"
+optional
+details="
+       Print the number of files/directories in the given unit:
+       human-readable, number, number/10^3, number/10^6, number/10^12,
+       number/10^15. The default is to print numbers in human-readable
+       format.
+"
+
+
+option "user-summary-sort" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"how to sort the user-summary"
+enum typestr="col_spec"
+values="name","uid","dir_count","file_count","size"
+default="size"
+optional
+details="
+       It is enough to specify the first letter of the column specifier,
+       e.g. \"--user-summary-sort f\" sorts by file count.
+"
+
+option "no-user-summary" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"do not print the user summary table"
+flag off
+
+
+option "user-list" -
+#~~~~~~~~~~~~~~~~~~~
+"how to print per-user directory listings"
+enum typestr="which"
+values="size","file_count","both","none"
+default="both"
+optional
+details="
+       Similar to the global directory listings mentioned above,
+       adu can print two directory listings per user. This option
+       controls which of the these should be printed.
+"
+option "no-global-summary" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"do not print the summary line"
+flag off
+
+option "global-list" -
+#~~~~~~~~~~~~~~~~~~~~~
+"how to print global directory listings"
+enum typestr="which"
+values="size","file_count","both","none"
+default="both"
+optional
+details="
+       By default adu prints two global directory listings: The
+       first prints the directory names ordered by the sum of the
+       sizes of the contained files while the second listing prints
+       them sorted by the number of files. This option can be used
+       to print only one or neither of these two listings.
+"
+option "print-base-dir" -
+#~~~~~~~~~~~~~~~~~~~~~~~~
+"whether to include the base-dir in the output"
+flag off
+details="
+       If this flag is given, all directories printed are prefixed
+       with the base directory. The default is to print paths relative
+       to the base dir.
+"
index a0666d35bde5d038bb0df48c954618f1041af16a..87f9c265984d1a1b160b8bf784a273e417c46198 100644 (file)
--- a/string.c
+++ b/string.c
@@ -166,3 +166,137 @@ __must_check int atoi64(const char *str, int64_t *result)
        *result = tmp;
        return 1;
 }
+
+/**
+ * Split string and return pointers to its parts.
+ *
+ * \param args The string to be split.
+ * \param argv_ptr Pointer to the list of substrings.
+ * \param delim Delimiter.
+ *
+ * This function modifies \a args by replacing each occurance of \a delim by
+ * zero. A \p NULL-terminated array of pointers to char* is allocated dynamically
+ * and these pointers are initialized to point to the broken-up substrings
+ * within \a args. A pointer to this array is returned via \a argv_ptr.
+ *
+ * \return The number of substrings found in \a args.
+ */
+__must_check unsigned split_args(char *args, char *** const argv_ptr, const char *delim)
+{
+       char *p = args;
+       char **argv;
+       size_t n = 0, i, j;
+
+       p = args + strspn(args, delim);
+       for (;;) {
+               i = strcspn(p, delim);
+               if (!i)
+                       break;
+               p += i;
+               n++;
+               p += strspn(p, delim);
+       }
+       *argv_ptr = adu_malloc((n + 1) * sizeof(char *));
+       argv = *argv_ptr;
+       i = 0;
+       p = args + strspn(args, delim);
+       while (p) {
+               argv[i] = p;
+               j = strcspn(p, delim);
+               if (!j)
+                       break;
+               p += strcspn(p, delim);
+               if (*p) {
+                       *p = '\0';
+                       p++;
+                       p += strspn(p, delim);
+               }
+               i++;
+       }
+       argv[n] = NULL;
+       return n;
+}
+
+static int check_uid_arg(const char *arg, uint32_t *uid)
+{
+       const uint32_t max = ~0U;
+       /*
+        * we need an 64-bit int for string -> uid conversion because strtoll()
+        * returns a signed value.
+        */
+       int64_t val;
+       int ret = atoi64(arg, &val);
+
+       if (ret < 0)
+               return ret;
+       if (val < 0 || val > max)
+               return -ERRNO_TO_ERROR(EINVAL);
+       *uid = val;
+       return 1;
+}
+
+int parse_uid_range(const char *orig_arg, struct uid_range *ur)
+{
+       int ret;
+       char *arg = adu_strdup(orig_arg), *p = strchr(arg, '-');
+
+       if (!p || p == arg) { /* -42 or 42 */
+               ret = check_uid_arg(p? p + 1 : arg, &ur->high);
+               if (ret < 0)
+                       goto out;
+               ur->low = p? 0 : ur->high;
+               ret = 1;
+               goto out;
+       }
+       /* 42- or 42-4711 */
+       *p = '\0';
+       p++;
+       ret = check_uid_arg(arg, &ur->low);
+       if (ret < 0)
+               goto out;
+       ur->high = ~0U;
+       if (*p) { /* 42-4711 */
+               ret = check_uid_arg(p, &ur->high);
+               if (ret < 0)
+                       goto out;
+       }
+       if (ur->low > ur->high)
+               ret = -ERRNO_TO_ERROR(EINVAL);
+out:
+       if (ret < 0)
+               ERROR_LOG("bad uid option: %s\n", orig_arg);
+       else
+               INFO_LOG("admissible uid range: %u - %u\n", ur->low,
+                       ur->high);
+       free(arg);
+       return ret;
+}
+
+int parse_uid_arg(const char *orig_arg, struct uid_range **ur)
+{
+       char *arg, **argv;
+       unsigned n;
+       int i, ret = 1;
+
+       if (!orig_arg)
+               return 0;
+       arg = adu_strdup(orig_arg);
+       n = split_args(arg, &argv, ",");
+       if (!n)
+               return -E_SYNTAX;
+       *ur = adu_malloc((n + 1) * sizeof(struct uid_range));
+       for (i = 0; i < n; i++) {
+               ret = parse_uid_range(argv[i], *ur + i);
+               if (ret < 0)
+                       break;
+       }
+       free(arg);
+       if (ret < 0) {
+               free(*ur);
+               *ur = NULL;
+       }
+       /* an empty range indicates the end of the list */
+       (*ur)[n].low = 1;
+       (*ur)[n].high = 0;
+       return n;
+}
index ebf3b17903565dfeff7742fba9a0ef0f0f50b364..7229b7d9d87fc3cb7452705d3215e225088f21c2 100644 (file)
--- a/string.h
+++ b/string.h
@@ -12,3 +12,4 @@ __must_check __malloc void *adu_calloc(size_t size);
 __must_check __malloc char *adu_strdup(const char *s);
 __must_check __malloc __printf_1_2 char *make_message(const char *fmt, ...);
 __must_check int atoi64(const char *str, int64_t *result);
+int parse_uid_arg(const char *orig_arg, struct uid_range **ur);