Work on character-set handling
[idzebra-moved-to-github.git] / util / charmap.c
1 /*
2  * Copyright (C) 1994, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: charmap.c,v $
7  * Revision 1.1  1996-05-31 09:07:18  quinn
8  * Work on character-set handling
9  *
10  *
11  */
12
13 /*
14  * Support module to handle character-conversions into and out of the
15  * Zebra dictionary.
16  */
17
18 #include <ctype.h>
19
20 #include <alexutil.h>
21 #include <yaz-util.h>
22 #include <charmap.h>
23 #include <tpath.h>
24
25 const char *CHR_UNKNOWN = "\001";
26 const char *CHR_SPACE   = "\002";
27 const char *CHR_BASE    = "\003";
28
29 extern char *data1_tabpath;
30
31 /*
32  * Character map trie node.
33  */
34 struct chr_t_entry
35 {
36     chr_t_entry **children; /* array of children */
37     unsigned char *target;  /* target for this node, if any */
38     unsigned char *equiv;   /* equivalent to, or sumthin */
39 } t_entry;
40
41 /*
42  * Add an entry to the character map.
43  */
44 static chr_t_entry *set_map_string(chr_t_entry *root, char *from, int len,
45     char *to)
46 {
47     if (!root)
48     {
49         root = xmalloc(sizeof(*root));
50         root->children = 0;
51         root->target = 0;
52     }
53     if (!len)
54         root->target = (unsigned char *) xstrdup(to);
55     else
56     {
57         if (!root->children)
58         {
59             int i;
60
61             root->children = xmalloc(sizeof(chr_t_entry*) * 256);
62             for (i = 0; i < 256; i++)
63                 root->children[i] = 0;
64         }
65         root->children[(unsigned char) *from] =
66             set_map_string(root->children[(unsigned char) *from], from + 1,
67             len - 1, to);
68     }
69     return root;
70 }
71
72 int chr_map_chrs(chr_t_entry *t, char **from, int len, int *read, char **to,
73     int max)
74 {
75     int i = 0;
76     unsigned char *s;
77
78     while (len && t->children && t->children[(unsigned char) **from])
79     {
80         t = t->children[(unsigned char) **from];
81         (*from)++;
82         len--;
83     }
84     /* if there were no matches, we are still at the root node,
85        which always has a null mapping */
86     for (s = t->target; *s && max; s++)
87     {
88         **to = *s;
89         s++;
90         (*to)++;
91         max--;
92         i++;
93     }
94     return i;
95 }
96
97 char **chr_map_input(chr_t_entry *t, char **from, int len)
98 {
99     static char *buf[2] = {0, 0}, str[2] = {0, 0};
100     char *start = *from;
101
102     if (t)
103     {
104         while (len && t->children && t->children[(unsigned char) **from])
105         {
106             t = t->children[(unsigned char) **from];
107             (*from)++;
108             len--;
109         }
110         buf[0] = (char*) t->target;
111     }
112     else /* null mapping */
113     {
114         if (isalnum(**from))
115         {
116             str[0] = **from;
117             buf[0] = str;
118         }
119         else if (isspace(**from))
120             buf[0] = (char*) CHR_SPACE;
121         else
122             buf[0] = (char*) CHR_UNKNOWN;
123     }
124     if (start == *from)
125         (*from)++;
126     return buf;
127     /* return (char*) t->target; */
128 }
129
130 static unsigned char prim(char **s)
131 {
132     unsigned char c;
133     unsigned int i;
134
135     if (**s == '\\')
136     {
137         (*s)++;
138         c = **s;
139         switch (c)
140         {
141             case '\\': c = '\\'; (*s)++; break;
142             case 'r': c = '\r'; (*s)++; break;
143             case 'n': c = '\n'; (*s)++; break;
144             case 't': c = '\t'; (*s)++; break;
145             case 's': c = ' '; (*s)++; break;
146             case 'x': sscanf(*s, "x%2x", &i); c = i; *s += 3; break;
147             case '{': case '[': case '(': case '}': case ']': case ')':
148                 (*s)++;
149                 break;
150             default: sscanf(*s, "%3o", &i); c = i; *s += 3; break;
151         }
152         return c;
153     }
154     c = **s;
155     ++(*s);
156     return c;
157 }
158
159 /*
160  * Callback function.
161  * Add an entry to the value space.
162  */
163 static void fun_addentry(char *s, void *data, int num)
164 {
165     chrmaptab *tab = data;
166     char tmp[2];
167
168     tmp[0] = num; tmp[1] = '\0';
169     tab->input = set_map_string(tab->input, s, strlen(s), tmp);
170     tab->output[num + tab->base_uppercase] = (unsigned char *) xstrdup(s);
171 }
172
173 /* 
174  * Callback function.
175  * Add a space-entry to the value space.
176  */
177 static void fun_addspace(char *s, void *data, int num)
178 {
179     chrmaptab *tab = data;
180     tab->input = set_map_string(tab->input, s, strlen(s), (char*) CHR_SPACE);
181 }
182
183 static int scan_string(char *s, void (*fun)(char *c, void *data, int num),
184     void *data, int *num)
185 {
186     unsigned char c, str[1024], begin, end;
187
188     while (*s)
189     {
190         switch (*s)
191         {
192             case '{':
193                 s++;
194                 begin = prim(&s);
195                 if (*s != '-')
196                 {
197                     logf(LOG_FATAL, "Bad range in char-map");
198                     return -1;
199                 }
200                 s++;
201                 end = prim(&s);
202                 if (end <= begin)
203                 {
204                     logf(LOG_FATAL, "Bad range in char-map");
205                     return -1;
206                 }
207                 s++;
208                 for (c = begin; c <= end; c++)
209                 {
210                     str[0] = c; str[1] = '\0';
211                     (*fun)((char *) str, data, num ? (*num)++ : 0);
212                 }
213                 break;
214             case '[': s++; abort(); break;
215             case '(': s++; abort(); break;
216             default:
217                 c = prim(&s);
218                 str[0] = c; str[1] = '\0';
219                 (*fun)((char *) str, data, num ? (*num)++ : 0);
220         }
221     }
222     return 0;
223 }
224
225 chrmaptab *chr_read_maptab(char *name)
226 {
227     FILE *f;
228     char line[512], *argv[50];
229     chrmaptab *res = xmalloc(sizeof(*res));
230     int argc, num = (int) *CHR_BASE, i;
231
232     if (!(f = yaz_path_fopen(data1_tabpath, name, "r")))
233     {
234         logf(LOG_WARN|LOG_ERRNO, "%s", name);
235         return 0;
236     }
237     res = xmalloc(sizeof(*res));
238     res->input = xmalloc(sizeof(*res->input));
239     res->input->target = (unsigned char*) CHR_UNKNOWN;
240     res->input->equiv = 0;
241 #if 0
242     res->input->children = xmalloc(sizeof(res->input) * 256);
243     for (i = 0; i < 256; i++)
244     {
245         res->input->children[i] = xmalloc(sizeof(*res->input));
246         res->input->children[i]->children = 0;
247         res->input->children[i]->target = CHR_UNKNOWN;
248         res->input->children[i]->equiv = 0;
249     }
250 #else
251     res->input->children = 0;
252 #endif
253     res->query_equiv = 0;
254     for (i = 0; i < 256; i++)
255     {
256         char *t = xmalloc(2);
257
258         t[0] = i;
259         t[1] = '\0';
260         res->output[i] = (unsigned char*)t;
261     }
262     res->output[(int) *CHR_SPACE] = (unsigned char *) " ";
263     res->output[(int) *CHR_UNKNOWN] = (unsigned char*) "@";
264     res->base_uppercase = 0;
265
266     while ((argc = readconf_line(f, line, 512, argv, 50)))
267         if (!yaz_matchstr(argv[0], "lowercase"))
268         {
269             if (argc != 2)
270             {
271                 logf(LOG_FATAL, "Syntax error in charmap");
272                 fclose(f);
273                 return 0;
274             }
275             if (scan_string(argv[1], fun_addentry, res, &num) < 0)
276             {
277                 logf(LOG_FATAL, "Bad value-set specification");
278                 fclose(f);
279                 return 0;
280             }
281             res->base_uppercase = num;
282             res->output[(int) *CHR_SPACE + num] = (unsigned char *) " ";
283             res->output[(int) *CHR_UNKNOWN + num] = (unsigned char*) "@";
284             num = (int) *CHR_BASE;
285         }
286         else if (!yaz_matchstr(argv[0], "uppercase"))
287         {
288             if (!res->base_uppercase)
289             {
290                 logf(LOG_FATAL, "Uppercase directive with no lowercase set");
291                 fclose(f);
292                 return 0;
293             }
294             if (argc != 2)
295             {
296                 logf(LOG_FATAL, "Syntax error in charmap");
297                 fclose(f);
298                 return 0;
299             }
300             if (scan_string(argv[1], fun_addentry, res, &num) < 0)
301             {
302                 logf(LOG_FATAL, "Bad value-set specification");
303                 fclose(f);
304                 return 0;
305             }
306         }
307         else if (!yaz_matchstr(argv[0], "space"))
308         {
309             if (argc != 2)
310             {
311                 logf(LOG_FATAL, "Syntax error in charmap");
312                 fclose(f);
313                 return 0;
314             }
315             if (scan_string(argv[1], fun_addspace, res, 0))
316             {
317                 logf(LOG_FATAL, "Bad space specification");
318                 fclose(f);
319                 return 0;
320             }
321         }
322         else
323         {
324 #if 0
325             logf(LOG_WARN, "Syntax error at '%s' in %s", line, file);
326             fclose(f);
327             return 0;
328 #endif
329         }
330     fclose(f);
331     return res;
332 }