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