diff src/c/urweb.c @ 756:8ce31c052dce

Subforms
author Adam Chlipala <adamc@hcoop.net>
date Tue, 28 Apr 2009 17:26:53 -0400
parents ee2feab275db
children 67cd8326f743
line wrap: on
line diff
--- a/src/c/urweb.c	Tue Apr 28 15:15:21 2009 -0400
+++ b/src/c/urweb.c	Tue Apr 28 17:26:53 2009 -0400
@@ -284,14 +284,17 @@
 } delta;
 
 typedef enum {
-  UNSET, NORMAL, FIL
+  UNSET, NORMAL, FIL, SUBFORM
 } input_kind;
 
-typedef struct {
+typedef struct input {
   input_kind kind;
   union {
     char *normal;
     uw_Basis_file file;
+    struct {
+      struct input *fields, *prev;
+    } subform;
   } data;
 } input;
 
@@ -299,7 +302,8 @@
   char *headers, *headers_end;
 
   buf outHeaders, page, heap, script;
-  input *inputs;
+  input *inputs, *subinputs, *cur_inputs;
+  size_t n_subinputs, used_subinputs;
 
   int source_count;
 
@@ -339,6 +343,9 @@
   ctx->script.start[0] = 0;
 
   ctx->inputs = calloc(uw_inputs_len, sizeof(input));
+  ctx->cur_inputs = NULL;
+  ctx->subinputs = malloc(0);
+  ctx->n_subinputs = ctx->used_subinputs = 0;
 
   ctx->db = NULL;
 
@@ -383,6 +390,7 @@
   buf_free(&ctx->page);
   buf_free(&ctx->heap);
   free(ctx->inputs);
+  free(ctx->subinputs);
   free(ctx->cleanup);
 
   for (i = 0; i < ctx->n_deltas; ++i)
@@ -392,6 +400,8 @@
 }
 
 void uw_reset_keep_error_message(uw_context ctx) {
+  size_t i;
+
   buf_reset(&ctx->outHeaders);
   buf_reset(&ctx->script);
   ctx->script.start[0] = 0;
@@ -412,6 +422,9 @@
 void uw_reset(uw_context ctx) {
   uw_reset_keep_request(ctx);
   memset(ctx->inputs, 0, uw_inputs_len * sizeof(input));
+  memset(ctx->subinputs, 0, ctx->n_subinputs * sizeof(input));
+  ctx->cur_inputs = NULL;
+  ctx->used_subinputs = 0;
 }
 
 void uw_db_init(uw_context);
@@ -564,17 +577,72 @@
 
 extern int uw_input_num(const char*);
 
+#define INP(ctx) (ctx->cur_inputs ? ctx->cur_inputs : ctx->inputs)
+
 void uw_set_input(uw_context ctx, const char *name, char *value) {
-  int n = uw_input_num(name);
-
-  if (n < 0)
-    uw_error(ctx, FATAL, "Bad input name %s", name);
-
-  if (n >= uw_inputs_len)
-    uw_error(ctx, FATAL, "For input name %s, index %d is out of range", name, n);
-
-  ctx->inputs[n].kind = NORMAL;
-  ctx->inputs[n].data.normal = value;
+  if (!strcasecmp(name, ".b")) {
+    size_t i;
+    int n = uw_input_num(value);
+    input *inps;
+
+    if (n < 0)
+      uw_error(ctx, FATAL, "Bad subform name %s", value);
+
+    if (n >= uw_inputs_len)
+      uw_error(ctx, FATAL, "For subform name %s, index %d is out of range", value, n);
+
+    if (ctx->used_subinputs + uw_inputs_len >= ctx->n_subinputs) {
+      input *new_subinputs = realloc(ctx->subinputs, sizeof(input) * (ctx->used_subinputs + uw_inputs_len));
+      size_t offset = new_subinputs - ctx->subinputs;
+
+      for (i = 0; i < ctx->used_subinputs; ++i)
+        if (new_subinputs[i].kind == SUBFORM) {
+          new_subinputs[i].data.subform.fields += offset;
+          if (new_subinputs[i].data.subform.prev != NULL)
+            new_subinputs[i].data.subform.prev += offset;
+        }
+
+      for (i = 0; i < uw_inputs_len; ++i)
+        if (ctx->inputs[i].kind == SUBFORM) {
+          ctx->inputs[i].data.subform.fields += offset;
+          if (ctx->inputs[i].data.subform.prev != NULL)
+            ctx->inputs[i].data.subform.prev += offset;
+        }
+
+      if (ctx->cur_inputs != NULL)
+        ctx->cur_inputs += offset;
+
+      ctx->n_subinputs = ctx->used_subinputs + uw_inputs_len;
+      ctx->subinputs = new_subinputs;
+    }
+
+    ctx->inputs[n].kind = SUBFORM;
+    ctx->inputs[n].data.subform.prev = ctx->cur_inputs;
+    ctx->cur_inputs = ctx->inputs[n].data.subform.fields = &ctx->subinputs[ctx->used_subinputs];
+
+    for (i = 0; i < uw_inputs_len; ++i)
+      ctx->subinputs[ctx->used_subinputs++].kind = UNUSED;
+  } else if (!strcasecmp(name, ".e")) {
+    input *tmp;
+
+    if (ctx->cur_inputs == NULL)
+      uw_error(ctx, FATAL, "Unmatched subform closer");
+
+    tmp = ctx->cur_inputs;
+    ctx->cur_inputs = tmp->data.subform.prev;
+    tmp->data.subform.prev = NULL;
+  } else {
+    int n = uw_input_num(name);
+
+    if (n < 0)
+      uw_error(ctx, FATAL, "Bad input name %s", name);
+
+    if (n >= uw_inputs_len)
+      uw_error(ctx, FATAL, "For input name %s, index %d is out of range", name, n);
+
+    INP(ctx)[n].kind = NORMAL;
+    INP(ctx)[n].data.normal = value;
+  }
 }
 
 char *uw_get_input(uw_context ctx, int n) {
@@ -583,13 +651,15 @@
   if (n >= uw_inputs_len)
     uw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
 
-  switch (ctx->inputs[n].kind) {
+  switch (INP(ctx)[n].kind) {
   case UNSET:
     return NULL;
   case FIL:
     uw_error(ctx, FATAL, "Tried to read a file form input as normal");
+  case SUBFORM:
+    uw_error(ctx, FATAL, "Tried to read a subform form input as normal");
   case NORMAL:
-    return ctx->inputs[n].data.normal;
+    return INP(ctx)[n].data.normal;
   default:
     uw_error(ctx, FATAL, "Impossible input kind");
   }
@@ -601,13 +671,15 @@
   if (n >= uw_inputs_len)
     uw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
 
-  switch (ctx->inputs[n].kind) {
+  switch (INP(ctx)[n].kind) {
   case UNSET:
     return "";
   case FIL:
     uw_error(ctx, FATAL, "Tried to read a file form input as normal");
+  case SUBFORM:
+    uw_error(ctx, FATAL, "Tried to read a subform form input as normal");
   case NORMAL:
-    return ctx->inputs[n].data.normal;
+    return INP(ctx)[n].data.normal;
   default:
     uw_error(ctx, FATAL, "Impossible input kind");
   }
@@ -634,7 +706,7 @@
   if (n >= uw_inputs_len)
     uw_error(ctx, FATAL, "Out-of-bounds file input index %d", n);
 
-  switch (ctx->inputs[n].kind) {
+  switch (INP(ctx)[n].kind) {
   case UNSET:
     {
       char *data = uw_malloc(ctx, 0);
@@ -642,14 +714,49 @@
       return f;
     }
   case FIL:
-    return ctx->inputs[n].data.file;
+    return INP(ctx)[n].data.file;
   case NORMAL:
     uw_error(ctx, FATAL, "Tried to read a normal form input as files");
+  case SUBFORM:
+    uw_error(ctx, FATAL, "Tried to read a subform form input as files");
   default:
     uw_error(ctx, FATAL, "Impossible input kind");
   }
 }
 
+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)
+    uw_error(ctx, FATAL, "Out-of-bounds subform index %d", n);
+
+  switch (INP(ctx)[n].kind) {
+  case UNSET:
+    uw_error(ctx, FATAL, "Missing subform");
+  case FIL:
+    uw_error(ctx, FATAL, "Tried to read a file form input as subform");
+  case NORMAL:
+    uw_error(ctx, FATAL, "Tried to read a normal form input as subform");
+  case SUBFORM:
+    INP(ctx)[n].data.subform.prev = ctx->cur_inputs;
+    ctx->cur_inputs = INP(ctx)[n].data.subform.fields;
+    return;
+  default:
+    uw_error(ctx, FATAL, "Impossible input kind");
+  }
+}
+
+void uw_leave_subform(uw_context ctx) {
+  input *tmp;
+
+  if (ctx->cur_inputs == NULL)
+    uw_error(ctx, FATAL, "Unmatched uw_leave_subform");
+
+  tmp = ctx->cur_inputs;
+  ctx->cur_inputs = tmp->data.subform.prev;
+  tmp->data.subform.prev = NULL;
+}
+
 void uw_set_script_header(uw_context ctx, const char *s) {
   ctx->script_header = s;
 }