No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / dist / heimdal / appl / ftp / ftpd / ls.c
blobe002e3e72225e8702d5c48873169c6cdf51b7915
1 /*
2 * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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. */
33 #ifndef TEST
34 #include "ftpd_locl.h"
36 __RCSID("$Heimdal: ls.c 16216 2005-10-22 13:15:43Z lha $"
37 "$NetBSD$");
39 #else
40 #include <stdio.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <time.h>
44 #include <dirent.h>
45 #include <sys/stat.h>
46 #include <unistd.h>
47 #include <pwd.h>
48 #include <grp.h>
49 #include <errno.h>
51 #define sec_fprintf2 fprintf
52 #define sec_fflush fflush
53 static void list_files(FILE *out, const char **files, int n_files, int flags);
54 static int parse_flags(const char *options);
56 int
57 main(int argc, char **argv)
59 int i = 1;
60 int flags;
61 if(argc > 1 && argv[1][0] == '-') {
62 flags = parse_flags(argv[1]);
63 i = 2;
64 } else
65 flags = parse_flags(NULL);
67 list_files(stdout, (const char **)argv + i, argc - i, flags);
68 return 0;
70 #endif
72 struct fileinfo {
73 struct stat st;
74 int inode;
75 int bsize;
76 char mode[11];
77 int n_link;
78 char *user;
79 char *group;
80 char *size;
81 char *major;
82 char *minor;
83 char *date;
84 char *filename;
85 char *link;
88 static void
89 free_fileinfo(struct fileinfo *f)
91 free(f->user);
92 free(f->group);
93 free(f->size);
94 free(f->major);
95 free(f->minor);
96 free(f->date);
97 free(f->filename);
98 free(f->link);
101 #define LS_DIRS (1 << 0)
102 #define LS_IGNORE_DOT (1 << 1)
103 #define LS_SORT_MODE (3 << 2)
104 #define SORT_MODE(f) ((f) & LS_SORT_MODE)
105 #define LS_SORT_NAME (1 << 2)
106 #define LS_SORT_MTIME (2 << 2)
107 #define LS_SORT_SIZE (3 << 2)
108 #define LS_SORT_REVERSE (1 << 4)
110 #define LS_SIZE (1 << 5)
111 #define LS_INODE (1 << 6)
112 #define LS_TYPE (1 << 7)
113 #define LS_DISP_MODE (3 << 8)
114 #define DISP_MODE(f) ((f) & LS_DISP_MODE)
115 #define LS_DISP_LONG (1 << 8)
116 #define LS_DISP_COLUMN (2 << 8)
117 #define LS_DISP_CROSS (3 << 8)
118 #define LS_SHOW_ALL (1 << 10)
119 #define LS_RECURSIVE (1 << 11)
120 #define LS_EXTRA_BLANK (1 << 12)
121 #define LS_SHOW_DIRNAME (1 << 13)
122 #define LS_DIR_FLAG (1 << 14) /* these files come via list_dir */
124 #ifndef S_ISTXT
125 #define S_ISTXT S_ISVTX
126 #endif
128 #if !defined(_S_IFMT) && defined(S_IFMT)
129 #define _S_IFMT S_IFMT
130 #endif
132 #ifndef S_ISSOCK
133 #define S_ISSOCK(mode) (((mode) & _S_IFMT) == S_IFSOCK)
134 #endif
136 #ifndef S_ISLNK
137 #define S_ISLNK(mode) (((mode) & _S_IFMT) == S_IFLNK)
138 #endif
140 static size_t
141 block_convert(size_t blocks)
143 #ifdef S_BLKSIZE
144 return blocks * S_BLKSIZE / 1024;
145 #else
146 return blocks * 512 / 1024;
147 #endif
150 static int
151 make_fileinfo(FILE *out, const char *filename, struct fileinfo *file, int flags)
153 char buf[128];
154 int file_type = 0;
155 struct stat *st = &file->st;
157 file->inode = st->st_ino;
158 file->bsize = block_convert(st->st_blocks);
160 if(S_ISDIR(st->st_mode)) {
161 file->mode[0] = 'd';
162 file_type = '/';
164 else if(S_ISCHR(st->st_mode))
165 file->mode[0] = 'c';
166 else if(S_ISBLK(st->st_mode))
167 file->mode[0] = 'b';
168 else if(S_ISREG(st->st_mode)) {
169 file->mode[0] = '-';
170 if(st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
171 file_type = '*';
173 else if(S_ISFIFO(st->st_mode)) {
174 file->mode[0] = 'p';
175 file_type = '|';
177 else if(S_ISLNK(st->st_mode)) {
178 file->mode[0] = 'l';
179 file_type = '@';
181 else if(S_ISSOCK(st->st_mode)) {
182 file->mode[0] = 's';
183 file_type = '=';
185 #ifdef S_ISWHT
186 else if(S_ISWHT(st->st_mode)) {
187 file->mode[0] = 'w';
188 file_type = '%';
190 #endif
191 else
192 file->mode[0] = '?';
194 char *x[] = { "---", "--x", "-w-", "-wx",
195 "r--", "r-x", "rw-", "rwx" };
196 strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]);
197 strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]);
198 strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]);
199 if((st->st_mode & S_ISUID)) {
200 if((st->st_mode & S_IXUSR))
201 file->mode[3] = 's';
202 else
203 file->mode[3] = 'S';
205 if((st->st_mode & S_ISGID)) {
206 if((st->st_mode & S_IXGRP))
207 file->mode[6] = 's';
208 else
209 file->mode[6] = 'S';
211 if((st->st_mode & S_ISTXT)) {
212 if((st->st_mode & S_IXOTH))
213 file->mode[9] = 't';
214 else
215 file->mode[9] = 'T';
218 file->n_link = st->st_nlink;
220 struct passwd *pwd;
221 pwd = getpwuid(st->st_uid);
222 if(pwd == NULL) {
223 if (asprintf(&file->user, "%u", (unsigned)st->st_uid) == -1)
224 file->user = NULL;
225 } else
226 file->user = strdup(pwd->pw_name);
227 if (file->user == NULL) {
228 syslog(LOG_ERR, "out of memory");
229 return -1;
233 struct group *grp;
234 grp = getgrgid(st->st_gid);
235 if(grp == NULL) {
236 if (asprintf(&file->group, "%u", (unsigned)st->st_gid) == -1)
237 file->group = NULL;
238 } else
239 file->group = strdup(grp->gr_name);
240 if (file->group == NULL) {
241 syslog(LOG_ERR, "out of memory");
242 return -1;
246 if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
247 #if defined(major) && defined(minor)
248 if (asprintf(&file->major, "%u", (unsigned)major(st->st_rdev)) == -1)
249 file->major = NULL;
250 if (asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev)) == -1)
251 file->minor = NULL;
252 #else
253 /* Don't want to use the DDI/DKI crap. */
254 if (asprintf(&file->major, "%u", (unsigned)st->st_rdev) == -1)
255 file->major = NULL;
256 if (asprintf(&file->minor, "%u", 0) == -1)
257 file->minor = NULL;
258 #endif
259 if (file->major == NULL || file->minor == NULL) {
260 syslog(LOG_ERR, "out of memory");
261 return -1;
263 } else {
264 if (asprintf(&file->size, "%lu", (unsigned long)st->st_size) == -1)
265 file->size = NULL;
269 time_t t = time(NULL);
270 time_t mtime = st->st_mtime;
271 struct tm *tm = localtime(&mtime);
272 if((t - mtime > 6*30*24*60*60) ||
273 (mtime - t > 6*30*24*60*60))
274 strftime(buf, sizeof(buf), "%b %e %Y", tm);
275 else
276 strftime(buf, sizeof(buf), "%b %e %H:%M", tm);
277 file->date = strdup(buf);
278 if (file->date == NULL) {
279 syslog(LOG_ERR, "out of memory");
280 return -1;
284 const char *p = strrchr(filename, '/');
285 if(p)
286 p++;
287 else
288 p = filename;
289 if((flags & LS_TYPE) && file_type != 0) {
290 if (asprintf(&file->filename, "%s%c", p, file_type) == -1)
291 file->filename = NULL;
292 } else
293 file->filename = strdup(p);
294 if (file->filename == NULL) {
295 syslog(LOG_ERR, "out of memory");
296 return -1;
299 if(S_ISLNK(st->st_mode)) {
300 int n;
301 n = readlink((char *)filename, buf, sizeof(buf) - 1);
302 if(n >= 0) {
303 buf[n] = '\0';
304 file->link = strdup(buf);
305 if (file->link == NULL) {
306 syslog(LOG_ERR, "out of memory");
307 return -1;
309 } else
310 sec_fprintf2(out, "readlink(%s): %s", filename, strerror(errno));
312 return 0;
315 static void
316 print_file(FILE *out,
317 int flags,
318 struct fileinfo *f,
319 int max_inode,
320 int max_bsize,
321 int max_n_link,
322 int max_user,
323 int max_group,
324 int max_size,
325 int max_major,
326 int max_minor,
327 int max_date)
329 if(f->filename == NULL)
330 return;
332 if(flags & LS_INODE) {
333 sec_fprintf2(out, "%*d", max_inode, f->inode);
334 sec_fprintf2(out, " ");
336 if(flags & LS_SIZE) {
337 sec_fprintf2(out, "%*d", max_bsize, f->bsize);
338 sec_fprintf2(out, " ");
340 sec_fprintf2(out, "%s", f->mode);
341 sec_fprintf2(out, " ");
342 sec_fprintf2(out, "%*d", max_n_link, f->n_link);
343 sec_fprintf2(out, " ");
344 sec_fprintf2(out, "%-*s", max_user, f->user);
345 sec_fprintf2(out, " ");
346 sec_fprintf2(out, "%-*s", max_group, f->group);
347 sec_fprintf2(out, " ");
348 if(f->major != NULL && f->minor != NULL)
349 sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor);
350 else
351 sec_fprintf2(out, "%*s", max_size, f->size);
352 sec_fprintf2(out, " ");
353 sec_fprintf2(out, "%*s", max_date, f->date);
354 sec_fprintf2(out, " ");
355 sec_fprintf2(out, "%s", f->filename);
356 if(f->link)
357 sec_fprintf2(out, " -> %s", f->link);
358 sec_fprintf2(out, "\r\n");
361 static int
362 compare_filename(struct fileinfo *a, struct fileinfo *b)
364 if(a->filename == NULL)
365 return 1;
366 if(b->filename == NULL)
367 return -1;
368 return strcmp(a->filename, b->filename);
371 static int
372 compare_mtime(struct fileinfo *a, struct fileinfo *b)
374 if(a->filename == NULL)
375 return 1;
376 if(b->filename == NULL)
377 return -1;
378 return b->st.st_mtime - a->st.st_mtime;
381 static int
382 compare_size(struct fileinfo *a, struct fileinfo *b)
384 if(a->filename == NULL)
385 return 1;
386 if(b->filename == NULL)
387 return -1;
388 return b->st.st_size - a->st.st_size;
391 static int list_dir(FILE*, const char*, int);
393 static int
394 find_log10(int num)
396 int i = 1;
397 while(num > 10) {
398 i++;
399 num /= 10;
401 return i;
405 * Operate as lstat but fake up entries for AFS mount points so we don't
406 * have to fetch them.
409 #ifdef KRB4
410 static int do_the_afs_dance = 1;
411 #endif
413 static int
414 lstat_file (const char *file, struct stat *sb)
416 #ifdef KRB4
417 if (do_the_afs_dance &&
418 k_hasafs()
419 && strcmp(file, ".")
420 && strcmp(file, "..")
421 && strcmp(file, "/"))
423 struct ViceIoctl a_params;
424 char *dir, *last;
425 char *path_bkp;
426 static ino_t ino_counter = 0, ino_last = 0;
427 int ret;
428 const int maxsize = 2048;
430 path_bkp = strdup (file);
431 if (path_bkp == NULL)
432 return -1;
434 a_params.out = malloc (maxsize);
435 if (a_params.out == NULL) {
436 free (path_bkp);
437 return -1;
440 /* If path contains more than the filename alone - split it */
442 last = strrchr (path_bkp, '/');
443 if (last != NULL) {
444 if(last[1] == '\0')
445 /* if path ended in /, replace with `.' */
446 a_params.in = ".";
447 else
448 a_params.in = last + 1;
449 while(last > path_bkp && *--last == '/');
450 if(*last != '/' || last != path_bkp) {
451 *++last = '\0';
452 dir = path_bkp;
453 } else
454 /* we got to the start, so this must be the root dir */
455 dir = "/";
456 } else {
457 /* file is relative to cdir */
458 dir = ".";
459 a_params.in = path_bkp;
462 a_params.in_size = strlen (a_params.in) + 1;
463 a_params.out_size = maxsize;
465 ret = k_pioctl (dir, VIOC_AFS_STAT_MT_PT, &a_params, 0);
466 free (a_params.out);
467 if (ret < 0) {
468 free (path_bkp);
470 if (errno != EINVAL)
471 return ret;
472 else
473 /* if we get EINVAL this is probably not a mountpoint */
474 return lstat (file, sb);
478 * wow this was a mountpoint, lets cook the struct stat
479 * use . as a prototype
482 ret = lstat (dir, sb);
483 free (path_bkp);
484 if (ret < 0)
485 return ret;
487 if (ino_last == sb->st_ino)
488 ino_counter++;
489 else {
490 ino_last = sb->st_ino;
491 ino_counter = 0;
493 sb->st_ino += ino_counter;
494 sb->st_nlink = 3;
496 return 0;
498 #endif /* KRB4 */
499 return lstat (file, sb);
502 #define IS_DOT_DOTDOT(X) ((X)[0] == '.' && ((X)[1] == '\0' || \
503 ((X)[1] == '.' && (X)[2] == '\0')))
505 static int
506 list_files(FILE *out, const char **files, int n_files, int flags)
508 struct fileinfo *fi;
509 int i;
510 int *dirs = NULL;
511 size_t total_blocks = 0;
512 int n_print = 0;
513 int ret = 0;
515 if(n_files == 0)
516 return 0;
518 if(n_files > 1)
519 flags |= LS_SHOW_DIRNAME;
521 fi = calloc(n_files, sizeof(*fi));
522 if (fi == NULL) {
523 syslog(LOG_ERR, "out of memory");
524 return -1;
526 for(i = 0; i < n_files; i++) {
527 if(lstat_file(files[i], &fi[i].st) < 0) {
528 sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno));
529 fi[i].filename = NULL;
530 } else {
531 int include_in_list = 1;
532 total_blocks += block_convert(fi[i].st.st_blocks);
533 if(S_ISDIR(fi[i].st.st_mode)) {
534 if(dirs == NULL)
535 dirs = calloc(n_files, sizeof(*dirs));
536 if(dirs == NULL) {
537 syslog(LOG_ERR, "%s: %m", files[i]);
538 ret = -1;
539 goto out;
541 dirs[i] = 1;
542 if((flags & LS_DIRS) == 0)
543 include_in_list = 0;
545 if(include_in_list) {
546 ret = make_fileinfo(out, files[i], &fi[i], flags);
547 if (ret)
548 goto out;
549 n_print++;
553 switch(SORT_MODE(flags)) {
554 case LS_SORT_NAME:
555 qsort(fi, n_files, sizeof(*fi),
556 (int (*)(const void*, const void*))compare_filename);
557 break;
558 case LS_SORT_MTIME:
559 qsort(fi, n_files, sizeof(*fi),
560 (int (*)(const void*, const void*))compare_mtime);
561 break;
562 case LS_SORT_SIZE:
563 qsort(fi, n_files, sizeof(*fi),
564 (int (*)(const void*, const void*))compare_size);
565 break;
567 if(DISP_MODE(flags) == LS_DISP_LONG) {
568 int max_inode = 0;
569 int max_bsize = 0;
570 int max_n_link = 0;
571 int max_user = 0;
572 int max_group = 0;
573 int max_size = 0;
574 int max_major = 0;
575 int max_minor = 0;
576 int max_date = 0;
577 for(i = 0; i < n_files; i++) {
578 if(fi[i].filename == NULL)
579 continue;
580 if(fi[i].inode > max_inode)
581 max_inode = fi[i].inode;
582 if(fi[i].bsize > max_bsize)
583 max_bsize = fi[i].bsize;
584 if(fi[i].n_link > max_n_link)
585 max_n_link = fi[i].n_link;
586 if(strlen(fi[i].user) > max_user)
587 max_user = strlen(fi[i].user);
588 if(strlen(fi[i].group) > max_group)
589 max_group = strlen(fi[i].group);
590 if(fi[i].major != NULL && strlen(fi[i].major) > max_major)
591 max_major = strlen(fi[i].major);
592 if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor)
593 max_minor = strlen(fi[i].minor);
594 if(fi[i].size != NULL && strlen(fi[i].size) > max_size)
595 max_size = strlen(fi[i].size);
596 if(strlen(fi[i].date) > max_date)
597 max_date = strlen(fi[i].date);
599 if(max_size < max_major + max_minor + 2)
600 max_size = max_major + max_minor + 2;
601 else if(max_size - max_minor - 2 > max_major)
602 max_major = max_size - max_minor - 2;
603 max_inode = find_log10(max_inode);
604 max_bsize = find_log10(max_bsize);
605 max_n_link = find_log10(max_n_link);
607 if(n_print > 0)
608 sec_fprintf2(out, "total %lu\r\n", (unsigned long)total_blocks);
609 if(flags & LS_SORT_REVERSE)
610 for(i = n_files - 1; i >= 0; i--)
611 print_file(out,
612 flags,
613 &fi[i],
614 max_inode,
615 max_bsize,
616 max_n_link,
617 max_user,
618 max_group,
619 max_size,
620 max_major,
621 max_minor,
622 max_date);
623 else
624 for(i = 0; i < n_files; i++)
625 print_file(out,
626 flags,
627 &fi[i],
628 max_inode,
629 max_bsize,
630 max_n_link,
631 max_user,
632 max_group,
633 max_size,
634 max_major,
635 max_minor,
636 max_date);
637 } else if(DISP_MODE(flags) == LS_DISP_COLUMN ||
638 DISP_MODE(flags) == LS_DISP_CROSS) {
639 int max_len = 0;
640 int size_len = 0;
641 int num_files = n_files;
642 int columns;
643 int j;
644 for(i = 0; i < n_files; i++) {
645 if(fi[i].filename == NULL) {
646 num_files--;
647 continue;
649 if(strlen(fi[i].filename) > max_len)
650 max_len = strlen(fi[i].filename);
651 if(find_log10(fi[i].bsize) > size_len)
652 size_len = find_log10(fi[i].bsize);
654 if(num_files == 0)
655 goto next;
656 if(flags & LS_SIZE) {
657 columns = 80 / (size_len + 1 + max_len + 1);
658 max_len = 80 / columns - size_len - 1;
659 } else {
660 columns = 80 / (max_len + 1); /* get space between columns */
661 max_len = 80 / columns;
663 if(flags & LS_SIZE)
664 sec_fprintf2(out, "total %lu\r\n",
665 (unsigned long)total_blocks);
666 if(DISP_MODE(flags) == LS_DISP_CROSS) {
667 for(i = 0, j = 0; i < n_files; i++) {
668 if(fi[i].filename == NULL)
669 continue;
670 if(flags & LS_SIZE)
671 sec_fprintf2(out, "%*u %-*s", size_len, fi[i].bsize,
672 max_len, fi[i].filename);
673 else
674 sec_fprintf2(out, "%-*s", max_len, fi[i].filename);
675 j++;
676 if(j == columns) {
677 sec_fprintf2(out, "\r\n");
678 j = 0;
681 if(j > 0)
682 sec_fprintf2(out, "\r\n");
683 } else {
684 int skip = (num_files + columns - 1) / columns;
685 j = 0;
686 for(i = 0; i < skip; i++) {
687 for(j = i; j < n_files;) {
688 while(j < n_files && fi[j].filename == NULL)
689 j++;
690 if(flags & LS_SIZE)
691 sec_fprintf2(out, "%*u %-*s", size_len, fi[j].bsize,
692 max_len, fi[j].filename);
693 else
694 sec_fprintf2(out, "%-*s", max_len, fi[j].filename);
695 j += skip;
697 sec_fprintf2(out, "\r\n");
700 } else {
701 for(i = 0; i < n_files; i++) {
702 if(fi[i].filename == NULL)
703 continue;
704 sec_fprintf2(out, "%s\r\n", fi[i].filename);
707 next:
708 if(((flags & LS_DIRS) == 0 || (flags & LS_RECURSIVE)) && dirs != NULL) {
709 for(i = 0; i < n_files; i++) {
710 if(dirs[i]) {
711 const char *p = strrchr(files[i], '/');
712 if(p == NULL)
713 p = files[i];
714 else
715 p++;
716 if(!(flags & LS_DIR_FLAG) || !IS_DOT_DOTDOT(p)) {
717 if((flags & LS_SHOW_DIRNAME)) {
718 if ((flags & LS_EXTRA_BLANK))
719 sec_fprintf2(out, "\r\n");
720 sec_fprintf2(out, "%s:\r\n", files[i]);
722 list_dir(out, files[i], flags | LS_DIRS | LS_EXTRA_BLANK);
727 out:
728 for(i = 0; i < n_files; i++)
729 free_fileinfo(&fi[i]);
730 free(fi);
731 if(dirs != NULL)
732 free(dirs);
733 return ret;
736 static void
737 free_files (char **files, int n)
739 int i;
741 for (i = 0; i < n; ++i)
742 free (files[i]);
743 free (files);
746 static int
747 hide_file(const char *filename, int flags)
749 if(filename[0] != '.')
750 return 0;
751 if((flags & LS_IGNORE_DOT))
752 return 1;
753 if(filename[1] == '\0' || (filename[1] == '.' && filename[2] == '\0')) {
754 if((flags & LS_SHOW_ALL))
755 return 0;
756 else
757 return 1;
759 return 0;
762 static int
763 list_dir(FILE *out, const char *directory, int flags)
765 DIR *d = opendir(directory);
766 struct dirent *ent;
767 char **files = NULL;
768 int n_files = 0;
769 int ret;
771 if(d == NULL) {
772 syslog(LOG_ERR, "%s: %m", directory);
773 return -1;
775 while((ent = readdir(d)) != NULL) {
776 void *tmp;
778 if(hide_file(ent->d_name, flags))
779 continue;
780 tmp = realloc(files, (n_files + 1) * sizeof(*files));
781 if (tmp == NULL) {
782 syslog(LOG_ERR, "%s: out of memory", directory);
783 free_files (files, n_files);
784 closedir (d);
785 return -1;
787 files = tmp;
788 ret = asprintf(&files[n_files], "%s/%s", directory, ent->d_name);
789 if (ret == -1) {
790 syslog(LOG_ERR, "%s: out of memory", directory);
791 free_files (files, n_files);
792 closedir (d);
793 return -1;
795 ++n_files;
797 closedir(d);
798 return list_files(out, (const char**)files, n_files, flags | LS_DIR_FLAG);
801 static int
802 parse_flags(const char *options)
804 #ifdef TEST
805 int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_COLUMN;
806 #else
807 int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_LONG;
808 #endif
810 const char *p;
811 if(options == NULL || *options != '-')
812 return flags;
813 for(p = options + 1; *p; p++) {
814 switch(*p) {
815 case '1':
816 flags = (flags & ~LS_DISP_MODE);
817 break;
818 case 'a':
819 flags |= LS_SHOW_ALL;
820 /*FALLTHROUGH*/
821 case 'A':
822 flags &= ~LS_IGNORE_DOT;
823 break;
824 case 'C':
825 flags = (flags & ~LS_DISP_MODE) | LS_DISP_COLUMN;
826 break;
827 case 'd':
828 flags |= LS_DIRS;
829 break;
830 case 'f':
831 flags = (flags & ~LS_SORT_MODE);
832 break;
833 case 'F':
834 flags |= LS_TYPE;
835 break;
836 case 'i':
837 flags |= LS_INODE;
838 break;
839 case 'l':
840 flags = (flags & ~LS_DISP_MODE) | LS_DISP_LONG;
841 break;
842 case 'r':
843 flags |= LS_SORT_REVERSE;
844 break;
845 case 'R':
846 flags |= LS_RECURSIVE;
847 break;
848 case 's':
849 flags |= LS_SIZE;
850 break;
851 case 'S':
852 flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE;
853 break;
854 case 't':
855 flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME;
856 break;
857 case 'x':
858 flags = (flags & ~LS_DISP_MODE) | LS_DISP_CROSS;
859 break;
860 /* these are a bunch of unimplemented flags from BSD ls */
861 case 'k': /* display sizes in kB */
862 case 'c': /* last change time */
863 case 'L': /* list symlink target */
864 case 'm': /* stream output */
865 case 'o': /* BSD file flags */
866 case 'p': /* display / after directories */
867 case 'q': /* print non-graphic characters */
868 case 'u': /* use last access time */
869 case 'T': /* display complete time */
870 case 'W': /* include whiteouts */
871 break;
874 return flags;
878 builtin_ls(FILE *out, const char *file)
880 int flags;
881 int ret;
883 if(*file == '-') {
884 flags = parse_flags(file);
885 file = ".";
886 } else
887 flags = parse_flags("");
889 ret = list_files(out, &file, 1, flags);
890 sec_fflush(out);
891 return ret;