1 /*
2 * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of KTH nor the names of its contributors may be
18 * used to endorse or promote products derived from this software without
19 * specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32
33 #ifndef TEST
34 #include "ftpd_locl.h"
35
36 RCSID("$Id$");
37
38 #else
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <time.h>
43 #include <dirent.h>
44 #include <sys/stat.h>
45 #include <unistd.h>
46 #include <pwd.h>
47 #include <grp.h>
48 #include <errno.h>
49
50 #define sec_fprintf2 fprintf
51 #define sec_fflush fflush
52 static void list_files(FILE *out, const char **files, int n_files, int flags);
53 static int parse_flags(const char *options);
54
55 int
main(int argc,char ** argv)56 main(int argc, char **argv)
57 {
58 int i = 1;
59 int flags;
60 if(argc > 1 && argv[1][0] == '-') {
61 flags = parse_flags(argv[1]);
62 i = 2;
63 } else
64 flags = parse_flags(NULL);
65
66 list_files(stdout, (const char **)argv + i, argc - i, flags);
67 return 0;
68 }
69 #endif
70
71 struct fileinfo {
72 struct stat st;
73 int inode;
74 int bsize;
75 char mode[11];
76 int n_link;
77 char *user;
78 char *group;
79 char *size;
80 char *major;
81 char *minor;
82 char *date;
83 char *filename;
84 char *link;
85 };
86
87 static void
free_fileinfo(struct fileinfo * f)88 free_fileinfo(struct fileinfo *f)
89 {
90 free(f->user);
91 free(f->group);
92 free(f->size);
93 free(f->major);
94 free(f->minor);
95 free(f->date);
96 free(f->filename);
97 free(f->link);
98 }
99
100 #define LS_DIRS (1 << 0)
101 #define LS_IGNORE_DOT (1 << 1)
102 #define LS_SORT_MODE (3 << 2)
103 #define SORT_MODE(f) ((f) & LS_SORT_MODE)
104 #define LS_SORT_NAME (1 << 2)
105 #define LS_SORT_MTIME (2 << 2)
106 #define LS_SORT_SIZE (3 << 2)
107 #define LS_SORT_REVERSE (1 << 4)
108
109 #define LS_SIZE (1 << 5)
110 #define LS_INODE (1 << 6)
111 #define LS_TYPE (1 << 7)
112 #define LS_DISP_MODE (3 << 8)
113 #define DISP_MODE(f) ((f) & LS_DISP_MODE)
114 #define LS_DISP_LONG (1 << 8)
115 #define LS_DISP_COLUMN (2 << 8)
116 #define LS_DISP_CROSS (3 << 8)
117 #define LS_SHOW_ALL (1 << 10)
118 #define LS_RECURSIVE (1 << 11)
119 #define LS_EXTRA_BLANK (1 << 12)
120 #define LS_SHOW_DIRNAME (1 << 13)
121 #define LS_DIR_FLAG (1 << 14) /* these files come via list_dir */
122
123 #ifndef S_ISTXT
124 #define S_ISTXT S_ISVTX
125 #endif
126
127 #if !defined(_S_IFMT) && defined(S_IFMT)
128 #define _S_IFMT S_IFMT
129 #endif
130
131 #ifndef S_ISSOCK
132 #define S_ISSOCK(mode) (((mode) & _S_IFMT) == S_IFSOCK)
133 #endif
134
135 #ifndef S_ISLNK
136 #define S_ISLNK(mode) (((mode) & _S_IFMT) == S_IFLNK)
137 #endif
138
139 static size_t
block_convert(size_t blocks)140 block_convert(size_t blocks)
141 {
142 #ifdef S_BLKSIZE
143 return blocks * S_BLKSIZE / 1024;
144 #else
145 return blocks * 512 / 1024;
146 #endif
147 }
148
149 static int
make_fileinfo(FILE * out,const char * filename,struct fileinfo * file,int flags)150 make_fileinfo(FILE *out, const char *filename, struct fileinfo *file, int flags)
151 {
152 char buf[128];
153 int file_type = 0;
154 struct stat *st = &file->st;
155
156 file->inode = st->st_ino;
157 file->bsize = block_convert(st->st_blocks);
158
159 if(S_ISDIR(st->st_mode)) {
160 file->mode[0] = 'd';
161 file_type = '/';
162 }
163 else if(S_ISCHR(st->st_mode))
164 file->mode[0] = 'c';
165 else if(S_ISBLK(st->st_mode))
166 file->mode[0] = 'b';
167 else if(S_ISREG(st->st_mode)) {
168 file->mode[0] = '-';
169 if(st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
170 file_type = '*';
171 }
172 else if(S_ISFIFO(st->st_mode)) {
173 file->mode[0] = 'p';
174 file_type = '|';
175 }
176 else if(S_ISLNK(st->st_mode)) {
177 file->mode[0] = 'l';
178 file_type = '@';
179 }
180 else if(S_ISSOCK(st->st_mode)) {
181 file->mode[0] = 's';
182 file_type = '=';
183 }
184 #ifdef S_ISWHT
185 else if(S_ISWHT(st->st_mode)) {
186 file->mode[0] = 'w';
187 file_type = '%';
188 }
189 #endif
190 else
191 file->mode[0] = '?';
192 {
193 char *x[] = { "---", "--x", "-w-", "-wx",
194 "r--", "r-x", "rw-", "rwx" };
195 strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]);
196 strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]);
197 strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]);
198 if((st->st_mode & S_ISUID)) {
199 if((st->st_mode & S_IXUSR))
200 file->mode[3] = 's';
201 else
202 file->mode[3] = 'S';
203 }
204 if((st->st_mode & S_ISGID)) {
205 if((st->st_mode & S_IXGRP))
206 file->mode[6] = 's';
207 else
208 file->mode[6] = 'S';
209 }
210 if((st->st_mode & S_ISTXT)) {
211 if((st->st_mode & S_IXOTH))
212 file->mode[9] = 't';
213 else
214 file->mode[9] = 'T';
215 }
216 }
217 file->n_link = st->st_nlink;
218 {
219 struct passwd *pwd;
220 pwd = getpwuid(st->st_uid);
221 if(pwd == NULL) {
222 if (asprintf(&file->user, "%u", (unsigned)st->st_uid) == -1)
223 file->user = NULL;
224 } else
225 file->user = strdup(pwd->pw_name);
226 if (file->user == NULL) {
227 syslog(LOG_ERR, "out of memory");
228 return -1;
229 }
230 }
231 {
232 struct group *grp;
233 grp = getgrgid(st->st_gid);
234 if(grp == NULL) {
235 if (asprintf(&file->group, "%u", (unsigned)st->st_gid) == -1)
236 file->group = NULL;
237 } else
238 file->group = strdup(grp->gr_name);
239 if (file->group == NULL) {
240 syslog(LOG_ERR, "out of memory");
241 return -1;
242 }
243 }
244
245 if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
246 #if defined(major) && defined(minor)
247 if (asprintf(&file->major, "%u", (unsigned)major(st->st_rdev)) == -1)
248 file->major = NULL;
249 if (asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev)) == -1)
250 file->minor = NULL;
251 #else
252 /* Don't want to use the DDI/DKI crap. */
253 if (asprintf(&file->major, "%u", (unsigned)st->st_rdev) == -1)
254 file->major = NULL;
255 if (asprintf(&file->minor, "%u", 0) == -1)
256 file->minor = NULL;
257 #endif
258 if (file->major == NULL || file->minor == NULL) {
259 syslog(LOG_ERR, "out of memory");
260 return -1;
261 }
262 } else {
263 if (asprintf(&file->size, "%lu", (unsigned long)st->st_size) == -1)
264 file->size = NULL;
265 }
266
267 {
268 time_t t = time(NULL);
269 time_t mtime = st->st_mtime;
270 struct tm *tm = localtime(&mtime);
271 if((t - mtime > 6*30*24*60*60) ||
272 (mtime - t > 6*30*24*60*60))
273 strftime(buf, sizeof(buf), "%b %e %Y", tm);
274 else
275 strftime(buf, sizeof(buf), "%b %e %H:%M", tm);
276 file->date = strdup(buf);
277 if (file->date == NULL) {
278 syslog(LOG_ERR, "out of memory");
279 return -1;
280 }
281 }
282 {
283 const char *p = strrchr(filename, '/');
284 if(p)
285 p++;
286 else
287 p = filename;
288 if((flags & LS_TYPE) && file_type != 0) {
289 if (asprintf(&file->filename, "%s%c", p, file_type) == -1)
290 file->filename = NULL;
291 } else
292 file->filename = strdup(p);
293 if (file->filename == NULL) {
294 syslog(LOG_ERR, "out of memory");
295 return -1;
296 }
297 }
298 if(S_ISLNK(st->st_mode)) {
299 int n;
300 n = readlink((char *)filename, buf, sizeof(buf) - 1);
301 if(n >= 0) {
302 buf[n] = '\0';
303 file->link = strdup(buf);
304 if (file->link == NULL) {
305 syslog(LOG_ERR, "out of memory");
306 return -1;
307 }
308 } else
309 sec_fprintf2(out, "readlink(%s): %s", filename, strerror(errno));
310 }
311 return 0;
312 }
313
314 static void
print_file(FILE * out,int flags,struct fileinfo * f,int max_inode,int max_bsize,int max_n_link,int max_user,int max_group,int max_size,int max_major,int max_minor,int max_date)315 print_file(FILE *out,
316 int flags,
317 struct fileinfo *f,
318 int max_inode,
319 int max_bsize,
320 int max_n_link,
321 int max_user,
322 int max_group,
323 int max_size,
324 int max_major,
325 int max_minor,
326 int max_date)
327 {
328 if(f->filename == NULL)
329 return;
330
331 if(flags & LS_INODE) {
332 sec_fprintf2(out, "%*d", max_inode, f->inode);
333 sec_fprintf2(out, " ");
334 }
335 if(flags & LS_SIZE) {
336 sec_fprintf2(out, "%*d", max_bsize, f->bsize);
337 sec_fprintf2(out, " ");
338 }
339 sec_fprintf2(out, "%s", f->mode);
340 sec_fprintf2(out, " ");
341 sec_fprintf2(out, "%*d", max_n_link, f->n_link);
342 sec_fprintf2(out, " ");
343 sec_fprintf2(out, "%-*s", max_user, f->user);
344 sec_fprintf2(out, " ");
345 sec_fprintf2(out, "%-*s", max_group, f->group);
346 sec_fprintf2(out, " ");
347 if(f->major != NULL && f->minor != NULL)
348 sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor);
349 else
350 sec_fprintf2(out, "%*s", max_size, f->size);
351 sec_fprintf2(out, " ");
352 sec_fprintf2(out, "%*s", max_date, f->date);
353 sec_fprintf2(out, " ");
354 sec_fprintf2(out, "%s", f->filename);
355 if(f->link)
356 sec_fprintf2(out, " -> %s", f->link);
357 sec_fprintf2(out, "\r\n");
358 }
359
360 static int
compare_filename(struct fileinfo * a,struct fileinfo * b)361 compare_filename(struct fileinfo *a, struct fileinfo *b)
362 {
363 if(a->filename == NULL)
364 return 1;
365 if(b->filename == NULL)
366 return -1;
367 return strcmp(a->filename, b->filename);
368 }
369
370 static int
compare_mtime(struct fileinfo * a,struct fileinfo * b)371 compare_mtime(struct fileinfo *a, struct fileinfo *b)
372 {
373 if(a->filename == NULL)
374 return 1;
375 if(b->filename == NULL)
376 return -1;
377 return b->st.st_mtime - a->st.st_mtime;
378 }
379
380 static int
compare_size(struct fileinfo * a,struct fileinfo * b)381 compare_size(struct fileinfo *a, struct fileinfo *b)
382 {
383 if(a->filename == NULL)
384 return 1;
385 if(b->filename == NULL)
386 return -1;
387 return b->st.st_size - a->st.st_size;
388 }
389
390 static int list_dir(FILE*, const char*, int);
391
392 static int
find_log10(int num)393 find_log10(int num)
394 {
395 int i = 1;
396 while(num > 10) {
397 i++;
398 num /= 10;
399 }
400 return i;
401 }
402
403 /*
404 * Operate as lstat but fake up entries for AFS mount points so we don't
405 * have to fetch them.
406 */
407
408 #ifdef KRB5
409 static int do_the_afs_dance = 1;
410 #endif
411
412 static int
lstat_file(const char * file,struct stat * sb)413 lstat_file (const char *file, struct stat *sb)
414 {
415 #ifdef KRB5
416 if (do_the_afs_dance &&
417 k_hasafs()
418 && strcmp(file, ".")
419 && strcmp(file, "..")
420 && strcmp(file, "/"))
421 {
422 struct ViceIoctl a_params;
423 char *dir, *last;
424 char *path_bkp;
425 static ino_t ino_counter = 0, ino_last = 0;
426 int ret;
427 const int maxsize = 2048;
428
429 path_bkp = strdup (file);
430 if (path_bkp == NULL)
431 return -1;
432
433 a_params.out = malloc (maxsize);
434 if (a_params.out == NULL) {
435 free (path_bkp);
436 return -1;
437 }
438
439 /* If path contains more than the filename alone - split it */
440
441 last = strrchr (path_bkp, '/');
442 if (last != NULL) {
443 if(last[1] == '\0')
444 /* if path ended in /, replace with `.' */
445 a_params.in = ".";
446 else
447 a_params.in = last + 1;
448 while(last > path_bkp && *--last == '/');
449 if(*last != '/' || last != path_bkp) {
450 *++last = '\0';
451 dir = path_bkp;
452 } else
453 /* we got to the start, so this must be the root dir */
454 dir = "/";
455 } else {
456 /* file is relative to cdir */
457 dir = ".";
458 a_params.in = path_bkp;
459 }
460
461 a_params.in_size = strlen (a_params.in) + 1;
462 a_params.out_size = maxsize;
463
464 ret = k_pioctl (dir, VIOC_AFS_STAT_MT_PT, &a_params, 0);
465 free (a_params.out);
466 if (ret < 0) {
467 free (path_bkp);
468
469 if (errno != EINVAL)
470 return ret;
471 else
472 /* if we get EINVAL this is probably not a mountpoint */
473 return lstat (file, sb);
474 }
475
476 /*
477 * wow this was a mountpoint, lets cook the struct stat
478 * use . as a prototype
479 */
480
481 ret = lstat (dir, sb);
482 free (path_bkp);
483 if (ret < 0)
484 return ret;
485
486 if (ino_last == sb->st_ino)
487 ino_counter++;
488 else {
489 ino_last = sb->st_ino;
490 ino_counter = 0;
491 }
492 sb->st_ino += ino_counter;
493 sb->st_nlink = 3;
494
495 return 0;
496 }
497 #endif /* KRB5 */
498 return lstat (file, sb);
499 }
500
501 #define IS_DOT_DOTDOT(X) ((X)[0] == '.' && ((X)[1] == '\0' || \
502 ((X)[1] == '.' && (X)[2] == '\0')))
503
504 static int
list_files(FILE * out,const char ** files,int n_files,int flags)505 list_files(FILE *out, const char **files, int n_files, int flags)
506 {
507 struct fileinfo *fi;
508 int i;
509 int *dirs = NULL;
510 size_t total_blocks = 0;
511 int n_print = 0;
512 int ret = 0;
513
514 if(n_files == 0)
515 return 0;
516
517 if(n_files > 1)
518 flags |= LS_SHOW_DIRNAME;
519
520 fi = calloc(n_files, sizeof(*fi));
521 if (fi == NULL) {
522 syslog(LOG_ERR, "out of memory");
523 return -1;
524 }
525 for(i = 0; i < n_files; i++) {
526 if(lstat_file(files[i], &fi[i].st) < 0) {
527 sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno));
528 fi[i].filename = NULL;
529 } else {
530 int include_in_list = 1;
531 total_blocks += block_convert(fi[i].st.st_blocks);
532 if(S_ISDIR(fi[i].st.st_mode)) {
533 if(dirs == NULL)
534 dirs = calloc(n_files, sizeof(*dirs));
535 if(dirs == NULL) {
536 syslog(LOG_ERR, "%s: %m", files[i]);
537 ret = -1;
538 goto out;
539 }
540 dirs[i] = 1;
541 if((flags & LS_DIRS) == 0)
542 include_in_list = 0;
543 }
544 if(include_in_list) {
545 ret = make_fileinfo(out, files[i], &fi[i], flags);
546 if (ret)
547 goto out;
548 n_print++;
549 }
550 }
551 }
552 switch(SORT_MODE(flags)) {
553 case LS_SORT_NAME:
554 qsort(fi, n_files, sizeof(*fi),
555 (int (*)(const void*, const void*))compare_filename);
556 break;
557 case LS_SORT_MTIME:
558 qsort(fi, n_files, sizeof(*fi),
559 (int (*)(const void*, const void*))compare_mtime);
560 break;
561 case LS_SORT_SIZE:
562 qsort(fi, n_files, sizeof(*fi),
563 (int (*)(const void*, const void*))compare_size);
564 break;
565 }
566 if(DISP_MODE(flags) == LS_DISP_LONG) {
567 int max_inode = 0;
568 int max_bsize = 0;
569 int max_n_link = 0;
570 int max_user = 0;
571 int max_group = 0;
572 int max_size = 0;
573 int max_major = 0;
574 int max_minor = 0;
575 int max_date = 0;
576 for(i = 0; i < n_files; i++) {
577 if(fi[i].filename == NULL)
578 continue;
579 if(fi[i].inode > max_inode)
580 max_inode = fi[i].inode;
581 if(fi[i].bsize > max_bsize)
582 max_bsize = fi[i].bsize;
583 if(fi[i].n_link > max_n_link)
584 max_n_link = fi[i].n_link;
585 if(strlen(fi[i].user) > max_user)
586 max_user = strlen(fi[i].user);
587 if(strlen(fi[i].group) > max_group)
588 max_group = strlen(fi[i].group);
589 if(fi[i].major != NULL && strlen(fi[i].major) > max_major)
590 max_major = strlen(fi[i].major);
591 if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor)
592 max_minor = strlen(fi[i].minor);
593 if(fi[i].size != NULL && strlen(fi[i].size) > max_size)
594 max_size = strlen(fi[i].size);
595 if(strlen(fi[i].date) > max_date)
596 max_date = strlen(fi[i].date);
597 }
598 if(max_size < max_major + max_minor + 2)
599 max_size = max_major + max_minor + 2;
600 else if(max_size - max_minor - 2 > max_major)
601 max_major = max_size - max_minor - 2;
602 max_inode = find_log10(max_inode);
603 max_bsize = find_log10(max_bsize);
604 max_n_link = find_log10(max_n_link);
605
606 if(n_print > 0)
607 sec_fprintf2(out, "total %lu\r\n", (unsigned long)total_blocks);
608 if(flags & LS_SORT_REVERSE)
609 for(i = n_files - 1; i >= 0; i--)
610 print_file(out,
611 flags,
612 &fi[i],
613 max_inode,
614 max_bsize,
615 max_n_link,
616 max_user,
617 max_group,
618 max_size,
619 max_major,
620 max_minor,
621 max_date);
622 else
623 for(i = 0; i < n_files; i++)
624 print_file(out,
625 flags,
626 &fi[i],
627 max_inode,
628 max_bsize,
629 max_n_link,
630 max_user,
631 max_group,
632 max_size,
633 max_major,
634 max_minor,
635 max_date);
636 } else if(DISP_MODE(flags) == LS_DISP_COLUMN ||
637 DISP_MODE(flags) == LS_DISP_CROSS) {
638 int max_len = 0;
639 int size_len = 0;
640 int num_files = n_files;
641 int columns;
642 int j;
643 for(i = 0; i < n_files; i++) {
644 if(fi[i].filename == NULL) {
645 num_files--;
646 continue;
647 }
648 if(strlen(fi[i].filename) > max_len)
649 max_len = strlen(fi[i].filename);
650 if(find_log10(fi[i].bsize) > size_len)
651 size_len = find_log10(fi[i].bsize);
652 }
653 if(num_files == 0)
654 goto next;
655 if(flags & LS_SIZE) {
656 columns = 80 / (size_len + 1 + max_len + 1);
657 max_len = 80 / columns - size_len - 1;
658 } else {
659 columns = 80 / (max_len + 1); /* get space between columns */
660 max_len = 80 / columns;
661 }
662 if(flags & LS_SIZE)
663 sec_fprintf2(out, "total %lu\r\n",
664 (unsigned long)total_blocks);
665 if(DISP_MODE(flags) == LS_DISP_CROSS) {
666 for(i = 0, j = 0; i < n_files; i++) {
667 if(fi[i].filename == NULL)
668 continue;
669 if(flags & LS_SIZE)
670 sec_fprintf2(out, "%*u %-*s", size_len, fi[i].bsize,
671 max_len, fi[i].filename);
672 else
673 sec_fprintf2(out, "%-*s", max_len, fi[i].filename);
674 j++;
675 if(j == columns) {
676 sec_fprintf2(out, "\r\n");
677 j = 0;
678 }
679 }
680 if(j > 0)
681 sec_fprintf2(out, "\r\n");
682 } else {
683 int skip = (num_files + columns - 1) / columns;
684
685 for(i = 0; i < skip; i++) {
686 for(j = i; j < n_files;) {
687 while(j < n_files && fi[j].filename == NULL)
688 j++;
689 if(flags & LS_SIZE)
690 sec_fprintf2(out, "%*u %-*s", size_len, fi[j].bsize,
691 max_len, fi[j].filename);
692 else
693 sec_fprintf2(out, "%-*s", max_len, fi[j].filename);
694 j += skip;
695 }
696 sec_fprintf2(out, "\r\n");
697 }
698 }
699 } else {
700 for(i = 0; i < n_files; i++) {
701 if(fi[i].filename == NULL)
702 continue;
703 sec_fprintf2(out, "%s\r\n", fi[i].filename);
704 }
705 }
706 next:
707 if(((flags & LS_DIRS) == 0 || (flags & LS_RECURSIVE)) && dirs != NULL) {
708 for(i = 0; i < n_files; i++) {
709 if(dirs[i]) {
710 const char *p = strrchr(files[i], '/');
711 if(p == NULL)
712 p = files[i];
713 else
714 p++;
715 if(!(flags & LS_DIR_FLAG) || !IS_DOT_DOTDOT(p)) {
716 if((flags & LS_SHOW_DIRNAME)) {
717 if ((flags & LS_EXTRA_BLANK))
718 sec_fprintf2(out, "\r\n");
719 sec_fprintf2(out, "%s:\r\n", files[i]);
720 }
721 list_dir(out, files[i], flags | LS_DIRS | LS_EXTRA_BLANK);
722 }
723 }
724 }
725 }
726 out:
727 for(i = 0; i < n_files; i++)
728 free_fileinfo(&fi[i]);
729 free(fi);
730 if(dirs != NULL)
731 free(dirs);
732 return ret;
733 }
734
735 static void
free_files(char ** files,int n)736 free_files (char **files, int n)
737 {
738 int i;
739
740 for (i = 0; i < n; ++i)
741 free (files[i]);
742 free (files);
743 }
744
745 static int
hide_file(const char * filename,int flags)746 hide_file(const char *filename, int flags)
747 {
748 if(filename[0] != '.')
749 return 0;
750 if((flags & LS_IGNORE_DOT))
751 return 1;
752 if(filename[1] == '\0' || (filename[1] == '.' && filename[2] == '\0')) {
753 if((flags & LS_SHOW_ALL))
754 return 0;
755 else
756 return 1;
757 }
758 return 0;
759 }
760
761 static int
list_dir(FILE * out,const char * directory,int flags)762 list_dir(FILE *out, const char *directory, int flags)
763 {
764 DIR *d = opendir(directory);
765 struct dirent *ent;
766 char **files = NULL;
767 int n_files = 0;
768 int ret;
769
770 if(d == NULL) {
771 syslog(LOG_ERR, "%s: %m", directory);
772 return -1;
773 }
774 while((ent = readdir(d)) != NULL) {
775 void *tmp;
776
777 if(hide_file(ent->d_name, flags))
778 continue;
779 tmp = realloc(files, (n_files + 1) * sizeof(*files));
780 if (tmp == NULL) {
781 syslog(LOG_ERR, "%s: out of memory", directory);
782 free_files (files, n_files);
783 closedir (d);
784 return -1;
785 }
786 files = tmp;
787 ret = asprintf(&files[n_files], "%s/%s", directory, ent->d_name);
788 if (ret == -1) {
789 syslog(LOG_ERR, "%s: out of memory", directory);
790 free_files (files, n_files);
791 closedir (d);
792 return -1;
793 }
794 ++n_files;
795 }
796 closedir(d);
797 return list_files(out, (const char**)files, n_files, flags | LS_DIR_FLAG);
798 }
799
800 static int
parse_flags(const char * options)801 parse_flags(const char *options)
802 {
803 #ifdef TEST
804 int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_COLUMN;
805 #else
806 int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_LONG;
807 #endif
808
809 const char *p;
810 if(options == NULL || *options != '-')
811 return flags;
812 for(p = options + 1; *p; p++) {
813 switch(*p) {
814 case '1':
815 flags = (flags & ~LS_DISP_MODE);
816 break;
817 case 'a':
818 flags |= LS_SHOW_ALL;
819 /*FALLTHROUGH*/
820 case 'A':
821 flags &= ~LS_IGNORE_DOT;
822 break;
823 case 'C':
824 flags = (flags & ~LS_DISP_MODE) | LS_DISP_COLUMN;
825 break;
826 case 'd':
827 flags |= LS_DIRS;
828 break;
829 case 'f':
830 flags = (flags & ~LS_SORT_MODE);
831 break;
832 case 'F':
833 flags |= LS_TYPE;
834 break;
835 case 'i':
836 flags |= LS_INODE;
837 break;
838 case 'l':
839 flags = (flags & ~LS_DISP_MODE) | LS_DISP_LONG;
840 break;
841 case 'r':
842 flags |= LS_SORT_REVERSE;
843 break;
844 case 'R':
845 flags |= LS_RECURSIVE;
846 break;
847 case 's':
848 flags |= LS_SIZE;
849 break;
850 case 'S':
851 flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE;
852 break;
853 case 't':
854 flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME;
855 break;
856 case 'x':
857 flags = (flags & ~LS_DISP_MODE) | LS_DISP_CROSS;
858 break;
859 /* these are a bunch of unimplemented flags from BSD ls */
860 case 'k': /* display sizes in kB */
861 case 'c': /* last change time */
862 case 'L': /* list symlink target */
863 case 'm': /* stream output */
864 case 'o': /* BSD file flags */
865 case 'p': /* display / after directories */
866 case 'q': /* print non-graphic characters */
867 case 'u': /* use last access time */
868 case 'T': /* display complete time */
869 case 'W': /* include whiteouts */
870 break;
871 }
872 }
873 return flags;
874 }
875
876 int
builtin_ls(FILE * out,const char * file)877 builtin_ls(FILE *out, const char *file)
878 {
879 int flags;
880 int ret;
881
882 if(*file == '-') {
883 flags = parse_flags(file);
884 file = ".";
885 } else
886 flags = parse_flags("");
887
888 ret = list_files(out, &file, 1, flags);
889 sec_fflush(out);
890 return ret;
891 }
892