simple ls
git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@7086 ec53bebd-3082-4978-b11e-865c3cabbd6b
This commit is contained in:
437
appl/ftp/ftpd/ls.c
Normal file
437
appl/ftp/ftpd/ls.c
Normal file
@@ -0,0 +1,437 @@
|
||||
/*
|
||||
* Copyright (c) 1999 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. */
|
||||
|
||||
#include "ftpd_locl.h"
|
||||
|
||||
RCSID("$Id$");
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#define LS_DIRS 1
|
||||
#define LS_IGNORE_DOT 2
|
||||
#define LS_SORT_MODE 12
|
||||
#define SORT_MODE(f) ((f) & LS_SORT_MODE)
|
||||
#define LS_SORT_NAME 4
|
||||
#define LS_SORT_MTIME 8
|
||||
#define LS_SORT_SIZE 12
|
||||
#define LS_SORT_REVERSE 16
|
||||
|
||||
#define LS_SIZE 32
|
||||
#define LS_INODE 64
|
||||
|
||||
static void
|
||||
make_fileinfo(const char *filename, struct fileinfo *file, int flags)
|
||||
{
|
||||
char buf[128];
|
||||
struct stat *st = &file->st;
|
||||
|
||||
file->inode = st->st_ino;
|
||||
#ifdef S_BLKSIZE
|
||||
file->bsize = st->st_blocks * S_BLKSIZE / 1024;
|
||||
#else
|
||||
file->bsize = st->st_blocks * 512 / 1024;
|
||||
#endif
|
||||
|
||||
if(S_ISDIR(st->st_mode))
|
||||
file->mode[0] = 'd';
|
||||
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] = '-';
|
||||
else if(S_ISFIFO(st->st_mode))
|
||||
file->mode[0] = 'p';
|
||||
else if(S_ISLNK(st->st_mode))
|
||||
file->mode[0] = 'l';
|
||||
else if(S_ISSOCK(st->st_mode))
|
||||
file->mode[0] = 's';
|
||||
else if(S_ISWHT(st->st_mode))
|
||||
file->mode[0] = 'w';
|
||||
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, "%d", st->st_uid);
|
||||
else
|
||||
file->user = strdup(pwd->pw_name);
|
||||
}
|
||||
{
|
||||
struct group *grp;
|
||||
grp = getgrgid(st->st_gid);
|
||||
if(grp == NULL)
|
||||
asprintf(&file->group, "%d", st->st_gid);
|
||||
else
|
||||
file->group = strdup(grp->gr_name);
|
||||
}
|
||||
|
||||
if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
|
||||
asprintf(&file->major, "%u", major(st->st_rdev));
|
||||
asprintf(&file->minor, "%u", minor(st->st_rdev));
|
||||
} else
|
||||
asprintf(&file->size, "%lu", (unsigned long)st->st_size);
|
||||
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
struct tm *tm = localtime(&st->st_mtime);
|
||||
if((t - st->st_mtime > 6*30*24*60*60) ||
|
||||
(st->st_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;
|
||||
file->filename = strdup(p);
|
||||
}
|
||||
if(S_ISLNK(st->st_mode)) {
|
||||
int n;
|
||||
n = readlink(filename, buf, sizeof(buf));
|
||||
if(n >= 0) {
|
||||
buf[n] = '\0';
|
||||
file->link = strdup(buf);
|
||||
} else
|
||||
warn("%s: readlink", filename);
|
||||
}
|
||||
}
|
||||
|
||||
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 a->st.st_mtime - b->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 a->st.st_size - b->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;
|
||||
}
|
||||
|
||||
static void
|
||||
list_files(FILE *out, char **files, int n_files, int flags)
|
||||
{
|
||||
struct fileinfo *fi;
|
||||
int i;
|
||||
fi = calloc(n_files, sizeof(*fi));
|
||||
for(i = 0; i < n_files; i++) {
|
||||
if(lstat(files[i], &fi[i].st) < 0) {
|
||||
sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno));
|
||||
fi[i].filename = NULL;
|
||||
} else {
|
||||
if((flags & LS_DIRS) == 0 && S_ISDIR(fi[i].st.st_mode)) {
|
||||
if(n_files > 1)
|
||||
sec_fprintf2(out, "%s:\r\n", files[i]);
|
||||
list_dir(out, files[i], flags);
|
||||
} else {
|
||||
make_fileinfo(files[i], &fi[i], flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
{
|
||||
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(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);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
warn("%s", directory);
|
||||
return;
|
||||
}
|
||||
while((ent = readdir(d)) != NULL) {
|
||||
if(ent->d_name[0] == '.') {
|
||||
if((ent->d_namlen == 1) ||
|
||||
(ent->d_namlen == 2 && ent->d_name[1] == '.') ||
|
||||
(flags & LS_IGNORE_DOT))
|
||||
continue;
|
||||
}
|
||||
files = realloc(files, (n_files + 1) * sizeof(*files));
|
||||
asprintf(&files[n_files++], "%s/%s", directory, ent->d_name);
|
||||
}
|
||||
closedir(d);
|
||||
list_files(out, files, n_files, flags | LS_DIRS);
|
||||
}
|
||||
|
||||
void
|
||||
builtin_ls(FILE *out, const char *file)
|
||||
{
|
||||
int c;
|
||||
int flags = LS_SORT_NAME;
|
||||
if(*file == '-') {
|
||||
const char *p;
|
||||
for(p = file + 1; *p; p++) {
|
||||
switch(*p) {
|
||||
case 'a':
|
||||
case 'A':
|
||||
flags &= ~LS_IGNORE_DOT;
|
||||
break;
|
||||
case 'C':
|
||||
break;
|
||||
case 'd':
|
||||
flags |= LS_DIRS;
|
||||
case 'f':
|
||||
flags = (flags & ~LS_SORT_MODE);
|
||||
break;
|
||||
case 'i':
|
||||
flags |= flags | LS_INODE;
|
||||
break;
|
||||
case 'l':
|
||||
break;
|
||||
case 't':
|
||||
flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME;
|
||||
break;
|
||||
case 's':
|
||||
flags |= LS_SIZE;
|
||||
break;
|
||||
case 'S':
|
||||
flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE;
|
||||
break;
|
||||
case 'r':
|
||||
flags |= LS_SORT_REVERSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
file = ".";
|
||||
}
|
||||
list_files(out, &file, 1, flags);
|
||||
sec_fflush(out);
|
||||
}
|
Reference in New Issue
Block a user