Merge branch 'master' of ssh://git.indexdata.com/home/git/pub/pazpar2
authorAdam Dickmeiss <adam@indexdata.dk>
Thu, 26 Apr 2012 12:23:17 +0000 (14:23 +0200)
committerAdam Dickmeiss <adam@indexdata.dk>
Thu, 26 Apr 2012 12:23:17 +0000 (14:23 +0200)
Conflicts:
src/settings.h

1  2 
src/http_command.c
src/pazpar2_config.c
src/settings.c
src/settings.h

diff --combined src/http_command.c
@@@ -93,6 -93,16 +93,16 @@@ struct http_sessions 
  static YAZ_MUTEX g_http_session_mutex = 0;
  static int g_http_sessions = 0;
  
+ int get_version(struct http_request *rq) {
+     const char *version = http_argbyname(rq, "version");
+     int version_no = 0;
+     if (version && strcmp(version, "")) {
+         version_no = atoi(version);
+     }
+     return version_no;
+ }
  int http_session_use(int delta)
  {
      int sessions;
@@@ -488,7 -498,6 +498,7 @@@ static void cmd_settings(struct http_ch
      {
          xmlDoc *doc = xmlParseMemory(rq->content_buf, rq->content_len);
          xmlNode *root_n;
 +        int ret;
          if (!doc)
          {
              error(rs, PAZPAR2_MALFORMED_SETTING, 0);
              return;
          }
          root_n = xmlDocGetRootElement(doc);
 -
 -        settings_read_node_x(root_n, s->psession, apply_local_setting);
 -
 +        ret = settings_read_node_x(root_n, s->psession, apply_local_setting);
          xmlFreeDoc(doc);
 +        if (ret)
 +        {
 +            error(rs, PAZPAR2_MALFORMED_SETTING, 0);
 +            release_session(c,s);
 +            return;
 +        }            
      }
      if (process_settings(s->psession, rq, rs) < 0)
      {
  static void termlist_response(struct http_channel *c, struct http_session *s, const char *cmd_status)
  {
      struct http_request *rq = c->request;
-     const char *name = http_argbyname(rq, "name");
-     const char *nums = http_argbyname(rq, "num");
+     const char *name    = http_argbyname(rq, "name");
+     const char *nums    = http_argbyname(rq, "num");
+     int version = get_version(rq);
      int num = 15;
      int status;
  
      }
      wrbuf_printf(c->wrbuf, "<activeclients>%d</activeclients>\n", status);
  
-     perform_termlist(c, s->psession, name, num);
+     perform_termlist(c, s->psession, name, num, version);
  
      response_close(c, "termlist");
  }
@@@ -681,7 -687,7 +692,7 @@@ static void bytarget_response(struct ht
      struct hitsbytarget *ht;
      struct http_request *rq = c->request;
      const char *settings = http_argbyname(rq, "settings");
+     int version = get_version(rq);
      ht = get_hitsbytarget(s->psession, &count, c->nmem);
      if (!cmd_status)
          /* Old protocol, always ok */
              wrbuf_puts(c->wrbuf, "</addinfo>\n");
          }
  
-         wrbuf_printf(c->wrbuf, "<records>%d</records>\n", ht[i].records);
+         wrbuf_printf(c->wrbuf, "<records>%d</records>\n", ht[i].records - ht[i].filtered);
+         if (version >= 2) {
+             wrbuf_printf(c->wrbuf, "<filtered>%d</filtered>\n", ht[i].filtered);
+             wrbuf_printf(c->wrbuf, "<approximation>" ODR_INT_PRINTF "</approximation>\n", ht[i].approximation);
+         }
          wrbuf_puts(c->wrbuf, "<state>");
          wrbuf_xmlputs(c->wrbuf, ht[i].state);
          wrbuf_puts(c->wrbuf, "</state>\n");
@@@ -1048,10 -1057,13 +1062,13 @@@ static void show_records(struct http_ch
      const char *start = http_argbyname(rq, "start");
      const char *num = http_argbyname(rq, "num");
      const char *sort = http_argbyname(rq, "sort");
+     int version = get_version(rq);
      int startn = 0;
      int numn = 20;
      int total;
      Odr_int total_hits;
+     Odr_int approx_hits;
      int i;
  
      if (!s)
  
      }
      
-     rl = show_range_start(s->psession, sp, startn, &numn, &total, &total_hits);
+     rl = show_range_start(s->psession, sp, startn, &numn, &total, &total_hits, &approx_hits);
  
      response_open(c, "show");
      wrbuf_printf(c->wrbuf, "\n<activeclients>%d</activeclients>\n", active);
      wrbuf_printf(c->wrbuf, "<merged>%d</merged>\n", total);
      wrbuf_printf(c->wrbuf, "<total>" ODR_INT_PRINTF "</total>\n", total_hits);
+     if (version >= 2) {
+         wrbuf_printf(c->wrbuf, "<approximation>" ODR_INT_PRINTF "</approximation>\n", approx_hits);
+     }
      wrbuf_printf(c->wrbuf, "<start>%d</start>\n", startn);
      wrbuf_printf(c->wrbuf, "<num>%d</num>\n", numn);
  
@@@ -1132,6 -1147,7 +1152,7 @@@ static void cmd_show(struct http_channe
      const char *block = http_argbyname(rq, "block");
      const char *sort = http_argbyname(rq, "sort");
      const char *block_error = http_argbyname(rq, "report");
      struct reclist_sortparms *sp;
      int status;
      int report_error = 0;
diff --combined src/pazpar2_config.c
@@@ -75,7 -75,8 +75,8 @@@ static void conf_metadata_assign(NMEM n
                                   int rank,
                                   int sortkey_offset,
                                   enum conf_metadata_mergekey mt,
-                                  const char *facetrule)
+                                  const char *facetrule,
+                                  const char *limitmap)
  {
      assert(nmem && metadata && name);
      
@@@ -96,6 -97,7 +97,7 @@@
      metadata->sortkey_offset = sortkey_offset;
      metadata->mergekey = mt;
      metadata->facetrule = nmem_strdup_null(nmem, facetrule);
+     metadata->limitmap = nmem_strdup_null(nmem, limitmap);
  }
  
  
@@@ -110,13 -112,14 +112,14 @@@ static void conf_sortkey_assign(NMEM nm
      sortkey->type = type;
  }
  
- static struct conf_service *service_init(struct conf_server *server,
+ struct conf_service *service_init(struct conf_server *server,
                                           int num_metadata, int num_sortkeys,
                                           const char *service_id)
  {
      struct conf_service * service = 0;
      NMEM nmem = nmem_create();
  
      service = nmem_malloc(nmem, sizeof(struct conf_service));
      service->mutex = 0;
      service->ref_count = 1;
      service->charsets = 0;
  
      service->id = service_id ? nmem_strdup(nmem, service_id) : 0;
+     // Setup a dictionary from server.
+     service->dictionary = 0;
+     service->settings = nmem_malloc(nmem, sizeof(struct settings));
+     service->settings->num_settings = PZ_MAX_EOF;
+     service->settings->settings = nmem_malloc(nmem, sizeof(struct setting*) * service->settings->num_settings);
+     memset(service->settings->settings, 0, sizeof(struct setting*) * service->settings->num_settings);
+     //  inherit_server_settings_values(service);
+     service->next = 0;
      service->num_metadata = num_metadata;
      service->metadata = 0;
      if (service->num_metadata)
          service->metadata 
          service->sortkeys 
              = nmem_malloc(nmem, 
                            sizeof(struct conf_sortkey) * service->num_sortkeys);
-     service->dictionary = 0;
      return service; 
  }
  
@@@ -160,7 -176,9 +176,9 @@@ static struct conf_metadata* conf_servi
      int rank,
      int sortkey_offset,
      enum conf_metadata_mergekey mt,
-     const char *facetrule)
+     const char *facetrule,
+     const char *limitmap
+     )
  {
      struct conf_metadata * md = 0;
  
      md = service->metadata + field_id;
      conf_metadata_assign(service->nmem, md, name, type, merge, setting,
                           brief, termlist, rank, sortkey_offset,
-                          mt, facetrule);
+                          mt, facetrule, limitmap);
      return md;
  }
  
