2 * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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. */
34 #include "ftpd_locl.h"
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
);
56 main(int argc
, char **argv
)
60 if(argc
> 1 && argv
[1][0] == '-') {
61 flags
= parse_flags(argv
[1]);
64 flags
= parse_flags(NULL
);
66 list_files(stdout
, (const char **)argv
+ i
, argc
- i
, flags
);
88 free_fileinfo(struct fileinfo
*f
)
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)
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 */
124 #define S_ISTXT S_ISVTX
127 #if !defined(_S_IFMT) && defined(S_IFMT)
128 #define _S_IFMT S_IFMT
132 #define S_ISSOCK(mode) (((mode) & _S_IFMT) == S_IFSOCK)
136 #define S_ISLNK(mode) (((mode) & _S_IFMT) == S_IFLNK)
140 block_convert(size_t blocks
)
143 return blocks
* S_BLKSIZE
/ 1024;
145 return blocks
* 512 / 1024;
150 make_fileinfo(FILE *out
, const char *filename
, struct fileinfo
*file
, int flags
)
154 struct stat
*st
= &file
->st
;
156 file
->inode
= st
->st_ino
;
157 file
->bsize
= block_convert(st
->st_blocks
);
159 if(S_ISDIR(st
->st_mode
)) {
163 else if(S_ISCHR(st
->st_mode
))
165 else if(S_ISBLK(st
->st_mode
))
167 else if(S_ISREG(st
->st_mode
)) {
169 if(st
->st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
))
172 else if(S_ISFIFO(st
->st_mode
)) {
176 else if(S_ISLNK(st
->st_mode
)) {
180 else if(S_ISSOCK(st
->st_mode
)) {
185 else if(S_ISWHT(st
->st_mode
)) {
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
))
204 if((st
->st_mode
& S_ISGID
)) {
205 if((st
->st_mode
& S_IXGRP
))
210 if((st
->st_mode
& S_ISTXT
)) {
211 if((st
->st_mode
& S_IXOTH
))
217 file
->n_link
= st
->st_nlink
;
220 pwd
= getpwuid(st
->st_uid
);
222 if (asprintf(&file
->user
, "%u", (unsigned)st
->st_uid
) == -1)
225 file
->user
= strdup(pwd
->pw_name
);
226 if (file
->user
== NULL
) {
227 syslog(LOG_ERR
, "out of memory");
233 grp
= getgrgid(st
->st_gid
);
235 if (asprintf(&file
->group
, "%u", (unsigned)st
->st_gid
) == -1)
238 file
->group
= strdup(grp
->gr_name
);
239 if (file
->group
== NULL
) {
240 syslog(LOG_ERR
, "out of memory");
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)
249 if (asprintf(&file
->minor
, "%u", (unsigned)minor(st
->st_rdev
)) == -1)
252 /* Don't want to use the DDI/DKI crap. */
253 if (asprintf(&file
->major
, "%u", (unsigned)st
->st_rdev
) == -1)
255 if (asprintf(&file
->minor
, "%u", 0) == -1)
258 if (file
->major
== NULL
|| file
->minor
== NULL
) {
259 syslog(LOG_ERR
, "out of memory");
263 if (asprintf(&file
->size
, "%lu", (unsigned long)st
->st_size
) == -1)
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
);
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");
283 const char *p
= strrchr(filename
, '/');
288 if((flags
& LS_TYPE
) && file_type
!= 0) {
289 if (asprintf(&file
->filename
, "%s%c", p
, file_type
) == -1)
290 file
->filename
= NULL
;
292 file
->filename
= strdup(p
);
293 if (file
->filename
== NULL
) {
294 syslog(LOG_ERR
, "out of memory");
298 if(S_ISLNK(st
->st_mode
)) {
300 n
= readlink((char *)filename
, buf
, sizeof(buf
) - 1);
303 file
->link
= strdup(buf
);
304 if (file
->link
== NULL
) {
305 syslog(LOG_ERR
, "out of memory");
309 sec_fprintf2(out
, "readlink(%s): %s", filename
, strerror(errno
));
315 print_file(FILE *out
,
323 unsigned int max_size
,
324 unsigned int max_major
,
325 unsigned int max_minor
,
328 if(f
->filename
== NULL
)
331 if(flags
& LS_INODE
) {
332 sec_fprintf2(out
, "%*d", max_inode
, f
->inode
);
333 sec_fprintf2(out
, " ");
335 if(flags
& LS_SIZE
) {
336 sec_fprintf2(out
, "%*d", max_bsize
, f
->bsize
);
337 sec_fprintf2(out
, " ");
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
);
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
);
356 sec_fprintf2(out
, " -> %s", f
->link
);
357 sec_fprintf2(out
, "\r\n");
361 compare_filename(struct fileinfo
*a
, struct fileinfo
*b
)
363 if(a
->filename
== NULL
)
365 if(b
->filename
== NULL
)
367 return strcmp(a
->filename
, b
->filename
);
371 compare_mtime(struct fileinfo
*a
, struct fileinfo
*b
)
373 if(a
->filename
== NULL
)
375 if(b
->filename
== NULL
)
377 return b
->st
.st_mtime
- a
->st
.st_mtime
;
381 compare_size(struct fileinfo
*a
, struct fileinfo
*b
)
383 if(a
->filename
== NULL
)
385 if(b
->filename
== NULL
)
387 return b
->st
.st_size
- a
->st
.st_size
;
390 static int list_dir(FILE*, const char*, int);
404 * Operate as lstat but fake up entries for AFS mount points so we don't
405 * have to fetch them.
409 static int do_the_afs_dance
= 1;
413 lstat_file (const char *file
, struct stat
*sb
)
416 if (do_the_afs_dance
&&
419 && strcmp(file
, "..")
420 && strcmp(file
, "/"))
422 struct ViceIoctl a_params
;
425 static ino_t ino_counter
= 0, ino_last
= 0;
427 const int maxsize
= 2048;
429 path_bkp
= strdup (file
);
430 if (path_bkp
== NULL
)
433 a_params
.out
= malloc (maxsize
);
434 if (a_params
.out
== NULL
) {
439 /* If path contains more than the filename alone - split it */
441 last
= strrchr (path_bkp
, '/');
444 /* if path ended in /, replace with `.' */
447 a_params
.in
= last
+ 1;
448 while(last
> path_bkp
&& *--last
== '/');
449 if(*last
!= '/' || last
!= path_bkp
) {
453 /* we got to the start, so this must be the root dir */
456 /* file is relative to cdir */
458 a_params
.in
= path_bkp
;
461 a_params
.in_size
= strlen (a_params
.in
) + 1;
462 a_params
.out_size
= maxsize
;
464 ret
= k_pioctl (dir
, VIOC_AFS_STAT_MT_PT
, &a_params
, 0);
472 /* if we get EINVAL this is probably not a mountpoint */
473 return lstat (file
, sb
);
477 * wow this was a mountpoint, lets cook the struct stat
478 * use . as a prototype
481 ret
= lstat (dir
, sb
);
486 if (ino_last
== sb
->st_ino
)
489 ino_last
= sb
->st_ino
;
492 sb
->st_ino
+= ino_counter
;
498 return lstat (file
, sb
);
501 #define IS_DOT_DOTDOT(X) ((X)[0] == '.' && ((X)[1] == '\0' || \
502 ((X)[1] == '.' && (X)[2] == '\0')))
505 list_files(FILE *out
, const char **files
, int n_files
, int flags
)
510 size_t total_blocks
= 0;
518 flags
|= LS_SHOW_DIRNAME
;
520 fi
= calloc(n_files
, sizeof(*fi
));
522 syslog(LOG_ERR
, "out of memory");
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
;
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
)) {
534 dirs
= calloc(n_files
, sizeof(*dirs
));
536 syslog(LOG_ERR
, "%s: %m", files
[i
]);
541 if((flags
& LS_DIRS
) == 0)
544 if(include_in_list
) {
545 ret
= make_fileinfo(out
, files
[i
], &fi
[i
], flags
);
552 switch(SORT_MODE(flags
)) {
554 qsort(fi
, n_files
, sizeof(*fi
),
555 (int (*)(const void*, const void*))compare_filename
);
558 qsort(fi
, n_files
, sizeof(*fi
),
559 (int (*)(const void*, const void*))compare_mtime
);
562 qsort(fi
, n_files
, sizeof(*fi
),
563 (int (*)(const void*, const void*))compare_size
);
566 if(DISP_MODE(flags
) == LS_DISP_LONG
) {
572 unsigned int max_size
= 0;
573 unsigned int max_major
= 0;
574 unsigned int max_minor
= 0;
576 for(i
= 0; i
< n_files
; i
++) {
577 if(fi
[i
].filename
== NULL
)
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
);
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
);
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
--)
623 for(i
= 0; i
< n_files
; i
++)
636 } else if(DISP_MODE(flags
) == LS_DISP_COLUMN
||
637 DISP_MODE(flags
) == LS_DISP_CROSS
) {
640 int num_files
= n_files
;
643 for(i
= 0; i
< n_files
; i
++) {
644 if(fi
[i
].filename
== NULL
) {
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
);
655 if(flags
& LS_SIZE
) {
656 columns
= 80 / (size_len
+ 1 + max_len
+ 1);
657 max_len
= 80 / columns
- size_len
- 1;
659 columns
= 80 / (max_len
+ 1); /* get space between columns */
660 max_len
= 80 / columns
;
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
)
670 sec_fprintf2(out
, "%*u %-*s", size_len
, fi
[i
].bsize
,
671 max_len
, fi
[i
].filename
);
673 sec_fprintf2(out
, "%-*s", max_len
, fi
[i
].filename
);
676 sec_fprintf2(out
, "\r\n");
681 sec_fprintf2(out
, "\r\n");
683 int skip
= (num_files
+ columns
- 1) / columns
;
685 for(i
= 0; i
< skip
; i
++) {
686 for(j
= i
; j
< n_files
;) {
687 while(j
< n_files
&& fi
[j
].filename
== NULL
)
690 sec_fprintf2(out
, "%*u %-*s", size_len
, fi
[j
].bsize
,
691 max_len
, fi
[j
].filename
);
693 sec_fprintf2(out
, "%-*s", max_len
, fi
[j
].filename
);
696 sec_fprintf2(out
, "\r\n");
700 for(i
= 0; i
< n_files
; i
++) {
701 if(fi
[i
].filename
== NULL
)
703 sec_fprintf2(out
, "%s\r\n", fi
[i
].filename
);
707 if(((flags
& LS_DIRS
) == 0 || (flags
& LS_RECURSIVE
)) && dirs
!= NULL
) {
708 for(i
= 0; i
< n_files
; i
++) {
710 const char *p
= strrchr(files
[i
], '/');
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
]);
721 list_dir(out
, files
[i
], flags
| LS_DIRS
| LS_EXTRA_BLANK
);
727 for(i
= 0; i
< n_files
; i
++)
728 free_fileinfo(&fi
[i
]);
736 free_files (char **files
, int n
)
740 for (i
= 0; i
< n
; ++i
)
746 hide_file(const char *filename
, int flags
)
748 if(filename
[0] != '.')
750 if((flags
& LS_IGNORE_DOT
))
752 if(filename
[1] == '\0' || (filename
[1] == '.' && filename
[2] == '\0')) {
753 if((flags
& LS_SHOW_ALL
))
762 list_dir(FILE *out
, const char *directory
, int flags
)
764 DIR *d
= opendir(directory
);
771 syslog(LOG_ERR
, "%s: %m", directory
);
774 while((ent
= readdir(d
)) != NULL
) {
777 if(hide_file(ent
->d_name
, flags
))
779 tmp
= realloc(files
, (n_files
+ 1) * sizeof(*files
));
781 syslog(LOG_ERR
, "%s: out of memory", directory
);
782 free_files (files
, n_files
);
787 ret
= asprintf(&files
[n_files
], "%s/%s", directory
, ent
->d_name
);
789 syslog(LOG_ERR
, "%s: out of memory", directory
);
790 free_files (files
, n_files
);
797 return list_files(out
, (const char**)files
, n_files
, flags
| LS_DIR_FLAG
);
801 parse_flags(const char *options
)
804 int flags
= LS_SORT_NAME
| LS_IGNORE_DOT
| LS_DISP_COLUMN
;
806 int flags
= LS_SORT_NAME
| LS_IGNORE_DOT
| LS_DISP_LONG
;
810 if(options
== NULL
|| *options
!= '-')
812 for(p
= options
+ 1; *p
; p
++) {
815 flags
= (flags
& ~LS_DISP_MODE
);
818 flags
|= LS_SHOW_ALL
;
821 flags
&= ~LS_IGNORE_DOT
;
824 flags
= (flags
& ~LS_DISP_MODE
) | LS_DISP_COLUMN
;
830 flags
= (flags
& ~LS_SORT_MODE
);
839 flags
= (flags
& ~LS_DISP_MODE
) | LS_DISP_LONG
;
842 flags
|= LS_SORT_REVERSE
;
845 flags
|= LS_RECURSIVE
;
851 flags
= (flags
& ~LS_SORT_MODE
) | LS_SORT_SIZE
;
854 flags
= (flags
& ~LS_SORT_MODE
) | LS_SORT_MTIME
;
857 flags
= (flags
& ~LS_DISP_MODE
) | LS_DISP_CROSS
;
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 */
877 builtin_ls(FILE *out
, const char *file
)
883 flags
= parse_flags(file
);
886 flags
= parse_flags("");
888 ret
= list_files(out
, &file
, 1, flags
);