4fbc33ca722356e38b1ef079589fa527d7bcb64c
[idzebra-moved-to-github.git] / util / res.c
1 /* $Id: res.c,v 1.48 2006-03-26 14:05:19 adam Exp $
2    Copyright (C) 1995-2005
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27 #ifdef WIN32
28 #include <io.h>
29 #endif
30 #if HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33
34 #include <yaz/yaz-util.h>
35 #include <idzebra/res.h>
36
37 #define YLOG_RES 0
38
39 struct res_entry {
40     char *name;
41     char *value;
42     struct res_entry *next;
43 };
44
45 struct res_struct {
46     struct res_entry *first, *last;
47     Res def_res;
48     Res over_res;
49 };
50
51 static struct res_entry *add_entry(Res r)
52 {
53     struct res_entry *resp;
54
55     if (!r->first)
56         resp = r->last = r->first =
57             (struct res_entry *) xmalloc(sizeof(*resp));
58     else
59     {
60         resp = (struct res_entry *) xmalloc(sizeof(*resp));
61         r->last->next = resp;
62         r->last = resp;
63     }
64     resp->next = NULL;
65     return resp;
66 }
67
68 static char *xstrdup_env(const char *src)
69 {
70     int i = 0;
71     int j = 0;
72     char *dst;
73     int env_strlen = 0;
74
75     while (src[i])
76     {
77         if (src[i] == '$' && src[i+1] == '{')
78         {
79             char envname[128];
80             char *env_val;
81             int k = 0;
82             i = i + 2;
83             while (k < 127 && src[i] && !strchr(":}\n\r\f", src[i]))
84                 envname[k++] = src[i++];
85             envname[k] = '\0';
86
87             env_val = getenv(envname);
88             if (env_val)
89                 env_strlen += 1 + strlen(env_val);
90             else
91                 env_strlen++;
92             while (src[i] && !strchr("}\n\r\f", src[i]))
93                 i++;
94             if (src[i] == '}')
95                 i++;
96         }
97         else
98             i++;
99     }
100     dst = xmalloc(1 + env_strlen + i);
101     i = 0;
102     while (src[i])
103     {
104         if (src[i] == '$' && src[i+1] == '{')
105         {
106             char envname[128];
107             char *env_val;
108             int k = 0;
109             i = i + 2;
110             while(k < 127 && src[i] && !strchr(":}\n\r\f", src[i]))
111                 envname[k++] = src[i++];
112             envname[k] = '\0';
113             env_val = getenv(envname);
114             if (env_val)
115             {
116                 strcpy(dst+j, env_val);
117                 j += strlen(env_val);
118             }
119             else if (src[i] == ':' && src[i+1] == '-') 
120             {
121                 i = i + 2;
122                 while (src[i] && !strchr("}\n\r\f", src[i]))
123                     dst[j++] = src[i++];
124             }
125             while (src[i] && !strchr("}\n\r\f", src[i]))
126                 i++;
127             if (src[i] == '}')
128                 i++;
129         }
130         else
131             dst[j++] = src[i++];
132     }
133     dst[j] = '\0';
134     return dst;
135 }
136
137 ZEBRA_RES res_read_file(Res r, const char *fname)
138 {
139     struct res_entry *resp;
140     char *line;
141     char *val_buf;
142     int val_size, val_max = 256;
143     char fr_buf[1024];
144     FILE *fr;
145
146     assert(r);
147
148     fr = fopen(fname, "r");
149     if (!fr)
150     {
151         yaz_log(YLOG_WARN|YLOG_ERRNO, "Cannot open `%s'", fname);
152         return ZEBRA_FAIL;
153     }
154     val_buf = (char*) xmalloc(val_max);
155     while (1)
156     {
157         line = fgets(fr_buf, sizeof(fr_buf)-1, fr);
158         if (!line)
159             break;
160         if (*line != '#')
161         {
162             int no = 0;
163             while (1)
164             {
165                 if (fr_buf[no] == 0 || fr_buf[no] == '\n' )
166                 {
167                     no = 0;
168                     break;
169                 }
170                 if (strchr(": \t", fr_buf[no]))
171                     break;
172                 no++;
173             }
174             if (!no)
175                 continue;
176             fr_buf[no++] = '\0';
177             resp = add_entry(r);
178             resp->name = (char*) xmalloc(no);
179             strcpy(resp->name, fr_buf);
180             
181             while (strchr(" \t", fr_buf[no]))
182                 no++;
183             val_size = 0;
184             while (1)
185             {
186                 if (fr_buf[no] == '\0' || strchr("\n\r\f", fr_buf[no]))
187                 {
188                     while (val_size > 0 &&
189                               (val_buf[val_size-1] == ' ' ||
190                                val_buf[val_size-1] == '\t'))
191                         val_size--;
192                     val_buf[val_size] = '\0';
193                     resp->value = xstrdup_env(val_buf);
194                     yaz_log(YLOG_DEBUG, "(name=%s,value=%s)",
195                          resp->name, resp->value);
196                     break;
197                 }
198                 else if (fr_buf[no] == '\\' && strchr("\n\r\f", fr_buf[no+1]))
199                 {
200                     line = fgets(fr_buf, sizeof(fr_buf)-1, fr);
201                     if (!line)
202                     {
203                         val_buf[val_size] = '\0';
204                         resp->value = xstrdup_env(val_buf);
205                         break;
206                     }
207                     no = 0;
208                 }
209                 else
210                 {
211                     val_buf[val_size++] = fr_buf[no++];
212                     if (val_size+1 >= val_max)
213                     {
214                         char *nb;
215
216                         nb = (char*) xmalloc(val_max+=1024);
217                         memcpy(nb, val_buf, val_size);
218                         xfree(val_buf);
219                         val_buf = nb;
220                     }
221                 }
222             }
223         }
224     }                
225     xfree(val_buf);
226     fclose(fr);
227     return ZEBRA_OK;
228 }
229 Res res_open(Res def_res, Res over_res)
230 {
231     Res r;
232     r = (Res) xmalloc(sizeof(*r));
233     r->first = r->last = NULL;
234     r->def_res = def_res;
235     r->over_res = over_res;
236     return r;
237 }
238
239 void res_clear(Res r)
240 {
241     struct res_entry *re, *re1;
242     for (re = r->first; re; re=re1)
243     {
244         if (re->name)
245             xfree(re->name);
246         if (re->value)
247             xfree(re->value);
248         re1 = re->next;
249         xfree(re);
250     }
251     r->first = r->last = NULL;
252 }
253
254 void res_close(Res r)
255 {
256     if (!r)
257         return;
258     res_clear(r);
259
260     xfree(r);
261 }
262
263 const char *res_get_prefix(Res r, const char *name, const char *prefix,
264                             const char *def)
265 {
266     const char *v = 0;;
267     if (prefix)
268     {
269         char rname[128];
270         
271         if (strlen(name) + strlen(prefix) >= (sizeof(rname)-2))
272             return 0;
273         strcpy(rname, prefix);
274         strcat(rname, ".");
275         strcat(rname, name);
276         v = res_get(r, rname);
277     }
278     if (!v)
279         v = res_get(r, name);
280     if (!v)
281         v = def;
282     return v;
283 }
284
285 const char *res_get(Res r, const char *name)
286 {
287     struct res_entry *re;
288     const char *v;
289
290     if (!r)
291         return 0;
292     
293     v = res_get(r->over_res, name);
294     if (v)
295         return v;
296
297     for (re = r->first; re; re=re->next)
298         if (re->value && !yaz_matchstr(re->name, name))
299             return re->value;
300
301     return res_get(r->def_res, name);
302 }
303
304 const char *res_get_def(Res r, const char *name, const char *def)
305 {
306     const char *t;
307
308     if (!(t = res_get(r, name)))
309     {
310         yaz_log(YLOG_DEBUG, "CAUTION: Using default resource %s:%s", name, def);
311         return def;
312     }
313     else
314         return t;
315 }
316
317 int res_get_match(Res r, const char *name, const char *value, const char *s)
318 {
319     const char *cn = res_get(r, name);
320
321     if (!cn)
322         cn = s;
323     if (cn && !yaz_matchstr(cn, value))
324         return 1;
325     return 0;
326 }
327
328 void res_set(Res r, const char *name, const char *value)
329 {
330     struct res_entry *re;
331     assert(r);
332
333     for (re = r->first; re; re=re->next)
334         if (re->value && !yaz_matchstr(re->name, name))
335         {
336             xfree(re->value);
337             re->value = xstrdup_env(value);
338             return;
339         }
340     re = add_entry(r);
341     re->name = xstrdup(name);
342     re->value = xstrdup_env(value);
343 }
344
345 int res_trav(Res r, const char *prefix, void *p,
346               void (*f)(void *p, const char *name, const char *value))
347 {
348     struct res_entry *re;
349     int l = 0;
350     int no = 0;
351     
352     if (!r)
353         return 0;
354     no = res_trav(r->over_res, prefix, p, f);
355     if (no)
356         return no;
357     if (prefix)
358         l = strlen(prefix);
359     for (re = r->first; re; re=re->next)
360         if (re->value)
361             if (l==0 || !memcmp(re->name, prefix, l))
362             {
363                 (*f)(p, re->name, re->value);
364                 no++;
365             }
366     if (!no)
367         return res_trav(r->def_res, prefix, p, f);
368     return no;
369 }
370
371
372 ZEBRA_RES res_write_file(Res r, const char *fname)
373 {
374     struct res_entry *re;
375     FILE *fr;
376
377     assert(r);
378     fr = fopen(fname, "w");
379     if (!fr)
380     {
381         yaz_log(YLOG_FATAL|YLOG_ERRNO, "Cannot create `%s'", fname);
382         return ZEBRA_FAIL;
383     }
384
385     for (re = r->first; re; re=re->next)
386     {
387         int no = 0;
388         int lefts = strlen(re->name)+2;
389
390         if (!re->value)
391             fprintf(fr, "%s\n", re->name);
392         else
393         {
394             fprintf(fr, "%s: ", re->name);
395             while (lefts + strlen(re->value+no) > 78)
396             {
397                 int i = 20;
398                 int ind = no+ 78-lefts;
399                 while (--i >= 0)
400                 {
401                     if (re->value[ind] == ' ')
402                         break;
403                     --ind;
404                 }
405                 if (i<0)
406                     ind = no + 78 - lefts;
407                 for (i = no; i != ind; i++)
408                     putc(re->value[i], fr);
409                 fprintf(fr, "\\\n");
410                 no=ind;
411                 lefts = 0;
412             }
413             fprintf(fr, "%s\n", re->value+no);
414         }
415     }
416     fclose(fr);
417     return ZEBRA_OK;
418 }
419
420 ZEBRA_RES res_get_int(Res r, const char *name, int *val)
421 {
422     const char *cp = res_get(r, name);
423     if (cp)
424     {
425         if (sscanf(cp, "%d", val) == 1)
426             return ZEBRA_OK;
427         yaz_log(YLOG_WARN, "Expected integer for resource %s", name);
428     }
429     return ZEBRA_FAIL;
430 }
431
432 /* == pop ================================================================= */
433 Res res_add_over (Res p, Res t)
434 {
435     if ((!p) || (!t))
436         return (0);
437     
438     while (p->over_res)
439         p = p->over_res;
440     
441     p->over_res = t;
442     return (p);
443 }
444
445 void res_remove_over (Res r)
446 {
447     if (!r)
448         return;
449     r->over_res = 0;
450 }
451
452 void res_close_over (Res r)
453 {
454     if (!r)
455         return;
456     if (r->over_res)
457         res_close(r->over_res);
458     r->over_res = 0;
459 }
460
461 void res_add (Res r, const char *name, const char *value)
462 {
463     struct res_entry *re;
464     assert (r);
465     if ((name) && (value)) 
466         yaz_log (YLOG_RES, "res_add res=%p, name=%s, value=%s", r, name, value);
467     
468     re = add_entry (r);
469     re->name = xstrdup (name);
470     re->value = xstrdup_env (value);
471 }
472
473 char **res_2_array (Res r)
474 {
475     struct res_entry *re;
476     int i = 0;
477     char **list;
478     
479     if (!r)
480         return 0;
481     
482     list = xmalloc(sizeof(char *));
483     
484     for (re = r->first; re; re=re->next) {
485         list = xrealloc(list, ((i+3) * sizeof(char *)));
486         list[i++] = strdup(re->name);
487         if (re->value) 
488             list[i++] = strdup(re->value);
489         else
490             list[i++] = strdup("");
491         yaz_log(YLOG_RES, "res2array: %s=%s",re->name, re->value);
492     }
493     list[i++] = 0;
494     return (list);
495 }
496
497 char **res_get_array(Res r, const char* name)
498 {
499     struct res_entry *re;
500     int i = 0;
501     char **list;
502     
503     if (!r)
504         return 0;
505     
506     list = xmalloc(sizeof(char *));
507     
508     for (re = r->first; re; re=re->next)
509         if (re->value && !yaz_matchstr (re->name, name))
510         {
511             list = xrealloc(list, (i+2) * sizeof(char *));
512             list[i++] = xstrdup(re->value);
513         }
514     
515     if (i == 0)
516         return (res_get_array(r->def_res, name));
517     
518     list[i++] = 0;
519     return (list);
520 }
521
522 void res_dump (Res r, int level) 
523 {
524     struct res_entry *re;
525     
526     if (!r)
527         return;
528     
529     for (re = r->first; re; re=re->next) {
530         printf("%*s - %s:='%s'\n",level * 4,"",re->name,re->value);
531     }
532     
533     if (r->def_res) {
534         printf ("%*s DEF ",level * 4,"");
535         res_dump (r->def_res, level + 1);
536     }
537     
538     if (r->over_res) {
539         printf ("%*s OVER ",level * 4,"");
540         res_dump (r->over_res, level + 1);
541     }
542 }