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

Last change on this file was c24b0dcb, checked in by Jakub Jermar <jakub@…>, 6 years ago

Rename capacity-related 'cap' to 'capa'

This allows to use 'cap' for capabilities.

  • Property mode set to 100644
File size: 12.1 KB
RevLine 
[36ab7c7]1/*
2 * Copyright (c) 2008 Tim Post
[216d6fc]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
[36ab7c7]6 * modification, are permitted provided that the following conditions
7 * are met:
[216d6fc]8 *
[36ab7c7]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.
[216d6fc]16 *
[36ab7c7]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.
[216d6fc]27 */
28
[7c3fb9b]29/*
30 * NOTE:
[216d6fc]31 * This is a bit of an ugly hack, working around the absence of fstat / etc.
[7c3fb9b]32 * As more stuff is completed and exposed in libc, this will improve
33 */
[216d6fc]34
[6afc9d7]35#include <errno.h>
[dd8ab1c]36#include <str_error.h>
[216d6fc]37#include <stdio.h>
38#include <stdlib.h>
[8d2dd7f2]39#include <stdint.h>
[216d6fc]40#include <dirent.h>
[e3a3e3ad]41#include <getopt.h>
[23a0368]42#include <vfs/vfs.h>
[19f857a]43#include <str.h>
[c24b0dcb]44#include <capa.h>
[216d6fc]45
[4567b31c]46#include "ls.h"
[216d6fc]47#include "errors.h"
48#include "config.h"
49#include "util.h"
50#include "entry.h"
51#include "cmds.h"
52
[a000878c]53static const char *cmdname = "ls";
[216d6fc]54
[4567b31c]55static ls_job_t ls;
56
[e3a3e3ad]57static struct option const long_options[] = {
58 { "help", no_argument, 0, 'h' },
[cfc3027]59 { "unsort", no_argument, 0, 'u' },
[9bdf1f2a]60 { "recursive", no_argument, 0, 'r' },
[0296ee1]61 { "exact-size", no_argument, 0, 'e' },
[a20a451]62 { "single-column", no_argument, 0, '1' },
[e3a3e3ad]63 { 0, 0, 0, 0 }
64};
65
[4567b31c]66/* Prototypes for the ls command, excluding entry points. */
67static unsigned int ls_start(ls_job_t *);
[0feb39b]68static errno_t ls_print(struct dir_elem_t *);
69static errno_t ls_print_single_column(struct dir_elem_t *);
[a27e370]70static int ls_cmp_type_name(const void *, const void *);
71static int ls_cmp_name(const void *, const void *);
[4567b31c]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
[41453d93]81 ls->exact_size = false;
[a20a451]82 ls->single_column = false;
[a27e370]83 ls->printer = ls_print;
[4567b31c]84 return 1;
85}
86
[ba63912]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 */
[0feb39b]98static errno_t ls_print(struct dir_elem_t *de)
[ba63912]99{
[0296ee1]100 int width = 13;
[a27e370]101
102 if (de->s.is_file) {
[f4f4b95]103 if (ls.exact_size) {
104 printf("%-40s\t%*llu\n", de->name, width, (long long) de->s.size);
[c878693]105 return EOK;
[a20a451]106 }
[c878693]107
[c24b0dcb]108 capa_spec_t capa;
109 capa_from_blocks(de->s.size, 1, &capa);
110 capa_simplify(&capa);
[c878693]111
112 char *rptr;
[c24b0dcb]113 errno_t rc = capa_format(&capa, &rptr);
[c878693]114 if (rc != EOK) {
115 return rc;
116 }
117
118 char *sep = str_rchr(rptr, ' ');
119 if (sep == NULL) {
120 free(rptr);
121 return ENOENT;
122 }
123
124 *sep = '\0';
125
126 printf("%-40s\t%*s %2s\n", de->name, width - 3, rptr, sep + 1);
127 free(rptr);
[a27e370]128 } else if (de->s.is_directory)
[0296ee1]129 printf("%-40s\t%*s\n", de->name, width, "<dir>");
[ba63912]130 else
131 printf("%-40s\n", de->name);
[f4f4b95]132
[0feb39b]133 return EOK;
[ba63912]134}
135
[0feb39b]136static errno_t ls_print_single_column(struct dir_elem_t *de)
[a27e370]137{
138 if (de->s.is_file) {
139 printf("%s\n", de->name);
140 } else {
141 printf("%s/\n", de->name);
142 }
[f4f4b95]143
[0feb39b]144 return EOK;
[ba63912]145}
146
[e3a3e3ad]147/** Compare 2 directory elements.
[a716a75]148 *
[e3a3e3ad]149 * It compares 2 elements of a directory : a file is considered
[ba63912]150 * as bigger than a directory, and if they have the same type,
[e3a3e3ad]151 * they are compared alphabetically.
[a716a75]152 *
[e3a3e3ad]153 * @param a Pointer to the structure of the first element.
154 * @param b Pointer to the structure of the second element.
[a716a75]155 *
[ba63912]156 * @return -1 if a < b, 1 otherwise.
[a716a75]157 */
[a27e370]158static int ls_cmp_type_name(const void *a, const void *b)
[a716a75]159{
[f2460a50]160 struct dir_elem_t const *da = a;
161 struct dir_elem_t const *db = b;
[a35b458]162
[ba63912]163 if ((da->s.is_directory && db->s.is_file) ||
164 ((da->s.is_directory == db->s.is_directory) &&
165 str_cmp(da->name, db->name) < 0))
[e3a3e3ad]166 return -1;
167 else
168 return 1;
[a716a75]169}
170
[a27e370]171/** Compare directories/files per name
172 *
173 * This comparision ignores the type of
174 * the node. Sorted will strictly by name.
175 *
176 */
177static int ls_cmp_name(const void *a, const void *b)
178{
179 struct dir_elem_t const *da = a;
180 struct dir_elem_t const *db = b;
181
182 return str_cmp(da->name, db->name);
183}
184
[a716a75]185/** Scan a directory.
186 *
187 * Scan the content of a directory and print it.
188 *
189 * @param d Name of the directory.
190 * @param dirp Directory stream.
[1e2629f]191 * @param sort 1 if the output must be sorted,
192 * 0 otherwise.
[a716a75]193 */
[1b20da0]194static signed int ls_scan_dir(const char *d, DIR *dirp,
[4567b31c]195 struct dir_elem_t **dir_list_ptr)
[216d6fc]196{
[a716a75]197 int alloc_blocks = 20;
[ba63912]198 int i;
[a716a75]199 int nbdirs = 0;
[b7fd2a0]200 errno_t rc;
[ba63912]201 int len;
202 char *buff;
203 struct dir_elem_t *tmp;
204 struct dir_elem_t *tosort;
205 struct dirent *dp;
[a35b458]206
[ba63912]207 if (!dirp)
[4567b31c]208 return -1;
[216d6fc]209
[ba63912]210 buff = (char *) malloc(PATH_MAX);
211 if (!buff) {
[216d6fc]212 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
[4567b31c]213 return -1;
[216d6fc]214 }
[a35b458]215
[ba63912]216 tosort = (struct dir_elem_t *) malloc(alloc_blocks * sizeof(*tosort));
217 if (!tosort) {
[a716a75]218 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
[e3a3e3ad]219 free(buff);
[4567b31c]220 return -1;
[a716a75]221 }
[a35b458]222
[216d6fc]223 while ((dp = readdir(dirp))) {
[ba63912]224 if (nbdirs + 1 > alloc_blocks) {
[a716a75]225 alloc_blocks += alloc_blocks;
[a35b458]226
[ba63912]227 tmp = (struct dir_elem_t *) realloc(tosort,
228 alloc_blocks * sizeof(struct dir_elem_t));
229 if (!tmp) {
[a716a75]230 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
[ba63912]231 goto out;
[a716a75]232 }
233 tosort = tmp;
234 }
[a35b458]235
[ba63912]236 /* fill the name field */
[a701812]237 tosort[nbdirs].name = (char *) malloc(str_size(dp->d_name) + 1);
[ba63912]238 if (!tosort[nbdirs].name) {
[e3a3e3ad]239 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
[ba63912]240 goto out;
[e3a3e3ad]241 }
[a701812]242
243 str_cpy(tosort[nbdirs].name, str_size(dp->d_name) + 1, dp->d_name);
[ba63912]244 len = snprintf(buff, PATH_MAX - 1, "%s/%s", d, tosort[nbdirs].name);
245 buff[len] = '\0';
246
[23a0368]247 rc = vfs_stat_path(buff, &tosort[nbdirs++].s);
248 if (rc != EOK) {
[e3a3e3ad]249 printf("ls: skipping bogus node %s\n", buff);
[dd8ab1c]250 printf("error=%s\n", str_error_name(rc));
[ba63912]251 goto out;
[a716a75]252 }
253 }
[a35b458]254
[a27e370]255 if (ls.sort) {
256 int (*compar)(const void *, const void *);
257 compar = ls.single_column ? ls_cmp_name : ls_cmp_type_name;
258 qsort(&tosort[0], nbdirs, sizeof(struct dir_elem_t), compar);
259 }
[a35b458]260
[f4f4b95]261 for (i = 0; i < nbdirs; i++) {
[0feb39b]262 if (ls.printer(&tosort[i]) != EOK) {
[f4f4b95]263 cli_error(CL_ENOMEM, "%s: Out of memory", cmdname);
264 goto out;
265 }
266 }
[4567b31c]267
268 /* Populate the directory list. */
[59cce22]269 if (ls.recursive && nbdirs > 0) {
[1b20da0]270 tmp = (struct dir_elem_t *) realloc(*dir_list_ptr,
[4567b31c]271 nbdirs * sizeof(struct dir_elem_t));
272 if (!tmp) {
273 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
274 goto out;
[1b20da0]275 }
[4567b31c]276 *dir_list_ptr = tmp;
277
278 for (i = 0; i < nbdirs; i++) {
279 (*dir_list_ptr)[i].name = str_dup(tosort[i].name);
280 if (!(*dir_list_ptr)[i].name) {
281 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
282 goto out;
283 }
284 }
285 }
[a35b458]286
[ba63912]287out:
[1433ecda]288 for (i = 0; i < nbdirs; i++)
[ba63912]289 free(tosort[i].name);
[a716a75]290 free(tosort);
[216d6fc]291 free(buff);
[4567b31c]292
293 return nbdirs;
294}
295
296/** Visit a directory recursively.
297 *
298 * ls_recursive visits all the subdirectories recursively and
299 * prints the files and directories in them.
300 *
301 * @param path Path the current directory being visited.
302 * @param dirp Directory stream.
303 */
304static unsigned int ls_recursive(const char *path, DIR *dirp)
305{
306 int i, nbdirs, ret;
307 unsigned int scope;
308 char *subdir_path;
309 DIR *subdirp;
310 struct dir_elem_t *dir_list;
[a35b458]311
[1433ecda]312 const char *const trailing_slash = "/";
[4567b31c]313
314 nbdirs = 0;
315 dir_list = (struct dir_elem_t *) malloc(sizeof(struct dir_elem_t));
316
317 printf("\n%s:\n", path);
318
319 subdir_path = (char *) malloc(PATH_MAX);
320 if (!subdir_path) {
321 ret = CMD_FAILURE;
322 goto out;
323 }
324
[1b20da0]325 nbdirs = ls_scan_dir(path, dirp, &dir_list);
[4567b31c]326 if (nbdirs == -1) {
327 ret = CMD_FAILURE;
328 goto out;
329 }
330
331 for (i = 0; i < nbdirs; ++i) {
332 memset(subdir_path, 0, PATH_MAX);
333
334 if (str_size(subdir_path) + str_size(path) + 1 <= PATH_MAX)
335 str_append(subdir_path, PATH_MAX, path);
[1433ecda]336 if (path[str_size(path) - 1] != '/' &&
[4567b31c]337 str_size(subdir_path) + str_size(trailing_slash) + 1 <= PATH_MAX)
338 str_append(subdir_path, PATH_MAX, trailing_slash);
339 if (str_size(subdir_path) +
340 str_size(dir_list[i].name) + 1 <= PATH_MAX)
341 str_append(subdir_path, PATH_MAX, dir_list[i].name);
342
343 scope = ls_scope(subdir_path, &dir_list[i]);
344 switch (scope) {
345 case LS_FILE:
346 break;
347 case LS_DIR:
348 subdirp = opendir(subdir_path);
349 if (!subdirp) {
350 /* May have been deleted between scoping it and opening it */
351 cli_error(CL_EFAIL, "Could not stat %s", dir_list[i].name);
352 ret = CMD_FAILURE;
353 goto out;
354 }
355
356 ret = ls_recursive(subdir_path, subdirp);
357 closedir(subdirp);
358 if (ret == CMD_FAILURE)
359 goto out;
360 break;
361 case LS_BOGUS:
362 ret = CMD_FAILURE;
363 goto out;
[1b20da0]364 }
[4567b31c]365 }
[a35b458]366
[1b20da0]367 ret = CMD_SUCCESS;
[4567b31c]368
369out:
370 for (i = 0; i < nbdirs; i++)
371 free(dir_list[i].name);
372 free(dir_list);
373 free(subdir_path);
374
375 return ret;
376}
377
378static unsigned int ls_scope(const char *path, struct dir_elem_t *de)
379{
[23a0368]380 if (vfs_stat_path(path, &de->s) != EOK) {
[26c02b22]381 cli_error(CL_ENOENT, "%s", path);
[4567b31c]382 return LS_BOGUS;
383 }
384
385 if (de->s.is_file)
386 return LS_FILE;
387 else if (de->s.is_directory)
388 return LS_DIR;
389
390 return LS_BOGUS;
[216d6fc]391}
392
[809813d]393void help_cmd_ls(unsigned int level)
[216d6fc]394{
395 if (level == HELP_SHORT) {
396 printf("`%s' lists files and directories.\n", cmdname);
397 } else {
398 help_cmd_ls(HELP_SHORT);
[e3a3e3ad]399 printf(
[1433ecda]400 "Usage: %s [options] [path]\n"
401 "If not path is given, the current working directory is used.\n"
402 "Options:\n"
[87ba3ceb]403 " -h, --help A short option summary\n"
404 " -u, --unsort Do not sort directory entries\n"
405 " -r, --recursive List subdirectories recursively\n"
[0296ee1]406 " -e, --exact-size File sizes will be unformatted (raw bytes count)\n"
[87ba3ceb]407 " -1, --single-column Only the names will be returned\n",
[1433ecda]408 cmdname);
[216d6fc]409 }
410
[809813d]411 return;
[216d6fc]412}
413
[809813d]414int cmd_ls(char **argv)
[216d6fc]415{
416 unsigned int argc;
[ba63912]417 struct dir_elem_t de;
[216d6fc]418 DIR *dirp;
[e3a3e3ad]419 int c, opt_ind;
[4567b31c]420 int ret = 0;
421 unsigned int scope;
422
423 if (!ls_start(&ls)) {
424 cli_error(CL_EFAIL, "%s: Could not initialize", cmdname);
425 return CMD_FAILURE;
426 }
[216d6fc]427
[43e02a6]428 argc = cli_count_args(argv);
[a35b458]429
[948222e4]430 c = 0;
431 optreset = 1;
432 optind = 0;
433 opt_ind = 0;
434
435 while (c != -1) {
[0296ee1]436 c = getopt_long(argc, argv, "hure1", long_options, &opt_ind);
[e3a3e3ad]437 switch (c) {
438 case 'h':
439 help_cmd_ls(HELP_LONG);
440 return CMD_SUCCESS;
441 case 'u':
[4567b31c]442 ls.sort = 0;
443 break;
[9bdf1f2a]444 case 'r':
[4567b31c]445 ls.recursive = 1;
[e3a3e3ad]446 break;
[0296ee1]447 case 'e':
[41453d93]448 ls.exact_size = true;
[a20a451]449 break;
450 case '1':
451 ls.single_column = true;
[a27e370]452 ls.printer = ls_print_single_column;
[a20a451]453 break;
[e3a3e3ad]454 }
455 }
[6afc9d7]456
[cfc3027]457 argc -= optind;
[6afc9d7]458
[ba63912]459 de.name = (char *) malloc(PATH_MAX);
460 if (!de.name) {
[6afc9d7]461 cli_error(CL_ENOMEM, "%s: Out of memory", cmdname);
[216d6fc]462 return CMD_FAILURE;
463 }
[4567b31c]464 memset(de.name, 0, PATH_MAX);
[6afc9d7]465
466 if (argc == 0) {
[d96d9bc]467 if (vfs_cwd_get(de.name, PATH_MAX) != EOK) {
[6afc9d7]468 cli_error(CL_EFAIL, "%s: Failed determining working "
469 "directory", cmdname);
470 return CMD_FAILURE;
471 }
472 } else {
[cfc3027]473 str_cpy(de.name, PATH_MAX, argv[optind]);
[6afc9d7]474 }
[415c7e0d]475
[4567b31c]476 scope = ls_scope(de.name, &de);
477 switch (scope) {
478 case LS_FILE:
[0feb39b]479 if (ls.printer(&de) != EOK) {
[f4f4b95]480 cli_error(CL_ENOMEM, "%s: Out of memory", cmdname);
481 return CMD_FAILURE;
482 }
[4567b31c]483 break;
484 case LS_DIR:
[ba63912]485 dirp = opendir(de.name);
[415c7e0d]486 if (!dirp) {
[216d6fc]487 /* May have been deleted between scoping it and opening it */
[ba63912]488 cli_error(CL_EFAIL, "Could not stat %s", de.name);
489 free(de.name);
[216d6fc]490 return CMD_FAILURE;
491 }
[4567b31c]492 if (ls.recursive)
493 ret = ls_recursive(de.name, dirp);
[1b20da0]494 else
[4567b31c]495 ret = ls_scan_dir(de.name, dirp, NULL);
496
[216d6fc]497 closedir(dirp);
[4567b31c]498 break;
499 case LS_BOGUS:
500 return CMD_FAILURE;
[216d6fc]501 }
502
[ba63912]503 free(de.name);
[216d6fc]504
[4567b31c]505 if (ret == -1 || ret == CMD_FAILURE)
506 return CMD_FAILURE;
507 else
508 return CMD_SUCCESS;
[216d6fc]509}
Note: See TracBrowser for help on using the repository browser.