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