@@@ -274,6 -292,7 +292,7 @@@ static int parse_metadata(struct conf_s
      xmlChar *xml_rank = 0;
      xmlChar *xml_setting = 0;
      xmlChar *xml_mergekey = 0;
+     xmlChar *xml_limitmap = 0;
      xmlChar *xml_icu_chain = 0;
      struct _xmlAttr *attr;
      for (attr = n->properties; attr; attr = attr->next)
          else if (!xmlStrcmp(attr->name, BAD_CAST "facetrule") &&
                   attr->children && attr->children->type == XML_TEXT_NODE)
              xml_icu_chain = attr->children->content;
+         else if (!xmlStrcmp(attr->name, BAD_CAST "limitmap") &&
+                  attr->children && attr->children->type == XML_TEXT_NODE)
+             xml_limitmap = attr->children->content;
          else
          {
              yaz_log(YLOG_FATAL, "Unknown metadata attribute '%s'", attr->name);
                                (const char *) xml_name,
                                type, merge, setting,
                                brief, termlist, rank, sortkey_offset,
-                               mergekey_type, (const char *) xml_icu_chain);
+                               mergekey_type, (const char *) xml_icu_chain, (const char *) xml_limitmap);
      (*md_node)++;
      return 0;
  }
@@@ -557,6 -579,15 +579,15 @@@ static struct conf_service *service_cre
              if (service_xslt_config(service, n))
                  return 0;
          }
