]> mj.ucw.cz Git - leo.git/commitdiff
Introduced multiple data sources
authorMartin Mares <mj@ucw.cz>
Sun, 15 Jun 2014 21:30:47 +0000 (23:30 +0200)
committerMartin Mares <mj@ucw.cz>
Sun, 15 Jun 2014 21:30:47 +0000 (23:30 +0200)
leo.c
leo.h
map.c
map.cf
map.h
osm.c
osm.h
xml.c

diff --git a/leo.c b/leo.c
index dea6cde89a684e6c87e83871add89e09ec6d261a..5d02f680a90c6e84fe69954e5e32bbf73a2df4d7 100644 (file)
--- a/leo.c
+++ b/leo.c
@@ -118,37 +118,9 @@ int main(int argc UNUSED, char **argv)
 
   osm_init();
   styles_init();
-
-  msg(L_INFO, "Parsing OSM");
-  osm_xml_parse(map_xml_input);
-  if (debug_dump_source)
-    {
-      puts("=== Source data ===");
-      osm_dump();
-    }
-  osm_make_multipolygons();
-
-  msg(L_INFO, "Projecting");
-  osm_project(map_projection);
-  if (debug_dump_after_proj)
-    {
-      puts("=== Map after projection ===");
-      osm_dump();
-    }
-
+  map_load_styles();
+  map_load_sources();
   map_set_scale();
-  if (debug_dump_after_scaling)
-    {
-      puts("=== Map after scaling ===");
-      osm_dump();
-    }
-
-  struct css_sheet *ss = css_load(map_style_sheet);
-  if (debug_dump_css)
-    {
-      puts("=== Stylesheet ===");
-      css_dump(ss);
-    }
 
   struct svg *svg = svg_open(map_svg_output);
   if (!map_rotate)
@@ -162,32 +134,9 @@ int main(int argc UNUSED, char **argv)
       svg_set_attr_dimen(svg, "height", page_width);
     }
 
-  struct style_results r;
-  style_init(&r);
   sym_init();
 
