Added a utility res_clear which removes/clears all items in a
[idzebra-moved-to-github.git] / util / res.c
1 /* $Id: res.c,v 1.44 2005-08-17 21:28:07 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 struct res_entry {
38     char *name;
39     char *value;
40     struct res_entry *next;
41 };
42
43 struct res_struct {
44     struct res_entry *first, *last;
45     char *name;
46     int  init;
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 static void reread (Res r)
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     r->init = 1;
148
149     if (!r->name)
150         return; 
151
152     fr = fopen (r->name, "r");
153     if (!fr)
154     {
155         yaz_log (YLOG_WARN|YLOG_ERRNO, "Cannot open `%s'", r->name);
156         return ;
157     }
158     val_buf = (char*) xmalloc (val_max);
159     while (1)
160     {
161         line = fgets (fr_buf, sizeof(fr_buf)-1, fr);
162         if (!line)
163             break;
164         if (*line == '#')
165         {
166             int no = 0;
167
168             while (fr_buf[no] && fr_buf[no] != '\n')
169                 no++;
170             fr_buf[no] = '\0';
171
172             resp = add_entry (r);
173             resp->name = (char*) xmalloc (no+1);
174             resp->value = NULL;
175             strcpy (resp->name, fr_buf);
176         }
177         else
178         {
179             int no = 0;
180             while (1)
181             {
182                 if (fr_buf[no] == 0 || fr_buf[no] == '\n' )
183                 {
184                     no = 0;
185                     break;
186                 }
187                 if (strchr (": \t", fr_buf[no]))
188                     break;
189                 no++;
190             }
191             if (!no)
192                 continue;
193             fr_buf[no++] = '\0';
194             resp = add_entry (r);
195             resp->name = (char*) xmalloc (no);
196             strcpy (resp->name, fr_buf);
197             
198             while (strchr (" \t", fr_buf[no]))
199                 no++;
200             val_size = 0;
201             while (1)
202             {
203                 if (fr_buf[no] == '\0' || strchr("\n\r\f", fr_buf[no]))
204                 {
205                     while (val_size > 0 &&
206                               (val_buf[val_size-1] == ' ' ||
207                                val_buf[val_size-1] == '\t'))
208                         val_size--;
209                     val_buf[val_size] = '\0';
210                     resp->value = xstrdup_env(val_buf);
211                     yaz_log (YLOG_DEBUG, "(name=%s,value=%s)",
212                          resp->name, resp->value);
213                     break;
214                 }
215                 else if (fr_buf[no] == '\\' && strchr ("\n\r\f", fr_buf[no+1]))
216                 {
217                     line = fgets (fr_buf, sizeof(fr_buf)-1, fr);
218                     if (!line)
219                     {
220                         val_buf[val_size] = '\0';
221                         resp->value = xstrdup_env(val_buf);
222                         break;
223                     }
224                     no = 0;
225                 }
226                 else
227                 {
228                     val_buf[val_size++] = fr_buf[no++];
229                     if (val_size+1 >= val_max)
230                     {
231                         char *nb;
232
233                         nb = (char*) xmalloc (val_max+=1024);
234                         memcpy (nb, val_buf, val_size);
235                         xfree (val_buf);
236                         val_buf = nb;
237                     }
238                 }
239             }
240         }
241     }                
242     xfree (val_buf);
243     fclose (fr);
244 }
245
246 Res res_open (const char *name, Res def_res, Res over_res)
247 {
248     Res r;
249
250     if (name)
251     {
252 #ifdef WIN32
253         if (access (name, 4))
254 #else
255         if (access (name, R_OK))
256 #endif
257         {
258             yaz_log (YLOG_WARN|YLOG_ERRNO, "Cannot open `%s'", name);
259             return 0;
260         }
261     }
262     r = (Res) xmalloc (sizeof(*r));
263     r->init = 0;
264     r->first = r->last = NULL;
265     if (name)
266         r->name = xstrdup (name);
267     else
268         r->name=0;
269     r->def_res = def_res;
270     r->over_res = over_res;
271     return r;
272 }
273
274 void res_clear (Res r)
275 {
276     if (r->init)
277     {
278         struct res_entry *re, *re1;
279         for (re = r->first; re; re=re1)
280         {
281             if (re->name)
282                 xfree (re->name);
283             if (re->value)
284                 xfree (re->value);
285             re1 = re->next;
286             xfree (re);
287         }
288     }
289     r->init = 0;
290     r->first = r->last = NULL;
291 }
292
293 void res_close (Res r)
294 {
295     if (!r)
296         return;
297     res_clear(r);
298
299     xfree (r->name);
300     xfree (r);
301 }
302
303 const char *res_get_prefix (Res r, const char *name, const char *prefix,
304                             const char *def)
305 {
306     const char *v = 0;;
307     if (prefix)
308     {
309         char rname[128];
310         
311         if (strlen(name) + strlen(prefix) >= (sizeof(rname)-2))
312             return 0;
313         strcpy(rname, prefix);
314         strcat(rname, ".");
315         strcat(rname, name);
316         v = res_get(r, rname);
317     }
318     if (!v)
319         v = res_get(r, name);
320     if (!v)
321         v = def;
322     return v;
323 }
324
325 const char *res_get (Res r, const char *name)
326 {
327     struct res_entry *re;
328     const char *v;
329
330     if (!r)
331         return 0;
332     
333     v = res_get(r->over_res, name);
334     if (v)
335         return v;
336
337     if (!r->init)
338         reread (r);
339     for (re = r->first; re; re=re->next)
340         if (re->value && !yaz_matchstr (re->name, name))
341             return re->value;
342
343     return res_get (r->def_res, name);
344 }
345
346 const char *res_get_def (Res r, const char *name, const char *def)
347 {
348     const char *t;
349
350     if (!(t = res_get (r, name)))
351     {
352         yaz_log (YLOG_DEBUG, "CAUTION: Using default resource %s:%s", name, def);
353         return def;
354     }
355     else
356         return t;
357 }
358
359 int res_get_match (Res r, const char *name, const char *value, const char *s)
360 {
361     const char *cn = res_get (r, name);
362
363     if (!cn)
364         cn = s;
365     if (cn && !yaz_matchstr (cn, value))
366         return 1;
367     return 0;
368 }
369
370 void res_set (Res r, const char *name, const char *value)
371 {
372     struct res_entry *re;
373     assert (r);
374     if (!r->init)
375         reread (r);
376
377     for (re = r->first; re; re=re->next)
378         if (re->value && !yaz_matchstr (re->name, name))
379         {
380             xfree (re->value);
381             re->value = xstrdup_env (value);
382             return;
383         }
384     re = add_entry (r);
385     re->name = xstrdup (name);
386     re->value = xstrdup_env (value);
387 }
388
389 int res_trav (Res r, const char *prefix, void *p,
390               void (*f)(void *p, const char *name, const char *value))
391 {
392     struct res_entry *re;
393     int l = 0;
394     int no = 0;
395     
396     if (!r)
397         return 0;
398     if (prefix)
399         l = strlen(prefix);
400     if (!r->init)
401         reread (r);
402     for (re = r->first; re; re=re->next)
403         if (re->value)
404             if (l==0 || !memcmp (re->name, prefix, l))
405             {
406                 (*f)(p, re->name, re->value);
407                 no++;
408             }
409     if (!no)
410         return res_trav (r->def_res, prefix, p, f);
411     return no;
412 }
413
414
415 int res_write (Res r)
416 {
417     struct res_entry *re;
418     FILE *fr;
419
420     assert (r);
421     if (!r->init)
422         reread (r);
423     if (!r->name)
424         return 0; /* ok, this was not from a file */
425     fr = fopen (r->name, "w");
426     if (!fr)
427     {
428         yaz_log (YLOG_FATAL|YLOG_ERRNO, "Cannot create `%s'", r->name);
429         exit (1);
430     }
431
432     for (re = r->first; re; re=re->next)
433     {
434         int no = 0;
435         int lefts = strlen(re->name)+2;
436
437         if (!re->value)
438             fprintf (fr, "%s\n", re->name);
439         else
440         {
441             fprintf (fr, "%s: ", re->name);
442             while (lefts + strlen(re->value+no) > 78)
443             {
444                 int i = 20;
445                 int ind = no+ 78-lefts;
446                 while (--i >= 0)
447                 {
448                     if (re->value[ind] == ' ')
449                         break;
450                     --ind;
451                 }
452                 if (i<0)
453                     ind = no + 78 - lefts;
454                 for (i = no; i != ind; i++)
455                     putc (re->value[i], fr);
456                 fprintf (fr, "\\\n");
457                 no=ind;
458                 lefts = 0;
459             }
460             fprintf (fr, "%s\n", re->value+no);
461         }
462     }
463     fclose (fr);
464     return 0;
465 }
466
467 ZEBRA_RES res_get_int(Res r, const char *name, int *val)
468 {
469     const char *cp = res_get(r, name);
470     if (cp)
471     {
472         if (sscanf(cp, "%d", val) == 1)
473             return ZEBRA_OK;
474         yaz_log(YLOG_WARN, "Expected integer for resource %s", name);
475     }
476     return ZEBRA_FAIL;
477 }