#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <glob.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "idmap.h"
#include "ls.h"
struct fileInfo {
char * name;
struct stat sb;
};
static void permsString(int mode, char * perms);
static int statFile(char * dir, char * fn, int flags, struct stat * sbp);
static int implicitListFile(int sock, char * path,
char * fn, struct stat * sbp, int flags);
static int nameCmp(const void * a, const void * b);
static int sizeCmp(const void * a, const void * b);
static int mtimeCmp(const void * a, const void * b);
static void multicolumnListing(int sock, struct fileInfo * files,
int filesCount, int flags);
static int sendDirContents(int sock, char * path,
char * fn, int flags);
static void permsString(int mode, char * perms) {
strcpy(perms, "----------");
if (mode & S_ISVTX) perms[9] = 't';
if (mode & S_IRUSR) perms[1] = 'r';
if (mode & S_IWUSR) perms[2] = 'w';
if (mode & S_IXUSR) perms[3] = 'x';
if (mode & S_IRGRP) perms[4] = 'r';
if (mode & S_IWGRP) perms[5] = 'w';
if (mode & S_IXGRP) perms[6] = 'x';
if (mode & S_IROTH) perms[7] = 'r';
if (mode & S_IWOTH) perms[8] = 'w';
if (mode & S_IXOTH) perms[9] = 'x';
if (mode & S_ISUID) {
if (mode & S_IXUSR)
perms[3] = 's';
else
perms[3] = 'S';
}
if (mode & S_ISGID) {
if (mode & S_IXGRP)
perms[6] = 's';
else
perms[6] = 'S';
}
if (S_ISDIR(mode))
perms[0] = 'd';
else if (S_ISLNK(mode)) {
perms[0] = 'l';
}
else if (S_ISFIFO(mode))
perms[0] = 'p';
else if (S_ISSOCK(mode))
perms[0] = 'l';
else if (S_ISCHR(mode)) {
perms[0] = 'c';
} else if (S_ISBLK(mode)) {
perms[0] = 'b';
}
}
static int statFile(char * dir, char * fn, int flags, struct stat * sbp) {
char * filename;
if (dir) {
filename = alloca(strlen(dir) + strlen(fn) + 2);
sprintf(filename, "%s/%s", dir, fn);
} else
filename = fn;
if (!(flags & SENDDIR_FOLLOWLINKS) || stat(filename, sbp)) {
if (lstat(filename, sbp)) {
return 1;
}
}
return 0;
}
char * fileStatStr(char * dir, char * fn, struct stat * sbp, int flags) {
char * info;
char perms[12];
char sizefield[15];
char ownerfield[9], groupfield[9];
char timefield[20] = "";
char * linkto;
char * namefield = fn;
time_t themtime;
time_t currenttime;
char * name;
int thisYear = 0;
int thisMonth = 0;
struct tm * tstruct;
int i;
char * filename;
if (!sbp) {
sbp = alloca(sizeof(*sbp));
if (statFile(dir, fn, flags, sbp))
return NULL;
}
permsString(sbp->st_mode, perms);
currenttime = time(NULL);
tstruct = localtime(¤ttime);
thisYear = tstruct->tm_year;
thisMonth = tstruct->tm_mon;
name = idSearchByUid(sbp->st_uid);
if (name)
sprintf(ownerfield, "%-8s", name);
else
sprintf(ownerfield, "%-8d", (int) sbp->st_uid);
name = idSearchByGid(sbp->st_gid);
if (name)
sprintf(groupfield, "%-8s", name);
else
sprintf(groupfield, "%-8d", (int) sbp->st_gid);
if (S_ISLNK(sbp->st_mode)) {
/* they don't reall want to see "opt -> /usr/opt@" */
linkto = alloca(1024);
strcpy(linkto, "(link)");
filename = alloca(strlen(dir) + strlen(fn) + 2);
sprintf(filename, "%s/%s", dir, fn);
i = readlink(filename, linkto, 1023);
if (i < 1)
strcpy(linkto, "(cannot read symlink)");
else
linkto[i] = 0;
namefield = alloca(strlen(fn) + strlen(linkto) + 10);
sprintf(namefield, "%s -> %s", fn, linkto);
sprintf(sizefield, "%d", i);
} else if (S_ISCHR(sbp->st_mode)) {
perms[0] = 'c';
sprintf(sizefield, "%3d, %3d", major(sbp->st_rdev),
minor(sbp->st_rdev));
} else if (S_ISBLK(sbp->st_mode)) {
perms[0] = 'b';
sprintf(sizefield, "%3d, %3d", major(sbp->st_rdev),
minor(sbp->st_rdev));
} else {
sprintf(sizefield, "%8ld", sbp->st_size);
}
/* this is important if sizeof(int_32) ! sizeof(time_t) */
themtime = sbp->st_mtime;
tstruct = localtime(&themtime);
if (tstruct->tm_year == thisYear ||
((tstruct->tm_year + 1) == thisYear && tstruct->tm_mon > thisMonth))
strftime(timefield, sizeof(timefield) - 1, "%b %d %H:%M", tstruct);
else
strftime(timefield, sizeof(timefield) - 1, "%b %d %Y", tstruct);
info = malloc(strlen(namefield) + strlen(timefield) + 85);
sprintf(info, "%s %3d %8s %8s %8s %s %s", perms, (int) sbp->st_nlink,
ownerfield, groupfield, sizefield, timefield, namefield);
return info;
}
/* Like listFiles(), but don't explode directories or wildcards */
static int implicitListFile(int sock, char * path,
char * fn, struct stat * sbp, int flags) {
char * info;
char fileType;
if (flags & SENDDIR_LONG) {
info = fileStatStr(path, fn, sbp, flags);
if (info) {
write(sock, info, strlen(info));
free(info);
}
} else {
write(sock, fn, strlen(fn));
}
if (flags & SENDDIR_FILETYPE) {
if (S_ISSOCK(sbp->st_mode)) {
fileType = '=';
} else if (S_ISFIFO(sbp->st_mode)) {
fileType = '|';
} else if (S_ISDIR(sbp->st_mode)) {
fileType = '/';
} else if (S_IRWXO & sbp->st_mode) {
fileType = '*';
} else {
fileType = '\0';
}
if (fileType) write(sock, &fileType, 1);
}
write(sock, "\n", 1);
return 0;
}
static int nameCmp(const void * a, const void * b) {
const struct fileInfo * one = a;
const struct fileInfo * two = b;
return (strcmp(one->name, two->name));
}
static int sizeCmp(const void * a, const void * b) {
const struct fileInfo * one = a;
const struct fileInfo * two = b;
/* list newer files first */
if (one->sb.st_size < two->sb.st_size)
return 1;
else if (one->sb.st_size > two->sb.st_size)
return -1;
return 0;
}
static int mtimeCmp(const void * a, const void * b) {
const struct fileInfo * one = a;
const struct fileInfo * two = b;
if (one->sb.st_mtime < two->sb.st_mtime)
return -1;
else if (one->sb.st_mtime > two->sb.st_mtime)
return 1;
return 0;
}
static void multicolumnListing(int sock, struct fileInfo * files,
int filesCount, int flags) {
int i, j, k;
int maxWidth = 0;
char format[20];
char * fileType = " ";
char * buf, * name = NULL;
int rows, columns;
if (!filesCount) return;
for (i = 0; i < filesCount; i++) {
j = strlen(files[i].name);
if (j > maxWidth) maxWidth = j;
}
maxWidth += 3;
buf = alloca(maxWidth + 1);
if (flags & SENDDIR_FILETYPE)
name = alloca(maxWidth);
columns = 80 / maxWidth;
if (columns == 0) columns = 1;
sprintf(format, "%%-%ds", 80 / columns);
rows = filesCount / columns;
if (filesCount % columns) rows++;
for (i = 0; i < rows; i++) {
j = i;
while (j < filesCount) {
if (flags & SENDDIR_FILETYPE) {
if (S_ISDIR(files[j].sb.st_mode))
fileType = "/";
else if (S_ISSOCK(files[j].sb.st_mode))
fileType = "=";
else if (S_ISFIFO(files[j].sb.st_mode))
fileType = "|";
else if (S_ISLNK(files[j].sb.st_mode))
fileType = "@";
else
fileType = " ";
strcpy(name, files[j].name);
strcat(name, fileType);
} else
name = files[j].name;
if ((j + rows) < filesCount)
k = sprintf(buf, format, name);
else
k = sprintf(buf, "%s", name);
j += rows;
write(sock, buf, k);
}
write(sock, "\n", 1);
}
}
static int sendDirContents(int sock, char * path, char * fn, int flags) {
struct dirent * ent;
int start, direction;
DIR * dir;
int filesAlloced, filesCount, i;
struct fileInfo * files, * newfiles;
int failed = 0;
int total = 0;
char buf[20];
char * fullpath;
char * subdir;
filesAlloced = 15;
filesCount = 0;
files = malloc(sizeof(*files) * filesAlloced);
if (fn) {
fullpath = alloca(strlen(path) + strlen(fn) + 2);
sprintf(fullpath, "%s/%s", path, fn);
} else
fullpath = path;
dir = opendir(fullpath);
do {
errno = 0;
ent = readdir(dir);
if (errno) {
fprintf(stderr, "Error reading directory entry: %s\n",
strerror(errno));
failed = 1;
} else if (ent && (*ent->d_name != '.' || (flags & SENDDIR_ALL))) {
if (filesCount == filesAlloced) {
filesAlloced += 15;
newfiles = realloc(files, sizeof(*files) * filesAlloced);
files = newfiles;
}
if (!failed) {
files[filesCount].name = strdup(ent->d_name);
if (statFile(fullpath, files[filesCount].name, flags,
&files[filesCount].sb)) {
fprintf(stderr, "stat of %s failed: %s\n" ,
files[filesCount].name, strerror(errno));
failed = 1;
} else {
total += files[filesCount].sb.st_size /
1024;
}
filesCount++;
}
}
} while (ent && !failed);
closedir(dir);
if (!failed) {
if (flags & SENDDIR_SORTMTIME) {
qsort(files, filesCount, sizeof(*files), mtimeCmp);
} else if (flags & SENDDIR_SORTSIZE) {
qsort(files, filesCount, sizeof(*files), sizeCmp);
} else if (!(flags & SENDDIR_SORTNONE)) {
qsort(files, filesCount, sizeof(*files), nameCmp);
}
if (flags & SENDDIR_SORTREVERSE) {
direction = -1;
start = filesCount - 1;
} else {
direction = 1;
start = 0;
}
if (fn) {
write(sock, fn, strlen(fn));
write(sock, ":\n", 2);
}
if (flags & SENDDIR_MULTICOLUMN) {
multicolumnListing(sock, files, filesCount, flags);
} else {
if (flags & SENDDIR_LONG) {
i = sprintf(buf, "total %d\n", total);
write(sock, buf, i);
}
for (i = start; i >= 0 && i < filesCount; i += direction) {
implicitListFile(sock, fullpath, files[i].name,
&files[i].sb, flags);
}
}
if (flags & SENDDIR_RECURSE) {
for (i = start; i >= 0 && i < filesCount && !failed;
i += direction) {
if (S_ISDIR(files[i].sb.st_mode) &&
strcmp(files[i].name, ".") &&
strcmp(files[i].name, "..")) {
write(sock, "\n", 1);
if (fn) {
subdir = malloc(strlen(fn) + strlen(files[i].name) + 2);
sprintf(subdir, "%s/%s", fn, files[i].name);
} else {
subdir = files[i].name;
}
failed = sendDirContents(sock, path, subdir, flags);
if (fn) free(subdir);
}
}
}
}
for (i = 0; i < filesCount; i++) {
free(files[i].name);
}
free(files);
return failed;
}
/* implements 'ls' */
void listFiles(char * path, char * fn, int flags) {
struct stat sb;
int i, rc;
char * filename, * this;
int isExplicit = 1;
glob_t matches;
int failed = 0;
if (!fn) {
fn = ".";
isExplicit = 0;
}
filename = malloc(strlen(fn) + strlen(path) + 2);
sprintf(filename, "%s/%s", path, fn);
rc = glob(filename, GLOB_NOSORT, NULL, &matches);
if (rc == GLOB_NOMATCH) {
fprintf(stderr, "File not found.\n");
return;
}
free(filename);
for (i = 0; i < matches.gl_pathc && !failed; i++) {
this = matches.gl_pathv[i] + strlen(path);
if (*this)
this++;
else
this = ".";
if (!statFile(path, this, flags, &sb)) {
if (S_ISDIR(sb.st_mode) && !(flags & SENDDIR_SIMPLEDIRS)) {
filename = malloc(strlen(path) + strlen(this) + 2);
sprintf(filename, "%s/%s", path, this);
failed = sendDirContents(1, filename, NULL, flags);
free(filename);
} else {
implicitListFile(1, path, this, &sb, flags);
}
} else {
write(1, matches.gl_pathv[i], strlen(matches.gl_pathv[i]));
write(1, ": file not found.\n", 18);
}
}
globfree(&matches);
close(1);
}