-  msg(L_INFO, "Applying stylesheet");
-  for (uns i = OSM_TYPE_NODE; i <= OSM_TYPE_MULTIPOLYGON; i++)
-    CLIST_FOR_EACH(struct osm_object *, o, osm_obj_list[i])
-      {
-       if (debug_dump_styling)
-         {
-           puts("===============================");
-           osm_obj_dump(o);
-         }
-       if (!map_object_visible_p(o))
-         {
-           if (debug_dump_styling)
-             printf("--> invisible\n");
-           continue;
-         }
-       style_begin(&r, o);
-       css_apply(ss, &r);
-       if (debug_dump_styling)
-         style_dump(&r);
-       sym_from_style(o, &r, svg);
-       style_end(&r);
-      }
+  map_apply_styles(svg);
 
   if (map_clip)
     {
diff --git a/leo.h b/leo.h
index d5d8a56a4c0dd021c9f314b156767182d530d16c..f753065443776bfa89d2f6ee7dc22f50ad551992 100644 (file)
--- a/leo.h
+++ b/leo.h
@@ -16,7 +16,3 @@ typedef u32 color_t;                  // 0x00RRGGBB
 
 extern uns debug_dump_source, debug_dump_after_proj, debug_dump_after_scaling;
 extern uns debug_dump_multipolygons, debug_dump_css, debug_dump_styling, debug_dump_symbols;
-
-/* xml.c */
-
-void osm_xml_parse(const char *name);
diff --git a/map.c b/map.c
index 455d7aab19c3206c95b81ed79df881d4163790e3..2aab9e2c66cdbe2687187277e37bc99ccea5de97 100644 (file)
--- a/map.c
+++ b/map.c
@@ -8,6 +8,7 @@
 #include <ucw/conf.h>
 #include <ucw/gary.h>
 #include <ucw/mempool.h>
+#include <ucw/simple-lists.h>
 
 #include <stdio.h>
 #include <math.h>
@@ -15,6 +16,8 @@
 #include "leo.h"
 #include "osm.h"
 #include "map.h"
+#include "css.h"
+#include "sym.h"
 
 double map_min_x, map_min_y;
 double map_max_x, map_max_y;
@@ -24,6 +27,34 @@ char *map_xml_input;
 char *map_projection;
 char *map_style_sheet;
 char *map_svg_output;
+clist map_sources;
+
+static struct cf_section map_style_cf = {
+#define P(x) PTR_TO(struct data_source_style, x)
+  CF_TYPE(struct data_source_style),
+  CF_ITEMS {
+    CF_STRING("Name", P(name)),
+    CF_END
+  }
+#undef P
+};
+
+static const char * const map_formats[] = {
+  "invalid",
+  "osmxml",
+};
+
+static struct cf_section map_source_cf = {
+#define P(x) PTR_TO(struct data_source, x)
+  CF_TYPE(struct data_source),
+  CF_ITEMS {
+    CF_STRING("File", P(file)),
+    CF_LOOKUP("Format", P(format), map_formats),
+    CF_LIST("StyleSheet", P(styles), &map_style_cf),
+    CF_END
+  }
+#undef P
+};
 
 static struct cf_section map_cf = {
   CF_ITEMS {
@@ -36,9 +67,8 @@ static struct cf_section map_cf = {
     CF_UNS("Clip", &map_clip),
     CF_UNS("Rotate", &map_rotate),
     CF_UNS("DrawBorder", &map_draw_border),
-    CF_STRING("XMLInput", &map_xml_input),
+    CF_LIST("Source", &map_sources, &map_source_cf),
     CF_STRING("Projection", &map_projection),
-    CF_STRING("StyleSheet", &map_style_sheet),
     CF_STRING("SVGOutput", &map_svg_output),
     CF_END
   }
@@ -77,21 +107,34 @@ void map_set_scale(void)
   double pmin_y = INFINITY, pmax_y = -INFINITY;
   double rmin_x = INFINITY, rmax_x = -INFINITY;
   double rmin_y = INFINITY, rmax_y = -INFINITY;
-  CLIST_FOR_EACH(struct osm_node *, n, osm_obj_list[OSM_TYPE_NODE])
+  CLIST_FOR_EACH(struct data_source *, ds, map_sources)
     {
-      pmin_x = MIN(pmin_x, n->x);
-      pmax_x = MAX(pmax_x, n->x);
-      pmin_y = MIN(pmin_y, n->y);
-      pmax_y = MAX(pmax_y, n->y);
-      n->x = (n->x - map_min_x) * map_scale + page_offset_x;
-      n->y = page_height - page_offset_y - (n->y - map_min_y) * map_scale;
-      rmin_x = MIN(rmin_x, n->x);
-      rmax_x = MAX(rmax_x, n->x);
-      rmin_y = MIN(rmin_y, n->y);
-      rmax_y = MAX(rmax_y, n->y);
+      CLIST_FOR_EACH(struct osm_node *, n, ds->osm->obj_list[OSM_TYPE_NODE])
+       {
+         pmin_x = MIN(pmin_x, n->x);
+         pmax_x = MAX(pmax_x, n->x);
+         pmin_y = MIN(pmin_y, n->y);
+         pmax_y = MAX(pmax_y, n->y);
+         n->x = (n->x - map_min_x) * map_scale + page_offset_x;
+         n->y = page_height - page_offset_y - (n->y - map_min_y) * map_scale;
+         rmin_x = MIN(rmin_x, n->x);
+         rmax_x = MAX(rmax_x, n->x);
+         rmin_y = MIN(rmin_y, n->y);
+         rmax_y = MAX(rmax_y, n->y);
+       }
     }
   msg(L_INFO, "Bounds before scaling: [%.10g,%.10g] x [%.10g,%.10g]", pmin_x, pmax_x, pmin_y, pmax_y);
   msg(L_INFO, "Bounds after scaling: [%.10g,%.10g] x [%.10g,%.10g]", rmin_x, rmax_x, rmin_y, rmax_y);
+
+  if (debug_dump_after_scaling)
+    {
+      puts("=== Map after scaling ===");
+      CLIST_FOR_EACH(struct data_source *, ds, map_sources)
+       {
+         osm_this = ds->osm;
+         osm_dump();
+       }
+    }
 }
 
 bool map_object_visible_p(struct osm_object *o)
@@ -134,3 +177,108 @@ bool map_object_visible_p(struct osm_object *o)
       ASSERT(0);
     }
 }
