ls.c revision 90926
155682Smarkm/*
290926Snectar * Copyright (c) 1999 - 2001 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of KTH nor the names of its contributors may be
1855682Smarkm *    used to endorse or promote products derived from this software without
1955682Smarkm *    specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
2255682Smarkm * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2455682Smarkm * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
2555682Smarkm * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2655682Smarkm * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2755682Smarkm * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2855682Smarkm * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2955682Smarkm * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3055682Smarkm * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3155682Smarkm * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
3255682Smarkm
3372445Sassar#ifndef TEST
3455682Smarkm#include "ftpd_locl.h"
3555682Smarkm
3690926SnectarRCSID("$Id: ls.c,v 1.23 2001/09/14 11:32:52 joda Exp $");
3755682Smarkm
3872445Sassar#else
3972445Sassar#include <stdio.h>
4072445Sassar#include <string.h>
4172445Sassar#include <stdlib.h>
4272445Sassar#include <time.h>
4372445Sassar#include <dirent.h>
4472445Sassar#include <sys/stat.h>
4572445Sassar#include <unistd.h>
4672445Sassar#include <pwd.h>
4772445Sassar#include <grp.h>
4872445Sassar#include <errno.h>
4972445Sassar
5072445Sassar#define sec_fprintf2 fprintf
5172445Sassar#define sec_fflush fflush
5290926Snectarstatic void list_files(FILE *out, const char **files, int n_files, int flags);
5390926Snectarstatic int parse_flags(const char *options);
5490926Snectar
5572445Sassarint
5672445Sassarmain(int argc, char **argv)
5772445Sassar{
5890926Snectar    int i = 1;
5990926Snectar    int flags;
6090926Snectar    if(argc > 1 && argv[1][0] == '-') {
6190926Snectar	flags = parse_flags(argv[1]);
6290926Snectar	i = 2;
6390926Snectar    } else
6490926Snectar	flags = parse_flags(NULL);
6590926Snectar
6690926Snectar    list_files(stdout, (const char **)argv + i, argc - i, flags);
6772445Sassar    return 0;
6872445Sassar}
6972445Sassar#endif
7072445Sassar
7155682Smarkmstruct fileinfo {
7255682Smarkm    struct stat st;
7355682Smarkm    int inode;
7455682Smarkm    int bsize;
7555682Smarkm    char mode[11];
7655682Smarkm    int n_link;
7755682Smarkm    char *user;
7855682Smarkm    char *group;
7955682Smarkm    char *size;
8055682Smarkm    char *major;
8155682Smarkm    char *minor;
8255682Smarkm    char *date;
8355682Smarkm    char *filename;
8455682Smarkm    char *link;
8555682Smarkm};
8655682Smarkm
8755682Smarkmstatic void
8855682Smarkmfree_fileinfo(struct fileinfo *f)
8955682Smarkm{
9055682Smarkm    free(f->user);
9155682Smarkm    free(f->group);
9255682Smarkm    free(f->size);
9355682Smarkm    free(f->major);
9455682Smarkm    free(f->minor);
9555682Smarkm    free(f->date);
9655682Smarkm    free(f->filename);
9755682Smarkm    free(f->link);
9855682Smarkm}
9955682Smarkm
10072445Sassar#define LS_DIRS		(1 << 0)
10172445Sassar#define LS_IGNORE_DOT	(1 << 1)
10272445Sassar#define LS_SORT_MODE	(3 << 2)
10355682Smarkm#define SORT_MODE(f) ((f) & LS_SORT_MODE)
10472445Sassar#define LS_SORT_NAME	(1 << 2)
10572445Sassar#define LS_SORT_MTIME	(2 << 2)
10672445Sassar#define LS_SORT_SIZE	(3 << 2)
10772445Sassar#define LS_SORT_REVERSE	(1 << 4)
10855682Smarkm
10972445Sassar#define LS_SIZE		(1 << 5)
11072445Sassar#define LS_INODE	(1 << 6)
11172445Sassar#define LS_TYPE		(1 << 7)
11272445Sassar#define LS_DISP_MODE	(3 << 8)
11372445Sassar#define DISP_MODE(f) ((f) & LS_DISP_MODE)
11472445Sassar#define LS_DISP_LONG	(1 << 8)
11572445Sassar#define LS_DISP_COLUMN	(2 << 8)
11672445Sassar#define LS_DISP_CROSS	(3 << 8)
11790926Snectar#define LS_SHOW_ALL	(1 << 10)
11890926Snectar#define LS_RECURSIVE	(1 << 11)
11990926Snectar#define LS_EXTRA_BLANK	(1 << 12)
12090926Snectar#define LS_SHOW_DIRNAME	(1 << 13)
12190926Snectar#define LS_DIR_FLAG	(1 << 14)	/* these files come via list_dir */
12255682Smarkm
12355682Smarkm#ifndef S_ISTXT
12455682Smarkm#define S_ISTXT S_ISVTX
12555682Smarkm#endif
12655682Smarkm
12755682Smarkm#ifndef S_ISSOCK
12855682Smarkm#define S_ISSOCK(mode)  (((mode) & _S_IFMT) == S_IFSOCK)
12955682Smarkm#endif
13055682Smarkm
13155682Smarkm#ifndef S_ISLNK
13255682Smarkm#define S_ISLNK(mode)   (((mode) & _S_IFMT) == S_IFLNK)
13355682Smarkm#endif
13455682Smarkm
13590926Snectarstatic size_t
13690926Snectarblock_convert(size_t blocks)
13790926Snectar{
13890926Snectar#ifdef S_BLKSIZE
13990926Snectar    return blocks * S_BLKSIZE / 1024;
14090926Snectar#else
14190926Snectar    return blocks * 512 / 1024;
14290926Snectar#endif
14390926Snectar}
14490926Snectar
14555682Smarkmstatic void
14690926Snectarmake_fileinfo(FILE *out, const char *filename, struct fileinfo *file, int flags)
14755682Smarkm{
14855682Smarkm    char buf[128];
14972445Sassar    int file_type = 0;
15055682Smarkm    struct stat *st = &file->st;
15155682Smarkm
15255682Smarkm    file->inode = st->st_ino;
15390926Snectar    file->bsize = block_convert(st->st_blocks);
15455682Smarkm
15572445Sassar    if(S_ISDIR(st->st_mode)) {
15655682Smarkm	file->mode[0] = 'd';
15772445Sassar	file_type = '/';
15872445Sassar    }
15955682Smarkm    else if(S_ISCHR(st->st_mode))
16055682Smarkm	file->mode[0] = 'c';
16155682Smarkm    else if(S_ISBLK(st->st_mode))
16255682Smarkm	file->mode[0] = 'b';
16372445Sassar    else if(S_ISREG(st->st_mode)) {
16455682Smarkm	file->mode[0] = '-';
16572445Sassar	if(st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
16672445Sassar	    file_type = '*';
16772445Sassar    }
16872445Sassar    else if(S_ISFIFO(st->st_mode)) {
16955682Smarkm	file->mode[0] = 'p';
17072445Sassar	file_type = '|';
17172445Sassar    }
17272445Sassar    else if(S_ISLNK(st->st_mode)) {
17355682Smarkm	file->mode[0] = 'l';
17472445Sassar	file_type = '@';
17572445Sassar    }
17672445Sassar    else if(S_ISSOCK(st->st_mode)) {
17755682Smarkm	file->mode[0] = 's';
17872445Sassar	file_type = '=';
17972445Sassar    }
18055682Smarkm#ifdef S_ISWHT
18172445Sassar    else if(S_ISWHT(st->st_mode)) {
18255682Smarkm	file->mode[0] = 'w';
18372445Sassar	file_type = '%';
18472445Sassar    }
18555682Smarkm#endif
18655682Smarkm    else
18755682Smarkm	file->mode[0] = '?';
18855682Smarkm    {
18955682Smarkm	char *x[] = { "---", "--x", "-w-", "-wx",
19055682Smarkm		      "r--", "r-x", "rw-", "rwx" };
19155682Smarkm	strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]);
19255682Smarkm	strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]);
19355682Smarkm	strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]);
19455682Smarkm	if((st->st_mode & S_ISUID)) {
19555682Smarkm	    if((st->st_mode & S_IXUSR))
19655682Smarkm		file->mode[3] = 's';
19755682Smarkm	    else
19855682Smarkm		file->mode[3] = 'S';
19955682Smarkm	}
20055682Smarkm	if((st->st_mode & S_ISGID)) {
20155682Smarkm	    if((st->st_mode & S_IXGRP))
20255682Smarkm		file->mode[6] = 's';
20355682Smarkm	    else
20455682Smarkm		file->mode[6] = 'S';
20555682Smarkm	}
20655682Smarkm	if((st->st_mode & S_ISTXT)) {
20755682Smarkm	    if((st->st_mode & S_IXOTH))
20855682Smarkm		file->mode[9] = 't';
20955682Smarkm	    else
21055682Smarkm		file->mode[9] = 'T';
21155682Smarkm	}
21255682Smarkm    }
21355682Smarkm    file->n_link = st->st_nlink;
21455682Smarkm    {
21555682Smarkm	struct passwd *pwd;
21655682Smarkm	pwd = getpwuid(st->st_uid);
21755682Smarkm	if(pwd == NULL)
21855682Smarkm	    asprintf(&file->user, "%u", (unsigned)st->st_uid);
21955682Smarkm	else
22055682Smarkm	    file->user = strdup(pwd->pw_name);
22155682Smarkm    }
22255682Smarkm    {
22355682Smarkm	struct group *grp;
22455682Smarkm	grp = getgrgid(st->st_gid);
22555682Smarkm	if(grp == NULL)
22655682Smarkm	    asprintf(&file->group, "%u", (unsigned)st->st_gid);
22755682Smarkm	else
22855682Smarkm	    file->group = strdup(grp->gr_name);
22955682Smarkm    }
23055682Smarkm
23155682Smarkm    if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
23255682Smarkm#if defined(major) && defined(minor)
23355682Smarkm	asprintf(&file->major, "%u", (unsigned)major(st->st_rdev));
23455682Smarkm	asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev));
23555682Smarkm#else
23655682Smarkm	/* Don't want to use the DDI/DKI crap. */
23755682Smarkm	asprintf(&file->major, "%u", (unsigned)st->st_rdev);
23855682Smarkm	asprintf(&file->minor, "%u", 0);
23955682Smarkm#endif
24055682Smarkm    } else
24155682Smarkm	asprintf(&file->size, "%lu", (unsigned long)st->st_size);
24255682Smarkm
24355682Smarkm    {
24455682Smarkm	time_t t = time(NULL);
24572445Sassar	time_t mtime = st->st_mtime;
24672445Sassar	struct tm *tm = localtime(&mtime);
24772445Sassar	if((t - mtime > 6*30*24*60*60) ||
24872445Sassar	   (mtime - t > 6*30*24*60*60))
24955682Smarkm	    strftime(buf, sizeof(buf), "%b %e  %Y", tm);
25055682Smarkm	else
25155682Smarkm	    strftime(buf, sizeof(buf), "%b %e %H:%M", tm);
25255682Smarkm	file->date = strdup(buf);
25355682Smarkm    }
25455682Smarkm    {
25555682Smarkm	const char *p = strrchr(filename, '/');
25655682Smarkm	if(p)
25755682Smarkm	    p++;
25855682Smarkm	else
25955682Smarkm	    p = filename;
26072445Sassar	if((flags & LS_TYPE) && file_type != 0)
26172445Sassar	    asprintf(&file->filename, "%s%c", p, file_type);
26272445Sassar	else
26372445Sassar	    file->filename = strdup(p);
26455682Smarkm    }
26555682Smarkm    if(S_ISLNK(st->st_mode)) {
26655682Smarkm	int n;
26755682Smarkm	n = readlink((char *)filename, buf, sizeof(buf));
26855682Smarkm	if(n >= 0) {
26955682Smarkm	    buf[n] = '\0';
27055682Smarkm	    file->link = strdup(buf);
27155682Smarkm	} else
27290926Snectar	    sec_fprintf2(out, "readlink(%s): %s", filename, strerror(errno));
27355682Smarkm    }
27455682Smarkm}
27555682Smarkm
27655682Smarkmstatic void
27755682Smarkmprint_file(FILE *out,
27855682Smarkm	   int flags,
27955682Smarkm	   struct fileinfo *f,
28055682Smarkm	   int max_inode,
28155682Smarkm	   int max_bsize,
28255682Smarkm	   int max_n_link,
28355682Smarkm	   int max_user,
28455682Smarkm	   int max_group,
28555682Smarkm	   int max_size,
28655682Smarkm	   int max_major,
28755682Smarkm	   int max_minor,
28855682Smarkm	   int max_date)
28955682Smarkm{
29055682Smarkm    if(f->filename == NULL)
29155682Smarkm	return;
29255682Smarkm
29355682Smarkm    if(flags & LS_INODE) {
29455682Smarkm	sec_fprintf2(out, "%*d", max_inode, f->inode);
29555682Smarkm	sec_fprintf2(out, "  ");
29655682Smarkm    }
29755682Smarkm    if(flags & LS_SIZE) {
29855682Smarkm	sec_fprintf2(out, "%*d", max_bsize, f->bsize);
29955682Smarkm	sec_fprintf2(out, "  ");
30055682Smarkm    }
30155682Smarkm    sec_fprintf2(out, "%s", f->mode);
30255682Smarkm    sec_fprintf2(out, "  ");
30355682Smarkm    sec_fprintf2(out, "%*d", max_n_link, f->n_link);
30455682Smarkm    sec_fprintf2(out, " ");
30555682Smarkm    sec_fprintf2(out, "%-*s", max_user, f->user);
30655682Smarkm    sec_fprintf2(out, "  ");
30755682Smarkm    sec_fprintf2(out, "%-*s", max_group, f->group);
30855682Smarkm    sec_fprintf2(out, "  ");
30955682Smarkm    if(f->major != NULL && f->minor != NULL)
31055682Smarkm	sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor);
31155682Smarkm    else
31255682Smarkm	sec_fprintf2(out, "%*s", max_size, f->size);
31355682Smarkm    sec_fprintf2(out, " ");
31455682Smarkm    sec_fprintf2(out, "%*s", max_date, f->date);
31555682Smarkm    sec_fprintf2(out, " ");
31655682Smarkm    sec_fprintf2(out, "%s", f->filename);
31755682Smarkm    if(f->link)
31855682Smarkm	sec_fprintf2(out, " -> %s", f->link);
31955682Smarkm    sec_fprintf2(out, "\r\n");
32055682Smarkm}
32155682Smarkm
32255682Smarkmstatic int
32355682Smarkmcompare_filename(struct fileinfo *a, struct fileinfo *b)
32455682Smarkm{
32555682Smarkm    if(a->filename == NULL)
32655682Smarkm	return 1;
32755682Smarkm    if(b->filename == NULL)
32855682Smarkm	return -1;
32955682Smarkm    return strcmp(a->filename, b->filename);
33055682Smarkm}
33155682Smarkm
33255682Smarkmstatic int
33355682Smarkmcompare_mtime(struct fileinfo *a, struct fileinfo *b)
33455682Smarkm{
33555682Smarkm    if(a->filename == NULL)
33655682Smarkm	return 1;
33755682Smarkm    if(b->filename == NULL)
33855682Smarkm	return -1;
33972445Sassar    return b->st.st_mtime - a->st.st_mtime;
34055682Smarkm}
34155682Smarkm
34255682Smarkmstatic int
34355682Smarkmcompare_size(struct fileinfo *a, struct fileinfo *b)
34455682Smarkm{
34555682Smarkm    if(a->filename == NULL)
34655682Smarkm	return 1;
34755682Smarkm    if(b->filename == NULL)
34855682Smarkm	return -1;
34972445Sassar    return b->st.st_size - a->st.st_size;
35055682Smarkm}
35155682Smarkm
35255682Smarkmstatic void
35355682Smarkmlist_dir(FILE *out, const char *directory, int flags);
35455682Smarkm
35555682Smarkmstatic int
35655682Smarkmlog10(int num)
35755682Smarkm{
35855682Smarkm    int i = 1;
35955682Smarkm    while(num > 10) {
36055682Smarkm	i++;
36155682Smarkm	num /= 10;
36255682Smarkm    }
36355682Smarkm    return i;
36455682Smarkm}
36555682Smarkm
36655682Smarkm/*
36755682Smarkm * Operate as lstat but fake up entries for AFS mount points so we don't
36855682Smarkm * have to fetch them.
36955682Smarkm */
37055682Smarkm
37172445Sassar#ifdef KRB4
37272445Sassarstatic int do_the_afs_dance = 1;
37372445Sassar#endif
37472445Sassar
37555682Smarkmstatic int
37655682Smarkmlstat_file (const char *file, struct stat *sb)
37755682Smarkm{
37855682Smarkm#ifdef KRB4
37972445Sassar    if (do_the_afs_dance &&
38072445Sassar	k_hasafs()
38155682Smarkm	&& strcmp(file, ".")
38272445Sassar	&& strcmp(file, "..")
38372445Sassar	&& strcmp(file, "/"))
38455682Smarkm    {
38555682Smarkm	struct ViceIoctl    a_params;
38672445Sassar	char               *dir, *last;
38755682Smarkm	char               *path_bkp;
38855682Smarkm	static ino_t	   ino_counter = 0, ino_last = 0;
38955682Smarkm	int		   ret;
39055682Smarkm	const int	   maxsize = 2048;
39155682Smarkm
39255682Smarkm	path_bkp = strdup (file);
39355682Smarkm	if (path_bkp == NULL)
39455682Smarkm	    return -1;
39555682Smarkm
39655682Smarkm	a_params.out = malloc (maxsize);
39755682Smarkm	if (a_params.out == NULL) {
39855682Smarkm	    free (path_bkp);
39955682Smarkm	    return -1;
40055682Smarkm	}
40155682Smarkm
40255682Smarkm	/* If path contains more than the filename alone - split it */
40355682Smarkm
40455682Smarkm	last = strrchr (path_bkp, '/');
40555682Smarkm	if (last != NULL) {
40672445Sassar	    if(last[1] == '\0')
40772445Sassar		/* if path ended in /, replace with `.' */
40872445Sassar		a_params.in = ".";
40972445Sassar	    else
41072445Sassar		a_params.in = last + 1;
41172445Sassar	    while(last > path_bkp && *--last == '/');
41272445Sassar	    if(*last != '/' || last != path_bkp) {
41372445Sassar		*++last = '\0';
41472445Sassar		dir = path_bkp;
41572445Sassar	    } else
41672445Sassar		/* we got to the start, so this must be the root dir */
41772445Sassar		dir = "/";
41872445Sassar	} else {
41972445Sassar	    /* file is relative to cdir */
42072445Sassar	    dir = ".";
42172445Sassar	    a_params.in = path_bkp;
42272445Sassar	}
42355682Smarkm
42455682Smarkm	a_params.in_size  = strlen (a_params.in) + 1;
42555682Smarkm	a_params.out_size = maxsize;
42655682Smarkm
42772445Sassar	ret = k_pioctl (dir, VIOC_AFS_STAT_MT_PT, &a_params, 0);
42855682Smarkm	free (a_params.out);
42955682Smarkm	if (ret < 0) {
43055682Smarkm	    free (path_bkp);
43155682Smarkm
43255682Smarkm	    if (errno != EINVAL)
43355682Smarkm		return ret;
43455682Smarkm	    else
43555682Smarkm		/* if we get EINVAL this is probably not a mountpoint */
43655682Smarkm		return lstat (file, sb);
43755682Smarkm	}
43855682Smarkm
43955682Smarkm	/*
44055682Smarkm	 * wow this was a mountpoint, lets cook the struct stat
44155682Smarkm	 * use . as a prototype
44255682Smarkm	 */
44355682Smarkm
44472445Sassar	ret = lstat (dir, sb);
44555682Smarkm	free (path_bkp);
44655682Smarkm	if (ret < 0)
44755682Smarkm	    return ret;
44855682Smarkm
44955682Smarkm	if (ino_last == sb->st_ino)
45055682Smarkm	    ino_counter++;
45155682Smarkm	else {
45255682Smarkm	    ino_last    = sb->st_ino;
45355682Smarkm	    ino_counter = 0;
45455682Smarkm	}
45555682Smarkm	sb->st_ino += ino_counter;
45655682Smarkm	sb->st_nlink = 3;
45755682Smarkm
45855682Smarkm	return 0;
45955682Smarkm    }
46055682Smarkm#endif /* KRB4 */
46155682Smarkm    return lstat (file, sb);
46255682Smarkm}
46355682Smarkm
46490926Snectar#define IS_DOT_DOTDOT(X) ((X)[0] == '.' && ((X)[1] == '\0' || \
46590926Snectar				((X)[1] == '.' && (X)[2] == '\0')))
46690926Snectar
46755682Smarkmstatic void
46855682Smarkmlist_files(FILE *out, const char **files, int n_files, int flags)
46955682Smarkm{
47055682Smarkm    struct fileinfo *fi;
47155682Smarkm    int i;
47290926Snectar    int *dirs = NULL;
47390926Snectar    size_t total_blocks = 0;
47490926Snectar    int n_print = 0;
47555682Smarkm
47690926Snectar    if(n_files > 1)
47790926Snectar	flags |= LS_SHOW_DIRNAME;
47890926Snectar
47955682Smarkm    fi = calloc(n_files, sizeof(*fi));
48055682Smarkm    if (fi == NULL) {
48155682Smarkm	sec_fprintf2(out, "ouf of memory\r\n");
48255682Smarkm	return;
48355682Smarkm    }
48455682Smarkm    for(i = 0; i < n_files; i++) {
48555682Smarkm	if(lstat_file(files[i], &fi[i].st) < 0) {
48655682Smarkm	    sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno));
48755682Smarkm	    fi[i].filename = NULL;
48855682Smarkm	} else {
48990926Snectar	    int include_in_list = 1;
49090926Snectar	    total_blocks += block_convert(fi[i].st.st_blocks);
49190926Snectar	    if(S_ISDIR(fi[i].st.st_mode)) {
49290926Snectar		if(dirs == NULL)
49390926Snectar		    dirs = calloc(n_files, sizeof(*dirs));
49490926Snectar		if(dirs == NULL) {
49590926Snectar		    sec_fprintf2(out, "%s: %s\r\n",
49690926Snectar				 files[i], strerror(errno));
49790926Snectar		    goto out;
49890926Snectar		}
49990926Snectar		dirs[i] = 1;
50090926Snectar		if((flags & LS_DIRS) == 0)
50190926Snectar		    include_in_list = 0;
50255682Smarkm	    }
50390926Snectar	    if(include_in_list) {
50490926Snectar		make_fileinfo(out, files[i], &fi[i], flags);
50590926Snectar		n_print++;
50690926Snectar	    }
50755682Smarkm	}
50855682Smarkm    }
50955682Smarkm    switch(SORT_MODE(flags)) {
51055682Smarkm    case LS_SORT_NAME:
51155682Smarkm	qsort(fi, n_files, sizeof(*fi),
51255682Smarkm	      (int (*)(const void*, const void*))compare_filename);
51355682Smarkm	break;
51455682Smarkm    case LS_SORT_MTIME:
51555682Smarkm	qsort(fi, n_files, sizeof(*fi),
51655682Smarkm	      (int (*)(const void*, const void*))compare_mtime);
51755682Smarkm	break;
51855682Smarkm    case LS_SORT_SIZE:
51955682Smarkm	qsort(fi, n_files, sizeof(*fi),
52055682Smarkm	      (int (*)(const void*, const void*))compare_size);
52155682Smarkm	break;
52255682Smarkm    }
52372445Sassar    if(DISP_MODE(flags) == LS_DISP_LONG) {
52455682Smarkm	int max_inode = 0;
52555682Smarkm	int max_bsize = 0;
52655682Smarkm	int max_n_link = 0;
52755682Smarkm	int max_user = 0;
52855682Smarkm	int max_group = 0;
52955682Smarkm	int max_size = 0;
53055682Smarkm	int max_major = 0;
53155682Smarkm	int max_minor = 0;
53255682Smarkm	int max_date = 0;
53355682Smarkm	for(i = 0; i < n_files; i++) {
53455682Smarkm	    if(fi[i].filename == NULL)
53555682Smarkm		continue;
53655682Smarkm	    if(fi[i].inode > max_inode)
53755682Smarkm		max_inode = fi[i].inode;
53855682Smarkm	    if(fi[i].bsize > max_bsize)
53955682Smarkm		max_bsize = fi[i].bsize;
54055682Smarkm	    if(fi[i].n_link > max_n_link)
54155682Smarkm		max_n_link = fi[i].n_link;
54255682Smarkm	    if(strlen(fi[i].user) > max_user)
54355682Smarkm		max_user = strlen(fi[i].user);
54455682Smarkm	    if(strlen(fi[i].group) > max_group)
54555682Smarkm		max_group = strlen(fi[i].group);
54655682Smarkm	    if(fi[i].major != NULL && strlen(fi[i].major) > max_major)
54755682Smarkm		max_major = strlen(fi[i].major);
54855682Smarkm	    if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor)
54955682Smarkm		max_minor = strlen(fi[i].minor);
55055682Smarkm	    if(fi[i].size != NULL && strlen(fi[i].size) > max_size)
55155682Smarkm		max_size = strlen(fi[i].size);
55255682Smarkm	    if(strlen(fi[i].date) > max_date)
55355682Smarkm		max_date = strlen(fi[i].date);
55455682Smarkm	}
55555682Smarkm	if(max_size < max_major + max_minor + 2)
55655682Smarkm	    max_size = max_major + max_minor + 2;
55755682Smarkm	else if(max_size - max_minor - 2 > max_major)
55855682Smarkm	    max_major = max_size - max_minor - 2;
55955682Smarkm	max_inode = log10(max_inode);
56055682Smarkm	max_bsize = log10(max_bsize);
56155682Smarkm	max_n_link = log10(max_n_link);
56290926Snectar
56390926Snectar	if(n_print > 0)
56490926Snectar	    sec_fprintf2(out, "total %lu\r\n", (unsigned long)total_blocks);
56555682Smarkm	if(flags & LS_SORT_REVERSE)
56655682Smarkm	    for(i = n_files - 1; i >= 0; i--)
56755682Smarkm		print_file(out,
56855682Smarkm			   flags,
56955682Smarkm			   &fi[i],
57055682Smarkm			   max_inode,
57155682Smarkm			   max_bsize,
57255682Smarkm			   max_n_link,
57355682Smarkm			   max_user,
57455682Smarkm			   max_group,
57555682Smarkm			   max_size,
57655682Smarkm			   max_major,
57755682Smarkm			   max_minor,
57855682Smarkm			   max_date);
57955682Smarkm	else
58055682Smarkm	    for(i = 0; i < n_files; i++)
58155682Smarkm		print_file(out,
58255682Smarkm			   flags,
58355682Smarkm			   &fi[i],
58455682Smarkm			   max_inode,
58555682Smarkm			   max_bsize,
58655682Smarkm			   max_n_link,
58755682Smarkm			   max_user,
58855682Smarkm			   max_group,
58955682Smarkm			   max_size,
59055682Smarkm			   max_major,
59155682Smarkm			   max_minor,
59255682Smarkm			   max_date);
59372445Sassar    } else if(DISP_MODE(flags) == LS_DISP_COLUMN ||
59472445Sassar	      DISP_MODE(flags) == LS_DISP_CROSS) {
59572445Sassar	int max_len = 0;
59690926Snectar	int size_len = 0;
59772445Sassar	int num_files = n_files;
59872445Sassar	int columns;
59972445Sassar	int j;
60072445Sassar	for(i = 0; i < n_files; i++) {
60172445Sassar	    if(fi[i].filename == NULL) {
60272445Sassar		num_files--;
60372445Sassar		continue;
60472445Sassar	    }
60572445Sassar	    if(strlen(fi[i].filename) > max_len)
60672445Sassar		max_len = strlen(fi[i].filename);
60790926Snectar	    if(log10(fi[i].bsize) > size_len)
60890926Snectar		size_len = log10(fi[i].bsize);
60972445Sassar	}
61090926Snectar	if(num_files == 0)
61190926Snectar	    goto next;
61290926Snectar	if(flags & LS_SIZE) {
61390926Snectar	    columns = 80 / (size_len + 1 + max_len + 1);
61490926Snectar	    max_len = 80 / columns - size_len - 1;
61590926Snectar	} else {
61690926Snectar	    columns = 80 / (max_len + 1); /* get space between columns */
61790926Snectar	    max_len = 80 / columns;
61890926Snectar	}
61990926Snectar	if(flags & LS_SIZE)
62090926Snectar	    sec_fprintf2(out, "total %lu\r\n",
62190926Snectar			 (unsigned long)total_blocks);
62272445Sassar	if(DISP_MODE(flags) == LS_DISP_CROSS) {
62372445Sassar	    for(i = 0, j = 0; i < n_files; i++) {
62472445Sassar		if(fi[i].filename == NULL)
62572445Sassar		    continue;
62690926Snectar		if(flags & LS_SIZE)
62790926Snectar		    sec_fprintf2(out, "%*u %-*s", size_len, fi[i].bsize,
62890926Snectar				 max_len, fi[i].filename);
62990926Snectar		else
63090926Snectar		    sec_fprintf2(out, "%-*s", max_len, fi[i].filename);
63172445Sassar		j++;
63272445Sassar		if(j == columns) {
63372445Sassar		    sec_fprintf2(out, "\r\n");
63472445Sassar		    j = 0;
63572445Sassar		}
63672445Sassar	    }
63772445Sassar	    if(j > 0)
63890926Snectar		sec_fprintf2(out, "\r\n");
63972445Sassar	} else {
64072445Sassar	    int skip = (num_files + columns - 1) / columns;
64172445Sassar	    j = 0;
64272445Sassar	    for(i = 0; i < skip; i++) {
64372445Sassar		for(j = i; j < n_files;) {
64472445Sassar		    while(j < n_files && fi[j].filename == NULL)
64572445Sassar			j++;
64690926Snectar		    if(flags & LS_SIZE)
64790926Snectar			sec_fprintf2(out, "%*u %-*s", size_len, fi[j].bsize,
64890926Snectar				     max_len, fi[j].filename);
64990926Snectar		    else
65090926Snectar			sec_fprintf2(out, "%-*s", max_len, fi[j].filename);
65172445Sassar		    j += skip;
65272445Sassar		}
65372445Sassar		sec_fprintf2(out, "\r\n");
65472445Sassar	    }
65572445Sassar	}
65672445Sassar    } else {
65772445Sassar	for(i = 0; i < n_files; i++) {
65872445Sassar	    if(fi[i].filename == NULL)
65972445Sassar		continue;
66072445Sassar	    sec_fprintf2(out, "%s\r\n", fi[i].filename);
66172445Sassar	}
66255682Smarkm    }
66390926Snectar next:
66490926Snectar    if(((flags & LS_DIRS) == 0 || (flags & LS_RECURSIVE)) && dirs != NULL) {
66590926Snectar	for(i = 0; i < n_files; i++) {
66690926Snectar	    if(dirs[i]) {
66790926Snectar		const char *p = strrchr(files[i], '/');
66890926Snectar		if(p == NULL)
66990926Snectar		    p = files[i];
67090926Snectar		else
67190926Snectar		    p++;
67290926Snectar		if(!(flags & LS_DIR_FLAG) || !IS_DOT_DOTDOT(p)) {
67390926Snectar		    if((flags & LS_SHOW_DIRNAME)) {
67490926Snectar			if ((flags & LS_EXTRA_BLANK))
67590926Snectar			    sec_fprintf2(out, "\r\n");
67690926Snectar			sec_fprintf2(out, "%s:\r\n", files[i]);
67790926Snectar		    }
67890926Snectar		    list_dir(out, files[i], flags | LS_DIRS | LS_EXTRA_BLANK);
67990926Snectar		}
68090926Snectar	    }
68190926Snectar	}
68290926Snectar    }
68390926Snectar out:
68472445Sassar    for(i = 0; i < n_files; i++)
68572445Sassar	free_fileinfo(&fi[i]);
68672445Sassar    free(fi);
68790926Snectar    if(dirs != NULL)
68890926Snectar	free(dirs);
68955682Smarkm}
69055682Smarkm
69155682Smarkmstatic void
69255682Smarkmfree_files (char **files, int n)
69355682Smarkm{
69455682Smarkm    int i;
69555682Smarkm
69655682Smarkm    for (i = 0; i < n; ++i)
69755682Smarkm	free (files[i]);
69855682Smarkm    free (files);
69955682Smarkm}
70055682Smarkm
70190926Snectarstatic int
70290926Snectarhide_file(const char *filename, int flags)
70390926Snectar{
70490926Snectar    if(filename[0] != '.')
70590926Snectar	return 0;
70690926Snectar    if((flags & LS_IGNORE_DOT))
70790926Snectar	return 1;
70890926Snectar    if(filename[1] == '\0' || (filename[1] == '.' && filename[2] == '\0')) {
70990926Snectar	if((flags & LS_SHOW_ALL))
71090926Snectar	    return 0;
71190926Snectar	else
71290926Snectar	    return 1;
71390926Snectar    }
71490926Snectar    return 0;
71590926Snectar}
71690926Snectar
71755682Smarkmstatic void
71855682Smarkmlist_dir(FILE *out, const char *directory, int flags)
71955682Smarkm{
72055682Smarkm    DIR *d = opendir(directory);
72155682Smarkm    struct dirent *ent;
72255682Smarkm    char **files = NULL;
72355682Smarkm    int n_files = 0;
72455682Smarkm
72555682Smarkm    if(d == NULL) {
72655682Smarkm	sec_fprintf2(out, "%s: %s\r\n", directory, strerror(errno));
72755682Smarkm	return;
72855682Smarkm    }
72955682Smarkm    while((ent = readdir(d)) != NULL) {
73055682Smarkm	void *tmp;
73155682Smarkm
73290926Snectar	if(hide_file(ent->d_name, flags))
73390926Snectar	    continue;
73455682Smarkm	tmp = realloc(files, (n_files + 1) * sizeof(*files));
73555682Smarkm	if (tmp == NULL) {
73655682Smarkm	    sec_fprintf2(out, "%s: out of memory\r\n", directory);
73755682Smarkm	    free_files (files, n_files);
73855682Smarkm	    closedir (d);
73955682Smarkm	    return;
74055682Smarkm	}
74155682Smarkm	files = tmp;
74255682Smarkm	asprintf(&files[n_files], "%s/%s", directory, ent->d_name);
74355682Smarkm	if (files[n_files] == NULL) {
74455682Smarkm	    sec_fprintf2(out, "%s: out of memory\r\n", directory);
74555682Smarkm	    free_files (files, n_files);
74655682Smarkm	    closedir (d);
74755682Smarkm	    return;
74855682Smarkm	}
74955682Smarkm	++n_files;
75055682Smarkm    }
75155682Smarkm    closedir(d);
75290926Snectar    list_files(out, (const char**)files, n_files, flags | LS_DIR_FLAG);
75355682Smarkm}
75455682Smarkm
75590926Snectarstatic int
75690926Snectarparse_flags(const char *options)
75790926Snectar{
75890926Snectar#ifdef TEST
75990926Snectar    int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_COLUMN;
76090926Snectar#else
76190926Snectar    int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_LONG;
76290926Snectar#endif
76390926Snectar
76490926Snectar    const char *p;
76590926Snectar    if(options == NULL || *options != '-')
76690926Snectar	return flags;
76790926Snectar    for(p = options + 1; *p; p++) {
76890926Snectar	switch(*p) {
76990926Snectar	case '1':
77090926Snectar	    flags = (flags & ~LS_DISP_MODE);
77190926Snectar	    break;
77290926Snectar	case 'a':
77390926Snectar	    flags |= LS_SHOW_ALL;
77490926Snectar	    /*FALLTHROUGH*/
77590926Snectar	case 'A':
77690926Snectar	    flags &= ~LS_IGNORE_DOT;
77790926Snectar	    break;
77890926Snectar	case 'C':
77990926Snectar	    flags = (flags & ~LS_DISP_MODE) | LS_DISP_COLUMN;
78090926Snectar	    break;
78190926Snectar	case 'd':
78290926Snectar	    flags |= LS_DIRS;
78390926Snectar	    break;
78490926Snectar	case 'f':
78590926Snectar	    flags = (flags & ~LS_SORT_MODE);
78690926Snectar	    break;
78790926Snectar	case 'F':
78890926Snectar	    flags |= LS_TYPE;
78990926Snectar	    break;
79090926Snectar	case 'i':
79190926Snectar	    flags |= LS_INODE;
79290926Snectar	    break;
79390926Snectar	case 'l':
79490926Snectar	    flags = (flags & ~LS_DISP_MODE) | LS_DISP_LONG;
79590926Snectar	    break;
79690926Snectar	case 'r':
79790926Snectar	    flags |= LS_SORT_REVERSE;
79890926Snectar	    break;
79990926Snectar	case 'R':
80090926Snectar	    flags |= LS_RECURSIVE;
80190926Snectar	    break;
80290926Snectar	case 's':
80390926Snectar	    flags |= LS_SIZE;
80490926Snectar	    break;
80590926Snectar	case 'S':
80690926Snectar	    flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE;
80790926Snectar	    break;
80890926Snectar	case 't':
80990926Snectar	    flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME;
81090926Snectar	    break;
81190926Snectar	case 'x':
81290926Snectar	    flags = (flags & ~LS_DISP_MODE) | LS_DISP_CROSS;
81390926Snectar	    break;
81490926Snectar	    /* these are a bunch of unimplemented flags from BSD ls */
81590926Snectar	case 'k': /* display sizes in kB */
81690926Snectar	case 'c': /* last change time */
81790926Snectar	case 'L': /* list symlink target */
81890926Snectar	case 'm': /* stream output */
81990926Snectar	case 'o': /* BSD file flags */
82090926Snectar	case 'p': /* display / after directories */
82190926Snectar	case 'q': /* print non-graphic characters */
82290926Snectar	case 'u': /* use last access time */
82390926Snectar	case 'T': /* display complete time */
82490926Snectar	case 'W': /* include whiteouts */
82590926Snectar	    break;
82690926Snectar	}
82790926Snectar    }
82890926Snectar    return flags;
82990926Snectar}
83090926Snectar
83155682Smarkmvoid
83255682Smarkmbuiltin_ls(FILE *out, const char *file)
83355682Smarkm{
83490926Snectar    int flags;
83555682Smarkm
83655682Smarkm    if(*file == '-') {
83790926Snectar	flags = parse_flags(file);
83855682Smarkm	file = ".";
83990926Snectar    } else
84090926Snectar	flags = parse_flags("");
84190926Snectar
84255682Smarkm    list_files(out, &file, 1, flags);
84355682Smarkm    sec_fflush(out);
84455682Smarkm}
845