+         else if (!strcmp((const char *) n->name, (const char *) "set"))
+         {
+             xmlChar *name= xmlGetProp(n, (xmlChar *) "name");
+             xmlChar *value = xmlGetProp(n, (xmlChar *) "value");
+             if (service->dictionary && name && value) {
+                 yaz_log(YLOG_DEBUG, "service set: %s=%s (Not implemented)", (char *) name, (char *) value);
+                 //service_aply_setting(service, name, value);
+             }
+         }
          else
          {
              yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
                      continue;
                  if (!strcmp((const char *) n->name, "settings"))
                  {
 +                    int ret;
                      xmlChar *src = xmlGetProp(n, (xmlChar *) "src");
                      if (src)
                      {
                          WRBUF w = wrbuf_alloc();
                          conf_dir_path(server->config, w, (const char *) src);
 -                        settings_read_file(service, wrbuf_cstr(w), pass);
 +                        ret = settings_read_file(service, wrbuf_cstr(w), pass);
                          wrbuf_destroy(w);
                          xmlFree(src);
                      }
                      else
                      {
 -                        settings_read_node(service, n, pass);
 +                        ret = settings_read_node(service, n, pass);
                      }
 +                    if (ret)
 +                        return 0;
                  }
              }
          }
      return service;
  }
  
 -static void inherit_server_settings(struct conf_service *s)
 +static int inherit_server_settings(struct conf_service *s)
  {
 +    int ret = 0;
      struct conf_server *server = s->server;
      if (!s->dictionary) /* service has no config settings ? */
      {
          {
              /* inherit settings from server */
              init_settings(s);
 -            settings_read_file(s, server->settings_fname, 1);
 -            settings_read_file(s, server->settings_fname, 2);
 +            if (settings_read_file(s, server->settings_fname, 1))
 +                ret = -1;
 +            if (settings_read_file(s, server->settings_fname, 2))
 +                ret = -1;
          }
          else
          {
-             yaz_log(YLOG_WARN, "service '%s' has no settings",
-                     s->id ? s->id : "unnamed");
+             yaz_log(YLOG_WARN, "server '%s' has no settings", s->id ? s->id : "unnamed");
              init_settings(s);
          }
      }
              s->charsets = pp2_charset_fact_create();
          }
      }
 +    return ret;
  }
  
  struct conf_service *service_create(struct conf_server *server,
                                      xmlNode *node)
  {
-     struct conf_service *service = service_create_static(server,
-                                                          node, 0);
+     struct conf_service *service = service_create_static(server, node, 0);
      if (service)
      {
          inherit_server_settings(service);
diff --combined src/settings.c
@@@ -71,6 -71,7 +71,7 @@@ static char *hard_settings[] = 
      "pz:max_connections",
      "pz:reuse_connections",
      "pz:termlist_term_factor",
+     "pz:termlist_term_count",
      "pz:preferred",
      "pz:extra_args",
      "pz:query_syntax",
@@@ -78,6 -79,8 +79,8 @@@
      "pz:limitmap:",
      "pz:url",
      "pz:sortmap:",
+     "pz:present_chunk",
+     "pz:block_timeout",
      0
  };
  
@@@ -99,13 -102,12 +102,12 @@@ int settings_num(struct conf_service *s
      return service->dictionary->num;
  }
  
- static int settings_lookup(struct conf_service *service, const char *name,
-                            int allow_create)
+ /* Find and possible create a new dictionary entry. Pass valid NMEM pointer if creation is allowed, otherwise null */
+ static int settings_index_lookup(struct setting_dictionary *dictionary, const char *name, NMEM nmem)
  {
      size_t maxlen;
      int i;
      const char *p;
-     struct setting_dictionary *dictionary = service->dictionary;
      
      assert(name);
  
      for (i = 0; i < dictionary->num; i++)
          if (!strncmp(name, dictionary->dict[i], maxlen))
              return i;
-     if (!allow_create)
+     if (!nmem)
          return -1;
      if (!strncmp("pz:", name, 3))
          yaz_log(YLOG_WARN, "Adding pz-type setting name %s", name);
      if (dictionary->num + 1 > dictionary->size)
      {
          char **tmp =
-             nmem_malloc(service->nmem, dictionary->size * 2 * sizeof(char*));
+             nmem_malloc(nmem, dictionary->size * 2 * sizeof(char*));
          memcpy(tmp, dictionary->dict, dictionary->size * sizeof(char*));
          dictionary->dict = tmp;
          dictionary->size *= 2;
      }
-     dictionary->dict[dictionary->num] = nmem_strdup(service->nmem, name);
+     dictionary->dict[dictionary->num] = nmem_strdup(nmem, name);
      dictionary->dict[dictionary->num][maxlen-1] = '\0';
      return dictionary->num++;
  }
  
  int settings_create_offset(struct conf_service *service, const char *name)
  {
-     return settings_lookup(service, name, 1);
+     return settings_index_lookup(service->dictionary, name, service->nmem);
  }
  
  int settings_lookup_offset(struct conf_service *service, const char *name)
  {
-     return settings_lookup(service, name, 0);
+     return settings_index_lookup(service->dictionary, name, 0);
  }
  
  char *settings_name(struct conf_service *service, int offset)
      return service->dictionary->dict[offset];
  }
  
