Use HAVE_UNISTD_H when including unistd.h.
[idzebra-moved-to-github.git] / util / res.c
1 /* $Id: res.c,v 1.43 2005-06-14 20:28:54 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_close (Res r)
275 {
276     if (!r)
277         return;
278     if (r->init)
279     {
280         struct res_entry *re, *re1;
281         for (re = r->first; re; re=re1)
282         {
283             if (re->name)
284                 xfree (re->name);
285             if (re->value)
286                 xfree (re->value);
287             re1 = re->next;
288             xfree (re);
289         }
290     }
291     xfree (r->name);
292     xfree (r);
293 }
294
295 const char *res_get_prefix (Res r, const char *name, const char *prefix,
296                             const char *def)
297 {
298     const char *v = 0;;
299     if (prefix)
300     {
301         char rname[128];
302         
303         if (strlen(name) + strlen(prefix) >= (sizeof(rname)-2))
304             return 0;
305         strcpy(rname, prefix);
306         strcat(rname, ".");
307         strcat(rname, name);
308         v = res_get(r, rname);
309     }
310     if (!v)
311         v = res_get(r, name);
312     if (!v)
313         v = def;
314     return v;
315 }
316
317 const char *res_get (Res r, const char *name)
318 {
319     struct res_entry *re;
320     const char *v;
321
322     if (!r)
323         return 0;
324     
325     v = res_get(r->over_res, name);
326     if (v)
327         return v;
328
329     if (!r->init)
330         reread (r);
331     for (re = r->first; re; re=re->next)
332         if (re->value && !yaz_matchstr (re->name, name))
333             return re->value;
334
335     return res_get (r->def_res, name);
336 }
337
338 const char *res_get_def (Res r, const char *name, const char *def)
339 {
340     const char *t;
341
342     if (!(t = res_get (r, name)))
343     {
344         yaz_log (YLOG_DEBUG, "CAUTION: Using default resource %s:%s", name, def);
345         return def;
346     }
347     else
348         return t;
349 }
350
351 int res_get_match (Res r, const char *name, const char *value, const char *s)
352 {
353     const char *cn = res_get (r, name);
354
355     if (!cn)
356         cn = s;
357     if (cn && !yaz_matchstr (cn, value))
358         return 1;
359     return 0;
360 }
361
362 void res_set (Res r, const char *name, const char *value)
363 {
364     struct res_entry *re;
365     assert (r);
366     if (!r->init)
367         reread (r);
368
369     for (re = r->first; re; re=re->next)
370         if (re->value && !yaz_matchstr (re->name, name))
371         {
372             xfree (re->value);
373             re->value = xstrdup_env (value);
374             return;
375         }
376     re = add_entry (r);
377     re->name = xstrdup (name);
378     re->value = xstrdup_env (value);
379 }
380
381 int res_trav (Res r, const char *prefix, void *p,
382               void (*f)(void *p, const char *name, const char *value))
383 {
384     struct res_entry *re;
385     int l = 0;
386     int no = 0;
387     
388     if (!r)
389         return 0;
390     if (prefix)
391         l = strlen(prefix);
392     if (!r->init)
393         reread (r);
394     for (re = r->first; re; re=re->next)
395         if (re->value)
396             if (l==0 || !memcmp (re->name, prefix, l))
397             {
398                 (*f)(p, re->name, re->value);
399                 no++;
400             }
401     if (!no)
402         return res_trav (r->def_res, prefix, p, f);
403     return no;
404 }
405
406
407 int res_write (Res r)
408 {
409     struct res_entry *re;
410     FILE *fr;
411
412     assert (r);
413     if (!r->init)
414         reread (r);
415     if (!r->name)
416         return 0; /* ok, this was not from a file */
417     fr = fopen (r->name, "w");
418     if (!fr)
419     {
420         yaz_log (YLOG_FATAL|YLOG_ERRNO, "Cannot create `%s'", r->name);
421         exit (1);
422     }
423
424     for (re = r->first; re; re=re->next)
425     {
426         int no = 0;
427         int lefts = strlen(re->name)+2;
428
429         if (!re->value)
430             fprintf (fr, "%s\n", re->name);
431         else
432         {
433             fprintf (fr, "%s: ", re->name);
434             while (lefts + strlen(re->value+no) > 78)
435             {
436                 int i = 20;
437                 int ind = no+ 78-lefts;
438                 while (--i >= 0)
439                 {
440                     if (re->value[ind] == ' ')
441                         break;
442                     --ind;
443                 }
444                 if (i<0)
445                     ind = no + 78 - lefts;
446                 for (i = no; i != ind; i++)
447                     putc (re->value[i], fr);
448                 fprintf (fr, "\\\n");
449                 no=ind;
450                 lefts = 0;
451             }
452             fprintf (fr, "%s\n", re->value+no);
453         }
454     }
455     fclose (fr);
456     return 0;
457 }
458
459 ZEBRA_RES res_get_int(Res r, const char *name, int *val)
460 {
461     const char *cp = res_get(r, name);
462     if (cp)
463     {
464         if (sscanf(cp, "%d", val) == 1)
465             return ZEBRA_OK;
466         yaz_log(YLOG_WARN, "Expected integer for resource %s", name);
467     }
468     return ZEBRA_FAIL;
469 }