changeset 1094:db52c32dbe42

All three current protocols work with move to using uw_app
author Adam Chlipala <adamc@hcoop.net>
date Sun, 27 Dec 2009 10:37:24 -0500
parents 8d3aa6c7cee0
children bed675db3aff
files Makefile.in include/request.h include/types.h include/urweb.h src/c/cgi.c src/c/fastcgi.c src/c/http.c src/c/request.c src/c/urweb.c src/cjr_print.sml src/compiler.sml src/mysql.sml src/postgres.sml src/sqlite.sml
diffstat 14 files changed, 258 insertions(+), 239 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.in	Sat Dec 26 11:56:40 2009 -0500
+++ b/Makefile.in	Sun Dec 27 10:37:24 2009 -0500
@@ -24,7 +24,7 @@
 	rm -rf .cm src/.cm
 
 lib/c/%.o: src/c/%.c include/*.h
-	gcc -O3 -I include -c $< -o $@ $(CFLAGS)
+	gcc -Wimplicit -O3 -I include -c $< -o $@ $(CFLAGS)
 
 src/urweb.cm: src/prefix.cm src/sources
 	cat src/prefix.cm src/sources \
--- a/include/request.h	Sat Dec 26 11:56:40 2009 -0500
+++ b/include/request.h	Sun Dec 27 10:37:24 2009 -0500
@@ -7,7 +7,7 @@
 
 typedef struct uw_rc *uw_request_context;
 
-void uw_request_init(void *logger_data, uw_logger log_error, uw_logger log_debug);
+void uw_request_init(uw_app *app, void *logger_data, uw_logger log_error, uw_logger log_debug);
 void uw_sign(const char *in, char *out);
 
 uw_request_context uw_new_request_context(void);
@@ -22,9 +22,10 @@
                           int (*send)(int sockfd, const void *buf, ssize_t len),
                           int (*close)(int fd));
 
-uw_context uw_request_new_context(void *logger_data, uw_logger log_error, uw_logger log_debug);
+uw_context uw_request_new_context(uw_app*, void *logger_data, uw_logger log_error, uw_logger log_debug);
 
 typedef struct {
+  uw_app *app;
   void *logger_data;
   uw_logger log_error, log_debug;
 } loggers;
--- a/include/types.h	Sat Dec 26 11:56:40 2009 -0500
+++ b/include/types.h	Sun Dec 27 10:37:24 2009 -0500
@@ -52,4 +52,26 @@
 typedef void (*uw_callback)(void *);
 typedef void (*uw_logger)(void*, const char *fmt, ...);
 
+typedef struct {
+  int inputs_len, timeout;
+  char *url_prefix;
+
+  void (*client_init)();
+  void (*initializer)(uw_context);
+  void (*expunger)(uw_context, uw_Basis_client);
+
+  void (*db_init)(uw_context);
+  int (*db_begin)(uw_context);
+  int (*db_commit)(uw_context);
+  int (*db_rollback)(uw_context);
+  void (*db_close)(uw_context);
+
+  void (*handle)(uw_context, char *);
+
+  int (*input_num)(const char*);
+  uw_Basis_string (*cookie_sig)(uw_context);
+  int (*check_url)(const char *);
+  int (*check_mime)(const char *);
+} uw_app;
+
 #endif
--- a/include/urweb.h	Sat Dec 26 11:56:40 2009 -0500
+++ b/include/urweb.h	Sun Dec 27 10:37:24 2009 -0500
@@ -11,6 +11,7 @@
 extern uw_unit uw_unit_v;
 
 void uw_global_init(void);
+void uw_app_init(uw_app*);
 
 void uw_client_connect(unsigned id, int pass, int sock,
                        int (*send)(int sockfd, const void *buf, size_t len),
@@ -20,12 +21,14 @@
 failure_kind uw_initialize(uw_context);
 
 uw_context uw_init(void);
+void uw_set_app(uw_context, uw_app*);
 void uw_set_db(uw_context, void*);
 void *uw_get_db(uw_context);
 void uw_free(uw_context);
 void uw_reset(uw_context);
 void uw_reset_keep_request(uw_context);
 void uw_reset_keep_error_message(uw_context);
+const char *uw_get_url_prefix(uw_context);
 
 failure_kind uw_begin_init(uw_context);
 void uw_set_on_success(char *);
--- a/src/c/cgi.c	Sat Dec 26 11:56:40 2009 -0500
+++ b/src/c/cgi.c	Sun Dec 27 10:37:24 2009 -0500
@@ -5,8 +5,11 @@
 #include <unistd.h>
 #include <stdarg.h>
 
+#include "urweb.h"
 #include "request.h"
 
+extern uw_app uw_application;
+
 static char *uppercased;
 static size_t uppercased_len;
 
@@ -51,7 +54,7 @@
 }
 
 int main(int argc, char *argv[]) {
-  uw_context ctx = uw_request_new_context(NULL, log_error, log_debug);
+  uw_context ctx = uw_request_new_context(&uw_application, NULL, log_error, log_debug);
   uw_request_context rc = uw_new_request_context();
   request_result rr;
   char *method = getenv("REQUEST_METHOD"),
@@ -97,7 +100,7 @@
 
   uw_set_on_success("");
   uw_set_headers(ctx, get_header, NULL);
-  uw_request_init(NULL, log_error, log_debug);
+  uw_request_init(&uw_application, NULL, log_error, log_debug);
 
   body[body_pos] = 0;
   rr = uw_request(rc, ctx, method, path, query_string, body, body_pos,
--- a/src/c/fastcgi.c	Sat Dec 26 11:56:40 2009 -0500
+++ b/src/c/fastcgi.c	Sun Dec 27 10:37:24 2009 -0500
@@ -10,6 +10,7 @@
 #include <unistd.h>
 #include <signal.h>
 #include <stdarg.h>
+#include <ctype.h>
 
 #include <pthread.h>
 
@@ -19,6 +20,8 @@
 
 #include "fastcgi.h"
 
+extern uw_app uw_application;
+
 typedef struct {
   unsigned char version;
   unsigned char type;
@@ -300,7 +303,7 @@
   int me = *(int *)data;
   FCGI_Input *in = fastcgi_input();
   FCGI_Output *out = fastcgi_output();
-  uw_context ctx = uw_request_new_context(out, log_error, log_debug);
+  uw_context ctx = uw_request_new_context(&uw_application, out, log_error, log_debug);
   uw_request_context rc = uw_new_request_context();
   headers hs;
   size_t body_size = 0;
@@ -489,7 +492,7 @@
   exit(0);
 }
 
-static loggers ls = {NULL, log_error, log_debug};
+static loggers ls = {&uw_application, NULL, log_error, log_debug};
 
 int main(int argc, char *argv[]) {
   // The skeleton for this function comes from Beej's sockets tutorial.
@@ -538,7 +541,7 @@
   }
 
   uw_set_on_success("");
-  uw_request_init(NULL, log_error, log_debug);
+  uw_request_init(&uw_application, NULL, log_error, log_debug);
 
   names = calloc(nthreads, sizeof(int));
 
--- a/src/c/http.c	Sat Dec 26 11:56:40 2009 -0500
+++ b/src/c/http.c	Sun Dec 27 10:37:24 2009 -0500
@@ -16,6 +16,8 @@
 #include "request.h"
 #include "queue.h"
 
+extern uw_app uw_application;
+
 int uw_backlog = 10;
 
 static char *get_header(void *data, const char *h) {
@@ -61,7 +63,7 @@
 
 static void *worker(void *data) {
   int me = *(int *)data;
-  uw_context ctx = uw_request_new_context(NULL, log_error, log_debug);
+  uw_context ctx = uw_request_new_context(&uw_application, NULL, log_error, log_debug);
   size_t buf_size = 2;
   char *buf = malloc(buf_size);
   uw_request_context rc = uw_new_request_context();
@@ -214,7 +216,7 @@
   exit(0);
 }
 
-static loggers ls = {NULL, log_error, log_debug};
+static loggers ls = {&uw_application, NULL, log_error, log_debug};
 
 int main(int argc, char *argv[]) {
   // The skeleton for this function comes from Beej's sockets tutorial.
@@ -262,7 +264,7 @@
     }
   }
 
-  uw_request_init(NULL, log_error, log_debug);
+  uw_request_init(&uw_application, NULL, log_error, log_debug);
 
   names = calloc(nthreads, sizeof(int));
 
--- a/src/c/request.c	Sat Dec 26 11:56:40 2009 -0500
+++ b/src/c/request.c	Sun Dec 27 10:37:24 2009 -0500
@@ -31,9 +31,10 @@
   return r;
 }
 
-uw_context uw_request_new_context(void *logger_data, uw_logger log_error, uw_logger log_debug) {
+uw_context uw_request_new_context(uw_app *app, void *logger_data, uw_logger log_error, uw_logger log_debug) {
   uw_context ctx = uw_init();
   int retries_left = MAX_RETRIES;
+  uw_set_app(ctx, app);
 
   while (1) {
     failure_kind fk = uw_begin_init(ctx);
@@ -95,26 +96,27 @@
   }
 }
 
-void uw_request_init(void *logger_data, uw_logger log_error, uw_logger log_debug) {
+void uw_request_init(uw_app *app, void *logger_data, uw_logger log_error, uw_logger log_debug) {
   uw_context ctx;
   failure_kind fk;
 
   uw_global_init();
+  uw_app_init(app);
 
-  ctx = uw_request_new_context(logger_data, log_error, log_debug);
+  ctx = uw_request_new_context(app, logger_data, log_error, log_debug);
 
   if (!ctx)
     exit(1);
 
   for (fk = uw_initialize(ctx); fk == UNLIMITED_RETRY; fk = uw_initialize(ctx)) {
     log_debug(logger_data, "Unlimited retry during init: %s\n", uw_error_message(ctx));
-    uw_db_rollback(ctx);
+    uw_rollback(ctx);
     uw_reset(ctx);
   }
 
   if (fk != SUCCESS) {
     log_error(logger_data, "Failed to initialize database!  %s\n", uw_error_message(ctx));
-    uw_db_rollback(ctx);
+    uw_rollback(ctx);
     exit(1);
   }
 
@@ -151,8 +153,6 @@
   free(r);
 }
 
-extern char *uw_url_prefix;
-
 request_result uw_request(uw_request_context rc, uw_context ctx,
                           char *method, char *path, char *query_string,
                           char *body, size_t body_len,
@@ -168,6 +168,7 @@
   char *boundary = NULL;
   size_t boundary_len;
   char *inputs;
+  const char *prefix = uw_get_url_prefix(ctx);
 
   uw_set_currentUrl(ctx, path);
 
@@ -208,8 +209,8 @@
     return FAILED;
   }
 
-  if (!strncmp(path, uw_url_prefix, strlen(uw_url_prefix))
-      && !strcmp(path + strlen(uw_url_prefix), ".msgs")) {
+  if (!strncmp(path, prefix, strlen(prefix))
+      && !strcmp(path + strlen(prefix), ".msgs")) {
     char *id = uw_Basis_requestHeader(ctx, "UrWeb-Client");
     char *pass = uw_Basis_requestHeader(ctx, "UrWeb-Pass");
 
@@ -435,13 +436,14 @@
 }
 
 typedef struct {
+  uw_app *app;
   void *logger_data;
   uw_logger log_error, log_debug;
 } loggers;
 
 void *client_pruner(void *data) {
   loggers *ls = (loggers *)data;
-  uw_context ctx = uw_request_new_context(ls->logger_data, ls->log_error, ls->log_debug);
+  uw_context ctx = uw_request_new_context(ls->app, ls->logger_data, ls->log_error, ls->log_debug);
 
   if (!ctx)
     exit(1);
--- a/src/c/urweb.c	Sat Dec 26 11:56:40 2009 -0500
+++ b/src/c/urweb.c	Sun Dec 27 10:37:24 2009 -0500
@@ -1,4 +1,4 @@
-#define _XOPEN_SOURCE 500
+#define _XOPEN_SOURCE 600
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -9,6 +9,8 @@
 #include <stdarg.h>
 #include <assert.h>
 #include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
 
 #include <pthread.h>
 
@@ -287,14 +289,14 @@
 
 // Global entry points
 
-extern void uw_client_init();
-
 void uw_global_init() {
   srand(time(NULL) ^ getpid());
 
   clients = malloc(0);
-
-  uw_client_init();
+}
+
+void uw_app_init(uw_app *app) {
+  app->client_init();
 }
 
 
@@ -349,13 +351,15 @@
 } global;
 
 struct uw_context {
+  uw_app *app;
+
   char *(*get_header)(void *, const char *);
   void *get_header_data;
 
   buf outHeaders, page, heap, script;
   int returning_indirectly;
   input *inputs, *subinputs, *cur_container;
-  size_t n_subinputs, used_subinputs;
+  size_t sz_inputs, n_subinputs, used_subinputs;
 
   int source_count;
 
@@ -367,15 +371,13 @@
 
   cleanup *cleanup, *cleanup_front, *cleanup_back;
 
-  const char *script_header, *url_prefix;
+  const char *script_header;
 
   int needs_push, needs_sig;
 
   size_t n_deltas, used_deltas;
   delta *deltas;
 
-  int timeout;
-
   client *client;
 
   transactional *transactionals;
@@ -389,11 +391,11 @@
   char error_message[ERROR_BUF_LEN];
 };
 
-extern int uw_inputs_len, uw_timeout;
-
 uw_context uw_init() {
   uw_context ctx = malloc(sizeof(struct uw_context));
 
+  ctx->app = NULL;
+
   ctx->get_header = NULL;
   ctx->get_header_data = NULL;
 
@@ -404,10 +406,10 @@
   buf_init(&ctx->script, 1);
   ctx->script.start[0] = 0;
 
-  ctx->inputs = calloc(uw_inputs_len, sizeof(input));
+  ctx->inputs = malloc(0);
   ctx->cur_container = NULL;
   ctx->subinputs = malloc(0);
-  ctx->n_subinputs = ctx->used_subinputs = 0;
+  ctx->sz_inputs = ctx->n_subinputs = ctx->used_subinputs = 0;
 
   ctx->db = NULL;
 
@@ -416,7 +418,6 @@
   ctx->cleanup_front = ctx->cleanup_back = ctx->cleanup = malloc(0);
 
   ctx->script_header = "";
-  ctx->url_prefix = "/";
   ctx->needs_push = 0;
   ctx->needs_sig = 0;
   
@@ -427,8 +428,6 @@
   ctx->n_deltas = ctx->used_deltas = 0;
   ctx->deltas = malloc(0);
 
-  ctx->timeout = uw_timeout;
-
   ctx->client = NULL;
 
   ctx->error_message[0] = 0;
@@ -444,6 +443,16 @@
   return ctx;
 }
 
+void uw_set_app(uw_context ctx, uw_app *app) {
+  ctx->app = app;
+
+  if (app->inputs_len > ctx->sz_inputs) {
+    ctx->sz_inputs = app->inputs_len;
+    ctx->inputs = realloc(ctx->inputs, ctx->sz_inputs * sizeof(input));
+    memset(ctx->inputs, 0, ctx->sz_inputs * sizeof(input));
+  }
+}
+
 void uw_set_db(uw_context ctx, void *db) {
   ctx->db = db;
 }
@@ -499,19 +508,16 @@
 
 void uw_reset(uw_context ctx) {
   uw_reset_keep_request(ctx);
-  memset(ctx->inputs, 0, uw_inputs_len * sizeof(input));
+  memset(ctx->inputs, 0, ctx->app->inputs_len * sizeof(input));
   memset(ctx->subinputs, 0, ctx->n_subinputs * sizeof(input));
   ctx->used_subinputs = 0;
 }
 
-void uw_db_init(uw_context);
-void uw_handle(uw_context, char *);
-
 failure_kind uw_begin_init(uw_context ctx) {
   int r = setjmp(ctx->jmp_buf);
 
   if (r == 0)
-    uw_db_init(ctx);
+    ctx->app->db_init(ctx);
 
   return r;
 }
@@ -521,8 +527,6 @@
   ctx->get_header_data = get_header_data;
 }
 
-int uw_db_begin(uw_context);
-
 static void uw_set_error(uw_context ctx, const char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
@@ -600,10 +604,10 @@
   int r = setjmp(ctx->jmp_buf);
 
   if (r == 0) {
-    if (uw_db_begin(ctx))
+    if (ctx->app->db_begin(ctx))
       uw_error(ctx, BOUNDED_RETRY, "Error running SQL BEGIN");
 
-    uw_handle(ctx, path);
+    ctx->app->handle(ctx, path);
   }
 
   return r;
@@ -628,8 +632,6 @@
   return ctx->error_message;
 }
 
-extern int uw_input_num(const char*);
-
 static input *INP(uw_context ctx) {
   if (ctx->cur_container == NULL)
     return ctx->inputs;
@@ -674,7 +676,7 @@
     if (ctx->subinputs != new_subinputs) {
       for (i = 0; i < ctx->used_subinputs; ++i)
         adjust_input(&new_subinputs[i], ctx->subinputs, new_subinputs, ctx->used_subinputs);
-      for (i = 0; i < uw_inputs_len; ++i)
+      for (i = 0; i < ctx->app->inputs_len; ++i)
         adjust_input(&ctx->inputs[i], ctx->subinputs, new_subinputs, ctx->used_subinputs);
 
       adjust_pointer(&ctx->cur_container, ctx->subinputs, new_subinputs, ctx->used_subinputs);
@@ -696,7 +698,7 @@
   //printf("Input name %s\n", name);
 
   if (!strcasecmp(name, ".b")) {
-    int n = uw_input_num(value);
+    int n = ctx->app->input_num(value);
     input *inps;
 
     if (n < 0) {
@@ -704,12 +706,12 @@
       return -1;
     }
 
-    if (n >= uw_inputs_len) {
+    if (n >= ctx->app->inputs_len) {
       uw_set_error(ctx, "For subform name %s, index %d is out of range", value, n);
       return -1;
     }
 
-    inps = check_input_space(ctx, uw_inputs_len);
+    inps = check_input_space(ctx, ctx->app->inputs_len);
 
     INP(ctx)[n].kind = SUBFORM;
     INP(ctx)[n].data.subform.parent = ctx->cur_container;
@@ -741,14 +743,14 @@
       return -1;
     }
   } else if (!strcasecmp(name, ".s")) {
-    int n = uw_input_num(value);
+    int n = ctx->app->input_num(value);
 
     if (n < 0) {
       uw_set_error(ctx, "Bad subforms name %s", value);
       return -1;
     }
 
-    if (n >= uw_inputs_len) {
+    if (n >= ctx->app->inputs_len) {
       uw_set_error(ctx, "For subforms name %s, index %d is out of range", value, n);
       return -1;
     }
@@ -770,7 +772,7 @@
       return -1;
     }
 
-    inps = check_input_space(ctx, uw_inputs_len + 1);
+    inps = check_input_space(ctx, ctx->app->inputs_len + 1);
 
     inps->kind = ENTRY;
     inps->data.entry.parent = ctx->cur_container;
@@ -780,7 +782,7 @@
     inps->data.entry.fields = inps+1;
     ctx->cur_container = inps;
   } else {
-    int n = uw_input_num(name);
+    int n = ctx->app->input_num(name);
 
     if (n < 0) {
       if (!strcmp(name, "null"))
@@ -789,7 +791,7 @@
       return -1;
     }
 
-    if (n >= uw_inputs_len) {
+    if (n >= ctx->app->inputs_len) {
       uw_set_error(ctx, "For input name %s, index %d is out of range", name, n);
       return -1;
     }
@@ -804,7 +806,7 @@
 char *uw_get_input(uw_context ctx, int n) {
   if (n < 0)
     uw_error(ctx, FATAL, "Negative input index %d", n);
-  if (n >= uw_inputs_len)
+  if (n >= ctx->app->inputs_len)
     uw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
 
   switch (INP(ctx)[n].kind) {
@@ -828,7 +830,7 @@
 char *uw_get_optional_input(uw_context ctx, int n) {
   if (n < 0)
     uw_error(ctx, FATAL, "Negative input index %d", n);
-  if (n >= uw_inputs_len)
+  if (n >= ctx->app->inputs_len)
     uw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
 
   switch (INP(ctx)[n].kind) {
@@ -850,14 +852,14 @@
 }
 
 int uw_set_file_input(uw_context ctx, const char *name, uw_Basis_file f) {
-  int n = uw_input_num(name);
+  int n = ctx->app->input_num(name);
 
   if (n < 0) {
     uw_set_error(ctx, "Bad file input name %s", name);
     return -1;
   }
 
-  if (n >= uw_inputs_len) {
+  if (n >= ctx->app->inputs_len) {
     uw_set_error(ctx, "For file input name %s, index %d is out of range", name, n);
     return -1;
   }
@@ -870,7 +872,6 @@
 
 void *uw_malloc(uw_context ctx, size_t len);
 
-
 static void parents(input *inp) {
   printf("Stack: %p\n", inp);
   while (inp) {
@@ -902,7 +903,7 @@
 uw_Basis_file uw_get_file_input(uw_context ctx, int n) {
   if (n < 0)
     uw_error(ctx, FATAL, "Negative file input index %d", n);
-  if (n >= uw_inputs_len)
+  if (n >= ctx->app->inputs_len)
     uw_error(ctx, FATAL, "Out-of-bounds file input index %d", n);
 
   switch (INP(ctx)[n].kind) {
@@ -930,7 +931,7 @@
 void uw_enter_subform(uw_context ctx, int n) {
   if (n < 0)
     uw_error(ctx, FATAL, "Negative subform index %d", n);
-  if (n >= uw_inputs_len)
+  if (n >= ctx->app->inputs_len)
     uw_error(ctx, FATAL, "Out-of-bounds subform index %d", n);
 
   switch (INP(ctx)[n].kind) {
@@ -969,7 +970,7 @@
 
   if (n < 0)
     uw_error(ctx, FATAL, "Negative subforms index %d", n);
-  if (n >= uw_inputs_len)
+  if (n >= ctx->app->inputs_len)
     uw_error(ctx, FATAL, "Out-of-bounds subforms index %d", n);
 
   switch (INP(ctx)[n].kind) {
@@ -1028,8 +1029,8 @@
   ctx->script_header = s;
 }
 
-void uw_set_url_prefix(uw_context ctx, const char *s) {
-  ctx->url_prefix = s;
+const char *uw_get_url_prefix(uw_context ctx) {
+  return ctx->app->url_prefix;
 }
 
 void uw_set_needs_push(uw_context ctx, int n) {
@@ -1204,12 +1205,10 @@
   }
 }
 
-extern uw_Basis_string uw_cookie_sig(uw_context);
-
 const char *uw_Basis_get_settings(uw_context ctx, uw_unit u) {
   if (ctx->client == NULL) {
     if (ctx->needs_sig) {
-      char *sig = uw_cookie_sig(ctx);
+      char *sig = ctx->app->cookie_sig(ctx);
       char *r = uw_malloc(ctx, strlen(sig) + 8);
       sprintf(r, "sig=\"%s\";", sig);
       return r;
@@ -1217,14 +1216,14 @@
     else
       return "";
   } else {
-    char *sig = ctx->needs_sig ? uw_cookie_sig(ctx) : "";
-    char *r = uw_malloc(ctx, 59 + 3 * INTS_MAX + strlen(ctx->url_prefix)
+    char *sig = ctx->needs_sig ? ctx->app->cookie_sig(ctx) : "";
+    char *r = uw_malloc(ctx, 59 + 3 * INTS_MAX + strlen(ctx->app->url_prefix)
                         + (ctx->needs_sig ? strlen(sig) + 7 : 0));
     sprintf(r, "client_id=%u;client_pass=%d;url_prefix=\"%s\";timeout=%d;%s%s%slistener();",
             ctx->client->id,
             ctx->client->pass,
-            ctx->url_prefix,
-            ctx->timeout,
+            ctx->app->url_prefix,
+            ctx->app->timeout,
             ctx->needs_sig ? "sig=\"" : "",
             sig,
             ctx->needs_sig ? "\";" : "");
@@ -2766,9 +2765,6 @@
   return uw_unit_v;
 }
 
-int uw_db_commit(uw_context);
-int uw_db_rollback(uw_context);
-
 void uw_commit(uw_context ctx) {
   unsigned i;
 
@@ -2782,7 +2778,7 @@
       if (ctx->transactionals[i].commit)
         ctx->transactionals[i].commit(ctx->transactionals[i].data);
 
-  if (uw_db_commit(ctx))
+  if (ctx->app->db_commit(ctx))
     uw_error(ctx, FATAL, "Error running SQL COMMIT");
 
   for (i = 0; i < ctx->used_deltas; ++i) {
@@ -2855,7 +2851,7 @@
     if (ctx->transactionals[i].free)
       ctx->transactionals[i].free(ctx->transactionals[i].data);
 
-  return uw_db_rollback(ctx);
+  return ctx->app->db_rollback(ctx);
 }
 
 void uw_register_transactional(uw_context ctx, void *data, uw_callback commit, uw_callback rollback,
@@ -2874,16 +2870,14 @@
 
 // "Garbage collection"
 
-void uw_expunger(uw_context ctx, uw_Basis_client cli);
-
 static failure_kind uw_expunge(uw_context ctx, uw_Basis_client cli) {
   int r = setjmp(ctx->jmp_buf);
 
   if (r == 0) {
-    if (uw_db_begin(ctx))
+    if (ctx->app->db_begin(ctx))
       uw_error(ctx, FATAL, "Error running SQL BEGIN");
-    uw_expunger(ctx, cli);
-    if (uw_db_commit(ctx))
+    ctx->app->expunger(ctx, cli);
+    if (ctx->app->db_commit(ctx))
       uw_error(ctx, FATAL, "Error running SQL COMMIT");
   }
 
@@ -2894,7 +2888,7 @@
   client *c, *next, *prev = NULL;
   time_t cutoff;
 
-  cutoff = time(NULL) - uw_timeout;
+  cutoff = time(NULL) - ctx->app->timeout;
 
   pthread_mutex_lock(&clients_mutex);
 
@@ -2911,14 +2905,14 @@
       while (fk == UNLIMITED_RETRY) {
         fk = uw_expunge(ctx, c->id);
         if (fk == UNLIMITED_RETRY) {
-          uw_db_rollback(ctx);
+          ctx->app->db_rollback(ctx);
           printf("Unlimited retry during expunge: %s\n", uw_error_message(ctx));
         }
       }
       if (fk == SUCCESS)
         free_client(c);
       else {
-        uw_db_rollback(ctx);
+        ctx->app->db_rollback(ctx);
         fprintf(stderr, "Expunge blocked by error: %s\n", uw_error_message(ctx));
       }
     }
@@ -2930,25 +2924,20 @@
   pthread_mutex_unlock(&clients_mutex);
 }
 
-void uw_initializer(uw_context ctx);
-
 failure_kind uw_initialize(uw_context ctx) {
   int r = setjmp(ctx->jmp_buf);
 
   if (r == 0) {
-    if (uw_db_begin(ctx))
+    if (ctx->app->db_begin(ctx))
       uw_error(ctx, FATAL, "Error running SQL BEGIN");
-    uw_initializer(ctx);
-    if (uw_db_commit(ctx))
+    ctx->app->initializer(ctx);
+    if (ctx->app->db_commit(ctx))
       uw_error(ctx, FATAL, "Error running SQL COMMIT");
   }
 
   return r;
 }
 
-extern int uw_check_url(const char *);
-extern int uw_check_mime(const char *);
-
 static int url_bad(uw_Basis_string s) {
   for (; *s; ++s)
     if (!isgraph(*s))
@@ -2960,7 +2949,7 @@
 uw_Basis_string uw_Basis_bless(uw_context ctx, uw_Basis_string s) {
   if (url_bad(s))
     uw_error(ctx, FATAL, "Invalid URL %s", uw_Basis_htmlifyString(ctx, s));
-  if (uw_check_url(s))
+  if (ctx->app->check_url(s))
     return s;
   else
     uw_error(ctx, FATAL, "Disallowed URL %s", uw_Basis_htmlifyString(ctx, s));
@@ -2969,7 +2958,7 @@
 uw_Basis_string uw_Basis_checkUrl(uw_context ctx, uw_Basis_string s) {
   if (url_bad(s))
     return NULL;
-  if (uw_check_url(s))
+  if (ctx->app->check_url(s))
     return s;
   else
     return NULL;
@@ -2987,7 +2976,7 @@
   if (!mime_format(s))
     uw_error(ctx, FATAL, "MIME type \"%s\" contains invalid character", uw_Basis_htmlifyString(ctx, s));
 
-  if (uw_check_mime(s))
+  if (ctx->app->check_mime(s))
     return s;
   else
     uw_error(ctx, FATAL, "Disallowed MIME type %s", uw_Basis_htmlifyString(ctx, s));
@@ -2997,7 +2986,7 @@
   if (!mime_format(s))
     return NULL;
 
-  if (uw_check_mime(s))
+  if (ctx->app->check_mime(s))
     return s;
   else
     return NULL;
@@ -3020,7 +3009,7 @@
 }
 
 uw_Basis_string uw_Basis_sigString(uw_context ctx, uw_unit u) {
-  return uw_cookie_sig(ctx);
+  return ctx->app->cookie_sig(ctx);
 }
 
 uw_Basis_string uw_Basis_fileName(uw_context ctx, uw_Basis_file f) {
--- a/src/cjr_print.sml	Sat Dec 26 11:56:40 2009 -0500
+++ b/src/cjr_print.sml	Sun Dec 27 10:37:24 2009 -0500
@@ -2604,10 +2604,6 @@
                                         string scripts
                                     end,
                                     string "\");",
-                                    newline,
-                                    string "uw_set_url_prefix(ctx, \"",
-                                    string (Settings.getUrlPrefix ()),
-                                    string "\");",
                                     newline]),
                      string "uw_set_needs_push(ctx, ",
                      string (case side of
@@ -2706,7 +2702,7 @@
                          NONE cookies
 
         fun makeChecker (name, rules : Settings.rule list) =
-            box [string "int ",
+            box [string "static int ",
                  string name,
                  string "(const char *s) {",
                  newline,
@@ -2772,34 +2768,22 @@
                       newline,
                       string "int uw_db_begin(uw_context ctx) { return 0; };",
                       newline,
+                      string "void uw_db_close(uw_context ctx) { };",
+                      newline,
                       string "int uw_db_commit(uw_context ctx) { return 0; };",
                       newline,
                       string "int uw_db_rollback(uw_context ctx) { return 0; };"],
              newline,
              newline,
 
-             string "const char *uw_url_prefix = \"",
-             string (Settings.getUrlPrefix ()),
-             string "\";",
-             newline,
-             newline,
-
              string "static const char begin_xhtml[] = \"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\" ?>\\n<!DOCTYPE html PUBLIC \\\"-//W3C//DTD XHTML 1.0 Transitional//EN\\\" \\\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\\\">\\n<html xmlns=\\\"http://www.w3.org/1999/xhtml\\\" xml:lang=\\\"en\\\" lang=\\\"en\\\">\";",
              newline,
              newline,
 
              p_list_sep newline (fn x => x) pds,
              newline,
-             string "int uw_inputs_len = ",
-             string (Int.toString (SM.foldl Int.max 0 fnums + 1)),
-             string ";",
              newline,
-             string "int uw_timeout = ",
-             string (Int.toString (Settings.getTimeout ())),
-             string ";",
-             newline,
-             newline,
-             string "int uw_input_num(char *name) {",
+             string "static int uw_input_num(const char *name) {",
              newline,
              makeSwitch (fnums, 0),
              string "}",
@@ -2816,7 +2800,7 @@
              newline,
              string "extern int uw_hash_blocksize;",
              newline,
-             string "uw_Basis_string uw_cookie_sig(uw_context ctx) {",
+             string "static uw_Basis_string uw_cookie_sig(uw_context ctx) {",
              newline,
              box [string "uw_Basis_string r = uw_malloc(ctx, uw_hash_blocksize);",
                   newline,
@@ -2832,7 +2816,7 @@
              newline,
              newline,
 
-             string "void uw_handle(uw_context ctx, char *request) {",
+             string "static void uw_handle(uw_context ctx, char *request) {",
              newline,
              string "if (!strcmp(request, \"",
              string (OS.Path.joinDirFile {dir = Settings.getUrlPrefix (),
@@ -2856,7 +2840,7 @@
              newline,
 
              if hasDb then
-                 box [string "void uw_expunger(uw_context ctx, uw_Basis_client cli) {",
+                 box [string "static void uw_expunger(uw_context ctx, uw_Basis_client cli) {",
                       newline,
                       box [p_enamed env (!expunge),
                            string "(ctx, cli);",
@@ -2865,7 +2849,7 @@
                       newline,
                       newline,
 
-                      string "void uw_initializer(uw_context ctx) {",
+                      string "static void uw_initializer(uw_context ctx) {",
                       newline,
                       box [p_list_sep (box []) (fn e => box [p_exp env e,
                                                              string ";",
@@ -2876,10 +2860,22 @@
                       string "}",
                       newline]
              else
-                 box [string "void uw_expunger(uw_context ctx, uw_Basis_client cli) { };",
+                 box [string "static void uw_expunger(uw_context ctx, uw_Basis_client cli) { };",
                       newline,
-                      string "void uw_initializer(uw_context ctx) { };",
-                      newline]]
+                      string "static void uw_initializer(uw_context ctx) { };",
+                      newline],
+
+             string "uw_app uw_application = {",
+             p_list_sep (box [string ",", newline]) string
+                        [Int.toString (SM.foldl Int.max 0 fnums + 1),
+                         Int.toString (Settings.getTimeout ()),
+                         "\"" ^ Settings.getUrlPrefix () ^ "\"",
+                         "uw_client_init", "uw_initializer", "uw_expunger",
+                         "uw_db_init", "uw_db_begin", "uw_db_commit", "uw_db_rollback", "uw_db_close",
+                         "uw_handle",
+                         "uw_input_num", "uw_cookie_sig", "uw_check_url", "uw_check_mime"],
+             string "};",
+             newline]
     end
 
 fun p_sql env (ds, _) =
--- a/src/compiler.sml	Sat Dec 26 11:56:40 2009 -0500
+++ b/src/compiler.sml	Sun Dec 27 10:37:24 2009 -0500
@@ -1028,7 +1028,7 @@
         val urweb_o = clibFile "urweb.o"
         val memmem_o = clibFile "memmem.o"
 
-        val compile = "gcc " ^ Config.gccArgs ^ " -Wstrict-prototypes -Werror -O3 -fno-inline -I " ^ Config.includ
+        val compile = "gcc " ^ Config.gccArgs ^ " -Wimplicit -Werror -O3 -fno-inline -I " ^ Config.includ
                       ^ " -c " ^ cname ^ " -o " ^ oname
         val link = "gcc -Werror -O3 -lm -lmhash -pthread " ^ Config.gccArgs ^ " " ^ libs ^ " " ^ urweb_o ^ " " ^ oname
                    ^ " " ^ memmem_o ^ " " ^ #link proto ^ " -o " ^ ename
--- a/src/mysql.sml	Sat Dec 26 11:56:40 2009 -0500
+++ b/src/mysql.sml	Sun Dec 27 10:37:24 2009 -0500
@@ -379,7 +379,7 @@
              newline,
              newline,
 
-             string "void uw_client_init(void) {",
+             string "static void uw_client_init(void) {",
              newline,
              box [string "uw_sqlfmtInt = \"%lld%n\";",
                   newline,
@@ -502,7 +502,7 @@
              newline,
              newline,
              
-             string "void uw_db_init(uw_context ctx) {",
+             string "static void uw_db_init(uw_context ctx) {",
              newline,
              string "MYSQL *mysql = mysql_init(NULL);",
              newline,
@@ -554,7 +554,7 @@
              newline,
              newline,
 
-             string "void uw_db_close(uw_context ctx) {",
+             string "static void uw_db_close(uw_context ctx) {",
              newline,
              string "uw_conn *conn = uw_get_db(ctx);",
              newline,
@@ -573,7 +573,7 @@
              newline,
              newline,
 
-             string "int uw_db_begin(uw_context ctx) {",
+             string "static int uw_db_begin(uw_context ctx) {",
              newline,
              string "uw_conn *conn = uw_get_db(ctx);",
              newline,
@@ -586,7 +586,7 @@
              newline,
              newline,
 
-             string "int uw_db_commit(uw_context ctx) {",
+             string "static int uw_db_commit(uw_context ctx) {",
              newline,
              string "uw_conn *conn = uw_get_db(ctx);",
              newline,
@@ -596,7 +596,7 @@
              newline,
              newline,
 
-             string "int uw_db_rollback(uw_context ctx) {",
+             string "static int uw_db_rollback(uw_context ctx) {",
              newline,
              string "uw_conn *conn = uw_get_db(ctx);",
              newline,
--- a/src/postgres.sml	Sat Dec 26 11:56:40 2009 -0500
+++ b/src/postgres.sml	Sun Dec 27 10:37:24 2009 -0500
@@ -249,27 +249,7 @@
 
 fun init {dbstring, prepared = ss, tables, views, sequences} =
     box [if #persistent (currentProtocol ()) then
-             box [string "void uw_client_init(void) {",
-                  newline,
-                  box [string "uw_sqlfmtInt = \"%lld::int8%n\";",
-                       newline,
-                       string "uw_sqlfmtFloat = \"%g::float8%n\";",
-                       newline,
-                       string "uw_Estrings = 1;",
-                       newline,
-                       string "uw_sqlsuffixString = \"::text\";",
-                       newline,
-                       string "uw_sqlsuffixChar = \"::char\";",
-                       newline,
-                       string "uw_sqlsuffixBlob = \"::bytea\";",
-                       newline,
-                       string "uw_sqlfmtUint4 = \"%u::int4%n\";",
-                       newline],
-                  string "}",
-                  newline,
-                  newline,
-
-                  string "static void uw_db_validate(uw_context ctx) {",
+             box [string "static void uw_db_validate(uw_context ctx) {",
                   newline,
                   string "PGconn *conn = uw_get_db(ctx);",
                   newline,
@@ -380,93 +360,111 @@
 
                   string "}",
                   newline,
-                  newline,
-
-                  string "void uw_db_close(uw_context ctx) {",
-                  newline,
-                  string "PQfinish(uw_get_db(ctx));",
-                  newline,
-                  string "}",
-                  newline,
-                  newline,
-
-                  string "int uw_db_begin(uw_context ctx) {",
-                  newline,
-                  string "PGconn *conn = uw_get_db(ctx);",
-                  newline,
-                  string "PGresult *res = PQexec(conn, \"BEGIN ISOLATION LEVEL SERIALIZABLE\");",
-                  newline,
-                  newline,
-                  string "if (res == NULL) return 1;",
-                  newline,
-                  newline,
-                  string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
-                  box [string "PQclear(res);",
-                       newline,
-                       string "return 1;",
-                       newline],
-                  string "}",
-                  newline,
-                  string "return 0;",
-                  newline,
-                  string "}",
-                  newline,
-                  newline,
-
-                  string "int uw_db_commit(uw_context ctx) {",
-                  newline,
-                  string "PGconn *conn = uw_get_db(ctx);",
-                  newline,
-                  string "PGresult *res = PQexec(conn, \"COMMIT\");",
-                  newline,
-                  newline,
-                  string "if (res == NULL) return 1;",
-                  newline,
-                  newline,
-                  string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
-                  box [string "PQclear(res);",
-                       newline,
-                       string "return 1;",
-                       newline],
-                  string "}",
-                  newline,
-                  string "return 0;",
-                  newline,
-                  string "}",
-                  newline,
-                  newline,
-
-                  string "int uw_db_rollback(uw_context ctx) {",
-                  newline,
-                  string "PGconn *conn = uw_get_db(ctx);",
-                  newline,
-                  string "PGresult *res = PQexec(conn, \"ROLLBACK\");",
-                  newline,
-                  newline,
-                  string "if (res == NULL) return 1;",
-                  newline,
-                  newline,
-                  string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
-                  box [string "PQclear(res);",
-                       newline,
-                       string "return 1;",
-                       newline],
-                  string "}",
-                  newline,
-                  string "return 0;",
-                  newline,
-                  string "}",
-                  newline,
                   newline]
          else
              box [string "static void uw_db_validate(uw_context ctx) { }",
                   newline,
                   string "static void uw_db_prepare(uw_context ctx) { }"],
 
+         string "static void uw_client_init(void) {",
+         newline,
+         box [string "uw_sqlfmtInt = \"%lld::int8%n\";",
+              newline,
+              string "uw_sqlfmtFloat = \"%g::float8%n\";",
+              newline,
+              string "uw_Estrings = 1;",
+              newline,
+              string "uw_sqlsuffixString = \"::text\";",
+              newline,
+              string "uw_sqlsuffixChar = \"::char\";",
+              newline,
+              string "uw_sqlsuffixBlob = \"::bytea\";",
+              newline,
+              string "uw_sqlfmtUint4 = \"%u::int4%n\";",
+              newline],
+         string "}",
          newline,
          newline,
 
-         string "void uw_db_init(uw_context ctx) {",
+         string "static void uw_db_close(uw_context ctx) {",
+         newline,
+         string "PQfinish(uw_get_db(ctx));",
+         newline,
+         string "}",
+         newline,
+         newline,
+
+         string "static int uw_db_begin(uw_context ctx) {",
+         newline,
+         string "PGconn *conn = uw_get_db(ctx);",
+         newline,
+         string "PGresult *res = PQexec(conn, \"BEGIN ISOLATION LEVEL SERIALIZABLE\");",
+         newline,
+         newline,
+         string "if (res == NULL) return 1;",
+         newline,
+         newline,
+         string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
+         box [string "PQclear(res);",
+              newline,
+              string "return 1;",
+              newline],
+         string "}",
+         newline,
+         string "return 0;",
+         newline,
+         string "}",
+         newline,
+         newline,
+
+         string "static int uw_db_commit(uw_context ctx) {",
+         newline,
+         string "PGconn *conn = uw_get_db(ctx);",
+         newline,
+         string "PGresult *res = PQexec(conn, \"COMMIT\");",
+         newline,
+         newline,
+         string "if (res == NULL) return 1;",
+         newline,
+         newline,
+         string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
+         box [string "PQclear(res);",
+              newline,
+              string "return 1;",
+              newline],
+         string "}",
+         newline,
+         string "return 0;",
+         newline,
+         string "}",
+         newline,
+         newline,
+
+         string "static int uw_db_rollback(uw_context ctx) {",
+         newline,
+         string "PGconn *conn = uw_get_db(ctx);",
+         newline,
+         string "PGresult *res = PQexec(conn, \"ROLLBACK\");",
+         newline,
+         newline,
+         string "if (res == NULL) return 1;",
+         newline,
+         newline,
+         string "if (PQresultStatus(res) != PGRES_COMMAND_OK) {",
+         box [string "PQclear(res);",
+              newline,
+              string "return 1;",
+              newline],
+         string "}",
+         newline,
+         string "return 0;",
+         newline,
+         string "}",
+
+         newline,
+         newline,
+
+         string "static void uw_db_init(uw_context ctx) {",
          newline,
          string "PGconn *conn = PQconnectdb(\"",
          string (String.toString dbstring),
--- a/src/sqlite.sml	Sat Dec 26 11:56:40 2009 -0500
+++ b/src/sqlite.sml	Sun Dec 27 10:37:24 2009 -0500
@@ -156,7 +156,7 @@
              newline,
              newline,
 
-             string "void uw_client_init(void) {",
+             string "static void uw_client_init(void) {",
              newline,
              box [string "uw_sqlfmtInt = \"%lld%n\";",
                   newline,
@@ -256,7 +256,7 @@
              newline,
              newline,
              
-             string "void uw_db_init(uw_context ctx) {",
+             string "static void uw_db_init(uw_context ctx) {",
              newline,
              string "sqlite3 *sqlite;",
              newline,
@@ -283,7 +283,7 @@
              newline,
              newline,
 
-             string "void uw_db_close(uw_context ctx) {",
+             string "static void uw_db_close(uw_context ctx) {",
              newline,
              string "uw_conn *conn = uw_get_db(ctx);",
              newline,
@@ -302,7 +302,7 @@
              newline,
              newline,
 
-             string "int uw_db_begin(uw_context ctx) {",
+             string "static int uw_db_begin(uw_context ctx) {",
              newline,
              string "uw_conn *conn = uw_get_db(ctx);",
              newline,
@@ -321,7 +321,7 @@
              newline,
              string "}",
              newline,
-             string "int uw_db_commit(uw_context ctx) {",
+             string "static int uw_db_commit(uw_context ctx) {",
              newline,
              string "uw_conn *conn = uw_get_db(ctx);",
              newline,
@@ -341,7 +341,7 @@
              newline,
              newline,
 
-             string "int uw_db_rollback(uw_context ctx) {",
+             string "static int uw_db_rollback(uw_context ctx) {",
              newline,
              string "uw_conn *conn = uw_get_db(ctx);",
              newline,