+ // Apply a session override to a database
+ void service_apply_setting(struct conf_service *service, char *setting, char *value)
+ {
+     struct setting *new = nmem_malloc(service->nmem, sizeof(*new));
+     int offset = settings_create_offset(service, setting);
+     expand_settings_array(&service->settings->settings, &service->settings->num_settings, offset, service->nmem);
+     new->precedence = 0;
+     new->target = NULL;
+     new->name = setting;
+     new->value = value;
+     new->next = service->settings->settings[offset];
+     service->settings->settings[offset] = new;
+ }
  static int isdir(const char *path)
  {
      struct stat st;
  }
  
  // Read settings from an XML file, calling handler function for each setting
 -void settings_read_node_x(xmlNode *n,
 -                          void *client_data,
 -                          void (*fun)(void *client_data,
 -                                      struct setting *set))
 +int settings_read_node_x(xmlNode *n,
 +                         void *client_data,
 +                         void (*fun)(void *client_data,
 +                                     struct setting *set))
  {
 -    xmlChar *namea, *targeta, *valuea, *usera, *precedencea;
 +    int ret_val = 0; /* success */
 +    char *namea = (char *) xmlGetProp(n, (xmlChar *) "name");
 +    char *targeta = (char *) xmlGetProp(n, (xmlChar *) "target");
 +    char *valuea = (char *) xmlGetProp(n, (xmlChar *) "value");
 +    char *usera = (char *) xmlGetProp(n, (xmlChar *) "user");
 +    char *precedencea = (char *) xmlGetProp(n, (xmlChar *) "precedence");
  
 -    namea = xmlGetProp(n, (xmlChar *) "name");
 -    targeta = xmlGetProp(n, (xmlChar *) "target");
 -    valuea = xmlGetProp(n, (xmlChar *) "value");
 -    usera = xmlGetProp(n, (xmlChar *) "user");
 -    precedencea = xmlGetProp(n, (xmlChar *) "precedence");
      for (n = n->children; n; n = n->next)
      {
          if (n->type != XML_ELEMENT_NODE)
              continue;
          if (!strcmp((const char *) n->name, "set"))
          {
 -            char *name, *target, *value, *user, *precedence;
 +            struct setting set;
 +            char *name = (char *) xmlGetProp(n, (xmlChar *) "name");
 +            char *target = (char *) xmlGetProp(n, (xmlChar *) "target");
 +            char *value = (char *) xmlGetProp(n, (xmlChar *) "value");
 +            char *user = (char *) xmlGetProp(n, (xmlChar *) "user");
 +            char *precedence = (char *) xmlGetProp(n, (xmlChar *) "precedence");
 +
 +            if (precedence)
 +                set.precedence = atoi((char *) precedence);
 +            else if (precedencea)
 +                set.precedence = atoi((char *) precedencea);
 +            else
 +                set.precedence = 0;
  
 -            name = (char *) xmlGetProp(n, (xmlChar *) "name");
 -            target = (char *) xmlGetProp(n, (xmlChar *) "target");
 -            value = (char *) xmlGetProp(n, (xmlChar *) "value");
 -            user = (char *) xmlGetProp(n, (xmlChar *) "user");
 -            precedence = (char *) xmlGetProp(n, (xmlChar *) "precedence");
 +            set.target = target ? target : targeta;
 +            set.name = name ? name : namea;
 +            set.value = value ? value : valuea;
 +            set.next = 0;
  
 -            if ((!name && !namea) || (!value && !valuea) || (!target && !targeta))
 -            {
 -                yaz_log(YLOG_FATAL, "set must specify name, value, and target");
 -                exit(1);
 -            }
 +            if (set.name && set.value && set.target)
 +                (*fun)(client_data, &set);
              else
              {
 -                struct setting set;
 -                char nameb[1024];
 -                char targetb[1024];
 -                char valueb[1024];
 -
 -                // Copy everything into a temporary buffer -- we decide
 -                // later if we are keeping it.
 -                if (precedence)
 -                    set.precedence = atoi((char *) precedence);
 -                else if (precedencea)
 -                    set.precedence = atoi((char *) precedencea);
 +                if (set.name)
 +                    yaz_log(YLOG_WARN, "missing value and/or target for "
 +                            "setting name=%s", set.name);
                  else
 -                    set.precedence = 0;
 -                if (target)
 -                    strcpy(targetb, target);
 -                else
 -                    strcpy(targetb, (const char *) targeta);
 -                set.target = targetb;
 -                if (name)
 -                    strcpy(nameb, name);
 -                else
 -                    strcpy(nameb, (const char *) namea);
 -                set.name = nameb;
 -                if (value)
 -                    strcpy(valueb, value);
 -                else
 -                    strcpy(valueb, (const char *) valuea);
 -                set.value = valueb;
 -                set.next = 0;
 -                (*fun)(client_data, &set);
 +                    yaz_log(YLOG_WARN, "missing name/value/target for setting");
 +                ret_val = -1;
              }
              xmlFree(name);
              xmlFree(precedence);
          }
          else
          {
 -            yaz_log(YLOG_FATAL, "Unknown element %s in settings file", (char*) n->name);
 -            exit(1);
 +            yaz_log(YLOG_WARN, "Unknown element %s in settings file", 
 +                    (char*) n->name);
 +            ret_val = -1;
          }
      }
      xmlFree(namea);
      xmlFree(valuea);
      xmlFree(usera);
      xmlFree(targeta);
 +    return ret_val;
  }
   
 -static void read_settings_file(const char *path,
 -                               void *client_data,
 -                               void (*fun)(void *client_data,
 -                                           struct setting *set))
 +static int read_settings_file(const char *path,
 +                              void *client_data,
 +                              void (*fun)(void *client_data,
 +                                          struct setting *set))
  {
      xmlDoc *doc = xmlParseFile(path);
      xmlNode *n;
 +    int ret;
  
      if (!doc)
      {
          yaz_log(YLOG_FATAL, "Failed to parse %s", path);
 -        exit(1);
 +        return -1;
      }
      n = xmlDocGetRootElement(doc);
 -    settings_read_node_x(n, client_data, fun);
 +    ret = settings_read_node_x(n, client_data, fun);
  
      xmlFreeDoc(doc);
 +    return ret;
  }
  
  
  // Recursively read files or directories, invoking a 
  // callback for each one
 -static void read_settings(const char *path,
 +static int read_settings(const char *path,
                            void *client_data,
                            void (*fun)(void *client_data,
                                        struct setting *set))
  {
 +    int ret = 0;
      DIR *d;
      struct dirent *de;
      char *dot;
          if (!(d = opendir(path)))
          {
              yaz_log(YLOG_FATAL|YLOG_ERRNO, "%s", path);
 -            exit(1);
 +            return -1;
          }
          while ((de = readdir(d)))
          {
              if (*de->d_name == '.' || !strcmp(de->d_name, "CVS"))
                  continue;
              sprintf(tmp, "%s/%s", path, de->d_name);
 -            read_settings(tmp, client_data, fun);
 +            if (read_settings(tmp, client_data, fun))
 +                ret = -1;
          }
          closedir(d);
      }
      else if ((dot = strrchr(path, '.')) && !strcmp(dot + 1, "xml"))
 -        read_settings_file(path, client_data, fun);
 +        ret = read_settings_file(path, client_data, fun);
 +    return ret;
  }
  
  // Determines if a ZURL is a wildcard, and what kind
