--- /dev/null
+/*
+ * Sherlock Library -- Memory Allocation
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lib.h"
+
+void *
+xmalloc(uns size)
+{
+ void *x = malloc(size);
+ if (!x)
+ die("Cannot allocate %d bytes of memory", size);
+ return x;
+}
--- /dev/null
+/*
+ * Sherlock Library -- String Allocation
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lib.h"
+
+byte *
+stralloc(byte *s)
+{
+ uns l = strlen(s);
+ byte *k = xmalloc(l + 1);
+ strcpy(k, s);
+ return k;
+}
--- /dev/null
+/*
+ * Sherlock Library -- Configuration-Dependent Definitions
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+/* Version */
+
+#define SHER_VER "0.1"
+
+/* Types */
+
+typedef unsigned char byte; /* exactly 8 bits, unsigned */
+typedef signed char sbyte; /* exactly 8 bits, signed */
+typedef unsigned short word; /* exactly 16 bits, unsigned */
+typedef short sword; /* exactly 16 bits, signed */
+typedef unsigned int ulg; /* exactly 32 bits, unsigned */
+typedef int slg; /* exactly 32 bits, signed */
+typedef unsigned int uns; /* at least 32 bits */
+
+/* Misc */
+
+#ifdef __GNUC__
+
+#undef inline
+#define NONRET __attribute__((noreturn))
+
+#else
+
+#define inline
+#define NONRET
+
+#endif
+
+#ifdef linux
+#define HAVE_FFS
+#endif
--- /dev/null
+/*
+ * Sherlock Library -- Content-Type Pattern Matching
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <stdio.h>
+
+#include "lib.h"
+#include "string.h"
+
+int
+match_ct_patt(byte *p, byte *t)
+{
+ if (*p == '*' && !p[1]) /* "*" matches everything */
+ return 1;
+
+ if (*p == '*' && p[1] == '/') /* "*" on the left-hand side */
+ {
+ while (*t && *t != ' ' && *t != ';' && *t != '/')
+ t++;
+ p += 2;
+ }
+ else /* Normal left-hand side */
+ {
+ while (*p != '/')
+ if (Cupcase(*p++) != Cupcase(*t++))
+ return 0;
+ p++;
+ }
+ if (*t != '/')
+ return 0;
+
+ if (*p == '*' && p[1] == '/') /* "*" on the right-hand side */
+ return 1;
+ while (*p)
+ if (Cupcase(*p++) != Cupcase(*t++))
+ return 0;
+ if (*t && *t != ' ' && *t != ';')
+ return 0;
+
+ return 1;
+}
--- /dev/null
+/*
+ * Sherlock Library -- Miscellaneous Functions
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <lib/config.h>
+
+/* Temporary Files */
+
+#define TMP_DIR "tmp"
+#define TMP_DIR_LEN 3
+
+struct tempfile {
+ int fh;
+ byte name[32];
+};
+
+void open_temp(struct tempfile *, byte *);
+void delete_temp(struct tempfile *);
+ulg temprand(uns);
+
+#define TF_GENERIC "t"
+#define TF_QUEUE_CONTROL "c"
+#define TF_QUEUE_DATA "d"
+
+/* Config Files */
+
+struct cfitem {
+ byte *name;
+ int type;
+ void *var;
+};
+
+#define CI_STOP 0
+#define CI_INT 1
+#define CI_STRING 2
+#define CI_FUNCTION 3
+
+typedef byte *(*ci_func)(struct cfitem *, byte *);
+
+void cf_read(byte *, struct cfitem *);
+
+/* Logging */
+
+#define L_DEBUG "<0>"
+#define L_INFO "<2>"
+#define L_WARN "<4>"
+#define L_ERROR "<6>"
+#define L_FATAL "<9>"
+
+void log(byte *, ...);
+void die(byte *, ...) NONRET;
+void initlog(byte *);
+
+/* Allocation */
+
+void *xmalloc(uns);
+byte *stralloc(byte *);
+
+/* Content-Type pattern matching */
+
+int match_ct_patt(byte *, byte *);
+
+/* Binary log */
+
+#ifdef HAVE_FFS
+#define log2(x) (ffs(x) - 1)
+#else
+int log2(ulg);
+#endif
+
+/* obj.c */
+
+struct odes { /* Object description */
+ struct oattr *attrs;
+};
+
+struct oattr { /* Object attribute */
+ struct oattr *next, *same;
+ byte attr;
+ byte val[1];
+};
+
+void obj_dump(struct odes *);
+struct odes *obj_fload(FILE *);
+struct odes *obj_new(void);
+struct odes *obj_load(byte *);
+void obj_fwrite(FILE *, struct odes *); /* Closes the file afterwards... */
+void obj_free(struct odes *);
+struct oattr *find_attr(struct odes *, uns);
+struct oattr *find_attr_last(struct odes *, uns);
+byte *find_aval(struct odes *, uns);
+struct oattr *set_attr(struct odes *, uns, byte *);
+struct oattr *set_attr_num(struct odes *, uns, uns);
+struct oattr *add_attr(struct odes *, struct oattr *, uns, byte *);
+
+/* oname.c */
+
+void mk_obj_name(byte *, ulg, byte *);
+FILE *create_obj_file(byte *, ulg);
+
+/* wordsplit.c */
+
+int wordsplit(byte *, byte **, uns);
+
+/* patmatch.c */
+
+int match_pattern(byte *, byte *);
--- /dev/null
+/*
+ * Sherlock Library -- Linked Lists
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <stdio.h>
+
+#include "lists.h"
+
+void
+add_tail(list *l, node *n)
+{
+ node *z = l->tail.prev;
+
+ n->next = &l->tail;
+ n->prev = z;
+ z->next = n;
+ l->tail.prev = n;
+}
+
+void
+add_head(list *l, node *n)
+{
+ node *z = l->head.next;
+
+ n->next = z;
+ n->prev = &l->head;
+ z->prev = n;
+ l->head.next = n;
+}
+
+void
+insert_node(node *n, node *after)
+{
+ node *z = after->next;
+
+ n->next = z;
+ n->prev = after;
+ after->next = n;
+ z->prev = n;
+}
+
+void
+rem_node(node *n)
+{
+ node *z = n->prev;
+ node *x = n->next;
+
+ z->next = x;
+ x->prev = z;
+}
+
+void
+init_list(list *l)
+{
+ l->head.next = &l->tail;
+ l->head.prev = NULL;
+ l->tail.next = NULL;
+ l->tail.prev = &l->head;
+}
+
+void
+add_tail_list(list *to, list *l)
+{
+ node *p = to->tail.prev;
+ node *q = l->head.next;
+
+ p->next = q;
+ q->prev = p;
+ q = l->tail.prev;
+ q->next = &to->tail;
+ to->tail.prev = q;
+}
--- /dev/null
+/*
+ * Sherlock Library -- Linked Lists
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+struct node {
+ struct node *next, *prev;
+};
+typedef struct node node;
+
+struct list {
+ struct node head, tail;
+};
+typedef struct list list;
+
+#define NODE (node *)
+#define HEAD(list) ((void *)((list).head.next))
+#define TAIL(list) ((void *)((list).tail.prev))
+#define DO_FOR_ALL(n,list) for((n)=HEAD(list);(NODE (n))->next; \
+ n=(void *)((NODE (n))->next))
+#define EMPTY_LIST(list) (!(list).head.next->next)
+
+void add_tail(list *, node *);
+void add_head(list *, node *);
+void rem_node(node *);
+void add_tail_list(list *, list *);
+void init_list(list *);
+void insert_node(node *, node *);
+
+#ifdef __GNUC__
+
+extern inline void
+add_tail(list *l, node *n)
+{
+ node *z = l->tail.prev;
+
+ n->next = &l->tail;
+ n->prev = z;
+ z->next = n;
+ l->tail.prev = n;
+}
+
+extern inline void
+add_head(list *l, node *n)
+{
+ node *z = l->head.next;
+
+ n->next = z;
+ n->prev = &l->head;
+ z->prev = n;
+ l->head.next = n;
+}
+
+extern inline void
+insert_node(node *n, node *after)
+{
+ node *z = after->next;
+
+ n->next = z;
+ n->prev = after;
+ after->next = n;
+ z->prev = n;
+}
+
+extern inline void
+rem_node(node *n)
+{
+ node *z = n->prev;
+ node *x = n->next;
+
+ z->next = x;
+ x->prev = z;
+}
+
+extern inline void
+init_list(list *l)
+{
+ l->head.next = &l->tail;
+ l->head.prev = NULL;
+ l->tail.next = NULL;
+ l->tail.prev = &l->head;
+}
+
+extern inline void
+add_tail_list(list *to, list *l)
+{
+ node *p = to->tail.prev;
+ node *q = l->head.next;
+
+ p->next = q;
+ q->prev = p;
+ q = l->tail.prev;
+ q->next = &to->tail;
+ to->tail.prev = q;
+}
+
+#endif
--- /dev/null
+/*
+ * Sherlock Library -- Logging
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "lib.h"
+
+static byte *progname = "???";
+static pid_t pid;
+
+static void
+logit(int level, byte *msg, va_list args)
+{
+ time_t tim;
+ struct tm *tm;
+ char buf[32];
+
+ tim = time(NULL);
+ tm = localtime(&tim);
+ strftime(buf, sizeof(buf), "%d-%m-%y %H-%M-%S", tm);
+ fprintf(stderr, "%s %s [%d] <%d> ", buf, progname, pid, level);
+ vfprintf(stderr, msg, args);
+ fputc('\n', stderr);
+ fflush(stderr);
+}
+
+void
+log(byte *msg, ...)
+{
+ int level = 2;
+ va_list args;
+
+ va_start(args, msg);
+ if (msg[0] == '<' && msg[1] >= '0' && msg[1] <= '9' && msg[2] == '>')
+ {
+ level = msg[1] - '0';
+ msg += 3;
+ }
+ logit(level, msg, args);
+ va_end(args);
+}
+
+void
+die(byte *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ logit(9, msg, args);
+ va_end(args);
+ exit(99);
+}
+
+static byte *
+basename(byte *n)
+{
+ byte *p = n;
+
+ while (*n)
+ if (*n++ == '/')
+ p = n;
+ return p;
+}
+
+void
+initlog(byte *argv0)
+{
+ progname = basename(argv0);
+ pid = getpid();
+}
--- /dev/null
+/*
+ * Sherlock Library -- Logging
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#define L_DEBUG "<0>"
+#define L_INFO "<2>"
+#define L_WARN "<4>"
+#define L_ERROR "<6>"
+#define L_FATAL "<9>"
+
+int log(byte *, ...);
+void initlog(byte *);
--- /dev/null
+/*
+ * Sherlock Library -- Binary Logarithm
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <stdio.h>
+
+#include "lib.h"
+
+int
+ffs(ulg x)
+{
+ ulg l;
+
+ if (!x)
+ return 0;
+
+ l = 0;
+ if (x & 0xffff0000) l += 16;
+ if (x & 0xff00ff00) l += 8;
+ if (x & 0xf0f0f0f0) l += 4;
+ if (x & 0xcccccccc) l += 2;
+ if (x & 0xaaaaaaaa) l++;
+ return l;
+}
--- /dev/null
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include <string.h> /* for memcpy() */
+#include "md5.h"
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len) /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32 t;
+ do {
+ t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32 *) ctx->in)[14] = ctx->bits[0];
+ ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32 *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32 buf[4], uint32 const in[16])
+{
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
--- /dev/null
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef __alpha
+typedef unsigned int uint32;
+#else
+typedef unsigned long uint32;
+#endif
+
+struct MD5Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+ unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(uint32 buf[4], uint32 const in[16]);
+
+#endif /* !MD5_H */
--- /dev/null
+/*
+ * Sherlock Library -- Shell-Like Pattern Matching (currently only '?' and '*')
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lib.h"
+
+int
+match_pattern(byte *p, byte *s)
+{
+ while (*p)
+ {
+ if (*p == '?' && *s)
+ p++, s++;
+ else if (*p == '*')
+ {
+ int z = p[1];
+
+ if (!z)
+ return 1;
+ while (s = strchr(s, z))
+ {
+ if (match_pattern(p+1, s))
+ return 1;
+ s++;
+ }
+ return 0;
+ }
+ else if (*p++ != *s++)
+ return 0;
+ }
+ return !*s;
+}
--- /dev/null
+/*
+ * Sherlock Library -- Character Classes
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include "string.h"
+
+unsigned char _c_cat[256] = {
+#define CHAR(code,upper,unacc,acc,cat) cat,
+#include "charmap.h"
+#undef CHAR
+};
--- /dev/null
+/*
+ * Sherlock Library -- Uppercase Map
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include "string.h"
+
+unsigned char _c_upper[256] = {
+#define CHAR(code,upper,unacc,acc,cat) upper,
+#include "charmap.h"
+#undef CHAR
+};
--- /dev/null
+/*
+ * Sherlock Library -- Temporary Files
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "lib.h"
+
+ulg
+temprand(uns key)
+{
+ static int seeded = 0;
+ ulg rand;
+
+ if (!seeded)
+ {
+ seeded = 1;
+ srand(getpid());
+ }
+ rand = random();
+ rand += key * 0xdeadbeef;
+ return rand;
+}
+
+void
+open_temp(struct tempfile *tf, byte *tftype)
+{
+ int retry = 50;
+ while (retry--)
+ {
+ sprintf(tf->name, TMP_DIR "/%s%08x", tftype, temprand(retry));
+ tf->fh = open(tf->name, O_RDWR | O_CREAT | O_EXCL, 0666);
+ if (tf->fh >= 0)
+ return;
+ }
+ die("Unable to create temporary file");
+}
+
+void
+delete_temp(struct tempfile *tf)
+{
+ close(tf->fh);
+ unlink(tf->name);
+}
--- /dev/null
+/*
+ * Sherlock Library -- URL Functions (according to RFC 1738 and 1808)
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "lib.h"
+#include "url.h"
+#include "string.h"
+
+/* Escaping and de-escaping */
+
+static uns
+enhex(uns x)
+{
+ return (x<10) ? (x + '0') : (x - 10 + 'A');
+}
+
+int
+url_deescape(byte *s, byte *d)
+{
+ byte *end = d + MAX_URL_SIZE - 10;
+ while (*s)
+ {
+ if (d >= end)
+ return URL_ERR_TOO_LONG;
+ if (*s == '%')
+ {
+ unsigned int val;
+ if (!Cxdigit(s[1]) || !Cxdigit(s[2]))
+ return URL_ERR_INVALID_ESCAPE;
+ val = Cxvalue(s[1])*16 + Cxvalue(s[2]);
+ if (!Cprint(val))
+ return URL_ERR_INVALID_ESCAPED_CHAR;
+ switch (val)
+ {
+ case ';':
+ val = NCC_SEMICOLON; break;
+ case '/':
+ val = NCC_SLASH; break;
+ case '?':
+ val = NCC_QUEST; break;
+ case ':':
+ val = NCC_COLON; break;
+ case '@':
+ val = NCC_AT; break;
+ case '=':
+ val = NCC_EQUAL; break;
+ case '&':
+ val = NCC_AND; break;
+ }
+ *d++ = val;
+ s += 3;
+ }
+ else if (*s >= 0x20 && *s <= 0x7e)
+ *d++ = *s++;
+ else
+ return URL_ERR_INVALID_CHAR;
+ }
+ *d = 0;
+ return 0;
+}
+
+int
+url_enescape(byte *s, byte *d)
+{
+ byte *end = d + MAX_URL_SIZE - 10;
+
+ while (*s)
+ {
+ if (d >= end)
+ return URL_ERR_TOO_LONG;
+ if ( *s >= 'A' && *s <= 'Z'
+ || *s >= 'a' && *s <= 'z'
+ || *s >= '0' && *s <= '9'
+ || *s == '$' || *s == '-' || *s == '.' || *s == '+'
+ || *s == '!' || *s == '*' || *s == '\'' || *s == '('
+ || *s == ')' || *s == '_' || *s == ';' || *s == '/'
+ || *s == '?' || *s == ':' || *s == '@' || *s == '='
+ || *s == '&')
+ *d++ = *s++;
+ else
+ {
+ uns val = (*s < NCC_MAX) ? ";/?:@=&"[*s] : *s;
+ *d++ = '%';
+ *d++ = enhex(val >> 4);
+ *d++ = enhex(val & 0x0f);
+ s++;
+ }
+ }
+ *d = 0;
+ return 0;
+}
+
+/* Split an URL (several parts may be copied to the destination buffer) */
+
+uns
+identify_protocol(byte *p)
+{
+ if (!strcasecmp(p, "http"))
+ return URL_PROTO_HTTP;
+ if (!strcasecmp(p, "ftp"))
+ return URL_PROTO_FTP;
+ return 0;
+}
+
+int
+url_split(byte *s, struct url *u, byte *d)
+{
+ bzero(u, sizeof(struct url));
+ u->port = ~0;
+ u->bufend = d + MAX_URL_SIZE - 10;
+
+ if (s[0] != '/') /* Seek for "protocol:" */
+ {
+ byte *p = s;
+ while (*p && Calnum(*p))
+ p++;
+ if (p != s && *p == ':')
+ {
+ u->protocol = d;
+ while (s < p)
+ *d++ = *s++;
+ *d++ = 0;
+ u->protoid = identify_protocol(u->protocol);
+ s++;
+ }
+ }
+
+ if (s[0] == '/') /* Host spec or absolute path */
+ {
+ if (s[1] == '/') /* Host spec */
+ {
+ byte *q, *w, *e;
+ char *ep;
+
+ s += 2;
+ q = d;
+ while (*s && *s != '/') /* Copy user:passwd@host:port */
+ *d++ = *s++;
+ *d++ = 0;
+ w = strchr(q, '@');
+ if (w) /* user:passwd present */
+ {
+ *w++ = 0;
+ u->user = q;
+ }
+ else
+ w = q;
+ e = strchr(w, ':');
+ if (e) /* host:port present */
+ {
+ *e++ = 0;
+ u->port = strtoul(e, &ep, 10);
+ if (ep && *ep || u->port > 65535 || !u->port)
+ return URL_ERR_INVALID_PORT;
+ }
+ u->host = w;
+ }
+ }
+
+ u->rest = s;
+ u->buf = d;
+ return 0;
+}
+
+/* Normalization according to given base URL */
+
+static uns std_ports[] = { ~0, 80, 21 }; /* Default port numbers */
+
+static int
+relpath_merge(struct url *u, struct url *b)
+{
+ byte *a = u->rest;
+ byte *o = b->rest;
+ byte *d = u->buf;
+ byte *e = u->bufend;
+ byte *p;
+
+ if (a[0] == '/') /* Absolute path => OK */
+ return 0;
+ if (o[0] != '/')
+ return URL_PATH_UNDERFLOW;
+
+ if (!a[0]) /* Empty relative URL is a special case */
+ {
+ u->rest = b->rest;
+ return 0;
+ }
+
+ u->rest = d;
+ p = strrchr(o, '/'); /* Must be found! */
+ while (o <= p) /* Copy original path */
+ {
+ if (d >= e)
+ return URL_ERR_TOO_LONG;
+ *d++ = *o++;
+ }
+
+ while (*a)
+ {
+ if (a[0] == '.')
+ {
+ if (a[1] == '/' || !a[1]) /* Skip "./" and ".$" */
+ {
+ a++;
+ if (a[0])
+ a++;
+ continue;
+ }
+ else if (a[1] == '.' && (a[2] == '/' || !a[2])) /* "../" */
+ {
+ a += 2;
+ if (d <= u->buf + 1)
+ return URL_PATH_UNDERFLOW;
+ d--; /* Discard trailing slash */
+ while (d[-1] != '/')
+ d--;
+ if (a[0])
+ a++;
+ continue;
+ }
+ }
+ while (a[0] && a[0] != '/')
+ {
+ if (d >= e)
+ return URL_ERR_TOO_LONG;
+ *d++ = *a++;
+ }
+ if (a[0])
+ *d++ = *a++;
+ }
+
+ *d++ = 0;
+ u->buf = d;
+ return 0;
+}
+
+int
+url_normalize(struct url *u, struct url *b)
+{
+ byte *k;
+
+ if (u->protocol && !u->protoid)
+ return 0;
+
+ if ((u->protoid == URL_PROTO_HTTP || (!u->protoid && b && b->protoid == URL_PROTO_HTTP))
+ && u->rest && (k = strchr(u->rest, '#')))
+ *k = 0; /* Kill fragment reference */
+
+ if (u->port == ~0)
+ u->port = std_ports[u->protoid];
+
+ if ( u->protocol && !u->host
+ || u->host && !*u->host
+ || !u->host && u->user
+ || !u->rest)
+ return URL_SYNTAX_ERROR;
+
+ if (u->protocol) /* Absolute URL */
+ return 0;
+
+ if (!b) /* Relative to something? */
+ return URL_ERR_REL_NOTHING;
+ if (!b->protoid)
+ return URL_ERR_UNKNOWN_PROTOCOL;
+
+ if (!u->protocol)
+ {
+ u->protocol = b->protocol;
+ u->protoid = b->protoid;
+ }
+
+ if (!u->host)
+ {
+ u->host = b->host;
+ u->user = b->user;
+ u->port = b->port;
+ return relpath_merge(u, b);
+ }
+
+ return 0;
+}
+
+/* Name canonicalization */
+
+static void
+lowercase(byte *b)
+{
+ if (b)
+ while (*b)
+ {
+ if (*b >= 'A' && *b <= 'Z')
+ *b = *b + 0x20;
+ b++;
+ }
+}
+
+static void
+kill_end_dot(byte *b)
+{
+ byte *k;
+
+ if (b)
+ {
+ k = b + strlen(b) - 1;
+ if (k > b && *k == '.')
+ *k = 0;
+ }
+}
+
+int
+url_canonicalize(struct url *u)
+{
+ lowercase(u->protocol);
+ lowercase(u->host);
+ kill_end_dot(u->host);
+ if ((!u->rest || !*u->rest) && (u->protoid == URL_PROTO_HTTP || u->protoid == URL_PROTO_FTP))
+ u->rest = "/";
+ return 0;
+}
+
+/* Pack a broken-down URL */
+
+byte *
+append(byte *d, byte *s, byte *e)
+{
+ if (d)
+ while (*s)
+ {
+ if (d >= e)
+ return NULL;
+ *d++ = *s++;
+ }
+ return d;
+}
+
+int
+url_pack(struct url *u, byte *d)
+{
+ byte *e = d + MAX_URL_SIZE - 10;
+
+ if (u->protocol)
+ {
+ d = append(d, u->protocol, e);
+ d = append(d, ":", e);
+ u->protoid = identify_protocol(u->protocol);
+ }
+ if (u->host)
+ {
+ d = append(d, "//", e);
+ if (u->user)
+ {
+ d = append(d, u->user, e);
+ d = append(d, "@", e);
+ }
+ d = append(d, u->host, e);
+ if (u->port != std_ports[u->protoid] && u->port != ~0)
+ {
+ char z[10];
+ sprintf(z, "%d", u->port);
+ d = append(d, ":", e);
+ d = append(d, z, e);
+ }
+ }
+ if (u->rest)
+ d = append(d, u->rest, e);
+ if (!d)
+ return URL_ERR_TOO_LONG;
+ *d = 0;
+ return 0;
+}
+
+/* Error messages */
+
+static char *errmsg[] = {
+ "Something is wrong",
+ "Too long",
+ "Invalid character",
+ "Invalid escape",
+ "Invalid escaped character",
+ "Invalid port number",
+ "Relative URL not allowed",
+ "Unknown protocol",
+ "Syntax error",
+ "Path underflow"
+};
+
+char *
+url_error(uns err)
+{
+ if (err >= sizeof(errmsg) / sizeof(char *))
+ err = 0;
+ return errmsg[err];
+}
+
+/* A "macro" for canonical split */
+
+int
+url_canon_split(byte *u, byte *buf1, byte *buf2, struct url *url)
+{
+ int err;
+
+ if (err = url_deescape(u, buf1))
+ return err;
+ if (err = url_split(buf1, url, buf2))
+ return err;
+ if (err = url_normalize(url, NULL))
+ return err;
+ return url_canonicalize(url);
+}
+
+/* Testing */
+
+#ifdef TEST
+
+int main(int argc, char **argv)
+{
+ char buf1[MAX_URL_SIZE], buf2[MAX_URL_SIZE], buf3[MAX_URL_SIZE], buf4[MAX_URL_SIZE];
+ int err;
+ struct url url, url0;
+
+ if (argc != 2)
+ return 1;
+ if (err = url_deescape(argv[1], buf1))
+ {
+ printf("deesc: error %d\n", err);
+ return 1;
+ }
+ printf("deesc: %s\n", buf1);
+ if (err = url_split(buf1, &url, buf2))
+ {
+ printf("split: error %d\n", err);
+ return 1;
+ }
+ printf("split: @%s@%s@%s@%d@%s\n", url.protocol, url.user, url.host, url.port, url.rest);
+ if (err = url_split("http://mj@www.hell.org/123/sub_dir/index.html", &url0, buf3))
+ {
+ printf("split base: error %d\n", err);
+ return 1;
+ }
+ if (err = url_normalize(&url0, NULL))
+ {
+ printf("normalize base: error %d\n", err);
+ return 1;
+ }
+ printf("base: @%s@%s@%s@%d@%s\n", url0.protocol, url0.user, url0.host, url0.port, url0.rest);
+ if (err = url_normalize(&url, &url0))
+ {
+ printf("normalize: error %d\n", err);
+ return 1;
+ }
+ printf("normalize: @%s@%s@%s@%d@%s\n", url.protocol, url.user, url.host, url.port, url.rest);
+ if (err = url_canonicalize(&url))
+ {
+ printf("canonicalize: error %d\n", err);
+ return 1;
+ }
+ printf("canonicalize: @%s@%s@%s@%d@%s\n", url.protocol, url.user, url.host, url.port, url.rest);
+ if (err = url_pack(&url, buf4))
+ {
+ printf("pack: error %d\n", err);
+ return 1;
+ }
+ printf("pack: %s\n", buf1);
+ if (err = url_enescape(buf4, buf2))
+ {
+ printf("enesc: error %d\n", err);
+ return 1;
+ }
+ printf("enesc: %s\n", buf2);
+ return 0;
+}
+
+#endif
--- /dev/null
+/*
+ * Sherlock Library -- URL Functions
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#define MAX_URL_SIZE 1024
+
+/* Non-control meanings of control characters */
+
+#define NCC_SEMICOLON 1
+#define NCC_SLASH 2
+#define NCC_QUEST 3
+#define NCC_COLON 4
+#define NCC_AT 5
+#define NCC_EQUAL 6
+#define NCC_AND 7
+#define NCC_MAX 8
+
+/* Remove/Introduce '%' escapes */
+
+int url_deescape(byte *, byte *);
+int url_enescape(byte *, byte *);
+
+/* URL splitting and normalization */
+
+struct url {
+ byte *protocol;
+ uns protoid;
+ byte *user;
+ byte *host;
+ uns port; /* ~0 if unspec */
+ byte *rest;
+ byte *buf, *bufend;
+};
+
+int url_split(byte *, struct url *, byte *);
+int url_normalize(struct url *, struct url *);
+int url_canonicalize(struct url *);
+int url_pack(struct url *, byte *);
+int url_canon_split(byte *, byte *, byte *, struct url *);
+uns identify_protocol(byte *);
+
+/* Error codes */
+
+char *url_error(uns);
+
+#define URL_ERR_TOO_LONG 1
+#define URL_ERR_INVALID_CHAR 2
+#define URL_ERR_INVALID_ESCAPE 3
+#define URL_ERR_INVALID_ESCAPED_CHAR 4
+#define URL_ERR_INVALID_PORT 5
+#define URL_ERR_REL_NOTHING 6
+#define URL_ERR_UNKNOWN_PROTOCOL 7
+#define URL_SYNTAX_ERROR 8
+#define URL_PATH_UNDERFLOW 9
+
+#define URL_PROTO_UNKNOWN 0
+#define URL_PROTO_HTTP 1
+#define URL_PROTO_FTP 2
+#define URL_PROTO_MAX 3
+
+#define URL_PNAMES { "unknown", "http", "ftp" }
--- /dev/null
+/*
+ * Sherlock Library -- Word Splitting
+ *
+ * (c) 1997 Martin Mares, <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <stdio.h>
+
+#include "lib.h"
+#include "string.h"
+
+int
+wordsplit(byte *src, byte **dst, uns max)
+{
+ int cnt = 0;
+
+ for(;;)
+ {
+ while (Cspace(*src))
+ *src++ = 0;
+ if (!*src)
+ break;
+ if (cnt >= max)
+ return -1;
+ dst[cnt++] = src;
+ while (*src && !Cspace(*src))
+ src++;
+ }
+ return cnt;
+}