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