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