]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
wizard predefined muxes dialog
authorJaroslav Kysela <perex@perex.cz>
Tue, 19 Jan 2016 10:33:51 +0000 (11:33 +0100)
committerJaroslav Kysela <perex@perex.cz>
Tue, 19 Jan 2016 14:14:15 +0000 (15:14 +0100)
src/api/api_wizard.c
src/input/mpegts/mpegts_dvb.h
src/input/mpegts/mpegts_network_dvb.c
src/webui/static/app/wizard.js
src/wizard.c
src/wizard.h

index 785fd3dae22bbf16eaf58dbe485eb8dfffe0dcd8..e68fbfebdc6c20f51dc6a5332678a45ff0b62b62 100644 (file)
@@ -86,8 +86,8 @@ api_wizard_init ( void )
     { "wizard/login/save",   ACCESS_ADMIN, wizard_idnode_save_simple, wizard_login },
     { "wizard/network/load", ACCESS_ADMIN, wizard_idnode_load_simple, wizard_network },
     { "wizard/network/save", ACCESS_ADMIN, wizard_idnode_save_simple, wizard_network },
-    { "wizard/input/load",   ACCESS_ADMIN, wizard_idnode_load_simple, wizard_input },
-    { "wizard/input/save",   ACCESS_ADMIN, wizard_idnode_save_simple, wizard_input },
+    { "wizard/muxes/load",   ACCESS_ADMIN, wizard_idnode_load_simple, wizard_muxes },
+    { "wizard/muxes/save",   ACCESS_ADMIN, wizard_idnode_save_simple, wizard_muxes },
     { "wizard/status/load",  ACCESS_ADMIN, wizard_idnode_load_simple, wizard_status },
     { "wizard/status/save",  ACCESS_ADMIN, wizard_idnode_save_simple, wizard_status },
     { "wizard/mapping/load", ACCESS_ADMIN, wizard_idnode_load_simple, wizard_mapping },
index a2f2ae304cb8f5902356807786cfad847c76e38c..0258e7cf2ee1e41d4bb4fb70516f9d04510b8a76 100644 (file)
@@ -75,6 +75,10 @@ dvb_mux_t *dvb_network_find_mux
 const idclass_t *dvb_network_mux_class(mpegts_network_t *mn);
 int dvb_network_get_orbital_pos(mpegts_network_t *mn);
 
+void dvb_network_scanfile_set ( dvb_network_t *ln, const char *id );
+
+htsmsg_t * dvb_network_class_scanfile_list ( void *o, const char *lang );
+
 /*
  *
  */
index cbec7c802d0f426dcfcf5fa3972d2971e2a52b7d..53104da3b0b986f27891e000fc236042cf9f6917 100644 (file)
@@ -59,19 +59,19 @@ dvb_network_class_scanfile_get ( void *o )
   static const char *s = NULL;
   return &s;
 }
-static int
-dvb_network_class_scanfile_set ( void *o, const void *s )
+
+void
+dvb_network_scanfile_set ( dvb_network_t *ln, const char *id )
 {
-  dvb_network_t *ln = o;
   dvb_mux_conf_t *dmc;
   scanfile_network_t *sfn;
   dvb_mux_t *mm;
 
   /* Find */
-  if (!s)
-    return 0;
-  if (!(sfn = scanfile_find(s)))
-    return 0;
+  if (!id)
+    return;
+  if (!(sfn = scanfile_find(id)))
+    return;
 
   /* Set satellite position */
   if (sfn->sfn_satpos != INT_MAX && ln->mn_satpos == INT_MAX)
@@ -80,7 +80,7 @@ dvb_network_class_scanfile_set ( void *o, const void *s )
   /* Create */
   LIST_FOREACH(dmc, &sfn->sfn_muxes, dmc_link) {
     if (!(mm = dvb_network_find_mux(ln, dmc, MPEGTS_ONID_NONE, MPEGTS_TSID_NONE))) {
-      mm = dvb_mux_create0(o, MPEGTS_ONID_NONE, MPEGTS_TSID_NONE,
+      mm = dvb_mux_create0(ln, MPEGTS_ONID_NONE, MPEGTS_TSID_NONE,
                            dmc, NULL, NULL);
       if (mm)
         mm->mm_config_save((mpegts_mux_t *)mm);
@@ -99,17 +99,51 @@ dvb_network_class_scanfile_set ( void *o, const void *s )
       }
     }
   }
+  return;
+}
+
+static int
+dvb_network_class_scanfile_set ( void *o, const void *s )
+{
+  dvb_network_scanfile_set(o, s);
   return 0;
 }
