Moved zebrautl.h to idzebra/util.h.
[idzebra-moved-to-github.git] / util / res.c
1 /* $Id: res.c,v 1.40 2005-03-30 09:25:25 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 #else
30 #include <unistd.h>
31 #endif
32
33 #include <idzebra/res.h>
34
35 struct res_entry {
36     char *name;
37     char *value;
38     struct res_entry *next;
39 };
40
41 struct res_struct {
42     struct res_entry *first, *last;
43     char *name;
44     int  init;
45     Res def_res;
46     Res over_res;
47 };
48
49 static struct res_entry *add_entry (Res r)
50 {
51     struct res_entry *resp;
52
53     if (!r->first)
54         resp = r->last = r->first =
55             (struct res_entry *) xmalloc (sizeof(*resp));
56     else
57     {
58         resp = (struct res_entry *) xmalloc (sizeof(*resp));
59         r->last->next = resp;
60         r->last = resp;
61     }
62     resp->next = NULL;
63     return resp;
64 }
65
66 static char *xstrdup_env(const char *src)
67 {
68     int i = 0;
69     int j = 0;
70     char *dst;
71     int env_strlen = 0;
72
73     while (src[i])
74     {
75         if (src[i] == '$' && src[i+1] == '{')
76         {
77             char envname[128];
78             char *env_val;
79             int k = 0;
80             i = i + 2;
81             while (k < 127 && src[i] && !strchr(":}\n\r\f", src[i]))
82                 envname[k++] = src[i++];
83             envname[k] = '\0';
84
85             env_val = getenv(envname);
86             if (env_val)
87                 env_strlen += 1 + strlen(env_val);
88             else
89                 env_strlen++;
90             while (src[i] && !strchr("}\n\r\f", src[i]))
91                 i++;
92             if (src[i] == '}')
93                 i++;
94         }
95         else
96             i++;
97     }
98     dst = xmalloc(1 + env_strlen + i);
99     i = 0;
100     while (src[i])
101     {
102         if (src[i] == '$' && src[i+1] == '{')
103         {
104             char envname[128];
105             char *env_val;
106             int k = 0;
107             i = i + 2;
108             while(k < 127 && src[i] && !strchr(":}\n\r\f", src[i]))
109                 envname[k++] = src[i++];
110             envname[k] = '\0';
111             env_val = getenv(envname);
112             if (env_val)
113             {
114                 strcpy(dst+j, env_val);
115                 j += strlen(env_val);
116             }
117             else if (src[i] == ':' && src[i+1] == '-') 
118             {
119                 i = i + 2;
120                 while (src[i] && !strchr("}\n\r\f", src[i]))
121                     dst[j++] = src[i++];
122             }
123             while (src[i] && !strchr("}\n\r\f", src[i]))
124                 i++;
125             if (src[i] == '}')
126                 i++;
127         }
128         else
129             dst[j++] = src[i++];
130     }
131     dst[j] = '\0';
132     return dst;
133 }
134
135 static void reread (Res r)
136 {
137     struct res_entry *resp;
138     char *line;
139     char *val_buf;
140     int val_size, val_max = 256;
141     char fr_buf[1024];
142     FILE *fr;
143
144     assert (r);
145     r->init = 1;
146
147     if (!r->name)
148         return; 
149
150     fr = fopen (r->name, "r");
151     if (!fr)
152     {
153         yaz_log (YLOG_WARN|YLOG_ERRNO, "Cannot open `%s'", r->name);
154         return ;
155     }
156     val_buf = (char*) xmalloc (val_max);
157     while (1)
158     {
159         line = fgets (fr_buf, sizeof(fr_buf)-1, fr);
160         if (!line)
161             break;
162         if (*line == '#')
163         {
164             int no = 0;
165
166             while (fr_buf[no] && fr_buf[no] != '\n')
167                 no++;
168             fr_buf[no] = '\0';
169
170             resp = add_entry (r);
171             resp->name = (char*) xmalloc (no+1);
172             resp->value = NULL;
173             strcpy (resp->name, fr_buf);
174         }
175         else
176         {
177             int no = 0;
178             while (1)
179             {
180                 if (fr_buf[no] == 0 || fr_buf[no] == '\n' )
181                 {
182                     no = 0;
183                     break;
184                 }
185                 if (strchr (": \t", fr_buf[no]))
186                     break;
187                 no++;
188             }
189             if (!no)
190                 continue;
191             fr_buf[no++] = '\0';
192             resp = add_entry (r);
193             resp->name = (char*) xmalloc (no);
194             strcpy (resp->name, fr_buf);
195             
196             while (strchr (" \t", fr_buf[no]))
197                 no++;
198             val_size = 0;
199             while (1)
200             {
201                 if (fr_buf[no] == '\0' || strchr("\n\r\f", fr_buf[no]))
202                 {
203                     while (val_size > 0 &&
204                               (val_buf[val_size-1] == ' ' ||
205                                val_buf[val_size-1] == '\t'))
206                         val_size--;
207                     val_buf[val_size] = '\0';
208                     resp->value = xstrdup_env(val_buf);
209                     yaz_log (YLOG_DEBUG, "(name=%s,value=%s)",
210                          resp->name, resp->value);
211                     break;
212                 }
213                 else if (fr_buf[no] == '\\' && strchr ("\n\r\f", fr_buf[no+1]))
214                 {
215                     line = fgets (fr_buf, sizeof(fr_buf)-1, fr);
216                     if (!line)
217                     {
218                         val_buf[val_size] = '\0';
219                         resp->value = xstrdup_env(val_buf);
220                         break;
221                     }
222                     no = 0;
223                 }
224                 else
225                 {
226                     val_buf[val_size++] = fr_buf[no++];
227                     if (val_size+1 >= val_max)
228                     {
229                         char *nb;
230
231                         nb = (char*) xmalloc (val_max+=1024);
232                         memcpy (nb, val_buf, val_size);
233                         xfree (val_buf);
234                         val_buf = nb;
235                     }
236                 }
237             }
238         }
239     }                
240     xfree (val_buf);
241     fclose (fr);
242 }
243
244 Res res_open (const char *name, Res def_res, Res over_res)
245 {
246     Res r;
247
248     if (name)
249     {
250 #ifdef WIN32
251         if (access (name, 4))
252 #else
253         if (access (name, R_OK))
254 #endif
255         {
256             yaz_log (YLOG_WARN|YLOG_ERRNO, "Cannot open `%s'", name);
257             return 0;
258         }
259     }
260     r = (Res) xmalloc (sizeof(*r));
261     r->init = 0;
262     r->first = r->last = NULL;
263     if (name)
264         r->name = xstrdup (name);
265     else
266         r->name=0;
267     r->def_res = def_res;
268     r->over_res = over_res;
269     return r;
270 }
271
272 void res_close (Res r)
273 {
274     if (!r)
275         return;
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     xfree (r->name);
290     xfree (r);
291 }
292
293 const char *res_get_prefix (Res r, const char *name, const char *prefix,
294                             const char *def)
295 {
296     const char *v = 0;;
297     if (prefix)
298     {
299         char rname[128];
300         
301         if (strlen(name) + strlen(prefix) >= (sizeof(rname)-2))
302             return 0;
303         strcpy(rname, prefix);
304         strcat(rname, ".");
305         strcat(rname, name);
306         v = res_get(r, rname);
307     }
308     if (!v)
309         v = res_get(r, name);
310     if (!v)
311         v = def;
312     return v;
313 }
314
315 const char *res_get (Res r, const char *name)
316 {
317     struct res_entry *re;
318     const char *v;
319
320     if (!r)
321         return 0;
322     
323     v = res_get(r->over_res, name);
324     if (v)
325         return v;
326
327     if (!r->init)
328         reread (r);
329     for (re = r->first; re; re=re->next)
330         if (re->value && !yaz_matchstr (re->name, name))
331             return re->value;
332
333     return res_get (r->def_res, name);
334 }
335
336 const char *res_get_def (Res r, const char *name, const char *def)
337 {
338     const char *t;
339
340     if (!(t = res_get (r, name)))
341     {
342         yaz_log (YLOG_DEBUG, "CAUTION: Using default resource %s:%s", name, def);
343         return def;
344     }
345     else
346         return t;
347 }
348
349 int res_get_match (Res r, const char *name, const char *value, const char *s)
350 {
351     const char *cn = res_get (r, name);
352
353     if (!cn)
354         cn = s;
355     if (cn && !yaz_matchstr (cn, value))
356         return 1;
357     return 0;
358 }
359
360 void res_set (Res r, const char *name, const char *value)
361 {
362     struct res_entry *re;
363     assert (r);
364     if (!r->init)
365         reread (r);
366
367     for (re = r->first; re; re=re->next)
368         if (re->value && !yaz_matchstr (re->name, name))
369         {
370             xfree (re->value);
371             re->value = xstrdup_env (value);
372             return;
373         }
374     re = add_entry (r);
375     re->name = xstrdup (name);
376     re->value = xstrdup_env (value);
377 }
378
379 int res_trav (Res r, const char *prefix, void *p,
380               void (*f)(void *p, const char *name, const char *value))
381 {
382     struct res_entry *re;
383     int l = 0;
384     int no = 0;
385     
386     if (!r)
387         return 0;
388     if (prefix)
389         l = strlen(prefix);
390     if (!r->init)
391         reread (r);
392     for (re = r->first; re; re=re->next)
393         if (re->value)
394             if (l==0 || !memcmp (re->name, prefix, l))
395             {
396                 (*f)(p, re->name, re->value);
397                 no++;
398             }
399     if (!no)
400         return res_trav (r->def_res, prefix, p, f);
401     return no;
402 }
403
404
405 int res_write (Res r)
406 {
407     struct res_entry *re;
408     FILE *fr;
409
410     assert (r);
411     if (!r->init)
412         reread (r);
413     if (!r->name)
414         return 0; /* ok, this was not from a file */
415     fr = fopen (r->name, "w");
416     if (!fr)
417     {
418         yaz_log (YLOG_FATAL|YLOG_ERRNO, "Cannot create `%s'", r->name);
419         exit (1);
420     }
421
422     for (re = r->first; re; re=re->next)
423     {
424         int no = 0;
425         int lefts = strlen(re->name)+2;
426
427         if (!re->value)
428             fprintf (fr, "%s\n", re->name);
429         else
430         {
431             fprintf (fr, "%s: ", re->name);
432             while (lefts + strlen(re->value+no) > 78)
433             {
434                 int i = 20;
435                 int ind = no+ 78-lefts;
436                 while (--i >= 0)
437                 {
438                     if (re->value[ind] == ' ')
439                         break;
440                     --ind;
441                 }
442                 if (i<0)
443                     ind = no + 78 - lefts;
444                 for (i = no; i != ind; i++)
445                     putc (re->value[i], fr);
446                 fprintf (fr, "\\\n");
447                 no=ind;
448                 lefts = 0;
449             }
450             fprintf (fr, "%s\n", re->value+no);
451         }
452     }
453     fclose (fr);
454     return 0;
455 }
456