]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/ini.c
Tweak leave SQL library links in rpm
[bacula/bacula] / bacula / src / lib / ini.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  * Handle simple configuration file such as "ini" files.
21  * key1 = val     # comment
22  * key2 = val     # <type>
23  *
24  */
25
26 #include "bacula.h"
27 #include "ini.h"
28
29 #define bfree_and_null_const(a) do{if(a){free((void *)a); (a)=NULL;}} while(0)
30 static int dbglevel = 100;
31
32 /* We use this structure to associate a key to the function */
33 struct ini_store {
34    const char *key;
35    const char *comment;
36    INI_ITEM_HANDLER *handler;
37 };
38
39 static struct ini_store funcs[] = {
40    {"@INT32@",  "Integer",          ini_store_int32},
41    {"@PINT32@", "Integer",          ini_store_pint32},
42    {"@PINT64@", "Positive Integer", ini_store_pint64},
43    {"@INT64@",  "Integer",          ini_store_int64},
44    {"@NAME@",   "Simple String",    ini_store_name},
45    {"@STR@",    "String",           ini_store_str},
46    {"@BOOL@",   "on/off",           ini_store_bool},
47    {"@ALIST@",  "String list",      ini_store_alist_str},
48    {"@DATE@",   "Date",             ini_store_date},
49 /* TODO: Add protocol for the FD @ASKFD@ */
50    {NULL,       NULL,               NULL}
51 };
52
53 /*
54  * Get handler code from handler @
55  */
56 const char *ini_get_store_code(INI_ITEM_HANDLER *handler)
57 {
58    for (int i = 0; funcs[i].key ; i++) {
59       if (funcs[i].handler == handler) {
60          return funcs[i].key;
61       }
62    }
63    return NULL;
64 }
65
66 /*
67  * Get handler function from handler name
68  */
69 INI_ITEM_HANDLER *ini_get_store_handler(const char *key)
70 {
71    for (int i = 0; funcs[i].key ; i++) {
72       if (!strcmp(funcs[i].key, key)) {
73          return funcs[i].handler;
74       }
75    }
76    return NULL;
77 }
78
79 /*
80  * Format a scanner error message
81  */
82 static void s_err(const char *file, int line, LEX *lc, const char *msg, ...)
83 {
84    ConfigFile *ini = (ConfigFile *)(lc->caller_ctx);
85    va_list arg_ptr;
86    char buf[MAXSTRING];
87
88    va_start(arg_ptr, msg);
89    bvsnprintf(buf, sizeof(buf), msg, arg_ptr);
90    va_end(arg_ptr);
91
92 #ifdef TEST_PROGRAM
93    printf("ERROR: Config file error: %s\n"
94           "            : Line %d, col %d of file %s\n%s\n",
95       buf, lc->line_no, lc->col_no, lc->fname, lc->line);
96 #endif
97
98    if (ini->jcr) {              /* called from core */
99       Jmsg(ini->jcr, M_ERROR, 0, _("Config file error: %s\n"
100                               "            : Line %d, col %d of file %s\n%s\n"),
101          buf, lc->line_no, lc->col_no, lc->fname, lc->line);
102
103 //   } else if (ini->ctx) {       /* called from plugin */
104 //      ini->bfuncs->JobMessage(ini->ctx, __FILE__, __LINE__, M_FATAL, 0,
105 //                    _("Config file error: %s\n"
106 //                      "            : Line %d, col %d of file %s\n%s\n"),
107 //                            buf, lc->line_no, lc->col_no, lc->fname, lc->line);
108 //
109    } else {                     /* called from ??? */
110       e_msg(file, line, M_ERROR, 0,
111             _("Config file error: %s\n"
112               "            : Line %d, col %d of file %s\n%s\n"),
113             buf, lc->line_no, lc->col_no, lc->fname, lc->line);
114    }
115 }
116
117 /* Reset free items */
118 void ConfigFile::clear_items()
119 {
120    if (!items) {
121       return;
122    }
123
124    for (int i=0; items[i].name; i++) {
125       if (items[i].found) {
126          /* special members require delete or free */
127          if (items[i].handler == ini_store_str) {
128             free(items[i].val.strval);
129             items[i].val.strval = NULL;
130
131          } else if (items[i].handler == ini_store_alist_str) {
132             delete items[i].val.alistval;
133             items[i].val.alistval = NULL;
134          }
135          items[i].found = false;
136       }
137    }
138 }
139
140 void ConfigFile::free_items()
141 {
142    if (items_allocated) {
143       for (int i=0; items[i].name; i++) {
144          bfree_and_null_const(items[i].name);
145          bfree_and_null_const(items[i].comment);
146          bfree_and_null_const(items[i].default_value);
147       }
148    }
149    if (items) {
150       free(items);
151    }
152    items = NULL;
153    items_allocated = false;
154 }
155
156 /* Get a particular item from the items list */
157 int ConfigFile::get_item(const char *name)
158 {
159    if (!items) {
160       return -1;
161    }
162
163    for (int i=0; i < MAX_INI_ITEMS && items[i].name; i++) {
164       if (strcasecmp(name, items[i].name) == 0) {
165          return i;
166       }
167    }
168    return -1;
169 }
170
171 /* Dump a buffer to a file in the working directory
172  * Needed to unserialise() a config
173  */
174 bool ConfigFile::dump_string(const char *buf, int32_t len)
175 {
176    FILE *fp;
177    bool ret=false;
178
179    if (!out_fname) {
180       out_fname = get_pool_memory(PM_FNAME);
181       make_unique_filename(&out_fname, (int)(intptr_t)this, (char*)"configfile");
182    }
183
184    fp = fopen(out_fname, "wb");
185    if (!fp) {
186       return ret;
187    }
188
189    if (fwrite(buf, len, 1, fp) == 1) {
190       ret = true;
191    }
192
193    fclose(fp);
194    return ret;
195 }
196
197 /* Dump the item table format to a text file (used by plugin) */
198 bool ConfigFile::serialize(const char *fname)
199 {
200    FILE *fp;
201    POOLMEM *tmp;
202    int32_t len;
203    bool ret = false;
204
205    if (!items) {
206       return ret;
207    }
208
209    fp = fopen(fname, "w");
210    if (!fp) {
211       return ret;
212    }
213
214    tmp = get_pool_memory(PM_MESSAGE);
215    len = serialize(&tmp);
216    if (fwrite(tmp, len, 1, fp) == 1) {
217       ret = true;
218    }
219    free_pool_memory(tmp);
220
221    fclose(fp);
222    return ret;
223 }
224
225 /* Dump the item table format to a text file (used by plugin) */
226 int ConfigFile::serialize(POOLMEM **buf)
227 {
228    int len;
229    POOLMEM *tmp, *tmp2;
230    if (!items) {
231       **buf = 0;
232       return 0;
233    }
234
235    len = Mmsg(buf, "# Plugin configuration file\n# Version %d\n", version);
236
237    tmp = get_pool_memory(PM_MESSAGE);
238    tmp2 = get_pool_memory(PM_MESSAGE);
239
240    for (int i=0; items[i].name ; i++) {
241       if (items[i].comment) {
242          Mmsg(tmp, "OptPrompt=%s\n", quote_string(tmp2, items[i].comment));
243          pm_strcat(buf, tmp);
244       }
245       if (items[i].default_value) {
246          Mmsg(tmp, "OptDefault=%s\n",
247               quote_string(tmp2, items[i].default_value));
248          pm_strcat(buf, tmp);
249       }
250       if (items[i].required) {
251          Mmsg(tmp, "OptRequired=yes\n");
252          pm_strcat(buf, tmp);
253       }
254
255       Mmsg(tmp, "%s=%s\n\n",
256            items[i].name, ini_get_store_code(items[i].handler));
257
258       /* variable = @INT64@ */
259       len = pm_strcat(buf, tmp);
260    }
261    free_pool_memory(tmp);
262    free_pool_memory(tmp2);
263
264    return len ;
265 }
266
267 /* Dump the item table content to a text file (used by director) */
268 int ConfigFile::dump_results(POOLMEM **buf)
269 {
270    int len;
271    POOLMEM *tmp, *tmp2;
272    if (!items) {
273       **buf = 0;
274       return 0;
275    }
276    len = Mmsg(buf, "# Plugin configuration file\n# Version %d\n", version);
277
278    tmp = get_pool_memory(PM_MESSAGE);
279    tmp2 = get_pool_memory(PM_MESSAGE);
280
281    for (int i=0; items[i].name ; i++) {
282       if (items[i].found) {
283          items[i].handler(NULL, this, &items[i]);
284          if (items[i].comment && *items[i].comment) {
285             Mmsg(tmp, "# %s\n", items[i].comment);
286             pm_strcat(buf, tmp);
287          }
288          if (items[i].handler == ini_store_str  ||
289              items[i].handler == ini_store_name ||
290              items[i].handler == ini_store_date)
291          {
292             Mmsg(tmp, "%s=%s\n\n",
293                  items[i].name,
294                  quote_string(tmp2, this->edit));
295
296          } else {
297             Mmsg(tmp, "%s=%s\n\n", items[i].name, this->edit);
298          }
299          len = pm_strcat(buf, tmp);
300       }
301    }
302    free_pool_memory(tmp);
303    free_pool_memory(tmp2);
304
305    return len ;
306 }
307
308 bool ConfigFile::parse()
309 {
310    int token, i;
311    bool ret = false;
312    bool found;
313
314    lc->options |= LOPT_NO_EXTERN;
315    lc->caller_ctx = (void *)this;
316
317    while ((token=lex_get_token(lc, T_ALL)) != T_EOF) {
318       if (token == T_EOL) {
319          continue;
320       }
321       found = false;
322       for (i=0; items[i].name; i++) {
323          if (strcasecmp(items[i].name, lc->str) == 0) {
324             if ((token = lex_get_token(lc, T_EQUALS)) == T_ERROR) {
325                Dmsg2(dbglevel, "in T_IDENT got token=%s str=%s\n", lex_tok_to_str(token), lc->str);
326                break;
327             }
328             Dmsg2(dbglevel, "parse got token=%s str=%s\n", lex_tok_to_str(token), lc->str);
329             Dmsg1(dbglevel, "calling handler for %s\n", items[i].name);
330             /* Call item handler */
331             ret = items[i].found = items[i].handler(lc, this, &items[i]);
332             found = true;
333             break;
334          }
335       }
336       if (!found) {
337          Dmsg1(dbglevel, "Unfound keyword=%s\n", lc->str);
338          scan_err1(lc, "Keyword %s not found", lc->str);
339          /* We can raise an error here */
340          break;
341       } else {
342          Dmsg1(dbglevel, "Found keyword=%s\n", items[i].name);
343       }
344       if (!ret) {
345          Dmsg1(dbglevel, "Error getting value for keyword=%s\n", items[i].name);
346          break;
347       }
348       Dmsg0(dbglevel, "Continue with while(token) loop\n");
349    }
350
351    for (i=0; items[i].name; i++) {
352       if (items[i].required && !items[i].found) {
353          scan_err1(lc, "%s required but not found", items[i].name);
354          ret = false;
355       }
356    }
357
358    lc = lex_close_file(lc);
359    return ret;
360 }
361
362 /* Parse a config file used by Plugin/Director */
363 bool ConfigFile::parse(const char *fname)
364 {
365    if (!items) {
366       return false;
367    }
368
369    if ((lc = lex_open_file(lc, fname, s_err)) == NULL) {
370       berrno be;
371       Emsg2(M_ERROR, 0, _("Cannot open config file %s: %s\n"),
372             fname, be.bstrerror());
373       return false;
374    }
375    return parse();          /* Parse file */
376 }
377
378 /* Parse a config buffer used by Plugin/Director */
379 bool ConfigFile::parse_buf(const char *buffer)
380 {
381    if (!items) {
382       return false;
383    }
384
385    if ((lc = lex_open_buf(lc, buffer, s_err)) == NULL) {
386       Emsg0(M_ERROR, 0, _("Cannot open lex\n"));
387       return false;
388    }
389    return parse();         /* Parse memory buffer */
390 }
391
392
393 /* Analyse the content of a ini file to build the item list
394  * It uses special syntax for datatype. Used by Director on Restore object
395  *
396  * OptPrompt = "Variable1"
397  * OptRequired
398  * OptDefault = 100
399  * Variable1 = @PINT32@
400  * ...
401  */
402 bool ConfigFile::unserialize(const char *fname)
403 {
404    int token, i, nb = 0;
405    bool ret=false;
406    const char **assign;
407
408    /* At this time, we allow only 32 different items */
409    int s = MAX_INI_ITEMS * sizeof (struct ini_items);
410
411    items = (struct ini_items *) malloc (s);
412    memset(items, 0, s);
413    items_allocated = true;
414
415    /* parse the file and generate the items structure on the fly */
416    if ((lc = lex_open_file(lc, fname, s_err)) == NULL) {
417       berrno be;
418       Emsg2(M_ERROR, 0, _("Cannot open config file %s: %s\n"),
419             fname, be.bstrerror());
420       return false;
421    }
422    lc->options |= LOPT_NO_EXTERN;
423    lc->caller_ctx = (void *)this;
424
425    while ((token=lex_get_token(lc, T_ALL)) != T_EOF) {
426       Dmsg1(dbglevel, "parse got token=%s\n", lex_tok_to_str(token));
427
428       if (token == T_EOL) {
429          continue;
430       }
431
432       ret = false;
433       assign = NULL;
434
435       if (nb >= MAX_INI_ITEMS) {
436          break;
437       }
438
439       if (strcasecmp("optprompt", lc->str) == 0) {
440          assign = &(items[nb].comment);
441
442       } else if (strcasecmp("optdefault", lc->str) == 0) {
443          assign = &(items[nb].default_value);
444
445       } else if (strcasecmp("optrequired", lc->str) == 0) {
446          items[nb].required = true;               /* Don't use argument */
447          scan_to_eol(lc);
448          continue;
449
450       } else {
451          items[nb].name = bstrdup(lc->str);
452       }
453
454       token = lex_get_token(lc, T_ALL);
455       Dmsg1(dbglevel, "in T_IDENT got token=%s\n", lex_tok_to_str(token));
456
457       if (token != T_EQUALS) {
458          scan_err1(lc, "expected an equals, got: %s", lc->str);
459          break;
460       }
461
462       /* We may allow blank variable */
463       if (lex_get_token(lc, T_STRING) == T_ERROR) {
464          break;
465       }
466
467       if (assign) {
468          *assign = bstrdup(lc->str);
469
470       } else {
471          if ((items[nb].handler = ini_get_store_handler(lc->str)) == NULL) {
472             scan_err1(lc, "expected a data type, got: %s", lc->str);
473             break;
474          }
475          nb++;
476       }
477       scan_to_eol(lc);
478       ret = true;
479    }
480
481    if (!ret) {
482       for (i = 0; i < nb ; i++) {
483          bfree_and_null_const(items[i].name);
484          bfree_and_null_const(items[i].comment);
485          bfree_and_null_const(items[i].default_value);
486          items[i].handler = NULL;
487          items[i].required = false;
488       }
489    }
490
491    lc = lex_close_file(lc);
492    return ret;
493 }
494
495 /* ----------------------------------------------------------------
496  * Handle data type. Import/Export
497  * ----------------------------------------------------------------
498  */
499 bool ini_store_str(LEX *lc, ConfigFile *inifile, ini_items *item)
500 {
501    if (!lc) {
502       Mmsg(inifile->edit, "%s", item->val.strval);
503       return true;
504    }
505    if (lex_get_token(lc, T_STRING) == T_ERROR) {
506       return false;
507    }
508    /* If already allocated, free first */
509    if (item->found && item->val.strval) {
510       free(item->val.strval);
511    }
512    item->val.strval = bstrdup(lc->str);
513    scan_to_eol(lc);
514    return true;
515 }
516
517 bool ini_store_name(LEX *lc, ConfigFile *inifile, ini_items *item)
518 {
519    if (!lc) {
520       Mmsg(inifile->edit, "%s", item->val.nameval);
521       return true;
522    }
523    if (lex_get_token(lc, T_NAME) == T_ERROR) {
524       Dmsg0(dbglevel, "Want token=T_NAME got T_ERROR\n");
525       return false;
526    }
527    Dmsg1(dbglevel, "ini_store_name: %s\n", lc->str);
528    strncpy(item->val.nameval, lc->str, sizeof(item->val.nameval));
529    scan_to_eol(lc);
530    return true;
531 }
532
533 bool ini_store_alist_str(LEX *lc, ConfigFile *inifile, ini_items *item)
534 {
535    alist *list;
536    if (!lc) {
537       /* TODO, write back the alist to edit buffer */
538       return true;
539    }
540    if (lex_get_token(lc, T_STRING) == T_ERROR) {
541       return false;
542    }
543
544    if (item->val.alistval == NULL) {
545       list = New(alist(10, owned_by_alist));
546    } else {
547       list = item->val.alistval;
548    }
549
550    Dmsg4(900, "Append %s to alist %p size=%d %s\n",
551          lc->str, list, list->size(), item->name);
552    list->append(bstrdup(lc->str));
553    item->val.alistval = list;
554
555    scan_to_eol(lc);
556    return true;
557 }
558
559 bool ini_store_pint64(LEX *lc, ConfigFile *inifile, ini_items *item)
560 {
561    if (!lc) {
562       Mmsg(inifile->edit, "%lld", item->val.int64val);
563       return true;
564    }
565    if (lex_get_token(lc, T_PINT64) == T_ERROR) {
566       return false;
567    }
568    item->val.int64val = lc->pint64_val;
569    scan_to_eol(lc);
570    return true;
571 }
572
573 bool ini_store_int64(LEX *lc, ConfigFile *inifile, ini_items *item)
574 {
575    if (!lc) {
576       Mmsg(inifile->edit, "%lld", item->val.int64val);
577       return true;
578    }
579    if (lex_get_token(lc, T_INT64) == T_ERROR) {
580       return false;
581    }
582    item->val.int64val = lc->int64_val;
583    scan_to_eol(lc);
584    return true;
585 }
586
587 bool ini_store_pint32(LEX *lc, ConfigFile *inifile, ini_items *item)
588 {
589    if (!lc) {
590       Mmsg(inifile->edit, "%d", item->val.int32val);
591       return true;
592    }
593    if (lex_get_token(lc, T_PINT32) == T_ERROR) {
594       return false;
595    }
596    item->val.int32val = lc->pint32_val;
597    scan_to_eol(lc);
598    return true;
599 }
600
601 bool ini_store_int32(LEX *lc, ConfigFile *inifile, ini_items *item)
602 {
603    if (!lc) {
604       Mmsg(inifile->edit, "%d", item->val.int32val);
605       return true;
606    }
607    if (lex_get_token(lc, T_INT32) == T_ERROR) {
608       return false;
609    }
610    item->val.int32val = lc->int32_val;
611    scan_to_eol(lc);
612    return true;
613 }
614
615 bool ini_store_bool(LEX *lc, ConfigFile *inifile, ini_items *item)
616 {
617    if (!lc) {
618       Mmsg(inifile->edit, "%s", item->val.boolval?"yes":"no");
619       return true;
620    }
621    if (lex_get_token(lc, T_NAME) == T_ERROR) {
622       return false;
623    }
624    if (strcasecmp(lc->str, "yes")  == 0 ||
625        strcasecmp(lc->str, "true") == 0 ||
626        strcasecmp(lc->str, "on")   == 0 ||
627        strcasecmp(lc->str, "1")    == 0)
628    {
629       item->val.boolval = true;
630
631    } else if (strcasecmp(lc->str, "no")    == 0 ||
632               strcasecmp(lc->str, "false") == 0 ||
633               strcasecmp(lc->str, "off")   == 0 ||
634               strcasecmp(lc->str, "0")     == 0)
635    {
636       item->val.boolval = false;
637
638    } else {
639       /* YES and NO must not be translated */
640       scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, ON, OFF, 0, 1, TRUE, or FALSE", lc->str);
641       return false;
642    }
643    scan_to_eol(lc);
644    return true;
645 }
646
647 bool ini_store_date(LEX *lc, ConfigFile *inifile, ini_items *item)
648 {
649    if (!lc) {
650       bstrutime(inifile->edit,sizeof_pool_memory(inifile->edit),item->val.btimeval);
651       return true;
652    }
653    if (lex_get_token(lc, T_STRING) == T_ERROR) {
654       return false;
655    }
656    item->val.btimeval = str_to_utime(lc->str);
657    if (item->val.btimeval == 0) {
658       return false;
659    }
660    scan_to_eol(lc);
661    return true;
662 }
663
664 /* ---------------------------------------------------------------- */
665 #ifdef TEST_PROGRAM
666 /* make ini
667  * export LD_LIBRARY_PATH=.libs/
668  * ./.libs/ini
669  */
670
671 #include <stdio.h>
672
673 int err=0;
674 int nb=0;
675 void _ok(const char *file, int l, const char *op, int value, const char *label)
676 {
677    nb++;
678    if (!value) {
679       err++;
680       printf("ERR %.45s %s:%i on %s\n", label, file, l, op);
681    } else {
682       printf("OK  %.45s\n", label);
683    }
684 }
685
686 #define ok(x, label) _ok(__FILE__, __LINE__, #x, (x), label)
687
688 void _nok(const char *file, int l, const char *op, int value, const char *label)
689 {
690    nb++;
691    if (value) {
692       err++;
693       printf("ERR %.45s %s:%i on !%s\n", label, file, l, op);
694    } else {
695       printf("OK  %.45s\n", label);
696    }
697 }
698
699 #define nok(x, label) _nok(__FILE__, __LINE__, #x, (x), label)
700
701 int report()
702 {
703    printf("Result %i/%i OK\n", nb - err, nb);
704    return err>0;
705 }
706
707 struct ini_items membuf_items[] = {
708    /* name          handler           comment             req */
709    {"client",      ini_store_name,    "Client name",       0},
710    {"serial",      ini_store_int32,   "Serial number",     1},
711    {"max_clients", ini_store_int32,   "Max Clients",       0},
712    {NULL,          NULL,              NULL,                0}
713 };
714
715
716 struct ini_items test_items[] = {
717    /* name          handler           comment             req */
718    {"datastore",   ini_store_name,    "Target Datastore", 0},
719    {"newhost",     ini_store_str,     "New Hostname",     1},
720    {"int64val",    ini_store_int64,   "Int64",            1},
721    {"list",        ini_store_alist_str, "list",           0},
722    {"bool",        ini_store_bool,    "Bool",             0},
723    {"pint64",      ini_store_pint64,  "pint",             0},
724    {"int32",       ini_store_int32,   "int 32bit",        0},
725    {"plugin.test", ini_store_str,     "test with .",      0},
726    {"adate",       ini_store_date,    "test with date",   0},
727    {NULL,          NULL,              NULL,               0}
728 };
729
730 /* In order to link with libbaccfg */
731 int32_t r_last;
732 int32_t r_first;
733 RES_HEAD **res_head;
734 void save_resource(int type, RES_ITEM *items, int pass){}
735 void dump_resource(int type, RES *ares, void sendit(void *sock, const char *fmt, ...), void *sock){}
736 void free_resource(RES *rres, int type){}
737 union URES {
738 };
739 RES_TABLE resources[] = {};
740 URES res_all;
741
742 int main()
743 {
744    FILE *fp;
745    int pos, size;
746    ConfigFile *ini = new ConfigFile();
747    POOLMEM *buf = get_pool_memory(PM_BSOCK);
748    char buffer[2000];
749
750    //debug_level=500;
751    printf("Begin Memory buffer Test\n");
752    ok(ini->register_items(membuf_items, sizeof(struct ini_items)), "Check sizeof ini_items");
753
754    if ((fp = fopen("test.cfg", "w")) == NULL) {
755       exit (1);
756    }
757    fprintf(fp, "client=JohnDoe\n");
758    fprintf(fp, "serial=2\n");
759    fprintf(fp, "max_clients=3\n");
760    fflush(fp);
761    fseek(fp, 0, SEEK_END);
762    size = ftell(fp);
763    fclose(fp);
764
765    if ((fp = fopen("test.cfg", "rb")) == NULL) {
766       printf("Could not open file test.cfg\n");
767       exit (1);
768    }
769
770    size = fread(buffer, 1, size, fp);
771    buffer[size] = 0;
772    //printf("size of read buffer is: %d\n", size);
773    //printf("====buf=%s\n", buffer);
774
775    ok(ini->parse_buf(buffer), "Test memory read with all members");
776
777    ini->clear_items();
778    fclose(fp);
779
780    //debug_level = 0;
781    printf("\n\nBegin Original Full Tests\n");
782    nok(ini->register_items(test_items, 5), "Check bad sizeof ini_items");
783    ok(ini->register_items(test_items, sizeof(struct ini_items)), "Check sizeof ini_items");
784
785    if ((fp = fopen("test.cfg", "w")) == NULL) {
786       exit (1);
787    }
788    fprintf(fp, "# this is a comment\ndatastore=datastore1\nnewhost=\"host1\"\n");
789    fflush(fp);
790
791    nok(ini->parse("test.cfg"), "Test missing member");
792    ini->clear_items();
793
794    fprintf(fp, "int64val=12 # with a comment\n");
795    fprintf(fp, "int64val=10 # with a comment\n");
796    fprintf(fp, "int32=100\n");
797    fprintf(fp, "bool=yes\n");
798    fprintf(fp, "plugin.test=parameter\n");
799    fprintf(fp, "adate=\"1970-01-02 12:00:00\"\n");
800
801    fflush(fp);
802
803    ok(ini->parse("test.cfg"), "Test with all members");
804
805    ok(ini->items[0].found, "Test presence of char[]");
806    ok(!strcmp(ini->items[0].val.nameval, "datastore1"), "Test char[]");
807    ok(ini->items[1].found, "Test presence of char*");
808    ok(!strcmp(ini->items[1].val.strval, "host1"), "Test char*");
809    ok(ini->items[2].found, "Test presence of int");
810    ok(ini->items[2].val.int64val == 10, "Test int");
811    ok(ini->items[4].val.boolval == true, "Test bool");
812    ok(ini->items[6].val.int32val == 100, "Test int 32");
813    ok(ini->items[6].val.btimeval != 126000, "Test btime");
814
815    alist *list = ini->items[3].val.alistval;
816    nok(ini->items[3].found, "Test presence of alist");
817
818    fprintf(fp, "list=a\nlist=b\nlist=c\n");
819    fflush(fp);
820
821    ini->clear_items();
822    ok(ini->parse("test.cfg"), "Test with all members");
823
824    list = ini->items[3].val.alistval;
825    ok(ini->items[3].found, "Test presence of alist");
826    ok(list != NULL, "Test list member");
827    ok(list->size() == 3, "Test list size");
828
829    ok(!strcmp((char *)list->get(0), "a"), "Testing alist[0]");
830    ok(!strcmp((char *)list->get(1), "b"), "Testing alist[1]");
831    ok(!strcmp((char *)list->get(2), "c"), "Testing alist[2]");
832
833    system("cp -f test.cfg test3.cfg");
834
835    fprintf(fp, "pouet='10, 11, 12'\n");
836    fprintf(fp, "pint=-100\n");
837    fprintf(fp, "int64val=-100\n"); /* TODO: fix negative numbers */
838    fflush(fp);
839
840    ini->clear_items();
841    ok(ini->parse("test.cfg"), "Test with errors");
842    nok(ini->items[5].found, "Test presence of positive int");
843
844    fclose(fp);
845    ini->clear_items();
846    ini->free_items();
847
848    /* Test  */
849    if ((fp = fopen("test2.cfg", "w")) == NULL) {
850       exit (1);
851    }
852    fprintf(fp,
853            "# this is a comment\n"
854            "optprompt=\"Datastore Name\"\n"
855            "datastore=@NAME@\n"
856            "optprompt=\"New Hostname to create\"\n"
857            "newhost=@STR@\n"
858            "optprompt=\"Some 64 integer\"\n"
859            "optrequired=yes\n"
860            "int64val=@INT64@\n"
861            "list=@ALIST@\n"
862            "bool=@BOOL@\n"
863            "pint64=@PINT64@\n"
864            "pouet=@STR@\n"
865            "int32=@INT32@\n"
866            "plugin.test=@STR@\n"
867            "adate=@DATE@\n"
868       );
869    fclose(fp);
870
871    ok(ini->unserialize("test2.cfg"), "Test dynamic parse");
872    ok(ini->serialize("test4.cfg"), "Try to dump the item table in a file");
873    ok(ini->serialize(&buf) > 0, "Try to dump the item table in a buffer");
874    ok(ini->parse("test3.cfg"), "Parse test file with dynamic grammar");
875
876    ok((pos = ini->get_item("datastore")) == 0, "Check datastore definition");
877    ok(ini->items[pos].found, "Test presence of char[]");
878    ok(!strcmp(ini->items[pos].val.nameval, "datastore1"), "Test char[]");
879    ok(!strcmp(ini->items[pos].comment, "Datastore Name"), "Check comment");
880    ok(ini->items[pos].required == false, "Check required");
881
882    ok((pos = ini->get_item("newhost")) == 1, "Check newhost definition");
883    ok(ini->items[pos].found, "Test presence of char*");
884    ok(!strcmp(ini->items[pos].val.strval, "host1"), "Test char*");
885    ok(ini->items[pos].required == false, "Check required");
886
887    ok((pos = ini->get_item("int64val")) == 2, "Check int64val definition");
888    ok(ini->items[pos].found, "Test presence of int");
889    ok(ini->items[pos].val.int64val == 10, "Test int");
890    ok(ini->items[pos].required == true, "Check required");
891
892    ok((pos = ini->get_item("bool")) == 4, "Check bool definition");
893    ok(ini->items[pos].val.boolval == true, "Test bool");
894
895    ok((pos = ini->get_item("adate")) == 9, "Check adate definition");
896    ok(ini->items[pos].val.btimeval == 126000, "Test date");
897
898    ok(ini->dump_results(&buf), "Test to dump results");
899    printf("<%s>\n", buf);
900
901    ini->clear_items();
902    ini->free_items();
903    report();
904
905    free_pool_memory(buf);
906    exit (0);
907 }
908
909
910 #endif