-static htsmsg_t *
-dvb_network_class_scanfile_list ( void *o, const char *lang, const char *type )
+
+htsmsg_t *
+dvb_network_class_scanfile_list ( void *o, const char *lang )
 {
   dvb_network_t *ln = o;
+  const char *type;
+  const idclass_t *clazz;
+
+  if (ln == NULL)
+    return NULL;
   htsmsg_t *e, *m = htsmsg_create_map();
   htsmsg_add_str(m, "type", "api");
   htsmsg_add_str(m, "uri", "dvb/scanfile/list");
   htsmsg_add_str(m, "stype", "none");
   e = htsmsg_create_map();
+  clazz = ln->mn_id.in_class;
+  if (clazz == &dvb_network_dvbt_class)
+    type = "dvbt";
+  else if (clazz == &dvb_network_dvbc_class)
+    type = "dvbc";
+  else if (clazz == &dvb_network_dvbs_class)
+    type = "dvbs";
+  else if (clazz == &dvb_network_atsc_t_class)
+    type = "atsc-t";
+  else if (clazz == &dvb_network_atsc_c_class)
+    type = "atsc-c";
+  else if (clazz == &dvb_network_isdb_t_class)
+    type = "isdb-t";
+  else if (clazz == &dvb_network_isdb_c_class)
+    type = "isdb-c";
+  else if (clazz == &dvb_network_isdb_s_class)
+    type = "isdb-s";
+  else if (clazz == &dvb_network_dab_class)
+    type = "dab";
+  else
+    type = "unk";
   htsmsg_add_str(e, "type", type);
   if (ln && ln->mn_satpos != INT_MAX)
     htsmsg_add_s32(e, "satpos", ln->mn_satpos);
@@ -117,52 +151,6 @@ dvb_network_class_scanfile_list ( void *o, const char *lang, const char *type )
   return m;
 }
 
-static htsmsg_t *
-dvb_network_dvbt_class_scanfile_list ( void *o, const char *lang )
-{
-  return dvb_network_class_scanfile_list(o, lang, "dvbt");
-}
-static htsmsg_t *
-dvb_network_dvbc_class_scanfile_list ( void *o, const char *lang )
-{
-  return dvb_network_class_scanfile_list(o, lang, "dvbc");
-}
-static htsmsg_t *
-dvb_network_dvbs_class_scanfile_list ( void *o, const char *lang )
-{
-  return dvb_network_class_scanfile_list(o, lang, "dvbs");
-}
-static htsmsg_t *
-dvb_network_atsc_t_class_scanfile_list ( void *o, const char *lang )
-{
-  return dvb_network_class_scanfile_list(o, lang, "atsc-t");
-}
-static htsmsg_t *
-dvb_network_atsc_c_class_scanfile_list ( void *o, const char *lang )
-{
-  return dvb_network_class_scanfile_list(o, lang, "atsc-c");
-}
-static htsmsg_t *
-dvb_network_isdb_t_class_scanfile_list ( void *o, const char *lang )
-{
-  return dvb_network_class_scanfile_list(o, lang, "isdb-t");
-}
-static htsmsg_t *
-dvb_network_isdb_c_class_scanfile_list ( void *o, const char *lang )
-{
-  return dvb_network_class_scanfile_list(o, lang, "isdb-c");
-}
-static htsmsg_t *
-dvb_network_isdb_s_class_scanfile_list ( void *o, const char *lang )
-{
-  return dvb_network_class_scanfile_list(o, lang, "isdb-s");
-}
-static htsmsg_t *
-dvb_network_dab_class_scanfile_list ( void *o, const char *lang )
-{
-  return dvb_network_class_scanfile_list(o, lang, "dab");
-}
-
 static const void *
 dvb_network_class_orbital_pos_get ( void *o )
 {
@@ -235,7 +223,7 @@ const idclass_t dvb_network_dvbt_class =
                      "may cause scanning to take longer than usual."),
       .set      = dvb_network_class_scanfile_set,
       .get      = dvb_network_class_scanfile_get,
