Update copyright year + FSF address
[idzebra-moved-to-github.git] / util / res.c
1 /* $Id: res.c,v 1.51 2006-08-14 10:40:34 adam Exp $
2    Copyright (C) 1995-2006
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 this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
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         if (def)
311             yaz_log(YLOG_DEBUG, "Using default resource %s:%s", name, def);
312         return def;
313     }
314     else
315         return t;
316 }
317
318 int res_get_match(Res r, const char *name, const char *value, const char *s)
319 {
320     const char *cn = res_get(r, name);
321
322     if (!cn)
323         cn = s;
324     if (cn && !yaz_matchstr(cn, value))
325         return 1;
326     return 0;
327 }
328
329 void res_set(Res r, const char *name, const char *value)
330 {
331     struct res_entry *re;
332     assert(r);
333
334     for (re = r->first; re; re=re->next)
335         if (re->value && !yaz_matchstr(re->name, name))
336         {
337             xfree(re->value);
338             re->value = xstrdup_env(value);
339             return;
340         }
341     re = add_entry(r);
342     re->name = xstrdup(name);
343     re->value = xstrdup_env(value);
344 }
345
346 int res_trav(Res r, const char *prefix, void *p,
347               void (*f)(void *p, const char *name, const char *value))
348 {
349     struct res_entry *re;
350     int l = 0;
351     int no = 0;
352     
353     if (!r)
354         return 0;
355     no = res_trav(r->over_res, prefix, p, f);
356     if (no)
357         return no;
358     if (prefix)
359         l = strlen(prefix);
360     for (re = r->first; re; re=re->next)
361         if (re->value)
362             if (l==0 || !memcmp(re->name, prefix, l))
363             {
364                 (*f)(p, re->name, re->value);
365                 no++;
366             }
367     if (!no)
368         return res_trav(r->def_res, prefix, p, f);
369     return no;
370 }
371
372
373 ZEBRA_RES res_write_file(Res r, const char *fname)
374 {
375     struct res_entry *re;
376     FILE *fr;
377
378     assert(r);
379     fr = fopen(fname, "w");
380     if (!fr)
381     {
382         yaz_log(YLOG_FATAL|YLOG_ERRNO, "Cannot create `%s'", fname);
383         return ZEBRA_FAIL;
384     }
385
386     for (re = r->first; re; re=re->next)
387     {
388         int no = 0;
389         int lefts = strlen(re->name)+2;
390
391         if (!re->value)
392             fprintf(fr, "%s\n", re->name);
393         else
394         {
395             fprintf(fr, "%s: ", re->name);
396             while (lefts + strlen(re->value+no) > 78)
397             {
398                 int i = 20;
399                 int ind = no+ 78-lefts;
400                 while (--i >= 0)
401                 {
402                     if (re->value[ind] == ' ')
403                         break;
404                     --ind;
405                 }
406                 if (i<0)
407                     ind = no + 78 - lefts;
408                 for (i = no; i != ind; i++)
409                     putc(re->value[i], fr);
410                 fprintf(fr, "\\\n");
411                 no=ind;
412                 lefts = 0;
413             }
414             fprintf(fr, "%s\n", re->value+no);
415         }
416     }
417     fclose(fr);
418     return ZEBRA_OK;
419 }
420
421 ZEBRA_RES res_get_int(Res r, const char *name, int *val)
422 {
423     const char *cp = res_get(r, name);
424     if (cp)
425     {
426         if (sscanf(cp, "%d", val) == 1)
427             return ZEBRA_OK;
428         yaz_log(YLOG_WARN, "Expected integer for resource %s", name);
429     }
430     return ZEBRA_FAIL;
431 }
432
433 /* == pop ================================================================= */
434 Res res_add_over (Res p, Res t)
435 {
436     if ((!p) || (!t))
437         return (0);
438     
439     while (p->over_res)
440         p = p->over_res;
441     
442     p->over_res = t;
443     return (p);
444 }
445
446 void res_remove_over (Res r)
447 {
448     if (!r)
449         return;
450     r->over_res = 0;
451 }
452
453 void res_close_over (Res r)
454 {
455     if (!r)
456         return;
457     if (r->over_res)
458         res_close(r->over_res);
459     r->over_res = 0;
460 }
461
462 void res_add (Res r, const char *name, const char *value)
463 {
464     struct res_entry *re;
465     assert (r);
466     if ((name) && (value)) 
467         yaz_log (YLOG_RES, "res_add res=%p, name=%s, value=%s", r, name, value);
468     
469     re = add_entry (r);
470     re->name = xstrdup (name);
471     re->value = xstrdup_env (value);
472 }
473
474 char **res_2_array (Res r)
475 {
476     struct res_entry *re;
477     int i = 0;
478     char **list;
479     
480     if (!r)
481         return 0;
482     
483     list = xmalloc(sizeof(char *));
484     
485     for (re = r->first; re; re=re->next) {
486         list = xrealloc(list, ((i+3) * sizeof(char *)));
487         list[i++] = strdup(re->name);
488         if (re->value) 
489             list[i++] = strdup(re->value);
490         else
491             list[i++] = strdup("");
492         yaz_log(YLOG_RES, "res2array: %s=%s",re->name, re->value);
493     }
494     list[i++] = 0;
495     return (list);
496 }
497
498 char **res_get_array(Res r, const char* name)
499 {
500     struct res_entry *re;
501     int i = 0;
502     char **list;
503     
504     if (!r)
505         return 0;
506     
507     list = xmalloc(sizeof(char *));
508     
509     for (re = r->first; re; re=re->next)
510         if (re->value && !yaz_matchstr (re->name, name))
511         {
512             list = xrealloc(list, (i+2) * sizeof(char *));
513             list[i++] = xstrdup(re->value);
514         }
515     
516     if (i == 0)
517         return (res_get_array(r->def_res, name));
518     
519     list[i++] = 0;
520     return (list);
521 }
522
523 void res_dump (Res r, int level) 
524 {
525     struct res_entry *re;
526     
527     if (!r)
528         return;
529     
530     for (re = r->first; re; re=re->next) {
531         printf("%*s - %s:='%s'\n",level * 4,"",re->name,re->value);
532     }
533     
534     if (r->def_res) {
535         printf ("%*s DEF ",level * 4,"");
536         res_dump (r->def_res, level + 1);
537     }
538     
539     if (r->over_res) {
540         printf ("%*s OVER ",level * 4,"");
541         res_dump (r->over_res, level + 1);
542     }
543 }
544 /*
545  * Local variables:
546  * c-basic-offset: 4
547  * indent-tabs-mode: nil
548  * End:
549  * vim: shiftwidth=4 tabstop=8 expandtab
550  */
551