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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since beb6b3d was e3a3e3ad, checked in by Jerome Portal <jeromeportal1@…>, 14 years ago

improve ls sorting, and add -h and -u options

  • Property mode set to 100644
File size: 8.2 KB
Line 
1/* Copyright (c) 2008, Tim Post <tinkertim@gmail.com>
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of the original program's authors nor the names of its
15 * contributors may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/* NOTE:
32 * This is a bit of an ugly hack, working around the absence of fstat / etc.
33 * As more stuff is completed and exposed in libc, this will improve */
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <unistd.h>
38#include <dirent.h>
39#include <fcntl.h>
40#include <getopt.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <str.h>
44#include <sort.h>
45
46#include "errors.h"
47#include "config.h"
48#include "util.h"
49#include "entry.h"
50#include "ls.h"
51#include "cmds.h"
52
53static const char *cmdname = "ls";
54
55static struct option const long_options[] = {
56 { "help", no_argument, 0, 'h' },
57 { "unsort", no_argument, 0, 'u' },
58 { 0, 0, 0, 0 }
59};
60
61/** Compare 2 directory elements.
62 *
63 * It compares 2 elements of a directory : a file is considered
64 * as lower than a directory, and if they have the same type,
65 * they are compared alphabetically.
66 *
67 * @param a Pointer to the structure of the first element.
68 * @param b Pointer to the structure of the second element.
69 * @param arg Pointer for an other and optionnal argument.
70 *
71 * @return -1 if a < b,
72 * 1 otherwise.
73 */
74static int ls_cmp(void *a, void *b, void *arg)
75{
76 int a_isdir = (*((struct dir_elem_t *)a)).isdir;
77 char * a_name = (*((struct dir_elem_t *)a)).name;
78
79 int b_isdir = (*((struct dir_elem_t *)b)).isdir;
80 char * b_name = (*((struct dir_elem_t *)b)).name;
81
82 if ((!a_isdir && b_isdir)
83 || (((!b_isdir && !a_isdir) || (b_isdir && a_isdir))
84 && str_cmp(a_name, b_name) < 0))
85 return -1;
86 else
87 return 1;
88}
89
90/** Scan a directory.
91 *
92 * Scan the content of a directory and print it.
93 *
94 * @param d Name of the directory.
95 * @param dirp Directory stream.
96 */
97static void ls_scan_dir(const char *d, DIR *dirp, int sort)
98{
99 int alloc_blocks = 20;
100 int i = 0;
101 int nbdirs = 0;
102 int rc = 0;
103 char * buff = NULL;
104 struct dir_elem_t * tmp = NULL;
105 struct dir_elem_t * tosort = NULL;
106 struct dirent * dp = NULL;
107 struct stat s;
108
109 if (! dirp)
110 return;
111
112 buff = (char *)malloc(PATH_MAX);
113 if (NULL == buff) {
114 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
115 return;
116 }
117 memset(buff, 0, sizeof(buff));
118
119 if (!sort) {
120 while ((dp = readdir(dirp))) {
121 memset(buff, 0, sizeof(buff));
122 /* Don't worry if inserting a double slash, this will be fixed by
123 * absolutize() later with subsequent calls to open() or readdir() */
124 snprintf(buff, PATH_MAX - 1, "%s/%s", d, dp->d_name);
125 ls_print(dp->d_name, buff);
126 }
127
128 free(buff);
129
130 return;
131 }
132
133 tosort = (struct dir_elem_t *)malloc(alloc_blocks*sizeof(struct dir_elem_t));
134 if (NULL == tosort) {
135 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
136 free(buff);
137 return;
138 }
139 memset(tosort, 0, sizeof(tosort));
140
141 while ((dp = readdir(dirp))) {
142 nbdirs++;
143
144 if (nbdirs > alloc_blocks) {
145 alloc_blocks += alloc_blocks;
146
147 tmp = (struct dir_elem_t *)realloc(tosort,
148 alloc_blocks*sizeof(struct dir_elem_t));
149 if (NULL == tmp) {
150 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
151 for(i=0;i<(nbdirs-1);i++) {
152 free(tosort[i].name);
153 }
154 free(tosort);
155 free(buff);
156 return;
157 }
158
159 tosort = tmp;
160 }
161
162 // fill the name field
163 tosort[nbdirs-1].name = (char *)malloc(str_length(dp->d_name)+1);
164 if (NULL == tosort[nbdirs-1].name) {
165 cli_error(CL_ENOMEM, "ls: failed to scan %s", d);
166 for(i=0;i<(nbdirs-1);i++) {
167 free(tosort[i].name);
168 }
169 free(tosort);
170 free(buff);
171 return;
172 }
173
174 memset(tosort[nbdirs-1].name, 0, str_length(dp->d_name)+1);
175 str_cpy(tosort[nbdirs-1].name, str_length(dp->d_name)+1, dp->d_name);
176
177 // fill the isdir field
178 memset(buff, 0, sizeof(buff));
179 snprintf(buff, PATH_MAX - 1, "%s/%s", d, tosort[nbdirs-1].name);
180
181 rc = stat(buff, &s);
182 if (rc != 0) {
183 printf("ls: skipping bogus node %s\n", buff);
184 printf("rc=%d\n", rc);
185 for(i=0;i<nbdirs;i++) {
186 free(tosort[i].name);
187 }
188 free(tosort);
189 free(buff);
190 return;
191 }
192
193 tosort[nbdirs-1].isdir = s.is_directory ? 1 : 0;
194 }
195
196 if (!qsort(&tosort[0], nbdirs, sizeof(struct dir_elem_t), ls_cmp, NULL)) {
197 printf("Sorting error.\n");
198 }
199
200 for(i=0;i<nbdirs;i++) {
201 memset(buff, 0, sizeof(buff));
202 /* Don't worry if inserting a double slash, this will be fixed by
203 * absolutize() later with subsequent calls to open() or readdir() */
204 snprintf(buff, PATH_MAX - 1, "%s/%s", d, tosort[i].name);
205 ls_print(tosort[i].name, buff);
206 free(tosort[i].name);
207 }
208
209 free(tosort);
210 free(buff);
211
212 return;
213}
214
215/** Print an entry.
216 *
217 * ls_print currently does nothing more than print the entry.
218 * In the future, we will likely pass the absolute path, and
219 * some sort of ls_options structure that controls how each
220 * entry is printed and what is printed about it.
221 *
222 * Now we just print basic DOS style lists.
223 *
224 * @param name Name of the entry.
225 * @param pathname Path of the entry.
226 */
227static void ls_print(const char *name, const char *pathname)
228{
229 struct stat s;
230 int rc;
231
232 rc = stat(pathname, &s);
233 if (rc != 0) {
234 /* Odd chance it was deleted from the time readdir() found it */
235 printf("ls: skipping bogus node %s\n", pathname);
236 printf("rc=%d\n", rc);
237 return;
238 }
239
240 if (s.is_file)
241 printf("%-40s\t%llu\n", name, (long long) s.size);
242 else if (s.is_directory)
243 printf("%-40s\t<dir>\n", name);
244 else
245 printf("%-40s\n", name);
246
247 return;
248}
249
250void help_cmd_ls(unsigned int level)
251{
252 if (level == HELP_SHORT) {
253 printf("`%s' lists files and directories.\n", cmdname);
254 } else {
255 help_cmd_ls(HELP_SHORT);
256 printf(
257 "Usage: %s [options] [path]\n"
258 "If not path is given, the current working directory is used.\n"
259 "Options:\n"
260 " -h, --help A short option summary\n"
261 " -u, --unsort Do not sort directory entries\n",
262 cmdname);
263 }
264
265 return;
266}
267
268int cmd_ls(char **argv)
269{
270 unsigned int argc;
271 struct stat s;
272 char *buff;
273 DIR *dirp;
274 int c, opt_ind;
275 int sort = 1;
276
277 argc = cli_count_args(argv);
278
279 for (c = 0, optind = 0, opt_ind = 0; c != -1;) {
280 c = getopt_long(argc, argv, "hu", long_options, &opt_ind);
281 switch (c) {
282 case 'h':
283 help_cmd_ls(HELP_LONG);
284 return CMD_SUCCESS;
285 case 'u':
286 sort = 0;
287 break;
288 }
289 }
290
291 int dir = (int)argc > optind ? (int)argc-1 : optind-1;
292 argc -= (optind-1);
293
294 buff = (char *) malloc(PATH_MAX);
295 if (NULL == buff) {
296 cli_error(CL_ENOMEM, "%s: ", cmdname);
297 return CMD_FAILURE;
298 }
299 memset(buff, 0, sizeof(buff));
300
301 if (argc == 1)
302 getcwd(buff, PATH_MAX);
303 else
304 str_cpy(buff, PATH_MAX, argv[dir]);
305
306 if (stat(buff, &s)) {
307 cli_error(CL_ENOENT, buff);
308 free(buff);
309 return CMD_FAILURE;
310 }
311
312 if (s.is_file) {
313 ls_print(buff, buff);
314 } else {
315 dirp = opendir(buff);
316 if (!dirp) {
317 /* May have been deleted between scoping it and opening it */
318 cli_error(CL_EFAIL, "Could not stat %s", buff);
319 free(buff);
320 return CMD_FAILURE;
321 }
322 ls_scan_dir(buff, dirp, sort);
323 closedir(dirp);
324 }
325
326 free(buff);
327
328 return CMD_SUCCESS;
329}
330
Note: See TracBrowser for help on using the repository browser.