-      .list     = dvb_network_dvbt_class_scanfile_list,
+      .list     = dvb_network_class_scanfile_list,
       .opts     = PO_NOSAVE,
     },
     {}
@@ -257,7 +245,7 @@ const idclass_t dvb_network_dvbc_class =
                      "may cause scanning to take longer than usual."),
       .set      = dvb_network_class_scanfile_set,
       .get      = dvb_network_class_scanfile_get,
-      .list     = dvb_network_dvbc_class_scanfile_list,
+      .list     = dvb_network_class_scanfile_list,
       .opts     = PO_NOSAVE,
     },
     {}
@@ -279,7 +267,7 @@ const idclass_t dvb_network_dvbs_class =
                      "may cause scanning to take longer than usual."),
       .set      = dvb_network_class_scanfile_set,
       .get      = dvb_network_class_scanfile_get,
-      .list     = dvb_network_dvbs_class_scanfile_list,
+      .list     = dvb_network_class_scanfile_list,
       .opts     = PO_NOSAVE,
     },
     {
@@ -311,7 +299,7 @@ const idclass_t dvb_network_atsc_t_class =
                      "may cause scanning to take longer than usual."),
       .set      = dvb_network_class_scanfile_set,
       .get      = dvb_network_class_scanfile_get,
-      .list     = dvb_network_atsc_t_class_scanfile_list,
+      .list     = dvb_network_class_scanfile_list,
       .opts     = PO_NOSAVE,
     },
     {}
@@ -333,7 +321,7 @@ const idclass_t dvb_network_atsc_c_class =
                      "may cause scanning to take longer than usual."),
       .set      = dvb_network_class_scanfile_set,
       .get      = dvb_network_class_scanfile_get,
-      .list     = dvb_network_atsc_c_class_scanfile_list,
+      .list     = dvb_network_class_scanfile_list,
       .opts     = PO_NOSAVE,
     },
     {}
@@ -355,7 +343,7 @@ const idclass_t dvb_network_isdb_t_class =
                      "may cause scanning to take longer than usual."),
       .set      = dvb_network_class_scanfile_set,
       .get      = dvb_network_class_scanfile_get,
-      .list     = dvb_network_isdb_t_class_scanfile_list,
+      .list     = dvb_network_class_scanfile_list,
       .opts     = PO_NOSAVE,
     },
     {}
@@ -377,7 +365,7 @@ const idclass_t dvb_network_isdb_c_class =
                      "may cause scanning to take longer than usual."),
       .set      = dvb_network_class_scanfile_set,
       .get      = dvb_network_class_scanfile_get,
-      .list     = dvb_network_isdb_c_class_scanfile_list,
+      .list     = dvb_network_class_scanfile_list,
       .opts     = PO_NOSAVE,
     },
     {}
@@ -399,7 +387,7 @@ const idclass_t dvb_network_isdb_s_class =
                      "may cause scanning to take longer than usual."),
       .set      = dvb_network_class_scanfile_set,
       .get      = dvb_network_class_scanfile_get,
-      .list     = dvb_network_isdb_s_class_scanfile_list,
+      .list     = dvb_network_class_scanfile_list,
       .opts     = PO_NOSAVE,
     },
     {}
@@ -421,7 +409,7 @@ const idclass_t dvb_network_dab_class =
                      "may cause scanning to take longer than usual."),
       .set      = dvb_network_class_scanfile_set,
       .get      = dvb_network_class_scanfile_get,
-      .list     = dvb_network_dab_class_scanfile_list,
+      .list     = dvb_network_class_scanfile_list,
       .opts     = PO_NOSAVE,
     },
     {}