@@@ -324,6 -351,72 +342,72 @@@ void expand_settings_array(struct setti
      }
  }
  
+ void expand_settings_array2(struct settings *settings, int offset, NMEM nmem)
+ {
+     assert(offset >= 0);
+     assert(settings);
+     if (offset >= settings->num_settings)
+     {
+         int i, n_num = offset + 10;
+         struct setting **n_ar = nmem_malloc(nmem, n_num * sizeof(*n_ar));
+         for (i = 0; i < settings->num_settings; i++)
+             n_ar[i] = settings->settings[i];
+         for (; i < n_num; i++)
+             n_ar[i] = 0;
+         settings->num_settings = n_num;
+         settings->settings = n_ar;
+     }
+ }
+ static void update_settings(struct setting *set, struct settings *settings, int offset, NMEM nmem)
+ {
+     struct setting **sp;
+     yaz_log(YLOG_LOG, "update service settings offset %d with %s=%s", offset, set->name, set->value);
+     expand_settings_array2(settings, offset, nmem);
+     // First we determine if this setting is overriding any existing settings
+     // with the same name.
+     assert(offset < settings->num_settings);
+     for (sp = &settings->settings[offset]; *sp; )
+         if (!strcmp((*sp)->name, set->name))
+         {
+             if ((*sp)->precedence < set->precedence)
+             {
+                 // We discard the value (nmem keeps track of the space)
+                 *sp = (*sp)->next; // unlink value from existing setting
+             }
+             else if ((*sp)->precedence > set->precedence)
+             {
+                 // Db contains a higher-priority setting. Abort search
+                 break;
+             }
+             else if (zurl_wildcard((*sp)->target) > zurl_wildcard(set->target))
+             {
+                 // target-specific value trumps wildcard. Delete.
+                 *sp = (*sp)->next; // unlink.....
+             }
+             else if (zurl_wildcard((*sp)->target) < zurl_wildcard(set->target))
+                 // Db already contains higher-priority setting. Abort search
+                 break;
+             else
+                 sp = &(*sp)->next;
+         }
+         else
+             sp = &(*sp)->next;
+     if (!*sp) // is null when there are no higher-priority settings, so we add one
+     {
+         struct setting *new = nmem_malloc(nmem, sizeof(*new));
+         memset(new, 0, sizeof(*new));
+         new->precedence = set->precedence;
+         new->target = nmem_strdup_null(nmem, set->target);
+         new->name = nmem_strdup_null(nmem, set->name);
+         new->value = nmem_strdup_null(nmem, set->value);
+         new->next = settings->settings[offset];
+         settings->settings[offset] = new;
+     }
+ }
  // This is called from grep_databases -- adds/overrides setting for a target
  // This is also where the rules for precedence of settings are implemented
  static void update_database_fun(void *context, struct database *db)
          return;
  
      offset = settings_create_offset(service, set->name);
