source: mainline/uspace/app/bdsh/cmds/modules/ls/ls.c@ 41453d93

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 41453d93 was 41453d93, checked in by Matthieu Riolo <matthieu.riolo@…>, 7 years ago

rename option well_formatted with exact_size

  • Property mode set to 100644
File size: 11.9 KB
Line 
1/*
2 * Copyright (c) 2008 Tim Post
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * NOTE:
31 * This is a bit of an ugly hack, working around the absence of fstat / etc.
32 * As more stuff is completed and exposed in libc, this will improve
33 */
34
35#include <errno.h>
36#include <str_error.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <stdint.h>
40#include <dirent.h>
41#include <getopt.h>
42#include <vfs/vfs.h>
43#include <str.h>
44#include <cap.h>
45
46#include "ls.h"
47#include "errors.h"
48#include "config.h"
49#include "util.h"
50#include "entry.h"
51#include "cmds.h"
52
53static const char *cmdname = "ls";
54
55static ls_job_t ls;
56
57static struct option const long_options[] = {
58 { "help", no_argument, 0, 'h' },
59 { "unsort", no_argument, 0, 'u' },
60 { "recursive", no_argument, 0, 'r' },
61 { "exact-size", no_argument, 0, 'e' },
62 { "single-column", no_argument, 0, '1' },
63 { 0, 0, 0, 0 }
64};
65
66/* Prototypes for the ls command, excluding entry points. */
67static unsigned int ls_start(ls_job_t *);
68static void ls_print(struct dir_elem_t *);
69static void ls_print_single_column(struct dir_elem_t *);
70static int ls_cmp_type_name(const void *, const void *);
71static int ls_cmp_name(const void *, const void *);
72static signed int ls_scan_dir(const char *, DIR *, struct dir_elem_t **);
73static unsigned int ls_recursive(const char *, DIR *);
74static unsigned int ls_scope(const char *, struct dir_elem_t *);
75
76static unsigned int ls_start(ls_job_t *ls)
77{
78 ls->recursive = 0;
79 ls->sort = 1;
80
81 ls->exact_size = false;
82 ls->single_column = false;
83 ls->printer = ls_print;
84 return 1;
85}
86
87/** Print an entry.
88 *
89 * ls_print currently does nothing more than print the entry.
90 * In the future, we will likely pass the absolute path, and
91 * some sort of ls_options structure that controls how each
92 * entry is printed and what is printed about it.
93 *
94 * Now we just print basic DOS style lists.
95 *
96 * @param de Directory element.
97 */
98static void ls_print(struct dir_elem_t *de)
99{
100 int width = 13;
101
102 if (de->s.is_file) {
103 if (!ls.exact_size) {
104 cap_spec_t cap;
105
106 cap_from_blocks(1, de->s.size, &cap);
107 cap_simplify(&cap);
108
109 char *rptr;
110 if (cap_format(&cap, &rptr) == EOK) {
111 char bytes[9], suffix[3];
112 sscanf(rptr, "%s %s", bytes, suffix);
113 free(rptr);
114
115 printf("%-40s\t%*s %2s\n", de->name, width - 3, bytes, suffix);
116
117 //if there is a failure with cap_format we simply print out an unformatted size
118 return;
119 }
120
121 }
122
123 printf("%-40s\t%*llu\n", de->name, width, (long long) de->s.size);
124 } else if (de->s.is_directory)
125 printf("%-40s\t%*s\n", de->name, width, "<dir>");
126 else
127 printf("%-40s\n", de->name);
128}
129
130static void ls_print_single_column(struct dir_elem_t *de)
131{
132 if (de->s.is_file) {
133 printf("%s\n", de->name);
134 } else {
135 printf("%s/\n", de->name);
136 }
137}
138
139/** Compare 2 directory elements.
140 *
141 * It compares 2 elements of a directory : a file is considered
142 * as bigger than a directory, and if they have the same type,
143 * they are compared alphabetically.
144 *
145 * @param a Pointer to the structure of the first element.
146 * @param b Pointer to the structure of the second element.
147 *
148 * @return -1 if a < b, 1 otherwise.
149 */
150static int ls_cmp_type_name(const void *a, const void *b)
151{
152 struct dir_elem_t const *da = a;
153 struct dir_elem_t const *db = b;
154
155 if ((da->s.is_directory && db->s.is_file) ||
156 ((da->s.is_directory == db->s.is_directory) &&
157 str_cmp(da->name, db->name) < 0))
158 return -1;
159 else
160 return 1;
161}
162
163/** Compare directories/files per name
164 *
165 * This comparision ignores the type of
166 * the node. Sorted will strictly by name.
167 *
168 */
169static int ls_cmp_name(const void *a, const void *b)
170{
171 struct dir_elem_t const *da = a;
172 struct dir_elem_t const *db = b;
173
174 return str_cmp(da->name, db->name);
175}
176
177/** Scan a directory.
178 *
179 * Scan the content of a directory and print it.
180 *
181 * @param d Name of the directory.
182 * @param dirp Directory stream.
183 * @param sort 1 if the output must be sorted,
184 * 0 otherwise.
185 */
186static signed int ls_scan_dir(const char *d, DIR *dirp,
187 struct dir_elem_t **dir_list_ptr)
188{
189 int alloc_blocks = 20;
190 int i;
191 int nbdirs = 0;
192 errno_t rc;
193 int len;
194 char *buff;
195 struct dir_elem_t *tmp;
196 struct dir_elem_t *tosort;
197 struct dirent *dp;
198
199 if (!dirp)
200 return -1;
201
202 buff = (char *) malloc(PATH_MAX);
203 if (!buff) {
204 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
205 return -1;
206 }
207
208 tosort = (struct dir_elem_t *) malloc(alloc_blocks * sizeof(*tosort));
209 if (!tosort) {
210 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
211 free(buff);
212 return -1;
213 }
214
215 while ((dp = readdir(dirp))) {
216 if (nbdirs + 1 > alloc_blocks) {
217 alloc_blocks += alloc_blocks;
218
219 tmp = (struct dir_elem_t *) realloc(tosort,
220 alloc_blocks * sizeof(struct dir_elem_t));
221 if (!tmp) {
222 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
223 goto out;
224 }
225 tosort = tmp;
226 }
227
228 /* fill the name field */
229 tosort[nbdirs].name = (char *) malloc(str_size(dp->d_name) + 1);
230 if (!tosort[nbdirs].name) {
231 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
232 goto out;
233 }
234
235 str_cpy(tosort[nbdirs].name, str_size(dp->d_name) + 1, dp->d_name);
236 len = snprintf(buff, PATH_MAX - 1, "%s/%s", d, tosort[nbdirs].name);
237 buff[len] = '\0';
238
239 rc = vfs_stat_path(buff, &tosort[nbdirs++].s);
240 if (rc != EOK) {
241 printf("ls: skipping bogus node %s\n", buff);
242 printf("error=%s\n", str_error_name(rc));
243 goto out;
244 }
245 }
246
247 if (ls.sort) {
248 int (*compar)(const void *, const void *);
249 compar = ls.single_column ? ls_cmp_name : ls_cmp_type_name;
250 qsort(&tosort[0], nbdirs, sizeof(struct dir_elem_t), compar);
251 }
252
253 for (i = 0; i < nbdirs; i++)
254 ls.printer(&tosort[i]);
255
256 /* Populate the directory list. */
257 if (ls.recursive) {
258 tmp = (struct dir_elem_t *) realloc(*dir_list_ptr,
259 nbdirs * sizeof(struct dir_elem_t));
260 if (!tmp) {
261 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
262 goto out;
263 }
264 *dir_list_ptr = tmp;
265
266 for (i = 0; i < nbdirs; i++) {
267 (*dir_list_ptr)[i].name = str_dup(tosort[i].name);
268 if (!(*dir_list_ptr)[i].name) {
269 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
270 goto out;
271 }
272 }
273 }
274
275out:
276 for (i = 0; i < nbdirs; i++)
277 free(tosort[i].name);
278 free(tosort);
279 free(buff);
280
281 return nbdirs;
282}
283
284/** Visit a directory recursively.
285 *
286 * ls_recursive visits all the subdirectories recursively and
287 * prints the files and directories in them.
288 *
289 * @param path Path the current directory being visited.
290 * @param dirp Directory stream.
291 */
292static unsigned int ls_recursive(const char *path, DIR *dirp)
293{
294 int i, nbdirs, ret;
295 unsigned int scope;
296 char *subdir_path;
297 DIR *subdirp;
298 struct dir_elem_t *dir_list;
299
300 const char *const trailing_slash = "/";
301
302 nbdirs = 0;
303 dir_list = (struct dir_elem_t *) malloc(sizeof(struct dir_elem_t));
304
305 printf("\n%s:\n", path);
306
307 subdir_path = (char *) malloc(PATH_MAX);
308 if (!subdir_path) {
309 ret = CMD_FAILURE;
310 goto out;
311 }
312
313 nbdirs = ls_scan_dir(path, dirp, &dir_list);
314 if (nbdirs == -1) {
315 ret = CMD_FAILURE;
316 goto out;
317 }
318
319 for (i = 0; i < nbdirs; ++i) {
320 memset(subdir_path, 0, PATH_MAX);
321
322 if (str_size(subdir_path) + str_size(path) + 1 <= PATH_MAX)
323 str_append(subdir_path, PATH_MAX, path);
324 if (path[str_size(path) - 1] != '/' &&
325 str_size(subdir_path) + str_size(trailing_slash) + 1 <= PATH_MAX)
326 str_append(subdir_path, PATH_MAX, trailing_slash);
327 if (str_size(subdir_path) +
328 str_size(dir_list[i].name) + 1 <= PATH_MAX)
329 str_append(subdir_path, PATH_MAX, dir_list[i].name);
330
331 scope = ls_scope(subdir_path, &dir_list[i]);
332 switch (scope) {
333 case LS_FILE:
334 break;
335 case LS_DIR:
336 subdirp = opendir(subdir_path);
337 if (!subdirp) {
338 /* May have been deleted between scoping it and opening it */
339 cli_error(CL_EFAIL, "Could not stat %s", dir_list[i].name);
340 ret = CMD_FAILURE;
341 goto out;
342 }
343
344 ret = ls_recursive(subdir_path, subdirp);
345 closedir(subdirp);
346 if (ret == CMD_FAILURE)
347 goto out;
348 break;
349 case LS_BOGUS:
350 ret = CMD_FAILURE;
351 goto out;
352 }
353 }
354
355 ret = CMD_SUCCESS;
356
357out:
358 for (i = 0; i < nbdirs; i++)
359 free(dir_list[i].name);
360 free(dir_list);
361 free(subdir_path);
362
363 return ret;
364}
365
366static unsigned int ls_scope(const char *path, struct dir_elem_t *de)
367{
368 if (vfs_stat_path(path, &de->s) != EOK) {
369 cli_error(CL_ENOENT, "%s", path);
370 return LS_BOGUS;
371 }
372
373 if (de->s.is_file)
374 return LS_FILE;
375 else if (de->s.is_directory)
376 return LS_DIR;
377
378 return LS_BOGUS;
379}
380
381void help_cmd_ls(unsigned int level)
382{
383 if (level == HELP_SHORT) {
384 printf("`%s' lists files and directories.\n", cmdname);
385 } else {
386 help_cmd_ls(HELP_SHORT);
387 printf(
388 "Usage: %s [options] [path]\n"
389 "If not path is given, the current working directory is used.\n"
390 "Options:\n"
391 " -h, --help A short option summary\n"
392 " -u, --unsort Do not sort directory entries\n"
393 " -r, --recursive List subdirectories recursively\n"
394 " -e, --exact-size File sizes will be unformatted (raw bytes count)\n"
395 " -1, --single-column Only the names will be returned\n",
396 cmdname);
397 }
398
399 return;
400}
401
402int cmd_ls(char **argv)
403{
404 unsigned int argc;
405 struct dir_elem_t de;
406 DIR *dirp;
407 int c, opt_ind;
408 int ret = 0;
409 unsigned int scope;
410
411 if (!ls_start(&ls)) {
412 cli_error(CL_EFAIL, "%s: Could not initialize", cmdname);
413 return CMD_FAILURE;
414 }
415
416 argc = cli_count_args(argv);
417
418 c = 0;
419 optreset = 1;
420 optind = 0;
421 opt_ind = 0;
422
423 while (c != -1) {
424 c = getopt_long(argc, argv, "hure1", long_options, &opt_ind);
425 switch (c) {
426 case 'h':
427 help_cmd_ls(HELP_LONG);
428 return CMD_SUCCESS;
429 case 'u':
430 ls.sort = 0;
431 break;
432 case 'r':
433 ls.recursive = 1;
434 break;
435 case 'e':
436 ls.exact_size = true;
437 break;
438 case '1':
439 ls.single_column = true;
440 ls.printer = ls_print_single_column;
441 break;
442 }
443 }
444
445 argc -= optind;
446
447 de.name = (char *) malloc(PATH_MAX);
448 if (!de.name) {
449 cli_error(CL_ENOMEM, "%s: Out of memory", cmdname);
450 return CMD_FAILURE;
451 }
452 memset(de.name, 0, PATH_MAX);
453
454 if (argc == 0) {
455 if (vfs_cwd_get(de.name, PATH_MAX) != EOK) {
456 cli_error(CL_EFAIL, "%s: Failed determining working "
457 "directory", cmdname);
458 return CMD_FAILURE;
459 }
460 } else {
461 str_cpy(de.name, PATH_MAX, argv[optind]);
462 }
463
464 scope = ls_scope(de.name, &de);
465 switch (scope) {
466 case LS_FILE:
467 ls.printer(&de);
468 break;
469 case LS_DIR:
470 dirp = opendir(de.name);
471 if (!dirp) {
472 /* May have been deleted between scoping it and opening it */
473 cli_error(CL_EFAIL, "Could not stat %s", de.name);
474 free(de.name);
475 return CMD_FAILURE;
476 }
477 if (ls.recursive)
478 ret = ls_recursive(de.name, dirp);
479 else
480 ret = ls_scan_dir(de.name, dirp, NULL);
481
482 closedir(dirp);
483 break;
484 case LS_BOGUS:
485 return CMD_FAILURE;
486 }
487
488 free(de.name);
489
490 if (ret == -1 || ret == CMD_FAILURE)
491 return CMD_FAILURE;
492 else
493 return CMD_SUCCESS;
494}
Note: See TracBrowser for help on using the repository browser.