#include "error.h"
#include "portable_io.h"
-/** The decimal representation of an uint64_t never exceeds that size. */
-#define FORMATED_VALUE_SIZE 25
-
+/* global list */
#define GLOBAL_LIST_ATOMS \
ATOM(size, SIZE) \
ATOM(files, COUNT) \
enum global_list_atoms {GLOBAL_LIST_ATOMS};
#undef ATOM
+/* global summary */
#define GLOBAL_SUMMARY_ATOMS \
ATOM(dirs, COUNT) \
ATOM(files, COUNT) \
enum global_summary_atoms {GLOBAL_SUMMARY_ATOMS};
#undef ATOM
+/* user list */
+#define USER_LIST_ATOMS \
+ ATOM(pw_name, STRING) \
+ ATOM(uid, ID) \
+ ATOM(size, SIZE) \
+ ATOM(files, COUNT) \
+ ATOM(dirname, STRING) \
+
+#define ATOM(x, y) { .name = #x, .type = AT_ ## y},
+struct atom user_list_atoms[] = {
+ USER_LIST_ATOMS
+ {.name = NULL}
+};
+#undef ATOM
+#define ATOM(x, y) ula_ ## x,
+enum user_list_atoms {USER_LIST_ATOMS};
+#undef ATOM
+
+/* user summary */
#define USER_SUMMARY_ATOMS \
ATOM(pw_name, STRING) \
ATOM(uid, ID) \
enum user_summary_atoms {USER_SUMMARY_ATOMS};
#undef ATOM
-
-
-/* these get filled in by the select command. */
-static char count_unit_buf[4] = "( )", size_unit_buf[4] = "( )";
-
struct global_list_info {
uint32_t count;
int ret;
int osl_errno;
};
-struct user_summary_info {
+struct user_list_info {
+ uint32_t count;
struct user_info *ui;
+ struct format_info *fi;
int ret;
int osl_errno;
};
-
-enum user_stats_flags {
- USF_PRINT_DIRNAME = 1,
- USF_PRINT_BYTES = 2,
- USF_PRINT_FILES = 4,
- USF_COMPUTE_SUMMARY = 8,
-};
-
-struct user_stats_info {
- uint32_t count;
- enum user_stats_flags flags;
+struct user_summary_info {
+ struct user_info *ui;
int ret;
int osl_errno;
- struct user_info *ui;
};
-static const uint64_t size_unit_divisors[] = {
- [size_unit_arg_b] = 1ULL,
- [size_unit_arg_k] = 1024ULL,
- [size_unit_arg_m] = 1024ULL * 1024ULL,
- [size_unit_arg_g] = 1024ULL * 1024ULL * 1024ULL,
- [size_unit_arg_t] = 1024ULL * 1024ULL * 1024ULL * 1024ULL,
-};
-
-static const uint64_t count_unit_divisors[] = {
-
- [count_unit_arg_n] = 1ULL,
- [count_unit_arg_k] = 1000ULL,
- [count_unit_arg_m] = 1000ULL * 1000ULL,
- [count_unit_arg_g] = 1000ULL * 1000ULL * 1000ULL,
- [count_unit_arg_t] = 1000ULL * 1000ULL * 1000ULL * 1000ULL,
-};
-
-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)
-{
- enum enum_size_unit u = unit;
- char unit_buf[2] = "\0\0";
-
- if (unit == size_unit_arg_h) /* human readable */
- for (u = size_unit_arg_b; u < size_unit_arg_t &&
- value > size_unit_divisors[u + 1]; u++)
- ; /* nothing */
- if (print_unit)
- unit_buf[0] = size_unit_abbrevs[u];
- sprintf(result, "%llu%s",
- (long long unsigned)value / size_unit_divisors[u], unit_buf);
- return u;
-}
-
-static enum enum_count_unit format_count_value(enum enum_count_unit unit,
- uint64_t value, int print_unit, char *result)
-{
- enum enum_count_unit u = unit;
- char unit_buf[2] = "\0\0";
-
- if (unit == count_unit_arg_h) /* human readable */
- for (u = count_unit_arg_n; u < count_unit_arg_t &&
- value > count_unit_divisors[u + 1]; u++)
- ; /* nothing */
- if (print_unit)
- unit_buf[0] = count_unit_abbrevs[u];
- sprintf(result, "%llu%s",
- (long long unsigned)value / count_unit_divisors[u], unit_buf);
- return u;
-}
static FILE *output_file;
return get_dir_name_by_number((uint64_t *)obj.data, name);
}
+static int get_dir_name_of_user_row(struct osl_row *user_table_row,
+ struct user_info *ui, char **dirname)
+{
+ struct osl_object obj;
+ int ret = osl(osl_get_object(ui->table, user_table_row,
+ UT_DIR_NUM, &obj));
+
+ if (ret < 0)
+ return ret;
+ return get_dir_name_by_number((uint64_t *)obj.data, dirname);
+}
+
static int get_num_files_of_row(struct osl_row *row, uint64_t *num_files)
{
struct osl_object obj;
return 1;
}
-
-static int user_stats_loop_function(struct osl_row *row, void *data)
-{
- struct user_stats_info *usi = data;
- struct osl_object obj;
- int ret, summary = usi->flags & USF_COMPUTE_SUMMARY;
- char formated_value[FORMATED_VALUE_SIZE];
-
- check_signals();
- if (!usi->count && !summary) {
- ret = -E_LOOP_COMPLETE;
- goto err;
- }
- if (summary || (usi->count && (usi->flags & USF_PRINT_FILES))) {
- uint64_t files;
- ret = osl(osl_get_object(usi->ui->table, row, UT_FILES, &obj));
- if (ret < 0)
- goto err;
- files = *(uint64_t *)obj.data;
- if (usi->count && (usi->flags & USF_PRINT_FILES)) {
- format_count_value(select_conf.count_unit_arg, files,
- select_conf.count_unit_arg == count_unit_arg_h,
- formated_value);
- ret = output("\t%s%s", formated_value,
- (usi->flags & (USF_PRINT_BYTES | USF_PRINT_DIRNAME))?
- "\t" : "\n");
- if (ret < 0)
- goto err;
- }
- if (summary)
- usi->ui->files += files;
- }
- if (summary || (usi->count && (usi->flags & USF_PRINT_BYTES))) {
- uint64_t bytes;
- ret = osl(osl_get_object(usi->ui->table, row, UT_BYTES, &obj));
- if (ret < 0)
- goto err;
- bytes = *(uint64_t *)obj.data;
- if (usi->count && (usi->flags & USF_PRINT_BYTES)) {
- format_size_value(select_conf.size_unit_arg, bytes,
- select_conf.size_unit_arg == size_unit_arg_h,
- formated_value);
- 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;
- usi->ui->dirs++;
- }
-
- }
- if (usi->count && (usi->flags & USF_PRINT_DIRNAME)) {
- char *dirname;
- ret = osl(osl_get_object(usi->ui->table, row, UT_DIR_NUM, &obj));
- if (ret < 0)
- goto err;
- ret = get_dir_name_by_number((uint64_t *)obj.data, &dirname);
- if (ret < 0)
- goto err;
- 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--;
- return 1;
-err:
- usi->ret = ret;
- usi->osl_errno = (ret == -E_OSL)? osl_errno : 0;
- return -1;
-}
-
static int check_loop_return(int ret, int loop_ret, int loop_osl_errno)
{
if (ret >= 0)
return for_each_admissible_user(print_user_summary_line, fi);
}
-static int print_user_list(struct user_info *ui, __a_unused void *data)
+static int user_list_loop_function(struct osl_row *row, void *data)
{
+ struct user_list_info *uli = data;
+ union atom_value values[] = {
+ [ula_pw_name] = {.string_value = uli->ui->pw_name?
+ uli->ui->pw_name : "?"},
+ [ula_uid] = {.num_value = (long long unsigned)uli->ui->uid},
+ [ula_files] = {.num_value = 0ULL},
+ [ula_size] = {.num_value = 0ULL},
+ [ula_dirname] = {.string_value = NULL}
+ };
+ uint64_t num;
int ret;
- struct user_stats_info usi;
- 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 = select_conf.limit_arg;
- usi.ui = ui;
- usi.flags = USF_PRINT_DIRNAME | USF_PRINT_BYTES | USF_COMPUTE_SUMMARY;
- 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;
- ret = output("\n");
- if (ret < 0)
- return ret;
- }
- if (ula == user_list_arg_file_count || ula == user_list_arg_both) {
- 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);
- 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;
- ret = output("\n");
- if (ret < 0)
- return ret;
- }
- 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,
- &usi.ret, &usi.osl_errno);
- if (ret < 0)
- return ret;
- }
- return 1;
+ char *dirname, *buf;
+
+ check_signals();
+ ret = -E_LOOP_COMPLETE;
+ if (!uli->count)
+ goto err;
+
+ ret = get_num_user_files(row, uli->ui, &num);
+ if (ret < 0)
+ goto err;
+ values[ula_files].num_value = num;
+
+ ret = get_num_user_bytes(row, uli->ui, &num);
+ if (ret < 0)
+ goto err;
+ values[ula_size].num_value = num;
+
+ ret = get_dir_name_of_user_row(row, uli->ui, &dirname);
+ if (ret < 0)
+ goto err;
+ values[ula_dirname].string_value = dirname;
+
+ buf = format_items(uli->fi, values);
+ free(dirname);
+ ret = output("%s", buf);
+ free(buf);
+ uli->count--;
+ return ret;
+err:
+ uli->ret = ret;
+ uli->osl_errno = (ret == -E_OSL)? osl_errno : 0;
+ return -1;
}
-static int print_user_lists(void)
+static int print_user_list(struct user_info *ui, void *data)
{
- return for_each_admissible_user(print_user_list, NULL);
+ struct format_info *fi = data;
+ int ret;
+ enum user_table_columns sort_column = UT_BYTES;
+ struct user_list_info uli = {
+ .ui = ui,
+ .fi = fi,
+ .count = select_conf.limit_arg
+ };
+
+ if (select_conf.list_sort_arg == list_sort_arg_file_count)
+ sort_column = UT_FILES;
+
+ if (!select_conf.no_headers_given) {
+ ret = output("%s (uid %u)\n",
+ ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid);
+ if (ret < 0)
+ return ret;
+ }
+ return adu_loop_reverse(ui->table, sort_column, &uli, user_list_loop_function,
+ &uli.ret, &uli.osl_errno);
}
static int global_list_loop_function(struct osl_row *row, void *data)
free_format_info(sli->global_summary_fi);
return ret;
case select_mode_arg_user_list:
- ret = print_user_lists();
+ ret = for_each_admissible_user(print_user_list,
+ sli->user_list_fi);
+ free_format_info(sli->user_list_fi);
return ret;
case select_mode_arg_user_summary:
ret = print_user_summary(sli->user_summary_fi);
} 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 (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)
goto out;
global_list_atoms, &sfi->global_list_fi);
if (ret < 0)
goto global_list_err;
+ ret = parse_format_string(select_conf.user_list_format_arg,
+ user_list_atoms, &sfi->user_list_fi);
+ if (ret < 0)
+ goto user_list_err;
return 1;
+user_list_err:
+ free_format_info(sfi->global_list_fi);
global_list_err:
free_format_info(sfi->global_summary_fi);
global_summary_err:
option "no-headers" -
#~~~~~~~~~~~~~~~~~~~~
-"supress descriptions for listings/tables"
+"supress descriptions for listings/summaries"
flag off
details="
This is mostly useful to feed the output of adu to scripts.
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"
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"
"
optional
+option "user-list-format" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~
+"How to format the user list"
+string typestr="<format>"
+default="%(size:r:5) %(files:r:5) %(dirname)\n"
+details="
+ pw_name: The user name
+ uid: The user id
+ files: The number of files
+ size: size of all files in that directory
+ dirname: The name of the directory
+"
+optional
option "user-summary-format" -
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"How to format the user summary"