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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1afa94d was f2460a50, checked in by Jiri Svoboda <jiri@…>, 8 years ago

qsort() compliant with C standard.

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