index fcd2b975695d88adb9a50dece502a2d53223c180..49077e0b99845425526451b4873dd3af0e596d5c 100644 (file)
@@ -10,8 +10,8 @@ tvheadend.wizard_start = function(page) {
     var tabMapping = {
         hello: 'base_config',
         login: 'access_entry',
-        network: 'mpegts_network',
-        input: 'tvadapters',
+        network: 'tvadapters',
+        muxes: 'mpegts_network',
         status: 'status_streams',
         mapping: 'channels',
     }
index 4529aced378a123972ae8f7423bab98c8c3d07a0..89ce8b4583cfd4dda5525017f1fc9b22c6ce884e 100644 (file)
@@ -21,6 +21,7 @@
 #include "access.h"
 #include "settings.h"
 #include "input.h"
+#include "input/mpegts/iptv/iptv_private.h"
 #include "wizard.h"
 
 /*
@@ -547,7 +548,8 @@ static void network_save(idnode_t *in)
   .id   = "tunerid" STRINGIFY(num), \
   .name = "Tuner", \
   .get  = network_get_tidvalue##num, \
-  .opts = PO_RDONLY | PO_PERSIST | PO_NOUI, \
+  .set  = network_set_tidvalue##num, \
+  .opts = PO_PERSIST | PO_NOUI, \
 }, { \
   .type = PT_STR, \
   .id   = "network" STRINGIFY(num), \
@@ -573,6 +575,13 @@ static const void *network_get_tidvalue##num(void *o) \
   snprintf(prop_sbuf, PROP_SBUF_LEN, "%s", w->tunerid[num-1]); \
   return &prop_sbuf_ptr; \
 } \
+static int network_set_tidvalue##num(void *o, const void *v) \
+{ \
+  wizard_page_t *p = o; \
+  wizard_network_t *w = p->aux; \
+  snprintf(w->tunerid[num-1], sizeof(w->tunerid[num-1]), "%s", (const char *)v); \
+  return 1; \
+} \
 static const void *network_get_value##num(void *o) \
 { \
   wizard_page_t *p = o; \
@@ -631,7 +640,7 @@ wizard_page_t *wizard_network(const char *lang)
     ICON(),
     DESCRIPTION(network),
     PREV_BUTTON(login),
-    NEXT_BUTTON(input),
+    NEXT_BUTTON(muxes),
   };
   wizard_page_t *page = page_init("network", "wizard_network", N_("Network settings"));
   idclass_t *ic = (idclass_t *)page->idnode.in_class;
@@ -685,47 +694,233 @@ wizard_page_t *wizard_network(const char *lang)
 }
 
 /*
- * Input settings
+ * Muxes settings
  */
 
-DESCRIPTION_FCN(input, N_("\
-Assign inputs to networks.\
-"))
+typedef struct wizard_muxes {
+  property_t props [WIZARD_NETWORKS * 3 + 10];
+  char network     [WIZARD_NETWORKS][64];
+  char networkid   [WIZARD_NETWORKS][UUID_HEX_SIZE];
+  char muxes       [WIZARD_NETWORKS][64];
+  char iptv_url    [WIZARD_NETWORKS][512];
+} wizard_muxes_t;
 
+static void muxes_free(wizard_page_t *page)
+{
+  page_free(page);
+}
 
-wizard_page_t *wizard_input(const char *lang)
+static void muxes_save(idnode_t *in)
 {
+  wizard_page_t *p = (wizard_page_t *)in;
+  wizard_muxes_t *w = p->aux;
+  mpegts_network_t *mn;
+  htsmsg_t *m;
+  int idx;
+
+  for (idx = 0; idx < WIZARD_NETWORKS; idx++) {
+    if (w->networkid[idx][0] == '\0')
+      continue;
+    mn = mpegts_network_find(w->networkid[idx]);
+    if (mn == NULL || !mn->mn_wizard)
+      continue;
+    if (idnode_is_instance(&mn->mn_id, &dvb_network_class) && w->muxes[idx][0]) {
+      dvb_network_scanfile_set((dvb_network_t *)mn, w->muxes[idx]);
+    } else if (idnode_is_instance(&mn->mn_id, &iptv_auto_network_class) &&
+               w->iptv_url[0]) {
+      m = htsmsg_create_map();
+      htsmsg_add_str(m, "url", w->iptv_url[idx]);
+      idnode_load(&mn->mn_id, m);
+      htsmsg_destroy(m);
+    }
+  }
+}
+
+#define MUXES(num) { \
+  .type = PT_STR, \
+  .id   = "network" STRINGIFY(num), \
+  .name = N_("Network"), \
+  .get  = muxes_get_nvalue##num, \
+  .opts = PO_RDONLY, \
+  .group = num, \
+}, { \
+  .type = PT_STR, \
+  .id   = "networkid" STRINGIFY(num), \
+  .name = "Network", \
+  .get  = muxes_get_idvalue##num, \
+  .set  = muxes_set_idvalue##num, \
+  .opts = PO_PERSIST | PO_NOUI, \
+}, { \
+  .type = PT_STR, \
+  .id   = "muxes" STRINGIFY(num), \
+  .name = N_("Pre-defined muxes"), \
+  .get  = muxes_get_value##num, \
+  .set  = muxes_set_value##num, \
+  .list = muxes_get_list##num, \
+  .group = num, \
+}
+
+#define MUXES_IPTV(num) { \
+  .type = PT_STR, \
+  .id   = "network" STRINGIFY(num), \
+  .name = N_("Network"), \
+  .get  = muxes_get_nvalue##num, \
+  .opts = PO_RDONLY, \
+  .group = num, \
+}, { \
+  .type = PT_STR, \
+  .id   = "networkid" STRINGIFY(num), \
+  .name = "Network", \
+  .get  = muxes_get_idvalue##num, \
+  .set  = muxes_set_idvalue##num, \
+  .opts = PO_PERSIST | PO_NOUI, \
+}, { \
+  .type = PT_STR, \
+  .id   = "muxes" STRINGIFY(num), \
+  .name = N_("URL"), \
+  .desc = N_("URL of the M3U playlist."), \
+  .get  = muxes_get_iptv_value##num, \
+  .set  = muxes_set_iptv_value##num, \
+  .group = num, \
+}
+
+#define MUXES_FCN(num) \
+static const void *muxes_get_nvalue##num(void *o) \
+{ \
+  wizard_page_t *p = o; \
+  wizard_muxes_t *w = p->aux; \
+  snprintf(prop_sbuf, PROP_SBUF_LEN, "%s", w->network[num-1]); \
+  return &prop_sbuf_ptr; \
+} \
+static const void *muxes_get_idvalue##num(void *o) \
+{ \
+  wizard_page_t *p = o; \
+  wizard_muxes_t *w = p->aux; \
+  snprintf(prop_sbuf, PROP_SBUF_LEN, "%s", w->networkid[num-1]); \
+  return &prop_sbuf_ptr; \
+} \
+static int muxes_set_idvalue##num(void *o, const void *v) \
+{ \
+  wizard_page_t *p = o; \
+  wizard_muxes_t *w = p->aux; \
+  snprintf(w->networkid[num-1], sizeof(w->networkid[num-1]), "%s", (const char *)v); \
+  return 1; \
+} \
+static const void *muxes_get_value##num(void *o) \
+{ \
+  wizard_page_t *p = o; \
+  wizard_muxes_t *w = p->aux; \
+  snprintf(prop_sbuf, PROP_SBUF_LEN, "%s", w->muxes[num-1]); \
+  return &prop_sbuf_ptr; \
+} \
+static int muxes_set_value##num(void *o, const void *v) \
+{ \
+  wizard_page_t *p = o; \
+  wizard_muxes_t *w = p->aux; \
+  snprintf(w->muxes[num-1], sizeof(w->muxes[num-1]), "%s", (const char *)v); \
+  return 1; \
+} \
+static htsmsg_t *muxes_get_list##num(void *o, const char *lang) \
+{ \
+  if (o == NULL) return NULL; \
+  wizard_page_t *p = o; \
+  wizard_muxes_t *w = p->aux; \
+  mpegts_network_t *mn = mpegts_network_find(w->networkid[num-1]); \
+  return mn ? dvb_network_class_scanfile_list(mn, lang) : NULL; \
+} \
+static const void *muxes_get_iptv_value##num(void *o) \
+{ \
+  wizard_page_t *p = o; \
+  wizard_muxes_t *w = p->aux; \
+  snprintf(prop_sbuf, PROP_SBUF_LEN, "%s", w->iptv_url[num-1]); \
+  return &prop_sbuf_ptr; \
+} \
+static int muxes_set_iptv_value##num(void *o, const void *v) \
+{ \
+  wizard_page_t *p = o; \
+  wizard_muxes_t *w = p->aux; \
+  snprintf(w->iptv_url[num-1], sizeof(w->iptv_url[num-1]), "%s", (const char *)v); \
+  return 1; \
+}
+
+MUXES_FCN(1)
+MUXES_FCN(2)
+MUXES_FCN(3)
+MUXES_FCN(4)
+MUXES_FCN(5)
+MUXES_FCN(6)
+
+DESCRIPTION_FCN(muxes, N_("\
+Assign predefined mxues to networks.\
+"))
+
+wizard_page_t *wizard_muxes(const char *lang)
+{
+  static const property_group_t groups[] = {
+    NETWORK_GROUP(1),
+    NETWORK_GROUP(2),
+    NETWORK_GROUP(3),
+    NETWORK_GROUP(4),
+    NETWORK_GROUP(5),
+    NETWORK_GROUP(6),
+    {}
+  };
+  static const property_t nprops[] = {
+    MUXES(1),
+    MUXES(2),
+    MUXES(3),
+    MUXES(4),
+    MUXES(5),
+    MUXES(6),
+  };
+  static const property_t iptvprops[] = {
+    MUXES_IPTV(1),
+    MUXES_IPTV(2),
+    MUXES_IPTV(3),
+    MUXES_IPTV(4),
+    MUXES_IPTV(5),
+    MUXES_IPTV(6),
+  };
   static const property_t props[] = {
-    {
-      .type     = PT_STR,
-      .id       = "input1",
-      .name     = N_("Input 1 network"),
-      .get      = hello_get_network,
-      .set      = hello_set_network,
-    },
-    {
-      .type     = PT_STR,
-      .id       = "input2",
-      .name     = N_("Input 2 network"),
-      .get      = hello_get_network,
-      .set      = hello_set_network,
-    },
-    {
-      .type     = PT_STR,
-      .id       = "input3",
-      .name     = N_("Input 3 network"),
-      .get      = hello_get_network,
-      .set      = hello_set_network,
-    },
     ICON(),
-    DESCRIPTION(input),
+    DESCRIPTION(muxes),
     PREV_BUTTON(network),
     NEXT_BUTTON(status),
-    {}
   };
-  wizard_page_t *page = page_init("input", "wizard_input", N_("Input / tuner settings"));
+  wizard_page_t *page = page_init("muxes", "wizard_muxes", N_("Assign predefined muxes to networks"));
   idclass_t *ic = (idclass_t *)page->idnode.in_class;
-  ic->ic_properties = props;
+  wizard_muxes_t *w;
+  mpegts_network_t *mn;
+  int idx, midx = 0;
+
+  page->aux = w = calloc(1, sizeof(wizard_network_t));
+  ic->ic_groups = groups;
+  ic->ic_properties = w->props;
+  ic->ic_save = muxes_save;
+  page->free = muxes_free;
+
+  for (idx = 0; idx < ARRAY_SIZE(props); idx++)
+    w->props[idx] = props[idx];
+
+  LIST_FOREACH(mn, &mpegts_network_all, mn_global_link)
+    if (mn->mn_wizard) {
+      mn->mn_display_name(mn, w->network[midx], sizeof(w->network[midx]));
+      idnode_uuid_as_str(&mn->mn_id, w->networkid[midx]);
+      if (idnode_is_instance(&mn->mn_id, &dvb_network_class)) {
+        w->props[idx++] = nprops[midx * 3 + 0];
+        w->props[idx++] = nprops[midx * 3 + 1];
+        w->props[idx++] = nprops[midx * 3 + 2];
+        midx++;
+      } else if (idnode_is_instance(&mn->mn_id, &iptv_auto_network_class)) {
+        w->props[idx++] = iptvprops[midx * 3 + 0];
+        w->props[idx++] = iptvprops[midx * 3 + 1];
+        w->props[idx++] = iptvprops[midx * 3 + 2];
+        midx++;
+      }
+    }
+
+  assert(idx < ARRAY_SIZE(w->props));
+
   return page;
 }
 
index 71768b9eb10c6f92f8e7185efdd27a1321c0d412..04096a3f13a3c9356f487431bcac15ca58602fec 100644 (file)
@@ -34,7 +34,7 @@ typedef wizard_page_t *(*wizard_build_fcn_t)(const char *lang);
 wizard_page_t *wizard_hello(const char *lang);
 wizard_page_t *wizard_login(const char *lang);
 wizard_page_t *wizard_network(const char *lang);
-wizard_page_t *wizard_input(const char *lang);
+wizard_page_t *wizard_muxes(const char *lang);
 wizard_page_t *wizard_status(const char *lang);
 wizard_page_t *wizard_mapping(const char *lang);