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