1 /*
2 * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
3 *
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
6 */
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <dirent.h>
14 #include <getopt.h>
15
16 #include "c.h"
17 #include "nls.h"
18 #include "strutils.h"
19
20 #include "libsmartcols.h"
21
22 static int add_children(struct libscols_table *tb,
23 struct libscols_line *ln, int fd);
24
25
26 enum { COL_MODE, COL_SIZE, COL_NAME };
27
28 struct libscols_column *sort_column;
29
30 /* add columns to the @tb */
31 static void setup_columns(struct libscols_table *tb, int notree)
32 {
33 if (!scols_table_new_column(tb, "MODE", 0.3, 0))
34 goto fail;
35 if (!scols_table_new_column(tb, "SIZE", 5, SCOLS_FL_RIGHT))
36 goto fail;
37
38 sort_column = scols_table_new_column(tb, "NAME", 0.5,
39 (notree ? 0 : SCOLS_FL_TREE) | SCOLS_FL_NOEXTREMES);
40 if (!sort_column)
41 goto fail;
42 scols_column_set_cmpfunc(sort_column, scols_cmpstr_cells, NULL);
43
44 return;
45 fail:
46 scols_unref_table(tb);
47 err(EXIT_FAILURE, "failed to create output columns");
48 }
49
50 /* add a new line to @tb, the content is based on @st */
51 static int add_line_from_stat(struct libscols_table *tb,
52 struct libscols_line *parent,
53 int parent_fd,
54 struct stat *st,
55 const char *name)
56 {
57 struct libscols_line *ln;
58 char modbuf[11], *p;
59 mode_t mode = st->st_mode;
60 int rc = 0;
61
62 ln = scols_table_new_line(tb, parent);
63 if (!ln)
64 err(EXIT_FAILURE, "failed to create output line");
65
66 /* MODE; local buffer, use scols_line_set_data() that calls strdup() */
67 xstrmode(mode, modbuf);
68 if (scols_line_set_data(ln, COL_MODE, modbuf))
69 goto fail;
70
71 /* SIZE; already allocated string, use scols_line_refer_data() */
72 p = size_to_human_string(0, st->st_size);
73 if (!p || scols_line_refer_data(ln, COL_SIZE, p))
74 goto fail;
75
76 /* NAME */
77 if (scols_line_set_data(ln, COL_NAME, name))
78 goto fail;
79
80 /* colors */
81 if (scols_table_colors_wanted(tb)) {
82 struct libscols_cell *ce = scols_line_get_cell(ln, COL_NAME);
83
84 if (S_ISDIR(mode))
85 scols_cell_set_color(ce, "blue");
86 else if (S_ISLNK(mode))
87 scols_cell_set_color(ce, "cyan");
88 else if (S_ISBLK(mode))
89 scols_cell_set_color(ce, "magenta");
90 else if ((mode & S_IXOTH) || (mode & S_IXGRP) || (mode & S_IXUSR))
91 scols_cell_set_color(ce, "green");
92 }
93
94 if (S_ISDIR(st->st_mode)) {
95 int fd;
96
97 if (parent_fd >= 0)
98 fd = openat(parent_fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
99 else
100 fd = open(name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
101 if (fd >= 0) {
102 rc = add_children(tb, ln, fd);
103 close(fd);
104 }
105 }
106 return rc;
107 fail:
108 err(EXIT_FAILURE, "failed to create cell data");
109 return -1;
110 }
111
112 /* read all entries from directory addressed by @fd */
113 static int add_children(struct libscols_table *tb,
114 struct libscols_line *ln,
115 int fd)
116 {
117 DIR *dir;
118 struct dirent *d;
119
120 dir = fdopendir(fd);
121 if (!dir)
122 return -errno;
123
124 while ((d = readdir(dir))) {
125 struct stat st;
126
127 if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
128 continue;
129 if (fstatat(fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0)
130 continue;
131 add_line_from_stat(tb, ln, fd, &st, d->d_name);
132 }
133 closedir(dir);
134 return 0;
135 }
136
137 static void add_lines(struct libscols_table *tb, const char *dirname)
138 {
139 struct stat st;
140
141 if (lstat(dirname, &st))
142 err(EXIT_FAILURE, "%s", dirname);
143
144 add_line_from_stat(tb, NULL, -1, &st, dirname);
145 }
146
147 static void __attribute__((__noreturn__)) usage(FILE *out)
148 {
149 fprintf(out, " %s [options] [<dir> ...]\n\n", program_invocation_short_name);
150 fputs(" -c, --csv display a csv-like output\n", out);
151 fputs(" -i, --ascii use ascii characters only\n", out);
152 fputs(" -l, --list use list format output\n", out);
153 fputs(" -n, --noheadings don't print headings\n", out);
154 fputs(" -p, --pairs use key=\"value\" output format\n", out);
155 fputs(" -J, --json use JSON output format\n", out);
156 fputs(" -r, --raw use raw output format\n", out);
157 fputs(" -s, --sort sort by NAME\n", out);
158 fputs(" -x, --tree-sort keep tree-like order (also for --list)\n", out);
159 fputs(" -S, --range-start <n> first line to print\n", out);
160 fputs(" -E, --range-end <n> last line to print\n", out);
161
162 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
163 }
164
165 int main(int argc, char *argv[])
166 {
167 struct libscols_table *tb;
168 int c, notree = 0, nstart = -1, nend = -1, sort = 0, force_tree_sort = 0;
169
170
171 static const struct option longopts[] = {
172 { "ascii", 0, NULL, 'i' },
173 { "csv", 0, NULL, 'c' },
174 { "list", 0, NULL, 'l' },
175 { "noheadings", 0, NULL, 'n' },
176 { "pairs", 0, NULL, 'p' },
177 { "json", 0, NULL, 'J' },
178 { "raw", 0, NULL, 'r' },
179 { "range-start",1, NULL, 'S' },
180 { "range-end", 1, NULL, 'E' },
181 { "sort", 0, NULL, 's' },
182 { "tree-sort", 0, NULL, 'x' },
183 { NULL, 0, NULL, 0 },
184 };
185
186 setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */
187
188 scols_init_debug(0);
189
190 tb = scols_new_table();
191 if (!tb)
192 err(EXIT_FAILURE, "failed to create output table");
193
194 while((c = getopt_long(argc, argv, "ciJlnprS:sE:x", longopts, NULL)) != -1) {
195 switch(c) {
196 case 'c':
197 scols_table_set_column_separator(tb, ",");
198 scols_table_enable_raw(tb, 1);
199 notree = 1;
200 break;
201 case 'i':
202 scols_table_enable_ascii(tb, 1);
203 break;
204 case 'J':
205 scols_table_set_name(tb, "scolstest");
206 scols_table_enable_json(tb, 1);
207 break;
208 case 'l':
209 notree = 1;
210 break;
211 case 'n':
212 scols_table_enable_noheadings(tb, 1);
213 break;
214 case 'p':
215 scols_table_enable_export(tb, 1);
216 notree = 1;
217 break;
218 case 'r':
219 scols_table_enable_raw(tb, 1);
220 notree = 1;
221 break;
222 case 'S':
223 nstart = strtos32_or_err(optarg, "failed to parse range start") - 1;
224 break;
225 case 's':
226 sort = 1;
227 break;
228 case 'E':
229 nend = strtos32_or_err(optarg, "failed to parse range end") - 1;
230 break;
231 case 'x':
232 force_tree_sort = 1;
233 break;
234 default:
235 usage(stderr);
236 }
237 }
238
239 scols_table_enable_colors(tb, isatty(STDOUT_FILENO));
240 setup_columns(tb, notree);
241
242 if (optind == argc)
243 add_lines(tb, ".");
244 else while (optind < argc)
245 add_lines(tb, argv[optind++]);
246
247 if (sort)
248 scols_sort_table(tb, sort_column);
249 if (force_tree_sort)
250 scols_sort_table_by_tree(tb);
251
252 if (nstart >= 0 || nend >= 0) {
253 /* print subset */
254 struct libscols_line *start = NULL, *end = NULL;
255
256 if (nstart >= 0)
257 start = scols_table_get_line(tb, nstart);
258 if (nend >= 0)
259 end = scols_table_get_line(tb, nend);
260
261 if (start || end)
262 scols_table_print_range(tb, start, end);
263 } else
264 /* print all table */
265 scols_print_table(tb);
266
267 scols_unref_table(tb);
268 return EXIT_SUCCESS;
269 }