C++ compilation.
[idzebra-moved-to-github.git] / util / charmap.c
index 112aba2..5b5b81e 100644 (file)
@@ -1,10 +1,56 @@
 /*
- * Copyright (C) 1994, Index Data I/S 
+ * Copyright (C) 1996-1998, Index Data
  * All rights reserved.
  * Sebastian Hammer, Adam Dickmeiss
  *
  * $Log: charmap.c,v $
- * Revision 1.1  1996-05-31 09:07:18  quinn
+ * Revision 1.15  1999-05-26 07:49:14  adam
+ * C++ compilation.
+ *
+ * Revision 1.14  1998/10/13 20:09:18  adam
+ * Changed call to readconf_line.
+ *
+ * Revision 1.13  1997/10/27 14:33:06  adam
+ * Moved towards generic character mapping depending on "structure"
+ * field in abstract syntax file. Fixed a few memory leaks. Fixed
+ * bug with negative integers when doing searches with relational
+ * operators.
+ *
+ * Revision 1.12  1997/09/05 15:30:11  adam
+ * Changed prototype for chr_map_input - added const.
+ * Added support for C++, headers uses extern "C" for public definitions.
+ *
+ * Revision 1.11  1997/09/05 09:52:32  adam
+ * Extra argument added to function chr_read_maptab (tab path).
+ *
+ * Revision 1.10  1997/07/01 13:01:08  adam
+ * Bug fix in routine find_entry: didn't take into account the len arg.
+ *
+ * Revision 1.9  1996/10/29 13:48:14  adam
+ * Updated to use zebrautl.h instead of alexutil.h.
+ *
+ * Revision 1.8  1996/10/18 12:39:23  adam
+ * Uses LOG_DEBUG instead of LOG_WARN for "Character map overlap".
+ *
+ * Revision 1.7  1996/06/06  12:08:56  quinn
+ * Fixed bug.
+ *
+ * Revision 1.6  1996/06/04  13:28:00  quinn
+ * More work on charmapping
+ *
+ * Revision 1.5  1996/06/04  08:32:15  quinn
+ * Moved default keymap to keychars.c
+ *
+ * Revision 1.4  1996/06/03  16:32:13  quinn
+ * Temporary bug-fix
+ *
+ * Revision 1.3  1996/06/03  15:17:46  quinn
+ * Fixed bug.
+ *
+ * Revision 1.2  1996/06/03  10:15:09  quinn
+ * Fixed bug in mapping function.
+ *
+ * Revision 1.1  1996/05/31  09:07:18  quinn
  * Work on character-set handling
  *
  *
  */
 
 #include <ctype.h>
+#include <string.h>
+#include <assert.h>
 
-#include <alexutil.h>
 #include <yaz-util.h>
 #include <charmap.h>
-#include <tpath.h>
+
+#define CHR_MAXSTR 1024
+#define CHR_MAXEQUIV 32
+
+int chr_map_chrs(chr_t_entry *t, char **from, int len,
+                int *read, char **to, int max);
 
 const char *CHR_UNKNOWN = "\001";
 const char *CHR_SPACE   = "\002";
 const char *CHR_BASE    = "\003";
 
-extern char *data1_tabpath;
+struct chrmaptab_info
+{
+    chr_t_entry *input;         /* mapping table for input data */
+    chr_t_entry *query_equiv;   /* mapping table for queries */
+    unsigned char *output[256]; /* return mapping - for display of registers */
+    int base_uppercase;         /* Start of upper-case ordinals */
+    char **tmp_buf;
+    NMEM nmem;
+};
 
 /*
  * Character map trie node.
@@ -36,35 +96,52 @@ struct chr_t_entry
     chr_t_entry **children; /* array of children */
     unsigned char *target;  /* target for this node, if any */
     unsigned char *equiv;   /* equivalent to, or sumthin */
-} t_entry;
+};
+
+/*
+ * General argument structure for callback functions (internal use only)
+ */
+typedef struct chrwork 
+{
+    chrmaptab map;
+    char string[CHR_MAXSTR+1];
+} chrwork;
 
 /*
  * Add an entry to the character map.
  */