-     expand_settings_array(&db->settings, &db->num_settings, offset,
-                           service->nmem);
+     expand_settings_array(&db->settings, &db->num_settings, offset, service->nmem);
  
      // First we determine if this setting is overriding  any existing settings
      // with the same name.
@@@ -410,18 -502,39 +493,39 @@@ static void initialize_hard_settings(st
  
  // Read any settings names introduced in service definition (config) and add to dictionary
  // This is done now to avoid errors if user settings are declared in session overrides
- static void initialize_soft_settings(struct conf_service *service)
+ void initialize_soft_settings(struct conf_service *service)
  {
      int i;
      for (i = 0; i < service->num_metadata; i++)
      {
          struct conf_metadata *md = &service->metadata[i];
  
-         if (md->setting == Metadata_setting_no)
-             continue;
-         settings_create_offset(service, md->name);
+         if (md->setting != Metadata_setting_no)
+             settings_create_offset(service, md->name);
+         // Also create setting for some metadata attributes.
+         if (md->limitmap) {
+             int index; 
+             WRBUF wrbuf = wrbuf_alloc();
+             yaz_log(YLOG_DEBUG, "Metadata %s has limitmap: %s ",md->name,  md->limitmap);
+             wrbuf_printf(wrbuf, "pz:limitmap:%s", md->name);
+             index = settings_create_offset(service, wrbuf_cstr(wrbuf));
+             if (index >= 0) {
+                 struct setting new;
+                 int offset;
+                 yaz_log(YLOG_DEBUG, "Service %s default %s=%s",
+                         (service->id ? service->id: "unknown"), wrbuf_cstr(wrbuf), md->limitmap);
+                 new.name = (char *) wrbuf_cstr(wrbuf);
+                 new.value = md->limitmap;
+                 new.next = 0;
+                 new.target = 0;
+                 new.precedence = 0;
+                 offset = settings_create_offset(service, new.name);
+                 update_settings(&new, service->settings, offset, service->nmem);
+             }
+             wrbuf_destroy(wrbuf);
+         // TODO same for facetmap
+         }
      }
  }
  
@@@ -447,22 -560,22 +551,22 @@@ void init_settings(struct conf_service 
      initialize_soft_settings(service);
  }
  
 -void settings_read_file(struct conf_service *service, const char *path,
 -                        int pass)
 +int settings_read_file(struct conf_service *service, const char *path,
 +                       int pass)
  {
      if (pass == 1)
 -        read_settings(path, service, prepare_target_dictionary);
 +        return read_settings(path, service, prepare_target_dictionary);
      else
 -        read_settings(path, service, update_databases);
 +        return read_settings(path, service, update_databases);
  }
  
 -void settings_read_node(struct conf_service *service, xmlNode *n,
 +int settings_read_node(struct conf_service *service, xmlNode *n,
                          int pass)
  {
      if (pass == 1)
 -        settings_read_node_x(n, service, prepare_target_dictionary);
 +        return settings_read_node_x(n, service, prepare_target_dictionary);
      else
 -        settings_read_node_x(n, service, update_databases);
 +        return settings_read_node_x(n, service, update_databases);
  }
  
  /*
diff --combined src/settings.h
@@@ -44,14 -44,17 +44,17 @@@ Foundation, Inc., 51 Franklin St, Fift
  #define PZ_MAX_CONNECTIONS      21
  #define PZ_REUSE_CONNECTIONS    22
  #define PZ_TERMLIST_TERM_FACTOR 23
- #define PZ_PREFERRED            24
- #define PZ_EXTRA_ARGS           25
- #define PZ_QUERY_SYNTAX         26
- #define PZ_FACETMAP             27
- #define PZ_LIMITMAP             28
- #define PZ_URL                  29
- #define PZ_SORTMAP              30
- #define PZ_MAX_EOF              31
+ #define PZ_TERMLIST_TERM_COUNT  24
+ #define PZ_PREFERRED            25
+ #define PZ_EXTRA_ARGS           26
+ #define PZ_QUERY_SYNTAX         27
+ #define PZ_FACETMAP             28
+ #define PZ_LIMITMAP             29
+ #define PZ_URL                  30
+ #define PZ_SORTMAP              31
+ #define PZ_PRESENT_CHUNK        32
+ #define PZ_BLOCK_TIMEOUT        33
+ #define PZ_MAX_EOF              34
  
  struct setting
  {
      struct setting *next;
  };
  
+ struct settings
+ {
+     // Array of pointer setting, index is looked up in setting_dictionary
+     struct setting **settings;
+     int num_settings;
+ };
 -void settings_read_file(struct conf_service *service, const char *path,
 -                        int pass);
 -void settings_read_node(struct conf_service *service, xmlNode *n,
 -                        int pass);
 +int settings_read_file(struct conf_service *service, const char *path,
 +                       int pass);
- int settings_read_node(struct conf_service *service, xmlNode *n,
-                        int pass);
++int settings_read_node(struct conf_service *service, xmlNode *n, int pass);
  int settings_num(struct conf_service *service);
  int settings_create_offset(struct conf_service *service, const char *name);
  int settings_lookup_offset(struct conf_service *service, const char *name);
  void init_settings(struct conf_service *service);
 -void settings_read_node_x(xmlNode *n,
 -                          void *client_data,
 -                          void (*fun)(void *client_data,
 -                                      struct setting *set));
 +int settings_read_node_x(xmlNode *n,
 +                         void *client_data,
 +                         void (*fun)(void *client_data,
 +                                     struct setting *set));
  void expand_settings_array(struct setting ***set_ar, int *num, int offset,
                             NMEM nmem);