+
+void map_load_styles(void)
+{
+  clist style_cache;
+  clist_init(&style_cache);
+
+  CLIST_FOR_EACH(struct data_source *, ds, map_sources)
+    {
+      CLIST_FOR_EACH(struct data_source_style *, ss, ds->styles)
+       {
+         CLIST_FOR_EACH(struct simp_node *, n, style_cache)
+           {
+             struct data_source_style *x = n->p;
+             if (!strcmp(x->name, ss->name))
+               {
+                 ss->css = x->css;
+                 break;
+               }
+           }
+         if (!ss->css)
+           {
+             msg(L_DEBUG, "Loading style sheet %s", ss->name);
+             ss->css = css_load(ss->name);
+
+             if (debug_dump_css)
+               {
+                 printf("=== Stylesheet %s ===", ss->name);
+                 css_dump(ss->css);
+               }
+
+             simp_append(cf_get_pool(), &style_cache)->p = ss;
+           }
+       }
+    }
+}
+
+static void map_load_source(struct data_source *ds)
+{
+  ds->osm = osm_init();
+
+  switch (ds->format)
+    {
+    case DATA_SOURCE_OSMXML:
+      msg(L_INFO, "Parsing %s as OSM XML", ds->file);
+      osm_xml_parse(ds->file);
+      if (debug_dump_source)
+       {
+         puts("=== Source data ===");
+         osm_dump();
+       }
+      osm_make_multipolygons();
+      break;
+    default:
+      die("Invalid data source format");
+    }
+
+  msg(L_INFO, "Projecting");
+  osm_project(map_projection);
+  if (debug_dump_after_proj)
+    {
+      puts("=== Map after projection ===");
+      osm_dump();
+    }
+}
+
+void map_load_sources(void)
+{
+  CLIST_FOR_EACH(struct data_source *, ds, map_sources)
+    map_load_source(ds);
+}
+
+void map_apply_styles(struct svg *svg)
+{
+  struct style_results r;
+  style_init(&r);
+
+  CLIST_FOR_EACH(struct data_source *, ds, map_sources)
+    {
+      msg(L_INFO, "Applying stylesheet on %s", ds->file);
+      for (uns i = OSM_TYPE_NODE; i <= OSM_TYPE_MULTIPOLYGON; i++)
+       CLIST_FOR_EACH(struct osm_object *, o, ds->osm->obj_list[i])
+         {
+           if (debug_dump_styling)
+             {
+               puts("===============================");
+               osm_obj_dump(o);
+             }
+           if (!map_object_visible_p(o))
+             {
+               if (debug_dump_styling)
+                 printf("--> invisible\n");
+               continue;
+             }
+           style_begin(&r, o);
+           CLIST_FOR_EACH(struct data_source_style *, ss, ds->styles)
+             css_apply(ss->css, &r);
+           if (debug_dump_styling)
+             style_dump(&r);
+           sym_from_style(o, &r, svg);
+           style_end(&r);
+         }
+    }
+
+  // FIXME: Ought to destroy the style_results
+}
diff --git a/map.cf b/map.cf
index a6ae531f17af7ca131a5d80431a7b567288cb0bc..c55a3e745f8f9df443d751584163314eee821034 100644 (file)
--- a/map.cf
+++ b/map.cf
@@ -1,6 +1,16 @@
 Map {
-       # Source file with XML map of OSM data
-       XMLInput dump.osm
+       # Data sources
+       Source {
+               # Input file
+               File dump.osm
+
+               # File format:
+               #       osmxml          OpenStreetMap XML
+               Format osmxml
+
+               # MapCSS stylesheet to apply (multiple style-sheets are allowed)
+               StyleSheet poskole.css
+       }
 
        # Projection of our map
        Projection "+proj=utm +zone=33 +ellps=WGS84"
@@ -28,9 +38,6 @@ Map {
        # Draw blue border around the requested rectangle
        DrawBorder 0
 
-       # MapCSS stylesheet to apply
-       StyleSheet poskole.css
-
        # Write SVG output here
        SVGOutput output.svg
 }
diff --git a/map.h b/map.h
index 127d30ac4cb8294a2b61d6ab730a379107a44e28..f031f7b65fdfd5d74c932bbb0f888fedbc3aa8c3 100644 (file)
--- a/map.h
+++ b/map.h
@@ -7,17 +7,43 @@
 #ifndef _LEO_MAP_H
 #define _LEO_MAP_H
 
+#include "svg.h"
+
 /* Map configuration */
 
 extern double map_min_x, map_min_y;
 extern double map_max_x, map_max_y;
 extern double page_width, page_height;
 extern uns map_clip, map_rotate, map_draw_border;
-extern char *map_xml_input;
 extern char *map_projection;
-extern char *map_style_sheet;
 extern char *map_svg_output;
 
+// Remember to update map.c:map_formats
+enum data_source_type {
+  DATA_SOURCE_INVALID,
+  DATA_SOURCE_OSMXML,
+};
+
+struct data_source {
+  cnode n;
+  // Configuration
+  char *file;
+  int format;
+  clist styles;                // of data_source_style
+  // Runtime
+  struct osm *osm;
+};
+
+struct data_source_style {
+  cnode n;
+  // Configuration
+  char *name;
+  // Runtime
+  struct css_sheet *css;
+};
+
+extern clist map_sources;
+
 /* Calculated by map_set_scale() */
 
 extern double map_scale;
@@ -27,4 +53,8 @@ extern double page_map_width, page_map_height;
 void map_set_scale(void);
 bool map_object_visible_p(struct osm_object *o);
 
+void map_load_styles(void);
+void map_load_sources(void);
+void map_apply_styles(struct svg *svg);
+
 #endif
diff --git a/osm.c b/osm.c
index 12529f60154f7224da17cd54a93a06e8f11332fb..325b592322287858a064f46faa10bd538b5fac96 100644 (file)
--- a/osm.c
+++ b/osm.c
@@ -17,7 +17,7 @@
 #include "leo.h"
 #include "osm.h"
 
-static struct mempool *osm_pool;
+struct osm *osm_this;
 
 /*** Generic objects ***/
 
@@ -26,8 +26,6 @@ struct osm_id_to_obj {
   struct osm_object *o;
 };
 
-clist osm_obj_list[OSM_TYPE_MAX];
-
 #define P(x) [OSM_TYPE_##x] = #x,
 const char * const osm_obj_type_names[OSM_TYPE_MAX] = {
   [OSM_TYPE_INVALID] = "Invalid",
@@ -44,13 +42,12 @@ const char * const osm_obj_type_names[OSM_TYPE_MAX] = {
 #define HASH_ATOMIC_TYPE osm_id_t
 #define HASH_WANT_FIND
 #define HASH_WANT_LOOKUP
-#define HASH_USE_POOL osm_pool
+#define HASH_TABLE_VARS struct mempool *pool;
+#define HASH_USE_POOL table->pool
 #define HASH_ZERO_FILL
 #define HASH_TABLE_DYNAMIC
 #include <ucw/hashtable.h>
 
-static struct osm_id_hash_table osm_id_hash[OSM_TYPE_MAX];
-
 osm_id_t osm_parse_id(const char *str)
 {
   uintmax_t id;
@@ -64,19 +61,19 @@ osm_id_t osm_parse_id(const char *str)
 struct osm_object *osm_obj_find_by_id(enum osm_object_type type, osm_id_t id)
 {
   ASSERT(type != OSM_TYPE_INVALID && type < OSM_TYPE_MAX);
-  struct osm_id_to_obj *ii = osm_id_hash_find(&osm_id_hash[type], id);
+  struct osm_id_to_obj *ii = osm_id_hash_find(osm_this->id_hash[type], id);
   return ii ? ii->o : NULL;
 }
 
 static void *osm_obj_new(enum osm_object_type type, osm_id_t id, uns size)
 {
   ASSERT(type != OSM_TYPE_INVALID && type < OSM_TYPE_MAX);
-  struct osm_id_to_obj *ii = osm_id_hash_lookup(&osm_id_hash[type], id);
+  struct osm_id_to_obj *ii = osm_id_hash_lookup(osm_this->id_hash[type], id);
   if (ii->o)
     die("Id %ju for type %s defined twice", (uintmax_t) id, osm_obj_type_names[type]);
 
-  struct osm_object *o = mp_alloc_zero(osm_pool, size);
-  clist_add_tail(&osm_obj_list[type], &o->n);
+  struct osm_object *o = mp_alloc_zero(osm_this->pool, size);
+  clist_add_tail(&osm_this->obj_list[type], &o->n);
   o->type = type;
   o->id = id;
   clist_init(&o->tags);
@@ -89,12 +86,12 @@ void osm_ref_add(struct osm_object *parent, clist *list, struct osm_object *son,
 {
   ASSERT(parent != son);
 
-  struct osm_ref *ref = mp_alloc(osm_pool, sizeof(*ref));
+  struct osm_ref *ref = mp_alloc(osm_this->pool, sizeof(*ref));
   ref->o = son;
   ref->role = role;
   clist_add_tail(list, &ref->n);
 
-  struct osm_ref *backref = mp_alloc(osm_pool, sizeof(*ref));
+  struct osm_ref *backref = mp_alloc(osm_this->pool, sizeof(*ref));
   backref->o = parent;
   backref->role = 0;
   clist_add_tail(&son->backrefs, &backref->n);
@@ -135,7 +132,7 @@ struct dict osm_key_dict, osm_value_dict;
 
 void osm_obj_add_tag_raw(struct osm_object *o, osm_key_t key, osm_val_t val)
 {
-  struct osm_tag *t = mp_alloc(osm_pool, sizeof(*t));
+  struct osm_tag *t = mp_alloc(osm_this->pool, sizeof(*t));
   t->key = key;
   t->val = val;
   clist_add_tail(&o->tags, &t->n);
@@ -199,7 +196,7 @@ void osm_node_dump(struct osm_node *n)
 void osm_node_dump_all(void)
 {
   printf("Known nodes:\n");
-  CLIST_FOR_EACH(struct osm_node *, n, osm_obj_list[OSM_TYPE_NODE])
+  CLIST_FOR_EACH(struct osm_node *, n, osm_this->obj_list[OSM_TYPE_NODE])
     osm_node_dump(n);
   putchar('\n');
 }
@@ -233,7 +230,7 @@ void osm_way_dump(struct osm_way *w)
 void osm_way_dump_all(void)
 {
   printf("Known ways:\n");
-  CLIST_FOR_EACH(struct osm_way *, w, osm_obj_list[OSM_TYPE_WAY])
+  CLIST_FOR_EACH(struct osm_way *, w, osm_this->obj_list[OSM_TYPE_WAY])
     osm_way_dump(w);
   putchar('\n');
 }
@@ -273,7 +270,7 @@ void osm_relation_dump(struct osm_relation *r)
 void osm_relation_dump_all(void)
 {
   printf("Known relations:\n");
-  CLIST_FOR_EACH(struct osm_relation *, r, osm_obj_list[OSM_TYPE_RELATION])
+  CLIST_FOR_EACH(struct osm_relation *, r, osm_this->obj_list[OSM_TYPE_RELATION])
     osm_relation_dump(r);
   putchar('\n');
 }
@@ -296,7 +293,7 @@ void osm_multipolygon_dump(struct osm_multipolygon *m)
 void osm_multipolygon_dump_all(void)
 {
   printf("Known multipolygons:\n");
-  CLIST_FOR_EACH(struct osm_multipolygon *, r, osm_obj_list[OSM_TYPE_MULTIPOLYGON])
+  CLIST_FOR_EACH(struct osm_multipolygon *, r, osm_this->obj_list[OSM_TYPE_MULTIPOLYGON])
     osm_multipolygon_dump(r);
   putchar('\n');
 }
@@ -378,7 +375,7 @@ static void mpg_walk_boundary(struct osm_multipolygon *m, bool inner)
     {
       if (!v->visited)
        {
-         struct osm_mpg_boundary *b = mp_alloc(osm_pool, sizeof(*b));
+         struct osm_mpg_boundary *b = mp_alloc(osm_this->pool, sizeof(*b));
          clist_add_tail(&m->boundaries, &b->n);
          b->inner = inner;
          clist_init(&b->nodes);
@@ -387,7 +384,7 @@ static void mpg_walk_boundary(struct osm_multipolygon *m, bool inner)
          while (!w->visited)
            {
              w->visited = 1;
-             struct osm_ref *f = mp_alloc(osm_pool, sizeof(*f));
+             struct osm_ref *f = mp_alloc(osm_this->pool, sizeof(*f));
              clist_add_tail(&b->nodes, &f->n);
              f->o = w->o;
              f->role = 0;
@@ -417,7 +414,7 @@ static void mpg_walk_boundary(struct osm_multipolygon *m, bool inner)
          if (w != v)
            osm_obj_warn(&mpg_current->o, "Boundary not closed at node %ju", (uintmax_t) w->o->id);
 
-         struct osm_ref *f = mp_alloc(osm_pool, sizeof(*f));
+         struct osm_ref *f = mp_alloc(osm_this->pool, sizeof(*f));
          clist_add_tail(&b->nodes, &f->n);
          f->o = v->o;
          f->role = 0;
@@ -470,7 +467,7 @@ void osm_make_multipolygons(void)
 {
   uns mpg_cnt = 0;
 
-  CLIST_FOR_EACH(struct osm_relation *, r, osm_obj_list[OSM_TYPE_RELATION])
+  CLIST_FOR_EACH(struct osm_relation *, r, osm_this->obj_list[OSM_TYPE_RELATION])
     if (osm_obj_find_tag(&r->o, KEY_TYPE) == VALUE_MULTIPOLYGON)
       {
        osm_multipolygon_make(r);
@@ -489,7 +486,7 @@ void osm_project(const char *spec)
   if (!osm_pj)
     die("Unable to initialize projection %s", spec);
 
-  CLIST_FOR_EACH(struct osm_node *, n, osm_obj_list[OSM_TYPE_NODE])
+  CLIST_FOR_EACH(struct osm_node *, n, osm_this->obj_list[OSM_TYPE_NODE])
     {
       projUV p;
       p.u = n->x * DEG_TO_RAD;
@@ -561,15 +558,24 @@ bool osm_obj_center(struct osm_object *o, double *xp, double *yp)
 
 /*** Globals ***/
 
-void osm_init(void)
+struct osm *osm_init(void)
 {
-  osm_pool = mp_new(65536);
+  struct mempool *mp = mp_new(65536);
+  struct osm *osm = mp_alloc_zero(mp, sizeof(*osm));
+  osm->pool = mp;
+
   for (int i=0; i<OSM_TYPE_MAX; i++)
     {
-      clist_init(&osm_obj_list[i]);
-      osm_id_hash_init(&osm_id_hash[i]);
+      clist_init(&osm->obj_list[i]);
+      osm->id_hash[i] = mp_alloc(mp, sizeof(struct osm_id_hash_table));
+      osm->id_hash[i]->pool = mp;
+      osm_id_hash_init(osm->id_hash[i]);
     }
-  osm_tag_init();
+
+  if (!osm_this)
+    osm_tag_init();
+  osm_this = osm;
+  return osm;
 }
 
 void osm_dump(void)
diff --git a/osm.h b/osm.h
index 80d9fd2df47b33470039f8fe87876813c6e24d67..ba240c63078655ad49b84bd054e9c3c5bdd41b02 100644 (file)
--- a/osm.h
+++ b/osm.h
@@ -77,10 +77,16 @@ struct osm_mpg_boundary {
   clist nodes;                 // List of osm_ref's (without back-references, since the boundary is not a regular object)
 };
 
-void osm_init(void);
-void osm_dump(void);
+struct osm {
+  struct mempool *pool;
+  clist obj_list[OSM_TYPE_MAX];
+  struct osm_id_hash_table *id_hash[OSM_TYPE_MAX];
+};
+
+extern struct osm *osm_this;
 
-extern clist osm_obj_list[OSM_TYPE_MAX];
+struct osm *osm_init(void);
+void osm_dump(void);
 
 osm_id_t osm_parse_id(const char *str);
 struct osm_object *osm_obj_find_by_id(enum osm_object_type type, osm_id_t id);
@@ -161,4 +167,8 @@ enum value_names {
 #undef P
 };
 
+/* xml.c */
+
+void osm_xml_parse(const char *name);
+
 #endif
diff --git a/xml.c b/xml.c
index 98f2f8b003c87599e8164adc5164e2904cdab0f8..53caec588ea6c617575e9fb4982d160dac5e5c6f 100644 (file)
--- a/xml.c
+++ b/xml.c
@@ -168,8 +168,6 @@ static void h_etag(struct xml_context *ctx)
 
 void osm_xml_parse(const char *name)
 {
-  msg(L_INFO, "Loading %s", name);
-
   struct xml_context ctx;
   xml_init(&ctx);
   ctx.h_warn = ctx.h_error = ctx.h_fatal = h_error;