-static chr_t_entry *set_map_string(chr_t_entry *root, char *from, int len,
-    char *to)
+static chr_t_entry *set_map_string(chr_t_entry *root, NMEM nmem,
+                                  const char *from, int len, char *to)
 {
     if (!root)
     {
-       root = xmalloc(sizeof(*root));
+       root = (chr_t_entry *) nmem_malloc(nmem, sizeof(*root));
        root->children = 0;
        root->target = 0;
     }
     if (!len)
-       root->target = (unsigned char *) xstrdup(to);
+    {
+       if (!root->target || (char*) root->target == CHR_SPACE ||
+           (char*) root->target == CHR_UNKNOWN)
+           root->target = (unsigned char *) nmem_strdup(nmem, to);
+       else if ((char*) to != CHR_SPACE)
+           logf(LOG_DEBUG, "Character map overlap");
+    }
     else
     {
        if (!root->children)
        {
            int i;
 
-           root->children = xmalloc(sizeof(chr_t_entry*) * 256);
+           root->children = (chr_t_entry **)
+               nmem_malloc(nmem, sizeof(chr_t_entry*) * 256);
            for (i = 0; i < 256; i++)
                root->children[i] = 0;
        }
-       root->children[(unsigned char) *from] =
-           set_map_string(root->children[(unsigned char) *from], from + 1,
-           len - 1, to);
+       if (!(root->children[(unsigned char) *from] =
+           set_map_string(root->children[(unsigned char) *from], nmem,
+                          from + 1, len - 1, to)))
+           return 0;
     }
     return root;
 }
@@ -94,37 +171,43 @@ int chr_map_chrs(chr_t_entry *t, char **from, int len, int *read, char **to,
     return i;
 }
 
-char **chr_map_input(chr_t_entry *t, char **from, int len)
+
+static chr_t_entry *find_entry(chr_t_entry *t, const char **from, int len)
 {
-    static char *buf[2] = {0, 0}, str[2] = {0, 0};
-    char *start = *from;
+    chr_t_entry *res;
 
-    if (t)
+    if (len && t->children && t->children[(unsigned char) **from])
     {
-       while (len && t->children && t->children[(unsigned char) **from])
-       {
-           t = t->children[(unsigned char) **from];
-           (*from)++;
-           len--;
-       }
-       buf[0] = (char*) t->target;
-    }
-    else /* null mapping */
-    {
-       if (isalnum(**from))
-       {
-           str[0] = **from;
-           buf[0] = str;
-       }
-       else if (isspace(**from))
-           buf[0] = (char*) CHR_SPACE;
-       else
-           buf[0] = (char*) CHR_UNKNOWN;
-    }
-    if (start == *from)
+       const char *pos = *from;
+
        (*from)++;
-    return buf;
-    /* return (char*) t->target; */
+       if ((res = find_entry(t->children[(unsigned char) *pos],
+           from, len - 1)))
+           return res;
+       /* no match */
+       *from = pos;
+    }
+    /* no children match. use ourselves, if we have a target */
+   return t->target ? t : 0;
+}
+
+const char **chr_map_input(chrmaptab maptab, const char **from, int len)
+{
+    chr_t_entry *t = maptab->input;
+    chr_t_entry *res;
+
+    if (!(res = find_entry(t, from, len)))
+       abort();
+    maptab->tmp_buf[0] = (char*) res->target;
+    maptab->tmp_buf[1] = NULL;
+    return (const char **) maptab->tmp_buf;
+}
+
+const char *chr_map_output(chrmaptab maptab, const char **from, int len)
+{
+    unsigned char c = ** (unsigned char **) from;
+    (*from)++;
+    return (const char*) maptab->output[c];
 }
 
 static unsigned char prim(char **s)
@@ -160,30 +243,59 @@ static unsigned char prim(char **s)
  * Callback function.
  * Add an entry to the value space.
  */
-static void fun_addentry(char *s, void *data, int num)
+static void fun_addentry(const char *s, void *data, int num)
 {
-    chrmaptab *tab = data;
+    chrmaptab tab = (chrmaptab) data;
     char tmp[2];
 
     tmp[0] = num; tmp[1] = '\0';
-    tab->input = set_map_string(tab->input, s, strlen(s), tmp);
-    tab->output[num + tab->base_uppercase] = (unsigned char *) xstrdup(s);
+    tab->input = set_map_string(tab->input, tab->nmem, s, strlen(s), tmp);
+    tab->output[num + tab->base_uppercase] =
+       (unsigned char *) nmem_strdup(tab->nmem, s);
 }
 
 /* 
  * Callback function.
  * Add a space-entry to the value space.
  */
