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"
36 __RCSID("$Heimdal: ls.c 16216 2005-10-22 13:15:43Z lha $"
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
);
57 main(int argc
, char **argv
)
61 if(argc
> 1 && argv
[1][0] == '-') {
62 flags
= parse_flags(argv
[1]);
65 flags
= parse_flags(NULL
);
67 list_files(stdout
, (const char **)argv
+ i
, argc
- i
, flags
);
89 free_fileinfo(struct fileinfo
*f
)
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 */
125 #define S_ISTXT S_ISVTX
128 #if !defined(_S_IFMT) && defined(S_IFMT)
129 #define _S_IFMT S_IFMT
133 #define S_ISSOCK(mode) (((mode) & _S_IFMT) == S_IFSOCK)
137 #define S_ISLNK(mode) (((mode) & _S_IFMT) == S_IFLNK)
141 block_convert(size_t blocks
)
144 return blocks
* S_BLKSIZE
/ 1024;
146 return blocks
* 512 / 1024;
151 make_fileinfo(FILE *out
, const char *filename
, struct fileinfo
*file
, int flags
)
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
)) {
164 else if(S_ISCHR(st
->st_mode
))
166 else if(S_ISBLK(st
->st_mode
))
168 else if(S_ISREG(st
->st_mode
)) {
170 if(st
->st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
))
173 else if(S_ISFIFO(st
->st_mode
)) {
177 else if(S_ISLNK(st
->st_mode
)) {
181 else if(S_ISSOCK(st
->st_mode
)) {
186 else if(S_ISWHT(st
->st_mode
)) {
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
))
205 if((st
->st_mode
& S_ISGID
)) {
206 if((st
->st_mode
& S_IXGRP
))
211 if((st
->st_mode
& S_ISTXT
)) {
212 if((st
->st_mode
& S_IXOTH
))
218 file
->n_link
= st
->st_nlink
;
221 pwd
= getpwuid(st
->st_uid
);
223 if (asprintf(&file
->user
, "%u", (unsigned)st
->st_uid
) == -1)
226 file
->user
= strdup(pwd
->pw_name
);
227 if (file
->user
== NULL
) {
228 syslog(LOG_ERR
, "out of memory");
234 grp
= getgrgid(st
->st_gid
);
236 if (asprintf(&file
->group
, "%u", (unsigned)st
->st_gid
) == -1)
239 file
->group
= strdup(grp
->gr_name
);
240 if (file
->group
== NULL
) {
241 syslog(LOG_ERR
, "out of memory");
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)
250 if (asprintf(&file
->minor
, "%u", (unsigned)minor(st
->st_rdev
)) == -1)
253 /* Don't want to use the DDI/DKI crap. */
254 if (asprintf(&file
->major
, "%u", (unsigned)st
->st_rdev
) == -1)
256 if (asprintf(&file
->minor
, "%u", 0) == -1)
259 if (file
->major
== NULL
|| file
->minor
== NULL
) {
260 syslog(LOG_ERR
, "out of memory");
264 if (asprintf(&file
->size
, "%lu", (unsigned long)st
->st_size
) == -1)
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
);
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");
284 const char *p
= strrchr(filename
, '/');
289 if((flags
& LS_TYPE
) && file_type
!= 0) {
290 if (asprintf(&file
->filename
, "%s%c", p
, file_type
) == -1)
291 file
->filename
= NULL
;
293 file
->filename
= strdup(p
);
294 if (file
->filename
== NULL
) {
295 syslog(LOG_ERR
, "out of memory");
299 if(S_ISLNK(st
->st_mode
)) {
301 n
= readlink((char *)filename
, buf
, sizeof(buf
) - 1);
304 file
->link
= strdup(buf
);
305 if (file
->link
== NULL
) {
306 syslog(LOG_ERR
, "out of memory");
310 sec_fprintf2(out
, "readlink(%s): %s", filename
, strerror(errno
));
316 print_file(FILE *out
,
329 if(f
->filename
== NULL
)
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
);
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
);
357 sec_fprintf2(out
, " -> %s", f
->link
);
358 sec_fprintf2(out
, "\r\n");
362 compare_filename(struct fileinfo
*a
, struct fileinfo
*b
)
364 if(a
->filename
== NULL
)
366 if(b
->filename
== NULL
)
368 return strcmp(a
->filename
, b
->filename
);
372 compare_mtime(struct fileinfo
*a
, struct fileinfo
*b
)
374 if(a
->filename
== NULL
)
376 if(b
->filename
== NULL
)
378 return b
->st
.st_mtime
- a
->st
.st_mtime
;
382 compare_size(struct fileinfo
*a
, struct fileinfo
*b
)
384 if(a
->filename
== NULL
)
386 if(b
->filename
== NULL
)
388 return b
->st
.st_size
- a
->st
.st_size
;
391 static int list_dir(FILE*, const char*, int);
405 * Operate as lstat but fake up entries for AFS mount points so we don't
406 * have to fetch them.
410 static int do_the_afs_dance
= 1;
414 lstat_file (const char *file
, struct stat
*sb
)
417 if (do_the_afs_dance
&&
420 && strcmp(file
, "..")
421 && strcmp(file
, "/"))
423 struct ViceIoctl a_params
;
426 static ino_t ino_counter
= 0, ino_last
= 0;
428 const int maxsize
= 2048;
430 path_bkp
= strdup (file
);
431 if (path_bkp
== NULL
)
434 a_params
.out
= malloc (maxsize
);
435 if (a_params
.out
== NULL
) {
440 /* If path contains more than the filename alone - split it */
442 last
= strrchr (path_bkp
, '/');
445 /* if path ended in /, replace with `.' */
448 a_params
.in
= last
+ 1;
449 while(last
> path_bkp
&& *--last
== '/');
450 if(*last
!= '/' || last
!= path_bkp
) {
454 /* we got to the start, so this must be the root dir */
457 /* file is relative to cdir */
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);
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
);
487 if (ino_last
== sb
->st_ino
)
490 ino_last
= sb
->st_ino
;
493 sb
->st_ino
+= ino_counter
;
499 return lstat (file
, sb
);
502 #define IS_DOT_DOTDOT(X) ((X)[0] == '.' && ((X)[1] == '\0' || \
503 ((X)[1] == '.' && (X)[2] == '\0')))
506 list_files(FILE *out
, const char **files
, int n_files
, int flags
)
511 size_t total_blocks
= 0;
519 flags
|= LS_SHOW_DIRNAME
;
521 fi
= calloc(n_files
, sizeof(*fi
));
523 syslog(LOG_ERR
, "out of memory");
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
;
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
)) {
535 dirs
= calloc(n_files
, sizeof(*dirs
));
537 syslog(LOG_ERR
, "%s: %m", files
[i
]);
542 if((flags
& LS_DIRS
) == 0)
545 if(include_in_list
) {
546 ret
= make_fileinfo(out
, files
[i
], &fi
[i
], flags
);
553 switch(SORT_MODE(flags
)) {
555 qsort(fi
, n_files
, sizeof(*fi
),
556 (int (*)(const void*, const void*))compare_filename
);
559 qsort(fi
, n_files
, sizeof(*fi
),
560 (int (*)(const void*, const void*))compare_mtime
);
563 qsort(fi
, n_files
, sizeof(*fi
),
564 (int (*)(const void*, const void*))compare_size
);
567 if(DISP_MODE(flags
) == LS_DISP_LONG
) {
577 for(i
= 0; i
< n_files
; i
++) {
578 if(fi
[i
].filename
== NULL
)
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
);
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
--)
624 for(i
= 0; i
< n_files
; i
++)
637 } else if(DISP_MODE(flags
) == LS_DISP_COLUMN
||
638 DISP_MODE(flags
) == LS_DISP_CROSS
) {
641 int num_files
= n_files
;
644 for(i
= 0; i
< n_files
; i
++) {
645 if(fi
[i
].filename
== NULL
) {
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
);
656 if(flags
& LS_SIZE
) {
657 columns
= 80 / (size_len
+ 1 + max_len
+ 1);
658 max_len
= 80 / columns
- size_len
- 1;
660 columns
= 80 / (max_len
+ 1); /* get space between columns */
661 max_len
= 80 / columns
;
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
)
671 sec_fprintf2(out
, "%*u %-*s", size_len
, fi
[i
].bsize
,
672 max_len
, fi
[i
].filename
);
674 sec_fprintf2(out
, "%-*s", max_len
, fi
[i
].filename
);
677 sec_fprintf2(out
, "\r\n");
682 sec_fprintf2(out
, "\r\n");
684 int skip
= (num_files
+ columns
- 1) / columns
;
686 for(i
= 0; i
< skip
; i
++) {
687 for(j
= i
; j
< n_files
;) {
688 while(j
< n_files
&& fi
[j
].filename
== NULL
)
691 sec_fprintf2(out
, "%*u %-*s", size_len
, fi
[j
].bsize
,
692 max_len
, fi
[j
].filename
);
694 sec_fprintf2(out
, "%-*s", max_len
, fi
[j
].filename
);
697 sec_fprintf2(out
, "\r\n");
701 for(i
= 0; i
< n_files
; i
++) {
702 if(fi
[i
].filename
== NULL
)
704 sec_fprintf2(out
, "%s\r\n", fi
[i
].filename
);
708 if(((flags
& LS_DIRS
) == 0 || (flags
& LS_RECURSIVE
)) && dirs
!= NULL
) {
709 for(i
= 0; i
< n_files
; i
++) {
711 const char *p
= strrchr(files
[i
], '/');
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
);
728 for(i
= 0; i
< n_files
; i
++)
729 free_fileinfo(&fi
[i
]);
737 free_files (char **files
, int n
)
741 for (i
= 0; i
< n
; ++i
)
747 hide_file(const char *filename
, int flags
)
749 if(filename
[0] != '.')
751 if((flags
& LS_IGNORE_DOT
))
753 if(filename
[1] == '\0' || (filename
[1] == '.' && filename
[2] == '\0')) {
754 if((flags
& LS_SHOW_ALL
))
763 list_dir(FILE *out
, const char *directory
, int flags
)
765 DIR *d
= opendir(directory
);
772 syslog(LOG_ERR
, "%s: %m", directory
);
775 while((ent
= readdir(d
)) != NULL
) {
778 if(hide_file(ent
->d_name
, flags
))
780 tmp
= realloc(files
, (n_files
+ 1) * sizeof(*files
));
782 syslog(LOG_ERR
, "%s: out of memory", directory
);
783 free_files (files
, n_files
);
788 ret
= asprintf(&files
[n_files
], "%s/%s", directory
, ent
->d_name
);
790 syslog(LOG_ERR
, "%s: out of memory", directory
);
791 free_files (files
, n_files
);
798 return list_files(out
, (const char**)files
, n_files
, flags
| LS_DIR_FLAG
);
802 parse_flags(const char *options
)
805 int flags
= LS_SORT_NAME
| LS_IGNORE_DOT
| LS_DISP_COLUMN
;
807 int flags
= LS_SORT_NAME
| LS_IGNORE_DOT
| LS_DISP_LONG
;
811 if(options
== NULL
|| *options
!= '-')
813 for(p
= options
+ 1; *p
; p
++) {
816 flags
= (flags
& ~LS_DISP_MODE
);
819 flags
|= LS_SHOW_ALL
;
822 flags
&= ~LS_IGNORE_DOT
;
825 flags
= (flags
& ~LS_DISP_MODE
) | LS_DISP_COLUMN
;
831 flags
= (flags
& ~LS_SORT_MODE
);
840 flags
= (flags
& ~LS_DISP_MODE
) | LS_DISP_LONG
;
843 flags
|= LS_SORT_REVERSE
;
846 flags
|= LS_RECURSIVE
;
852 flags
= (flags
& ~LS_SORT_MODE
) | LS_SORT_SIZE
;
855 flags
= (flags
& ~LS_SORT_MODE
) | LS_SORT_MTIME
;
858 flags
= (flags
& ~LS_DISP_MODE
) | LS_DISP_CROSS
;
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 */
878 builtin_ls(FILE *out
, const char *file
)
884 flags
= parse_flags(file
);
887 flags
= parse_flags("");
889 ret
= list_files(out
, &file
, 1, flags
);