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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 87ba3ceb was 87ba3ceb, checked in by GitHub <noreply@…>, 7 years ago

adding new options to help message

  • Property mode set to 100644
File size: 11.0 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 { "well-formatted", no_argument, 0, 'w' },
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 int ls_cmp(const void *, const void *);
70static signed int ls_scan_dir(const char *, DIR *, struct dir_elem_t **);
71static unsigned int ls_recursive(const char *, DIR *);
72static unsigned int ls_scope(const char *, struct dir_elem_t *);
73
74static unsigned int ls_start(ls_job_t *ls)
75{
76 ls->recursive = 0;
77 ls->sort = 1;
78
79 ls->well_formatted = false;
80 ls->single_column = false;
81
82 return 1;
83}
84
85/** Print an entry.
86 *
87 * ls_print currently does nothing more than print the entry.
88 * In the future, we will likely pass the absolute path, and
89 * some sort of ls_options structure that controls how each
90 * entry is printed and what is printed about it.
91 *
92 * Now we just print basic DOS style lists.
93 *
94 * @param de Directory element.
95 */
96static void ls_print(struct dir_elem_t *de)
97{
98 if (!ls.single_column && de->s.is_file) {
99 if (ls.well_formatted) {
100 cap_spec_t cap;
101
102 cap_from_blocks(1, de->s.size, &cap);
103 cap_simplify(&cap);
104
105 char *rptr;
106 if (cap_format(&cap, &rptr) == EOK) {
107 printf("%-40s\t%s\n", de->name, rptr);
108 free(rptr);
109 //if there is a failure with cap_format we simply print out an unformatted size
110 return;
111 }
112
113 }
114
115 printf("%-40s\t%llu\n", de->name, (long long) de->s.size);
116 } else if (!ls.single_column && de->s.is_directory)
117 printf("%-40s\t<dir>\n", de->name);
118 else
119 printf("%-40s\n", de->name);
120}
121
122/** Compare 2 directory elements.
123 *
124 * It compares 2 elements of a directory : a file is considered
125 * as bigger than a directory, and if they have the same type,
126 * they are compared alphabetically.
127 *
128 * @param a Pointer to the structure of the first element.
129 * @param b Pointer to the structure of the second element.
130 *
131 * @return -1 if a < b, 1 otherwise.
132 */
133static int ls_cmp(const void *a, const void *b)
134{
135 struct dir_elem_t const *da = a;
136 struct dir_elem_t const *db = b;
137
138 if ((da->s.is_directory && db->s.is_file) ||
139 ((da->s.is_directory == db->s.is_directory) &&
140 str_cmp(da->name, db->name) < 0))
141 return -1;
142 else
143 return 1;
144}
145
146/** Scan a directory.
147 *
148 * Scan the content of a directory and print it.
149 *
150 * @param d Name of the directory.
151 * @param dirp Directory stream.
152 * @param sort 1 if the output must be sorted,
153 * 0 otherwise.
154 */
155static signed int ls_scan_dir(const char *d, DIR *dirp,
156 struct dir_elem_t **dir_list_ptr)
157{
158 int alloc_blocks = 20;
159 int i;
160 int nbdirs = 0;
161 errno_t rc;
162 int len;
163 char *buff;
164 struct dir_elem_t *tmp;
165 struct dir_elem_t *tosort;
166 struct dirent *dp;
167
168 if (!dirp)
169 return -1;
170
171 buff = (char *) malloc(PATH_MAX);
172 if (!buff) {
173 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
174 return -1;
175 }
176
177 tosort = (struct dir_elem_t *) malloc(alloc_blocks * sizeof(*tosort));
178 if (!tosort) {
179 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
180 free(buff);
181 return -1;
182 }
183
184 while ((dp = readdir(dirp))) {
185 if (nbdirs + 1 > alloc_blocks) {
186 alloc_blocks += alloc_blocks;
187
188 tmp = (struct dir_elem_t *) realloc(tosort,
189 alloc_blocks * sizeof(struct dir_elem_t));
190 if (!tmp) {
191 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
192 goto out;
193 }
194 tosort = tmp;
195 }
196
197 /* fill the name field */
198 tosort[nbdirs].name = (char *) malloc(str_size(dp->d_name) + 1);
199 if (!tosort[nbdirs].name) {
200 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
201 goto out;
202 }
203
204 str_cpy(tosort[nbdirs].name, str_size(dp->d_name) + 1, dp->d_name);
205 len = snprintf(buff, PATH_MAX - 1, "%s/%s", d, tosort[nbdirs].name);
206 buff[len] = '\0';
207
208 rc = vfs_stat_path(buff, &tosort[nbdirs++].s);
209 if (rc != EOK) {
210 printf("ls: skipping bogus node %s\n", buff);
211 printf("error=%s\n", str_error_name(rc));
212 goto out;
213 }
214 }
215
216 if (ls.sort)
217 qsort(&tosort[0], nbdirs, sizeof(struct dir_elem_t), ls_cmp);
218
219 for (i = 0; i < nbdirs; i++)
220 ls_print(&tosort[i]);
221
222 /* Populate the directory list. */
223 if (ls.recursive) {
224 tmp = (struct dir_elem_t *) realloc(*dir_list_ptr,
225 nbdirs * sizeof(struct dir_elem_t));
226 if (!tmp) {
227 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
228 goto out;
229 }
230 *dir_list_ptr = tmp;
231
232 for (i = 0; i < nbdirs; i++) {
233 (*dir_list_ptr)[i].name = str_dup(tosort[i].name);
234 if (!(*dir_list_ptr)[i].name) {
235 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
236 goto out;
237 }
238 }
239 }
240
241out:
242 for (i = 0; i < nbdirs; i++)
243 free(tosort[i].name);
244 free(tosort);
245 free(buff);
246
247 return nbdirs;
248}
249
250/** Visit a directory recursively.
251 *
252 * ls_recursive visits all the subdirectories recursively and
253 * prints the files and directories in them.
254 *
255 * @param path Path the current directory being visited.
256 * @param dirp Directory stream.
257 */
258static unsigned int ls_recursive(const char *path, DIR *dirp)
259{
260 int i, nbdirs, ret;
261 unsigned int scope;
262 char *subdir_path;
263 DIR *subdirp;
264 struct dir_elem_t *dir_list;
265
266 const char *const trailing_slash = "/";
267
268 nbdirs = 0;
269 dir_list = (struct dir_elem_t *) malloc(sizeof(struct dir_elem_t));
270
271 printf("\n%s:\n", path);
272
273 subdir_path = (char *) malloc(PATH_MAX);
274 if (!subdir_path) {
275 ret = CMD_FAILURE;
276 goto out;
277 }
278
279 nbdirs = ls_scan_dir(path, dirp, &dir_list);
280 if (nbdirs == -1) {
281 ret = CMD_FAILURE;
282 goto out;
283 }
284
285 for (i = 0; i < nbdirs; ++i) {
286 memset(subdir_path, 0, PATH_MAX);
287
288 if (str_size(subdir_path) + str_size(path) + 1 <= PATH_MAX)
289 str_append(subdir_path, PATH_MAX, path);
290 if (path[str_size(path) - 1] != '/' &&
291 str_size(subdir_path) + str_size(trailing_slash) + 1 <= PATH_MAX)
292 str_append(subdir_path, PATH_MAX, trailing_slash);
293 if (str_size(subdir_path) +
294 str_size(dir_list[i].name) + 1 <= PATH_MAX)
295 str_append(subdir_path, PATH_MAX, dir_list[i].name);
296
297 scope = ls_scope(subdir_path, &dir_list[i]);
298 switch (scope) {
299 case LS_FILE:
300 break;
301 case LS_DIR:
302 subdirp = opendir(subdir_path);
303 if (!subdirp) {
304 /* May have been deleted between scoping it and opening it */
305 cli_error(CL_EFAIL, "Could not stat %s", dir_list[i].name);
306 ret = CMD_FAILURE;
307 goto out;
308 }
309
310 ret = ls_recursive(subdir_path, subdirp);
311 closedir(subdirp);
312 if (ret == CMD_FAILURE)
313 goto out;
314 break;
315 case LS_BOGUS:
316 ret = CMD_FAILURE;
317 goto out;
318 }
319 }
320
321 ret = CMD_SUCCESS;
322
323out:
324 for (i = 0; i < nbdirs; i++)
325 free(dir_list[i].name);
326 free(dir_list);
327 free(subdir_path);
328
329 return ret;
330}
331
332static unsigned int ls_scope(const char *path, struct dir_elem_t *de)
333{
334 if (vfs_stat_path(path, &de->s) != EOK) {
335 cli_error(CL_ENOENT, "%s", path);
336 return LS_BOGUS;
337 }
338
339 if (de->s.is_file)
340 return LS_FILE;
341 else if (de->s.is_directory)
342 return LS_DIR;
343
344 return LS_BOGUS;
345}
346
347void help_cmd_ls(unsigned int level)
348{
349 if (level == HELP_SHORT) {
350 printf("`%s' lists files and directories.\n", cmdname);
351 } else {
352 help_cmd_ls(HELP_SHORT);
353 printf(
354 "Usage: %s [options] [path]\n"
355 "If not path is given, the current working directory is used.\n"
356 "Options:\n"
357 " -h, --help A short option summary\n"
358 " -u, --unsort Do not sort directory entries\n"
359 " -r, --recursive List subdirectories recursively\n"
360 " -w, --well-formatted File sizes will be formatted\n"
361 " -1, --single-column Only the names will be returned\n",
362 cmdname);
363 }
364
365 return;
366}
367
368int cmd_ls(char **argv)
369{
370 unsigned int argc;
371 struct dir_elem_t de;
372 DIR *dirp;
373 int c, opt_ind;
374 int ret = 0;
375 unsigned int scope;
376
377 if (!ls_start(&ls)) {
378 cli_error(CL_EFAIL, "%s: Could not initialize", cmdname);
379 return CMD_FAILURE;
380 }
381
382 argc = cli_count_args(argv);
383
384 c = 0;
385 optreset = 1;
386 optind = 0;
387 opt_ind = 0;
388
389 while (c != -1) {
390 c = getopt_long(argc, argv, "hurw1", long_options, &opt_ind);
391 switch (c) {
392 case 'h':
393 help_cmd_ls(HELP_LONG);
394 return CMD_SUCCESS;
395 case 'u':
396 ls.sort = 0;
397 break;
398 case 'r':
399 ls.recursive = 1;
400 break;
401 case 'w':
402 ls.well_formatted = true;
403 break;
404 case '1':
405 ls.single_column = true;
406 break;
407 }
408 }
409
410 argc -= optind;
411
412 de.name = (char *) malloc(PATH_MAX);
413 if (!de.name) {
414 cli_error(CL_ENOMEM, "%s: Out of memory", cmdname);
415 return CMD_FAILURE;
416 }
417 memset(de.name, 0, PATH_MAX);
418
419 if (argc == 0) {
420 if (vfs_cwd_get(de.name, PATH_MAX) != EOK) {
421 cli_error(CL_EFAIL, "%s: Failed determining working "
422 "directory", cmdname);
423 return CMD_FAILURE;
424 }
425 } else {
426 str_cpy(de.name, PATH_MAX, argv[optind]);
427 }
428
429 scope = ls_scope(de.name, &de);
430 switch (scope) {
431 case LS_FILE:
432 ls_print(&de);
433 break;
434 case LS_DIR:
435 dirp = opendir(de.name);
436 if (!dirp) {
437 /* May have been deleted between scoping it and opening it */
438 cli_error(CL_EFAIL, "Could not stat %s", de.name);
439 free(de.name);
440 return CMD_FAILURE;
441 }
442 if (ls.recursive)
443 ret = ls_recursive(de.name, dirp);
444 else
445 ret = ls_scan_dir(de.name, dirp, NULL);
446
447 closedir(dirp);
448 break;
449 case LS_BOGUS:
450 return CMD_FAILURE;
451 }
452
453 free(de.name);
454
455 if (ret == -1 || ret == CMD_FAILURE)
456 return CMD_FAILURE;
457 else
458 return CMD_SUCCESS;
459}
Note: See TracBrowser for help on using the repository browser.