summaryrefslogtreecommitdiffstats
path: root/collage
diff options
context:
space:
mode:
authorErik Troan <ewt@redhat.com>1999-08-07 16:16:29 +0000
committerErik Troan <ewt@redhat.com>1999-08-07 16:16:29 +0000
commita7d5886e5420bd7b12ac7203ada5886330354de2 (patch)
tree24d6535f9d8d846a8cbf1b0b96f4615fd69200c6 /collage
parentbf6e9064b9f5ce40b78e61a40f9b24b48a962e37 (diff)
downloadanaconda-a7d5886e5420bd7b12ac7203ada5886330354de2.tar.gz
anaconda-a7d5886e5420bd7b12ac7203ada5886330354de2.tar.xz
anaconda-a7d5886e5420bd7b12ac7203ada5886330354de2.zip
*** empty log message ***
Diffstat (limited to 'collage')
-rw-r--r--collage/Makefile7
-rw-r--r--collage/collage.c44
-rw-r--r--collage/commands.c386
-rw-r--r--collage/commands.h16
-rw-r--r--collage/idmap.c146
-rw-r--r--collage/idmap.h7
-rw-r--r--collage/ls.c499
-rw-r--r--collage/ls.h20
-rwxr-xr-xcollage/mkcollagelinks6
9 files changed, 1131 insertions, 0 deletions
diff --git a/collage/Makefile b/collage/Makefile
new file mode 100644
index 000000000..d1224990c
--- /dev/null
+++ b/collage/Makefile
@@ -0,0 +1,7 @@
+CFLAGS = -Wall -g
+LDFLAGS = -g
+LOADLIBES = -L../isys -lpopt -lz -lisys -lresolv -lrpm
+
+all: collage
+
+collage: collage.o commands.o idmap.o ls.o
diff --git a/collage/collage.c b/collage/collage.c
new file mode 100644
index 000000000..ddc4ae28e
--- /dev/null
+++ b/collage/collage.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "commands.h"
+
+struct commandTableEntry {
+ char * name;
+ int (*fn)(int argc, char ** argv);
+};
+
+struct commandTableEntry commandTable[] = {
+ { "umount", umountCommand },
+ { "mount", mountCommand },
+ { "mkdir", mkdirCommand },
+ { "mknod", mknodCommand },
+ { "cat", catCommand },
+ { "ls", lsCommand },
+ { "ln", lnCommand },
+ { "rm", rmCommand },
+ { "chmod", chmodCommand },
+ { "lsmod", lsmodCommand },
+ { "uncpio", uncpioCommand },
+ { NULL, NULL }
+};
+
+int main (int argc, char ** argv) {
+ int len = strlen(argv[0]);
+ struct commandTableEntry * cmd;
+
+ for (cmd = commandTable; cmd->name; cmd++) {
+ if (!strcmp(argv[0] + len - strlen(cmd->name), cmd->name))
+ break;
+ }
+
+ if (cmd->name)
+ return cmd->fn(argc, argv);
+
+ printf("collage may be run as:\n");
+ for (cmd = commandTable; cmd->name; cmd++)
+ printf("\t%s\n", cmd->name);
+
+ return 1;
+}
+
diff --git a/collage/commands.c b/collage/commands.c
new file mode 100644
index 000000000..5dde54a64
--- /dev/null
+++ b/collage/commands.c
@@ -0,0 +1,386 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <asm/page.h>
+#include <sys/swap.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+#include <zlib.h>
+
+#include "../isys/imount.h"
+#include "../isys/isys.h"
+
+#include "commands.h"
+#include "idmap.h"
+#include "ls.h"
+#include "popt.h"
+
+static int copyfd(int to, int from);
+
+static int copyfd(int to, int from) {
+ char buf[1024];
+ int size;
+
+ while ((size = read(from, buf, sizeof(buf))) > 0) {
+ if (write(to, buf, size) != size) {
+ fprintf(stderr, "error writing output: %s\n", strerror(errno));
+ return 1;
+ }
+ }
+
+ if (size < 0) {
+ fprintf(stderr, "error reading input: %s\n", strerror(errno));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int catFile(char * filename) {
+ int fd;
+ int rc;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno));
+ return 1;
+ }
+
+ rc = copyfd(1, fd);
+ close(fd);
+
+ return rc;
+}
+
+int catCommand(int argc, char ** argv) {
+ char ** argptr = argv + 1;
+ int rc;
+
+ if (!*argptr) {
+ return copyfd(1, 0);
+ } else {
+ while (*argptr) {
+ rc = catFile(*argptr);
+ if (rc) return rc;
+ argptr++;
+ }
+ }
+
+ return 0;
+}
+
+int lsmodCommand(int argc, char ** argv) {
+ puts("Module: #pages: Used by:");
+ catFile("/proc/modules");
+
+ return 0;
+}
+
+#define MOUNT_USAGE fprintf(stderr, "usage: mount -t <fs> <device> <dir>\n" \
+ " (if /dev/ is left off the device name, a " \
+ "temporary node will be created)\n")
+
+int mountCommand(int argc, char ** argv) {
+ char * dev, * dir;
+ char * fs;
+
+ if (argc < 2) {
+ return catFile("/proc/mounts");
+ } else if (argc == 3) {
+ if (strchr(argv[1], ':'))
+ fs = "nfs";
+ else
+ fs = "ext2";
+ dev = argv[1];
+ dir = argv[2];
+ } else if (argc != 5) {
+ MOUNT_USAGE;
+ return 1;
+ } else {
+ if (strcmp(argv[1], "-t")) {
+ MOUNT_USAGE;
+ return 1;
+ }
+
+ fs = argv[2];
+ dev = argv[3];
+ dir = argv[4];
+
+ }
+
+ if (!strncmp(dev, "/dev/", 5) && access(dev, X_OK))
+ dev += 5;
+
+ if (doPwMount(dev, dir, fs, 0, 1, NULL, NULL))
+ return 1;
+
+ return 0;
+}
+
+int umountCommand(int argc, char ** argv) {
+ if (argc != 2) {
+ fprintf(stderr, "umount expects a single argument\n");
+ return 1;
+ }
+
+ if (umount(argv[1])) {
+ fprintf(stderr, "error unmounting %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+
+ return 0;
+}
+
+int mkdirCommand(int argc, char ** argv) {
+ char ** argptr = argv + 1;
+
+ if (argc < 2) {
+ fprintf(stderr, "umount expects one or more arguments\n");
+ return 1;
+ }
+
+ while (*argptr) {
+ if (mkdir(*argptr, 0755)) {
+ fprintf(stderr, "error creating directory %s: %s\n", *argptr,
+ strerror(errno));
+ return 1;
+ }
+
+ argptr++;
+ }
+
+ return 0;
+}
+
+int mknodCommand(int argc, char ** argv) {
+ int major, minor;
+ char * path;
+ int mode = 0600;
+ char *end;
+
+ if (argc != 5 && argc != 2) {
+ fprintf(stderr, "usage: mknod <path> [b|c] <major> <minor> or mknod <path>\n");
+ return 1;
+ }
+
+ path = argv[1];
+
+ if (argc == 2) {
+ end = path + strlen(path) - 1;
+ while (end > path && *end != '/') end--;
+
+ if (devMakeInode(end, path)) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if (!strcmp(argv[2], "b"))
+ mode |= S_IFBLK;
+ else if (!strcmp(argv[2], "c"))
+ mode |= S_IFCHR;
+ else {
+ fprintf(stderr, "unknown node type %s\n", argv[2]);
+ return 1;
+ }
+
+ major = strtol(argv[3], &end, 0);
+ if (*end) {
+ fprintf(stderr, "bad major number %s\n", argv[3]);
+ return 1;
+ }
+
+ minor = strtol(argv[4], &end, 0);
+ if (*end) {
+ fprintf(stderr, "bad minor number %s\n", argv[4]);
+ return 1;
+ }
+
+ if (mknod(path, mode, makedev(major, minor))) {
+ fprintf(stderr, "mknod failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ return 0;
+}
+
+int lnCommand(int argc, char ** argv) {
+ char ** argptr = argv + 1;
+ int force = 0, soft = 0;
+ int rc;
+
+ while (*argptr && **argptr == '-') {
+ if (!strcmp(*argptr, "-f"))
+ force = 1;
+ else if (!strcmp(*argptr, "-s"))
+ soft = 1;
+ else if (!strcmp(*argptr, "-fs") || !strcmp(*argptr, "-sf"))
+ force = soft = 1;
+ else {
+ fprintf(stderr, "ln: unknown argument %s\n", *argptr);
+ return 1;
+ }
+
+ argptr++;
+ }
+
+ if (!*argptr || !(*argptr + 1) || *(argptr + 2)) {
+ fprintf(stderr, "ln requires exactly two filenames\n");
+ return 1;
+ }
+
+ if (force) unlink(*(argptr + 1));
+ if (soft)
+ rc = symlink(*argptr, *(argptr + 1));
+ else
+ rc = link(*argptr, *(argptr + 1));
+
+ if (rc) {
+ perror("error");
+ return 1;
+ }
+
+ return 0;
+}
+
+int rmCommand(int argc, char ** argv) {
+ char ** argptr = argv + 1;
+
+ if (argc < 2) {
+ fprintf(stderr, "rm expects one or more arguments "
+ "(no flags are supported");
+ return 1;
+ }
+
+ while (*argptr) {
+ if (unlink(*argptr)) {
+ fprintf(stderr, "unlink of %s failed: %s\n", *argptr,
+ strerror(errno));
+ return 1;
+ }
+
+ argptr++;
+ }
+
+ return 0;
+}
+
+int chmodCommand(int argc, char ** argv) {
+ char ** argptr = argv + 2;
+ int mode;
+ char * end;
+
+ if (argc < 3) {
+ fprintf(stderr, "usage: chmod <mode> <one or files>\n");
+ return 1;
+ }
+
+ mode = strtol(argv[1], &end, 8);
+ if (*end) {
+ fprintf(stderr, "illegal mode %s\n", argv[1]);
+ return 1;
+ }
+
+ while (*argptr) {
+ if (chmod(*argptr, mode)) {
+ fprintf(stderr, "error in chmod of %s to 0%o: %s\n", *argptr,
+ mode, strerror(errno));
+ return 1;
+ }
+
+ argptr++;
+ }
+
+ return 0;
+}
+
+int uncpioCommand(int argc, char ** argv) {
+ int rc;
+ char * fail;
+
+ if (argc != 1) {
+ fprintf(stderr, "uncpio reads from stdin");
+ return 1;
+ }
+
+ rc = cpioInstallArchive(gzdopen(0, "r"), NULL, 0, NULL, NULL, &fail);
+ return (rc != 0);
+}
+
+int lsCommand(int argc, char ** argv) {
+ poptContext optCon;
+ int flags = 0;
+ int rc;
+ char path[1024];
+ struct poptOption ksOptions[] = {
+ { NULL, 'l', 0, NULL, 'l' },
+ { NULL, 'C', 0, NULL, 'C' },
+ { NULL, 'd', 0, NULL, 'd' },
+ { NULL, 'g', 0, NULL, 'g' },
+ { NULL, 'n', 0, NULL, 'n' },
+ { NULL, 'p', 0, NULL, 'p' },
+ { NULL, 'a', 0, NULL, 'a' },
+ { NULL, 'L', 0, NULL, 'L' },
+ { NULL, 'f', 0, NULL, 'f' },
+ { NULL, 'r', 0, NULL, 'r' },
+ { NULL, 't', 0, NULL, 't' },
+ { NULL, 'S', 0, NULL, 'S' },
+ { NULL, 'R', 0, NULL, 'R' },
+ { NULL, '\0', 0, NULL, '\0' }
+ };
+
+ optCon = poptGetContext(NULL, argc, argv, ksOptions, 0);
+ if (isatty(1)) flags |= SENDDIR_MULTICOLUMN;
+
+ while ((rc = poptGetNextOpt(optCon)) >= 0) {
+ switch (rc) {
+ case 'l':
+ flags |= SENDDIR_LONG; flags &= ~SENDDIR_MULTICOLUMN;
+ break;
+ case 'C':
+ flags |= SENDDIR_MULTICOLUMN; flags &= ~SENDDIR_LONG;
+ break;
+ case 'd': flags |= SENDDIR_SIMPLEDIRS; break;
+ case 'g': /* ignored */ break;
+ case 'n': flags |= SENDDIR_NUMIDS; break;
+ case 'p': case 'F': flags |= SENDDIR_FILETYPE; break;
+ case 'a': flags |= SENDDIR_ALL; break;
+ case 'L': flags |= SENDDIR_FOLLOWLINKS; break;
+ case 'f': flags |= SENDDIR_SORTNONE; break;
+ case 'r': flags |= SENDDIR_SORTREVERSE; break;
+ case 't': flags |= SENDDIR_SORTMTIME; break;
+ case 'S': flags |= SENDDIR_SORTSIZE; break;
+ case 'R': flags |= SENDDIR_RECURSE; break;
+ }
+ }
+
+ getcwd(path, 1000);
+
+ if (rc < -1) {
+ fprintf(stderr, "argument error: %s %s",
+ poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
+ poptStrerror(rc));
+ } else {
+ idInit();
+
+ argv = poptGetArgs(optCon);
+ if (argv) {
+ while (*argv) {
+ if (argv[0][0] == '/')
+ listFiles("", *argv, flags);
+ else
+ listFiles(path, *argv, flags);
+ argv++;
+ }
+ } else {
+ listFiles(path, "", flags);
+ }
+ }
+
+ return 0;
+}
diff --git a/collage/commands.h b/collage/commands.h
new file mode 100644
index 000000000..0b9172b0e
--- /dev/null
+++ b/collage/commands.h
@@ -0,0 +1,16 @@
+#ifndef H_MOUNT
+#define H_MOUNT
+
+int catCommand(int argc, char ** argv);
+int chmodCommand(int argc, char ** argv);
+int lsmodCommand(int argc, char ** argv);
+int mkdirCommand(int argc, char ** argv);
+int mknodCommand(int argc, char ** argv);
+int mountCommand(int argc, char ** argv);
+int rmCommand(int argc, char ** argv);
+int lnCommand(int argc, char ** argv);
+int lsCommand(int argc, char ** argv);
+int umountCommand(int argc, char ** argv);
+int uncpioCommand(int argc, char ** argv);
+
+#endif
diff --git a/collage/idmap.c b/collage/idmap.c
new file mode 100644
index 000000000..bede4457b
--- /dev/null
+++ b/collage/idmap.c
@@ -0,0 +1,146 @@
+#include <grp.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "idmap.h"
+
+struct idMap_s {
+ struct idElement * byId;
+ int numEntries;
+};
+
+typedef struct idMap_s * idMap;
+
+struct idElement {
+ long int id;
+ char * name;
+};
+
+typedef void * (*iterFn)(void);
+typedef int (*infoFn)(void * item, struct idElement * el);
+
+static idMap uidMap = NULL;
+static idMap gidMap = NULL;
+
+static int idCmp(const void * a, const void * b) {
+ const struct idElement * one = a;
+ const struct idElement * two = b;
+
+ if (one->id < two->id)
+ return -1;
+ else if (one->id > two->id)
+ return 1;
+
+ return 0;
+}
+
+static idMap readmap(iterFn fn, infoFn info) {
+ idMap map;
+ int alloced;
+ void * res;
+ struct idElement * newEntries;
+
+ map = malloc(sizeof(*map));
+ if (!map) {
+ return NULL;
+ }
+
+ alloced = 5;
+ map->byId = malloc(sizeof(*map->byId) * alloced);
+ if (!map->byId) {
+ free(map);
+ return NULL;
+ }
+ map->numEntries = 0;
+
+ while ((res = fn())) {
+ if (map->numEntries == alloced) {
+ alloced += 5;
+ newEntries = realloc(map->byId,
+ sizeof(*map->byId) * alloced);
+ if (!newEntries) {
+ /* FIXME: this doesn't free the id names */
+ free(map->byId);
+ free(map);
+ return NULL;
+ }
+
+ map->byId = newEntries;
+ }
+
+ if (info(res, map->byId + map->numEntries++)) {
+ /* FIXME: this doesn't free the id names */
+ free(map->byId);
+ free(map);
+ return NULL;
+ }
+ }
+
+ map->byId = realloc(map->byId,
+ sizeof(*map->byId) * map->numEntries);
+
+ qsort(map->byId, map->numEntries, sizeof(*map->byId), idCmp);
+
+ return map;
+}
+
+static int pwInfo(struct passwd * pw, struct idElement * el) {
+ el->id = pw->pw_uid;
+ el->name = strdup(pw->pw_name);
+
+ return el->name == NULL;
+}
+
+static int grInfo(struct group * gr, struct idElement * el) {
+ el->id = gr->gr_gid;
+ el->name = strdup(gr->gr_name);
+
+ return el->name == NULL;
+}
+
+idMap readUIDmap(void) {
+ idMap result;
+
+ result = readmap((void *) getpwent, (void *) pwInfo);
+ endpwent();
+
+ return result;
+}
+
+idMap readGIDmap(void) {
+ idMap result;
+
+ result = readmap((void *) getgrent, (void *) grInfo);
+ endgrent();
+
+ return result;
+}
+
+char * idSearchByUid(long int id) {
+ struct idElement el = { id, NULL };
+ struct idElement * match;
+
+ match = bsearch(&el, uidMap->byId, uidMap->numEntries,
+ sizeof(*uidMap->byId), idCmp);
+
+ if (match) return match->name; else return NULL;
+}
+
+char * idSearchByGid(long int id) {
+ struct idElement el = { id, NULL };
+ struct idElement * match;
+
+ match = bsearch(&el, gidMap->byId, gidMap->numEntries,
+ sizeof(*gidMap->byId), idCmp);
+
+ if (match) return match->name; else return NULL;
+}
+
+int idInit(void) {
+ if (!(uidMap = readUIDmap())) return 1;
+ if (!(gidMap = readGIDmap())) return 1;
+
+ return 0;
+}
diff --git a/collage/idmap.h b/collage/idmap.h
new file mode 100644
index 000000000..45e9f9d8b
--- /dev/null
+++ b/collage/idmap.h
@@ -0,0 +1,7 @@
+#ifndef H_IDMAP
+
+char * idSearchByUid(long int id);
+char * idSearchByGid(long int id);
+int idInit(void);
+
+#endif
diff --git a/collage/ls.c b/collage/ls.c
new file mode 100644
index 000000000..ae827cd51
--- /dev/null
+++ b/collage/ls.c
@@ -0,0 +1,499 @@
+#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(&currenttime);
+ 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);
+}
diff --git a/collage/ls.h b/collage/ls.h
new file mode 100644
index 000000000..fed3ac5b2
--- /dev/null
+++ b/collage/ls.h
@@ -0,0 +1,20 @@
+#ifndef H_LS
+#define H_LS
+
+#define SENDDIR_ALL (1 << 0)
+#define SENDDIR_LONG (1 << 1)
+#define SENDDIR_RECURSE (1 << 2)
+#define SENDDIR_SIMPLEDIRS (1 << 3)
+#define SENDDIR_NUMIDS (1 << 4)
+#define SENDDIR_FILETYPE (1 << 5)
+#define SENDDIR_FOLLOWLINKS (1 << 6)
+#define SENDDIR_SORTNONE (1 << 7)
+#define SENDDIR_SORTMTIME (1 << 8)
+#define SENDDIR_SORTSIZE (1 << 9)
+#define SENDDIR_SORTREVERSE (1 << 9)
+#define SENDDIR_MULTICOLUMN (1 << 10)
+
+char * fileStatStr(char * dir, char * fn, struct stat * sbp, int flags);
+void listFiles(char * path, char * fn, int flags);
+
+#endif
diff --git a/collage/mkcollagelinks b/collage/mkcollagelinks
new file mode 100755
index 000000000..4da43d3fc
--- /dev/null
+++ b/collage/mkcollagelinks
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+read line
+while read line; do
+ ln -sf $1 $line
+done