-static void fun_addspace(char *s, void *data, int num)
+static void fun_addspace(const char *s, void *data, int num)
 {
-    chrmaptab *tab = data;
-    tab->input = set_map_string(tab->input, s, strlen(s), (char*) CHR_SPACE);
+    chrmaptab tab = (chrmaptab) data;
+    tab->input = set_map_string(tab->input, tab->nmem, s, strlen(s),
+                               (char*) CHR_SPACE);
 }
 
-static int scan_string(char *s, void (*fun)(char *c, void *data, int num),
-    void *data, int *num)
+/*
+ * Create a string containing the mapped characters provided.
+ */
+static void fun_mkstring(const char *s, void *data, int num)
+{
+    chrwork *arg = (chrwork *) data;
+    const char **res, *p = s;
+
+    res = chr_map_input(arg->map, &s, strlen(s));
+    if (*res == (char*) CHR_UNKNOWN)
+       logf(LOG_WARN, "Map: '%s' has no mapping", p);
+    strncat(arg->string, *res, CHR_MAXSTR - strlen(arg->string));
+    arg->string[CHR_MAXSTR] = '\0';
+}
+
+/*
+ * Add a map to the string contained in the argument.
+ */
+static void fun_addmap(const char *s, void *data, int num)
+{
+    chrwork *arg = (chrwork *) data;
+
+    assert(arg->map->input);
+    set_map_string(arg->map->input, arg->map->nmem, s, strlen(s), arg->string);
+}
+
+static int scan_string(char *s,
+                      void (*fun)(const char *c, void *data, int num),
+                      void *data, int *num)
 {
-    unsigned char c, str[1024], begin, end;
+    unsigned char c, str[1024], begin, end, *p;
 
     while (*s)
     {
@@ -212,7 +324,22 @@ static int scan_string(char *s, void (*fun)(char *c, void *data, int num),
                }
                break;
            case '[': s++; abort(); break;
-           case '(': s++; abort(); break;
+           case '(':
+               p = (unsigned char*) ++s;
+               /* Find the end-marker, ignoring escapes */
+               do
+               {
+                   if (!(p = (unsigned char*) strchr((char*) p, ')')))
+                   {
+                       logf(LOG_FATAL, "Missing ')' in string");
+                       return -1;
+                   }
+               }
+               while (*(p -  1) == '\\');
+               *p = 0;
+               (*fun)(s, data, num ? (*num)++ : 0);
+               s = (char*) p + 1;
+               break;
            default:
                c = prim(&s);
                str[0] = c; str[1] = '\0';
@@ -222,49 +349,53 @@ static int scan_string(char *s, void (*fun)(char *c, void *data, int num),
     return 0;
 }
 
-chrmaptab *chr_read_maptab(char *name)
+chrmaptab chrmaptab_create(const char *tabpath, const char *name, int map_only)
 {
     FILE *f;
     char line[512], *argv[50];
-    chrmaptab *res = xmalloc(sizeof(*res));
+    chrmaptab res;
+    int lineno = 0;
     int argc, num = (int) *CHR_BASE, i;
 
-    if (!(f = yaz_path_fopen(data1_tabpath, name, "r")))
+    if (!(f = yaz_path_fopen(tabpath, name, "r")))
     {
        logf(LOG_WARN|LOG_ERRNO, "%s", name);
        return 0;
     }
-    res = xmalloc(sizeof(*res));
-    res->input = xmalloc(sizeof(*res->input));
+    res = (chrmaptab) xmalloc(sizeof(*res));
+    res->nmem = nmem_create ();
+    res->tmp_buf = (char **)
+       nmem_malloc (res->nmem, sizeof(*res->tmp_buf) * 100);
+    res->input = (chr_t_entry *) nmem_malloc(res->nmem, sizeof(*res->input));
     res->input->target = (unsigned char*) CHR_UNKNOWN;
     res->input->equiv = 0;
-#if 0
-    res->input->children = xmalloc(sizeof(res->input) * 256);
+    res->input->children = (chr_t_entry **)
+       nmem_malloc(res->nmem, sizeof(res->input) * 256);
     for (i = 0; i < 256; i++)
     {
-       res->input->children[i] = xmalloc(sizeof(*res->input));
+       res->input->children[i] = (chr_t_entry *)
+           nmem_malloc(res->nmem, sizeof(*res->input));
        res->input->children[i]->children = 0;
-       res->input->children[i]->target = CHR_UNKNOWN;
+       if (map_only)
+       {
+           res->input->children[i]->target = (unsigned char *)
+               nmem_malloc (res->nmem, 2 * sizeof(char));
+           res->input->children[i]->target[0] = i;
+           res->input->children[i]->target[1] = 0;
+       }
+       else
+           res->input->children[i]->target = (unsigned char*) CHR_UNKNOWN;
        res->input->children[i]->equiv = 0;
     }
-#else
-    res->input->children = 0;
-#endif
     res->query_equiv = 0;
-    for (i = 0; i < 256; i++)
-    {
-       char *t = xmalloc(2);
-
-       t[0] = i;
-       t[1] = '\0';
-       res->output[i] = (unsigned char*)t;
-    }
+    for (i = *CHR_BASE; i < 256; i++)
+       res->output[i] = 0;
     res->output[(int) *CHR_SPACE] = (unsigned char *) " ";
     res->output[(int) *CHR_UNKNOWN] = (unsigned char*) "@";
     res->base_uppercase = 0;
 
-    while ((argc = readconf_line(f, line, 512, argv, 50)))
-       if (!yaz_matchstr(argv[0], "lowercase"))
+    while ((argc = readconf_line(f, &lineno, line, 512, argv, 50)))
+       if (!map_only && !yaz_matchstr(argv[0], "lowercase"))
        {
            if (argc != 2)
            {
@@ -283,7 +414,7 @@ chrmaptab *chr_read_maptab(char *name)
            res->output[(int) *CHR_UNKNOWN + num] = (unsigned char*) "@";
            num = (int) *CHR_BASE;
        }
-       else if (!yaz_matchstr(argv[0], "uppercase"))
+       else if (!map_only && !yaz_matchstr(argv[0], "uppercase"))
        {
            if (!res->base_uppercase)
            {
@@ -304,7 +435,7 @@ chrmaptab *chr_read_maptab(char *name)
                return 0;
            }
        }
-       else if (!yaz_matchstr(argv[0], "space"))
+       else if (!map_only && !yaz_matchstr(argv[0], "space"))
        {
            if (argc != 2)
            {
@@ -312,21 +443,50 @@ chrmaptab *chr_read_maptab(char *name)
                fclose(f);
                return 0;
            }
-           if (scan_string(argv[1], fun_addspace, res, 0))
+           if (scan_string(argv[1], fun_addspace, res, 0) < 0)
            {
                logf(LOG_FATAL, "Bad space specification");
                fclose(f);
                return 0;
            }
        }
+       else if (!yaz_matchstr(argv[0], "map"))
+       {
+           chrwork buf;
+
+           if (argc != 3)
+           {
+               logf(LOG_FATAL, "charmap MAP directive requires 2 args");
+               fclose(f);
+               return 0;
+           }
+           buf.map = res;
+           buf.string[0] = '\0';
+           if (scan_string(argv[2], fun_mkstring, &buf, 0) < 0)
+           {
+               logf(LOG_FATAL, "Bad map target");
+               fclose(f);
+               return 0;
+           }
+           if (scan_string(argv[1], fun_addmap, &buf, 0) < 0)
+           {
+               logf(LOG_FATAL, "Bad map source");
+               fclose(f);
+               return 0;
+           }
+       }
        else
        {
-#if 0
-           logf(LOG_WARN, "Syntax error at '%s' in %s", line, file);
-           fclose(f);
-           return 0;
-#endif
+           logf(LOG_WARN, "Syntax error at '%s' in %s", line, name);
        }
     fclose(f);
     return res;
 }
+
+void chrmaptab_destroy(chrmaptab tab)
+{
+    nmem_destroy (tab->nmem);
+    xfree (tab);
+}
+
+