/* * Copyright (C) 2010-2014 Karel Zak * * This file may be redistributed under the terms of the * GNU Lesser General Public License. */ #include #include #include #include #include #include #include #include #include "c.h" #include "nls.h" #include "strutils.h" #include "libsmartcols.h" static int add_children(struct libscols_table *tb, struct libscols_line *ln, int fd); enum { COL_MODE, COL_SIZE, COL_NAME }; /* add columns to the @tb */ static void setup_columns(struct libscols_table *tb, int notree) { if (!scols_table_new_column(tb, "MODE", 0.3, 0)) goto fail; if (!scols_table_new_column(tb, "SIZE", 5, SCOLS_FL_RIGHT)) goto fail; if (!scols_table_new_column(tb, "NAME", 0.5, (notree ? 0 : SCOLS_FL_TREE) | SCOLS_FL_NOEXTREMES)) goto fail; return; fail: scols_unref_table(tb); err(EXIT_FAILURE, "failed to create output columns"); } /* add a new line to @tb, the content is based on @st */ static int add_line_from_stat(struct libscols_table *tb, struct libscols_line *parent, int parent_fd, struct stat *st, const char *name) { struct libscols_line *ln; char modbuf[11], *p; mode_t mode = st->st_mode; int rc = 0; ln = scols_table_new_line(tb, parent); if (!ln) err(EXIT_FAILURE, "failed to create output line"); /* MODE; local buffer, use scols_line_set_data() that calls strdup() */ xstrmode(mode, modbuf); if (scols_line_set_data(ln, COL_MODE, modbuf)) goto fail; /* SIZE; already allocated string, use scols_line_refer_data() */ p = size_to_human_string(0, st->st_size); if (!p || scols_line_refer_data(ln, COL_SIZE, p)) goto fail; /* NAME */ if (scols_line_set_data(ln, COL_NAME, name)) goto fail; /* colors */ if (scols_table_colors_wanted(tb)) { struct libscols_cell *ce = scols_line_get_cell(ln, COL_NAME); if (S_ISDIR(mode)) scols_cell_set_color(ce, "blue"); else if (S_ISLNK(mode)) scols_cell_set_color(ce, "cyan"); else if (S_ISBLK(mode)) scols_cell_set_color(ce, "magenta"); else if ((mode & S_IXOTH) || (mode & S_IXGRP) || (mode & S_IXUSR)) scols_cell_set_color(ce, "green"); } if (S_ISDIR(st->st_mode)) { int fd; if (parent_fd >= 0) fd = openat(parent_fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC); else fd = open(name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC); if (fd >= 0) { rc = add_children(tb, ln, fd); close(fd); } } return rc; fail: err(EXIT_FAILURE, "failed to create cell data"); return -1; } /* read all entries from directory addressed by @fd */ static int add_children(struct libscols_table *tb, struct libscols_line *ln, int fd) { DIR *dir; struct dirent *d; dir = fdopendir(fd); if (!dir) return -errno; while ((d = readdir(dir))) { struct stat st; if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) continue; if (fstatat(fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) continue; add_line_from_stat(tb, ln, fd, &st, d->d_name); } closedir(dir); return 0; } static void add_lines(struct libscols_table *tb, const char *dirname) { struct stat st; if (lstat(dirname, &st)) err(EXIT_FAILURE, "%s", dirname); add_line_from_stat(tb, NULL, -1, &st, dirname); } static void __attribute__((__noreturn__)) usage(FILE *out) { fprintf(out, " %s [options] [ ...]\n\n", program_invocation_short_name); fputs(" -c, --csv display a csv-like output\n", out); fputs(" -i, --ascii use ascii characters only\n", out); fputs(" -l, --list use list format output\n", out); fputs(" -n, --noheadings don't print headings\n", out); fputs(" -p, --pairs use key=\"value\" output format\n", out); fputs(" -J, --json use JSON output format\n", out); fputs(" -r, --raw use raw output format\n", out); fputs(" -S, --range-start first line to print\n", out); fputs(" -E, --range-end last line to print\n", out); exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); } int main(int argc, char *argv[]) { struct libscols_table *tb; int c, notree = 0, nstart = -1, nend = -1; static const struct option longopts[] = { { "ascii", 0, NULL, 'i' }, { "csv", 0, NULL, 'c' }, { "list", 0, NULL, 'l' }, { "noheadings", 0, NULL, 'n' }, { "pairs", 0, NULL, 'p' }, { "json", 0, NULL, 'J' }, { "raw", 0, NULL, 'r' }, { "range-start",1, NULL, 'S' }, { "range-end", 1, NULL, 'E' }, { NULL, 0, NULL, 0 }, }; setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */ scols_init_debug(0); tb = scols_new_table(); if (!tb) err(EXIT_FAILURE, "failed to create output table"); while((c = getopt_long(argc, argv, "ciJlnprS:E:", longopts, NULL)) != -1) { switch(c) { case 'c': scols_table_set_column_separator(tb, ","); scols_table_enable_raw(tb, 1); notree = 1; break; case 'i': scols_table_enable_ascii(tb, 1); break; case 'J': scols_table_set_name(tb, "scolstest"); scols_table_enable_json(tb, 1); break; case 'l': notree = 1; break; case 'n': scols_table_enable_noheadings(tb, 1); break; case 'p': scols_table_enable_export(tb, 1); notree = 1; break; case 'r': scols_table_enable_raw(tb, 1); notree = 1; break; case 'S': nstart = strtos32_or_err(optarg, "failed to parse range start") - 1; break; case 'E': nend = strtos32_or_err(optarg, "failed to parse range end") - 1; break; default: usage(stderr); } } scols_table_enable_colors(tb, isatty(STDOUT_FILENO)); setup_columns(tb, notree); if (optind == argc) add_lines(tb, "."); else while (optind < argc) add_lines(tb, argv[optind++]); if (nstart >= 0 || nend >= 0) { /* print subset */ struct libscols_line *start = NULL, *end = NULL; if (nstart >= 0) start = scols_table_get_line(tb, nstart); if (nend >= 0) end = scols_table_get_line(tb, nend); if (start || end) scols_table_print_range(tb, start, end); } else /* print all table */ scols_print_table(tb); scols_unref_table(tb); return EXIT_SUCCESS; }