845 lines
20 KiB
C
845 lines
20 KiB
C
/*
|
|
* Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
|
|
* (Royal Institute of Technology, Stockholm, Sweden).
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of KTH nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
|
|
|
|
#ifndef TEST
|
|
#include "ftpd_locl.h"
|
|
|
|
RCSID("$Id: ls.c,v 1.23 2001/09/14 11:32:52 joda Exp $");
|
|
|
|
#else
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <errno.h>
|
|
|
|
#define sec_fprintf2 fprintf
|
|
#define sec_fflush fflush
|
|
static void list_files(FILE *out, const char **files, int n_files, int flags);
|
|
static int parse_flags(const char *options);
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int i = 1;
|
|
int flags;
|
|
if(argc > 1 && argv[1][0] == '-') {
|
|
flags = parse_flags(argv[1]);
|
|
i = 2;
|
|
} else
|
|
flags = parse_flags(NULL);
|
|
|
|
list_files(stdout, (const char **)argv + i, argc - i, flags);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
struct fileinfo {
|
|
struct stat st;
|
|
int inode;
|
|
int bsize;
|
|
char mode[11];
|
|
int n_link;
|
|
char *user;
|
|
char *group;
|
|
char *size;
|
|
char *major;
|
|
char *minor;
|
|
char *date;
|
|
char *filename;
|
|
char *link;
|
|
};
|
|
|
|
static void
|
|
free_fileinfo(struct fileinfo *f)
|
|
{
|
|
free(f->user);
|
|
free(f->group);
|
|
free(f->size);
|
|
free(f->major);
|
|
free(f->minor);
|
|
free(f->date);
|
|
free(f->filename);
|
|
free(f->link);
|
|
}
|
|
|
|
#define LS_DIRS (1 << 0)
|
|
#define LS_IGNORE_DOT (1 << 1)
|
|
#define LS_SORT_MODE (3 << 2)
|
|
#define SORT_MODE(f) ((f) & LS_SORT_MODE)
|
|
#define LS_SORT_NAME (1 << 2)
|
|
#define LS_SORT_MTIME (2 << 2)
|
|
#define LS_SORT_SIZE (3 << 2)
|
|
#define LS_SORT_REVERSE (1 << 4)
|
|
|
|
#define LS_SIZE (1 << 5)
|
|
#define LS_INODE (1 << 6)
|
|
#define LS_TYPE (1 << 7)
|
|
#define LS_DISP_MODE (3 << 8)
|
|
#define DISP_MODE(f) ((f) & LS_DISP_MODE)
|
|
#define LS_DISP_LONG (1 << 8)
|
|
#define LS_DISP_COLUMN (2 << 8)
|
|
#define LS_DISP_CROSS (3 << 8)
|
|
#define LS_SHOW_ALL (1 << 10)
|
|
#define LS_RECURSIVE (1 << 11)
|
|
#define LS_EXTRA_BLANK (1 << 12)
|
|
#define LS_SHOW_DIRNAME (1 << 13)
|
|
#define LS_DIR_FLAG (1 << 14) /* these files come via list_dir */
|
|
|
|
#ifndef S_ISTXT
|
|
#define S_ISTXT S_ISVTX
|
|
#endif
|
|
|
|
#ifndef S_ISSOCK
|
|
#define S_ISSOCK(mode) (((mode) & _S_IFMT) == S_IFSOCK)
|
|
#endif
|
|
|
|
#ifndef S_ISLNK
|
|
#define S_ISLNK(mode) (((mode) & _S_IFMT) == S_IFLNK)
|
|
#endif
|
|
|
|
static size_t
|
|
block_convert(size_t blocks)
|
|
{
|
|
#ifdef S_BLKSIZE
|
|
return blocks * S_BLKSIZE / 1024;
|
|
#else
|
|
return blocks * 512 / 1024;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
make_fileinfo(FILE *out, const char *filename, struct fileinfo *file, int flags)
|
|
{
|
|
char buf[128];
|
|
int file_type = 0;
|
|
struct stat *st = &file->st;
|
|
|
|
file->inode = st->st_ino;
|
|
file->bsize = block_convert(st->st_blocks);
|
|
|
|
if(S_ISDIR(st->st_mode)) {
|
|
file->mode[0] = 'd';
|
|
file_type = '/';
|
|
}
|
|
else if(S_ISCHR(st->st_mode))
|
|
file->mode[0] = 'c';
|
|
else if(S_ISBLK(st->st_mode))
|
|
file->mode[0] = 'b';
|
|
else if(S_ISREG(st->st_mode)) {
|
|
file->mode[0] = '-';
|
|
if(st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
|
|
file_type = '*';
|
|
}
|
|
else if(S_ISFIFO(st->st_mode)) {
|
|
file->mode[0] = 'p';
|
|
file_type = '|';
|
|
}
|
|
else if(S_ISLNK(st->st_mode)) {
|
|
file->mode[0] = 'l';
|
|
file_type = '@';
|
|
}
|
|
else if(S_ISSOCK(st->st_mode)) {
|
|
file->mode[0] = 's';
|
|
file_type = '=';
|
|
}
|
|
#ifdef S_ISWHT
|
|
else if(S_ISWHT(st->st_mode)) {
|
|
file->mode[0] = 'w';
|
|
file_type = '%';
|
|
}
|
|
#endif
|
|
else
|
|
file->mode[0] = '?';
|
|
{
|
|
char *x[] = { "---", "--x", "-w-", "-wx",
|
|
"r--", "r-x", "rw-", "rwx" };
|
|
strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]);
|
|
strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]);
|
|
strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]);
|
|
if((st->st_mode & S_ISUID)) {
|
|
if((st->st_mode & S_IXUSR))
|
|
file->mode[3] = 's';
|
|
else
|
|
file->mode[3] = 'S';
|
|
}
|
|
if((st->st_mode & S_ISGID)) {
|
|
if((st->st_mode & S_IXGRP))
|
|
file->mode[6] = 's';
|
|
else
|
|
file->mode[6] = 'S';
|
|
}
|
|
if((st->st_mode & S_ISTXT)) {
|
|
if((st->st_mode & S_IXOTH))
|
|
file->mode[9] = 't';
|
|
else
|
|
file->mode[9] = 'T';
|
|
}
|
|
}
|
|
file->n_link = st->st_nlink;
|
|
{
|
|
struct passwd *pwd;
|
|
pwd = getpwuid(st->st_uid);
|
|
if(pwd == NULL)
|
|
asprintf(&file->user, "%u", (unsigned)st->st_uid);
|
|
else
|
|
file->user = strdup(pwd->pw_name);
|
|
}
|
|
{
|
|
struct group *grp;
|
|
grp = getgrgid(st->st_gid);
|
|
if(grp == NULL)
|
|
asprintf(&file->group, "%u", (unsigned)st->st_gid);
|
|
else
|
|
file->group = strdup(grp->gr_name);
|
|
}
|
|
|
|
if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
|
|
#if defined(major) && defined(minor)
|
|
asprintf(&file->major, "%u", (unsigned)major(st->st_rdev));
|
|
asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev));
|
|
#else
|
|
/* Don't want to use the DDI/DKI crap. */
|
|
asprintf(&file->major, "%u", (unsigned)st->st_rdev);
|
|
asprintf(&file->minor, "%u", 0);
|
|
#endif
|
|
} else
|
|
asprintf(&file->size, "%lu", (unsigned long)st->st_size);
|
|
|
|
{
|
|
time_t t = time(NULL);
|
|
time_t mtime = st->st_mtime;
|
|
struct tm *tm = localtime(&mtime);
|
|
if((t - mtime > 6*30*24*60*60) ||
|
|
(mtime - t > 6*30*24*60*60))
|
|
strftime(buf, sizeof(buf), "%b %e %Y", tm);
|
|
else
|
|
strftime(buf, sizeof(buf), "%b %e %H:%M", tm);
|
|
file->date = strdup(buf);
|
|
}
|
|
{
|
|
const char *p = strrchr(filename, '/');
|
|
if(p)
|
|
p++;
|
|
else
|
|
p = filename;
|
|
if((flags & LS_TYPE) && file_type != 0)
|
|
asprintf(&file->filename, "%s%c", p, file_type);
|
|
else
|
|
file->filename = strdup(p);
|
|
}
|
|
if(S_ISLNK(st->st_mode)) {
|
|
int n;
|
|
n = readlink((char *)filename, buf, sizeof(buf));
|
|
if(n >= 0) {
|
|
buf[n] = '\0';
|
|
file->link = strdup(buf);
|
|
} else
|
|
sec_fprintf2(out, "readlink(%s): %s", filename, strerror(errno));
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
if(f->filename == NULL)
|
|
return;
|
|
|
|
if(flags & LS_INODE) {
|
|
sec_fprintf2(out, "%*d", max_inode, f->inode);
|
|
sec_fprintf2(out, " ");
|
|
}
|
|
if(flags & LS_SIZE) {
|
|
sec_fprintf2(out, "%*d", max_bsize, f->bsize);
|
|
sec_fprintf2(out, " ");
|
|
}
|
|
sec_fprintf2(out, "%s", f->mode);
|
|
sec_fprintf2(out, " ");
|
|
sec_fprintf2(out, "%*d", max_n_link, f->n_link);
|
|
sec_fprintf2(out, " ");
|
|
sec_fprintf2(out, "%-*s", max_user, f->user);
|
|
sec_fprintf2(out, " ");
|
|
sec_fprintf2(out, "%-*s", max_group, f->group);
|
|
sec_fprintf2(out, " ");
|
|
if(f->major != NULL && f->minor != NULL)
|
|
sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor);
|
|
else
|
|
sec_fprintf2(out, "%*s", max_size, f->size);
|
|
sec_fprintf2(out, " ");
|
|
sec_fprintf2(out, "%*s", max_date, f->date);
|
|
sec_fprintf2(out, " ");
|
|
sec_fprintf2(out, "%s", f->filename);
|
|
if(f->link)
|
|
sec_fprintf2(out, " -> %s", f->link);
|
|
sec_fprintf2(out, "\r\n");
|
|
}
|
|
|
|
static int
|
|
compare_filename(struct fileinfo *a, struct fileinfo *b)
|
|
{
|
|
if(a->filename == NULL)
|
|
return 1;
|
|
if(b->filename == NULL)
|
|
return -1;
|
|
return strcmp(a->filename, b->filename);
|
|
}
|
|
|
|
static int
|
|
compare_mtime(struct fileinfo *a, struct fileinfo *b)
|
|
{
|
|
if(a->filename == NULL)
|
|
return 1;
|
|
if(b->filename == NULL)
|
|
return -1;
|
|
return b->st.st_mtime - a->st.st_mtime;
|
|
}
|
|
|
|
static int
|
|
compare_size(struct fileinfo *a, struct fileinfo *b)
|
|
{
|
|
if(a->filename == NULL)
|
|
return 1;
|
|
if(b->filename == NULL)
|
|
return -1;
|
|
return b->st.st_size - a->st.st_size;
|
|
}
|
|
|
|
static void
|
|
list_dir(FILE *out, const char *directory, int flags);
|
|
|
|
static int
|
|
log10(int num)
|
|
{
|
|
int i = 1;
|
|
while(num > 10) {
|
|
i++;
|
|
num /= 10;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* Operate as lstat but fake up entries for AFS mount points so we don't
|
|
* have to fetch them.
|
|
*/
|
|
|
|
#ifdef KRB4
|
|
static int do_the_afs_dance = 1;
|
|
#endif
|
|
|
|
static int
|
|
lstat_file (const char *file, struct stat *sb)
|
|
{
|
|
#ifdef KRB4
|
|
if (do_the_afs_dance &&
|
|
k_hasafs()
|
|
&& strcmp(file, ".")
|
|
&& strcmp(file, "..")
|
|
&& strcmp(file, "/"))
|
|
{
|
|
struct ViceIoctl a_params;
|
|
char *dir, *last;
|
|
char *path_bkp;
|
|
static ino_t ino_counter = 0, ino_last = 0;
|
|
int ret;
|
|
const int maxsize = 2048;
|
|
|
|
path_bkp = strdup (file);
|
|
if (path_bkp == NULL)
|
|
return -1;
|
|
|
|
a_params.out = malloc (maxsize);
|
|
if (a_params.out == NULL) {
|
|
free (path_bkp);
|
|
return -1;
|
|
}
|
|
|
|
/* If path contains more than the filename alone - split it */
|
|
|
|
last = strrchr (path_bkp, '/');
|
|
if (last != NULL) {
|
|
if(last[1] == '\0')
|
|
/* if path ended in /, replace with `.' */
|
|
a_params.in = ".";
|
|
else
|
|
a_params.in = last + 1;
|
|
while(last > path_bkp && *--last == '/');
|
|
if(*last != '/' || last != path_bkp) {
|
|
*++last = '\0';
|
|
dir = path_bkp;
|
|
} else
|
|
/* we got to the start, so this must be the root dir */
|
|
dir = "/";
|
|
} else {
|
|
/* file is relative to cdir */
|
|
dir = ".";
|
|
a_params.in = path_bkp;
|
|
}
|
|
|
|
a_params.in_size = strlen (a_params.in) + 1;
|
|
a_params.out_size = maxsize;
|
|
|
|
ret = k_pioctl (dir, VIOC_AFS_STAT_MT_PT, &a_params, 0);
|
|
free (a_params.out);
|
|
if (ret < 0) {
|
|
free (path_bkp);
|
|
|
|
if (errno != EINVAL)
|
|
return ret;
|
|
else
|
|
/* if we get EINVAL this is probably not a mountpoint */
|
|
return lstat (file, sb);
|
|
}
|
|
|
|
/*
|
|
* wow this was a mountpoint, lets cook the struct stat
|
|
* use . as a prototype
|
|
*/
|
|
|
|
ret = lstat (dir, sb);
|
|
free (path_bkp);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (ino_last == sb->st_ino)
|
|
ino_counter++;
|
|
else {
|
|
ino_last = sb->st_ino;
|
|
ino_counter = 0;
|
|
}
|
|
sb->st_ino += ino_counter;
|
|
sb->st_nlink = 3;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* KRB4 */
|
|
return lstat (file, sb);
|
|
}
|
|
|
|
#define IS_DOT_DOTDOT(X) ((X)[0] == '.' && ((X)[1] == '\0' || \
|
|
((X)[1] == '.' && (X)[2] == '\0')))
|
|
|
|
static void
|
|
list_files(FILE *out, const char **files, int n_files, int flags)
|
|
{
|
|
struct fileinfo *fi;
|
|
int i;
|
|
int *dirs = NULL;
|
|
size_t total_blocks = 0;
|
|
int n_print = 0;
|
|
|
|
if(n_files > 1)
|
|
flags |= LS_SHOW_DIRNAME;
|
|
|
|
fi = calloc(n_files, sizeof(*fi));
|
|
if (fi == NULL) {
|
|
sec_fprintf2(out, "ouf of memory\r\n");
|
|
return;
|
|
}
|
|
for(i = 0; i < n_files; i++) {
|
|
if(lstat_file(files[i], &fi[i].st) < 0) {
|
|
sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno));
|
|
fi[i].filename = NULL;
|
|
} else {
|
|
int include_in_list = 1;
|
|
total_blocks += block_convert(fi[i].st.st_blocks);
|
|
if(S_ISDIR(fi[i].st.st_mode)) {
|
|
if(dirs == NULL)
|
|
dirs = calloc(n_files, sizeof(*dirs));
|
|
if(dirs == NULL) {
|
|
sec_fprintf2(out, "%s: %s\r\n",
|
|
files[i], strerror(errno));
|
|
goto out;
|
|
}
|
|
dirs[i] = 1;
|
|
if((flags & LS_DIRS) == 0)
|
|
include_in_list = 0;
|
|
}
|
|
if(include_in_list) {
|
|
make_fileinfo(out, files[i], &fi[i], flags);
|
|
n_print++;
|
|
}
|
|
}
|
|
}
|
|
switch(SORT_MODE(flags)) {
|
|
case LS_SORT_NAME:
|
|
qsort(fi, n_files, sizeof(*fi),
|
|
(int (*)(const void*, const void*))compare_filename);
|
|
break;
|
|
case LS_SORT_MTIME:
|
|
qsort(fi, n_files, sizeof(*fi),
|
|
(int (*)(const void*, const void*))compare_mtime);
|
|
break;
|
|
case LS_SORT_SIZE:
|
|
qsort(fi, n_files, sizeof(*fi),
|
|
(int (*)(const void*, const void*))compare_size);
|
|
break;
|
|
}
|
|
if(DISP_MODE(flags) == LS_DISP_LONG) {
|
|
int max_inode = 0;
|
|
int max_bsize = 0;
|
|
int max_n_link = 0;
|
|
int max_user = 0;
|
|
int max_group = 0;
|
|
int max_size = 0;
|
|
int max_major = 0;
|
|
int max_minor = 0;
|
|
int max_date = 0;
|
|
for(i = 0; i < n_files; i++) {
|
|
if(fi[i].filename == NULL)
|
|
continue;
|
|
if(fi[i].inode > max_inode)
|
|
max_inode = fi[i].inode;
|
|
if(fi[i].bsize > max_bsize)
|
|
max_bsize = fi[i].bsize;
|
|
if(fi[i].n_link > max_n_link)
|
|
max_n_link = fi[i].n_link;
|
|
if(strlen(fi[i].user) > max_user)
|
|
max_user = strlen(fi[i].user);
|
|
if(strlen(fi[i].group) > max_group)
|
|
max_group = strlen(fi[i].group);
|
|
if(fi[i].major != NULL && strlen(fi[i].major) > max_major)
|
|
max_major = strlen(fi[i].major);
|
|
if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor)
|
|
max_minor = strlen(fi[i].minor);
|
|
if(fi[i].size != NULL && strlen(fi[i].size) > max_size)
|
|
max_size = strlen(fi[i].size);
|
|
if(strlen(fi[i].date) > max_date)
|
|
max_date = strlen(fi[i].date);
|
|
}
|
|
if(max_size < max_major + max_minor + 2)
|
|
max_size = max_major + max_minor + 2;
|
|
else if(max_size - max_minor - 2 > max_major)
|
|
max_major = max_size - max_minor - 2;
|
|
max_inode = log10(max_inode);
|
|
max_bsize = log10(max_bsize);
|
|
max_n_link = log10(max_n_link);
|
|
|
|
if(n_print > 0)
|
|
sec_fprintf2(out, "total %lu\r\n", (unsigned long)total_blocks);
|
|
if(flags & LS_SORT_REVERSE)
|
|
for(i = n_files - 1; i >= 0; i--)
|
|
print_file(out,
|
|
flags,
|
|
&fi[i],
|
|
max_inode,
|
|
max_bsize,
|
|
max_n_link,
|
|
max_user,
|
|
max_group,
|
|
max_size,
|
|
max_major,
|
|
max_minor,
|
|
max_date);
|
|
else
|
|
for(i = 0; i < n_files; i++)
|
|
print_file(out,
|
|
flags,
|
|
&fi[i],
|
|
max_inode,
|
|
max_bsize,
|
|
max_n_link,
|
|
max_user,
|
|
max_group,
|
|
max_size,
|
|
max_major,
|
|
max_minor,
|
|
max_date);
|
|
} else if(DISP_MODE(flags) == LS_DISP_COLUMN ||
|
|
DISP_MODE(flags) == LS_DISP_CROSS) {
|
|
int max_len = 0;
|
|
int size_len = 0;
|
|
int num_files = n_files;
|
|
int columns;
|
|
int j;
|
|
for(i = 0; i < n_files; i++) {
|
|
if(fi[i].filename == NULL) {
|
|
num_files--;
|
|
continue;
|
|
}
|
|
if(strlen(fi[i].filename) > max_len)
|
|
max_len = strlen(fi[i].filename);
|
|
if(log10(fi[i].bsize) > size_len)
|
|
size_len = log10(fi[i].bsize);
|
|
}
|
|
if(num_files == 0)
|
|
goto next;
|
|
if(flags & LS_SIZE) {
|
|
columns = 80 / (size_len + 1 + max_len + 1);
|
|
max_len = 80 / columns - size_len - 1;
|
|
} else {
|
|
columns = 80 / (max_len + 1); /* get space between columns */
|
|
max_len = 80 / columns;
|
|
}
|
|
if(flags & LS_SIZE)
|
|
sec_fprintf2(out, "total %lu\r\n",
|
|
(unsigned long)total_blocks);
|
|
if(DISP_MODE(flags) == LS_DISP_CROSS) {
|
|
for(i = 0, j = 0; i < n_files; i++) {
|
|
if(fi[i].filename == NULL)
|
|
continue;
|
|
if(flags & LS_SIZE)
|
|
sec_fprintf2(out, "%*u %-*s", size_len, fi[i].bsize,
|
|
max_len, fi[i].filename);
|
|
else
|
|
sec_fprintf2(out, "%-*s", max_len, fi[i].filename);
|
|
j++;
|
|
if(j == columns) {
|
|
sec_fprintf2(out, "\r\n");
|
|
j = 0;
|
|
}
|
|
}
|
|
if(j > 0)
|
|
sec_fprintf2(out, "\r\n");
|
|
} else {
|
|
int skip = (num_files + columns - 1) / columns;
|
|
j = 0;
|
|
for(i = 0; i < skip; i++) {
|
|
for(j = i; j < n_files;) {
|
|
while(j < n_files && fi[j].filename == NULL)
|
|
j++;
|
|
if(flags & LS_SIZE)
|
|
sec_fprintf2(out, "%*u %-*s", size_len, fi[j].bsize,
|
|
max_len, fi[j].filename);
|
|
else
|
|
sec_fprintf2(out, "%-*s", max_len, fi[j].filename);
|
|
j += skip;
|
|
}
|
|
sec_fprintf2(out, "\r\n");
|
|
}
|
|
}
|
|
} else {
|
|
for(i = 0; i < n_files; i++) {
|
|
if(fi[i].filename == NULL)
|
|
continue;
|
|
sec_fprintf2(out, "%s\r\n", fi[i].filename);
|
|
}
|
|
}
|
|
next:
|
|
if(((flags & LS_DIRS) == 0 || (flags & LS_RECURSIVE)) && dirs != NULL) {
|
|
for(i = 0; i < n_files; i++) {
|
|
if(dirs[i]) {
|
|
const char *p = strrchr(files[i], '/');
|
|
if(p == NULL)
|
|
p = files[i];
|
|
else
|
|
p++;
|
|
if(!(flags & LS_DIR_FLAG) || !IS_DOT_DOTDOT(p)) {
|
|
if((flags & LS_SHOW_DIRNAME)) {
|
|
if ((flags & LS_EXTRA_BLANK))
|
|
sec_fprintf2(out, "\r\n");
|
|
sec_fprintf2(out, "%s:\r\n", files[i]);
|
|
}
|
|
list_dir(out, files[i], flags | LS_DIRS | LS_EXTRA_BLANK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
out:
|
|
for(i = 0; i < n_files; i++)
|
|
free_fileinfo(&fi[i]);
|
|
free(fi);
|
|
if(dirs != NULL)
|
|
free(dirs);
|
|
}
|
|
|
|
static void
|
|
free_files (char **files, int n)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < n; ++i)
|
|
free (files[i]);
|
|
free (files);
|
|
}
|
|
|
|
static int
|
|
hide_file(const char *filename, int flags)
|
|
{
|
|
if(filename[0] != '.')
|
|
return 0;
|
|
if((flags & LS_IGNORE_DOT))
|
|
return 1;
|
|
if(filename[1] == '\0' || (filename[1] == '.' && filename[2] == '\0')) {
|
|
if((flags & LS_SHOW_ALL))
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
list_dir(FILE *out, const char *directory, int flags)
|
|
{
|
|
DIR *d = opendir(directory);
|
|
struct dirent *ent;
|
|
char **files = NULL;
|
|
int n_files = 0;
|
|
|
|
if(d == NULL) {
|
|
sec_fprintf2(out, "%s: %s\r\n", directory, strerror(errno));
|
|
return;
|
|
}
|
|
while((ent = readdir(d)) != NULL) {
|
|
void *tmp;
|
|
|
|
if(hide_file(ent->d_name, flags))
|
|
continue;
|
|
tmp = realloc(files, (n_files + 1) * sizeof(*files));
|
|
if (tmp == NULL) {
|
|
sec_fprintf2(out, "%s: out of memory\r\n", directory);
|
|
free_files (files, n_files);
|
|
closedir (d);
|
|
return;
|
|
}
|
|
files = tmp;
|
|
asprintf(&files[n_files], "%s/%s", directory, ent->d_name);
|
|
if (files[n_files] == NULL) {
|
|
sec_fprintf2(out, "%s: out of memory\r\n", directory);
|
|
free_files (files, n_files);
|
|
closedir (d);
|
|
return;
|
|
}
|
|
++n_files;
|
|
}
|
|
closedir(d);
|
|
list_files(out, (const char**)files, n_files, flags | LS_DIR_FLAG);
|
|
}
|
|
|
|
static int
|
|
parse_flags(const char *options)
|
|
{
|
|
#ifdef TEST
|
|
int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_COLUMN;
|
|
#else
|
|
int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_LONG;
|
|
#endif
|
|
|
|
const char *p;
|
|
if(options == NULL || *options != '-')
|
|
return flags;
|
|
for(p = options + 1; *p; p++) {
|
|
switch(*p) {
|
|
case '1':
|
|
flags = (flags & ~LS_DISP_MODE);
|
|
break;
|
|
case 'a':
|
|
flags |= LS_SHOW_ALL;
|
|
/*FALLTHROUGH*/
|
|
case 'A':
|
|
flags &= ~LS_IGNORE_DOT;
|
|
break;
|
|
case 'C':
|
|
flags = (flags & ~LS_DISP_MODE) | LS_DISP_COLUMN;
|
|
break;
|
|
case 'd':
|
|
flags |= LS_DIRS;
|
|
break;
|
|
case 'f':
|
|
flags = (flags & ~LS_SORT_MODE);
|
|
break;
|
|
case 'F':
|
|
flags |= LS_TYPE;
|
|
break;
|
|
case 'i':
|
|
flags |= LS_INODE;
|
|
break;
|
|
case 'l':
|
|
flags = (flags & ~LS_DISP_MODE) | LS_DISP_LONG;
|
|
break;
|
|
case 'r':
|
|
flags |= LS_SORT_REVERSE;
|
|
break;
|
|
case 'R':
|
|
flags |= LS_RECURSIVE;
|
|
break;
|
|
case 's':
|
|
flags |= LS_SIZE;
|
|
break;
|
|
case 'S':
|
|
flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE;
|
|
break;
|
|
case 't':
|
|
flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME;
|
|
break;
|
|
case 'x':
|
|
flags = (flags & ~LS_DISP_MODE) | LS_DISP_CROSS;
|
|
break;
|
|
/* these are a bunch of unimplemented flags from BSD ls */
|
|
case 'k': /* display sizes in kB */
|
|
case 'c': /* last change time */
|
|
case 'L': /* list symlink target */
|
|
case 'm': /* stream output */
|
|
case 'o': /* BSD file flags */
|
|
case 'p': /* display / after directories */
|
|
case 'q': /* print non-graphic characters */
|
|
case 'u': /* use last access time */
|
|
case 'T': /* display complete time */
|
|
case 'W': /* include whiteouts */
|
|
break;
|
|
}
|
|
}
|
|
return flags;
|
|
}
|
|
|
|
void
|
|
builtin_ls(FILE *out, const char *file)
|
|
{
|
|
int flags;
|
|
|
|
if(*file == '-') {
|
|
flags = parse_flags(file);
|
|
file = ".";
|
|
} else
|
|
flags = parse_flags("");
|
|
|
|
list_files(out, &file, 1, flags);
|
|
sec_fflush(out);
|
|
}
|