From cd402a1100674c315c9dbba10c5a37d119329f52 Mon Sep 17 00:00:00 2001 From: Johan Danielsson Date: Sun, 3 Oct 1999 16:36:30 +0000 Subject: [PATCH] simple ls git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@7086 ec53bebd-3082-4978-b11e-865c3cabbd6b --- appl/ftp/ftpd/ls.c | 437 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 appl/ftp/ftpd/ls.c diff --git a/appl/ftp/ftpd/ls.c b/appl/ftp/ftpd/ls.c new file mode 100644 index 000000000..a62c3b586 --- /dev/null +++ b/appl/ftp/ftpd/ls.c @@ -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); +}