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

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

qsort() compliant with C standard.

  • Property mode set to 100644
File size: 10.2 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
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
[6afc9d7]33#include <errno.h>
[216d6fc]34#include <stdio.h>
35#include <stdlib.h>
[8d2dd7f2]36#include <stdint.h>
[216d6fc]37#include <dirent.h>
[e3a3e3ad]38#include <getopt.h>
[23a0368]39#include <vfs/vfs.h>
[19f857a]40#include <str.h>
[216d6fc]41
[4567b31c]42#include "ls.h"
[216d6fc]43#include "errors.h"
44#include "config.h"
45#include "util.h"
46#include "entry.h"
47#include "cmds.h"
48
[a000878c]49static const char *cmdname = "ls";
[216d6fc]50
[4567b31c]51static ls_job_t ls;
52
[e3a3e3ad]53static struct option const long_options[] = {
54 { "help", no_argument, 0, 'h' },
[cfc3027]55 { "unsort", no_argument, 0, 'u' },
[9bdf1f2a]56 { "recursive", no_argument, 0, 'r' },
[e3a3e3ad]57 { 0, 0, 0, 0 }
58};
59
[4567b31c]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 *);
[f2460a50]63static int ls_cmp(const void *, const void *);
[4567b31c]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
[ba63912]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
[e3a3e3ad]97/** Compare 2 directory elements.
[a716a75]98 *
[e3a3e3ad]99 * It compares 2 elements of a directory : a file is considered
[ba63912]100 * as bigger than a directory, and if they have the same type,
[e3a3e3ad]101 * they are compared alphabetically.
[a716a75]102 *
[e3a3e3ad]103 * @param a Pointer to the structure of the first element.
104 * @param b Pointer to the structure of the second element.
[a716a75]105 *
[ba63912]106 * @return -1 if a < b, 1 otherwise.
[a716a75]107 */
[f2460a50]108static int ls_cmp(const void *a, const void *b)
[a716a75]109{
[f2460a50]110 struct dir_elem_t const *da = a;
111 struct dir_elem_t const *db = b;
[a716a75]112
[ba63912]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))
[e3a3e3ad]116 return -1;
117 else
118 return 1;
[a716a75]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.
[1e2629f]127 * @param sort 1 if the output must be sorted,
128 * 0 otherwise.
[a716a75]129 */
[4567b31c]130static signed int ls_scan_dir(const char *d, DIR *dirp,
131 struct dir_elem_t **dir_list_ptr)
[216d6fc]132{
[a716a75]133 int alloc_blocks = 20;
[ba63912]134 int i;
[a716a75]135 int nbdirs = 0;
[ba63912]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;
[e3a3e3ad]142
[ba63912]143 if (!dirp)
[4567b31c]144 return -1;
[216d6fc]145
[ba63912]146 buff = (char *) malloc(PATH_MAX);
147 if (!buff) {
[216d6fc]148 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
[4567b31c]149 return -1;
[216d6fc]150 }
[e3a3e3ad]151
[ba63912]152 tosort = (struct dir_elem_t *) malloc(alloc_blocks * sizeof(*tosort));
153 if (!tosort) {
[a716a75]154 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
[e3a3e3ad]155 free(buff);
[4567b31c]156 return -1;
[a716a75]157 }
158
[216d6fc]159 while ((dp = readdir(dirp))) {
[ba63912]160 if (nbdirs + 1 > alloc_blocks) {
[a716a75]161 alloc_blocks += alloc_blocks;
162
[ba63912]163 tmp = (struct dir_elem_t *) realloc(tosort,
164 alloc_blocks * sizeof(struct dir_elem_t));
165 if (!tmp) {
[a716a75]166 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
[ba63912]167 goto out;
[a716a75]168 }
169 tosort = tmp;
170 }
171
[ba63912]172 /* fill the name field */
[a701812]173 tosort[nbdirs].name = (char *) malloc(str_size(dp->d_name) + 1);
[ba63912]174 if (!tosort[nbdirs].name) {
[e3a3e3ad]175 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
[ba63912]176 goto out;
[e3a3e3ad]177 }
[a701812]178
179 str_cpy(tosort[nbdirs].name, str_size(dp->d_name) + 1, dp->d_name);
[ba63912]180 len = snprintf(buff, PATH_MAX - 1, "%s/%s", d, tosort[nbdirs].name);
181 buff[len] = '\0';
182
[23a0368]183 rc = vfs_stat_path(buff, &tosort[nbdirs++].s);
184 if (rc != EOK) {
[e3a3e3ad]185 printf("ls: skipping bogus node %s\n", buff);
[23a0368]186 printf("error=%d\n", rc);
[ba63912]187 goto out;
[a716a75]188 }
189 }
190
[f2460a50]191 if (ls.sort)
192 qsort(&tosort[0], nbdirs, sizeof(struct dir_elem_t), ls_cmp);
[a716a75]193
[ba63912]194 for (i = 0; i < nbdirs; i++)
195 ls_print(&tosort[i]);
[4567b31c]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 }
[a716a75]215
[ba63912]216out:
217 for(i = 0; i < nbdirs; i++)
218 free(tosort[i].name);
[a716a75]219 free(tosort);
[216d6fc]220 free(buff);
[4567b31c]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{
[23a0368]309 if (vfs_stat_path(path, &de->s) != EOK) {
[26c02b22]310 cli_error(CL_ENOENT, "%s", path);
[4567b31c]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;
[216d6fc]320}
321
[809813d]322void help_cmd_ls(unsigned int level)
[216d6fc]323{
324 if (level == HELP_SHORT) {
325 printf("`%s' lists files and directories.\n", cmdname);
326 } else {
327 help_cmd_ls(HELP_SHORT);
[e3a3e3ad]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"
[4567b31c]333 " -u, --unsort Do not sort directory entries\n"
[9bdf1f2a]334 " -r, --recursive List subdirectories recursively\n",
[e3a3e3ad]335 cmdname);
[216d6fc]336 }
337
[809813d]338 return;
[216d6fc]339}
340
[809813d]341int cmd_ls(char **argv)
[216d6fc]342{
343 unsigned int argc;
[ba63912]344 struct dir_elem_t de;
[216d6fc]345 DIR *dirp;
[e3a3e3ad]346 int c, opt_ind;
[4567b31c]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 }
[216d6fc]354
[43e02a6]355 argc = cli_count_args(argv);
[e3a3e3ad]356
[86cf96d]357 for (c = 0, optreset = 1, optind = 0, opt_ind = 0; c != -1;) {
[9bdf1f2a]358 c = getopt_long(argc, argv, "hur", long_options, &opt_ind);
[e3a3e3ad]359 switch (c) {
360 case 'h':
361 help_cmd_ls(HELP_LONG);
362 return CMD_SUCCESS;
363 case 'u':
[4567b31c]364 ls.sort = 0;
365 break;
[9bdf1f2a]366 case 'r':
[4567b31c]367 ls.recursive = 1;
[e3a3e3ad]368 break;
369 }
370 }
[6afc9d7]371
[cfc3027]372 argc -= optind;
[6afc9d7]373
[ba63912]374 de.name = (char *) malloc(PATH_MAX);
375 if (!de.name) {
[6afc9d7]376 cli_error(CL_ENOMEM, "%s: Out of memory", cmdname);
[216d6fc]377 return CMD_FAILURE;
378 }
[4567b31c]379 memset(de.name, 0, PATH_MAX);
[6afc9d7]380
381 if (argc == 0) {
[d96d9bc]382 if (vfs_cwd_get(de.name, PATH_MAX) != EOK) {
[6afc9d7]383 cli_error(CL_EFAIL, "%s: Failed determining working "
384 "directory", cmdname);
385 return CMD_FAILURE;
386 }
387 } else {
[cfc3027]388 str_cpy(de.name, PATH_MAX, argv[optind]);
[6afc9d7]389 }
[415c7e0d]390
[4567b31c]391 scope = ls_scope(de.name, &de);
392 switch (scope) {
393 case LS_FILE:
[ba63912]394 ls_print(&de);
[4567b31c]395 break;
396 case LS_DIR:
[ba63912]397 dirp = opendir(de.name);
[415c7e0d]398 if (!dirp) {
[216d6fc]399 /* May have been deleted between scoping it and opening it */
[ba63912]400 cli_error(CL_EFAIL, "Could not stat %s", de.name);
401 free(de.name);
[216d6fc]402 return CMD_FAILURE;
403 }
[4567b31c]404 if (ls.recursive)
405 ret = ls_recursive(de.name, dirp);
406 else
407 ret = ls_scan_dir(de.name, dirp, NULL);
408
[216d6fc]409 closedir(dirp);
[4567b31c]410 break;
411 case LS_BOGUS:
412 return CMD_FAILURE;
[216d6fc]413 }
414
[ba63912]415 free(de.name);
[216d6fc]416
[4567b31c]417 if (ret == -1 || ret == CMD_FAILURE)
418 return CMD_FAILURE;
419 else
420 return CMD_SUCCESS;
[216d6fc]421}
422
Note: See TracBrowser for help on using the repository browser.