Files
heimdal/appl/ftp/ftpd/ls.c
Björn Groenvall 4abf3bf24c SVR4 don't have major and minor but a new interface.
git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@7145 ec53bebd-3082-4978-b11e-865c3cabbd6b
1999-10-15 15:05:25 +00:00

452 lines
11 KiB
C

/*
* 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
#ifndef S_ISTXT
#define S_ISTXT S_ISVTX
#endif
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';
#ifdef S_ISWHT
else if(S_ISWHT(st->st_mode))
file->mode[0] = 'w';
#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, "%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)) {
#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);
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 (flags & LS_IGNORE_DOT)
continue;
if (ent->d_name[1] == 0) /* Ignore . */
continue;
if (ent->d_name[1] == '.' && ent->d_name[2] == 0) /* Ignore .. */
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);
}