Avoid memory corruption with a large number of families.
[gedcom-parse.git] / t / src / standalone.c
1 /* Test program for the Gedcom library.
2    Copyright (C) 2001, 2002 The Genes Development Team
3    This file is part of the Gedcom parser library.
4    Contributed by Peter Verthez <Peter.Verthez@advalvas.be>, 2001.
5
6    The Gedcom parser library is free software; you can redistribute it
7    and/or modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The Gedcom parser library is distributed in the hope that it will be
12    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the Gedcom parser library; if not, write to the
18    Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 /* $Id$ */
22 /* $Name$ */
23
24 #include "gedcom.h"
25 #include "output.h"
26 #include "portability.h"
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <locale.h>
31 #include <errno.h>
32 #include <iconv.h>
33 #include "utf8tools.h"
34
35 #define BOGUS_FILE_NAME "bogus.ged"
36 int total_conv_fails = 0;
37
38 void show_help ()
39 {
40   printf("gedcom-parse test program for libgedcom\n\n");
41   printf("Usage:  testgedcom [options] file\n");
42   printf("Options:\n");
43   printf("  -h    Show this help text\n");
44   printf("  -nc   Disable compatibility mode\n");
45   printf("  -fi   Fail immediately on errors\n");
46   printf("  -fd   Deferred fail on errors, but parse completely\n");
47   printf("  -fn   No fail on errors\n");
48   printf("  -dg   Debug setting: only libgedcom debug messages\n");
49   printf("  -da   Debug setting: libgedcom + yacc debug messages\n");
50   printf("  -2    Run the test parse 2 times instead of once\n");
51   printf("  -3    Run the test parse 3 times instead of once\n");
52   printf("  -b    Parse a bogus file (bogus.ged) before parsing the main file\n");
53   printf("  -q    No output to standard output\n");
54   printf("  -o <outfile>  File to generate output to (def. testgedcom.out)\n");
55 }
56
57 Gedcom_ctxt header_start(Gedcom_rec rec, int level, Gedcom_val xref, char *tag,
58                          char *raw_value, int tag_value,
59                          Gedcom_val parsed_value)
60 {
61   output(1, "Header start\n");
62   return (Gedcom_ctxt)1;
63 }
64
65 void header_end(Gedcom_rec rec, Gedcom_ctxt self, Gedcom_val parsed_value)
66 {
67   output(1, "Header end, context is %ld\n", void_ptr_to_int(self));
68 }
69
70 #define MAXFAMILY 100
71 char family_xreftags[MAXFAMILY][255];
72 int  family_nr = 1;
73
74 Gedcom_ctxt family_start(Gedcom_rec rec, int level, Gedcom_val xref, char *tag,
75                          char *raw_value, int tag_value,
76                          Gedcom_val parsed_value)
77 {
78   struct xref_value *xr = GEDCOM_XREF_PTR(xref);
79   output(1, "Family start, xref is %s\n", xr->string);
80   if (family_nr < MAXFAMILY) {
81     printf("%d\n", family_nr);
82     strcpy(family_xreftags[family_nr], xr->string);
83   }
84   xr->object = (Gedcom_ctxt)int_to_void_ptr(family_nr);
85   return (Gedcom_ctxt)int_to_void_ptr(family_nr++);
86 }
87
88 Gedcom_ctxt rec_start(Gedcom_rec rec, int level, Gedcom_val xref, char *tag,
89                       char *raw_value, int tag_value,
90                       Gedcom_val parsed_value)
91 {
92   char* xref_str = NULL;
93   if (! GEDCOM_IS_NULL(xref))
94     xref_str = GEDCOM_XREF_PTR(xref)->string;
95   output(1, "Rec %s start, xref is %s\n", tag, xref_str);
96   return (Gedcom_ctxt)int_to_void_ptr(tag_value);
97 }
98
99 Gedcom_ctxt note_start(Gedcom_rec rec, int level, Gedcom_val xref, char *tag,
100                        char *raw_value, int tag_value,
101                        Gedcom_val parsed_value)
102 {
103   output(1, "== %d %s (%d) %s (xref is %s)\n",
104          level, tag, tag_value, GEDCOM_STRING(parsed_value),
105          GEDCOM_XREF_PTR(xref)->string);
106   return (Gedcom_ctxt)int_to_void_ptr(tag_value);
107 }
108
109 void family_end(Gedcom_rec rec, Gedcom_ctxt self, Gedcom_val parsed_value)
110 {
111   char* family_xref = "<NOTSAVED>";
112   int   family_nr = void_ptr_to_int(self);
113   if (family_nr < MAXFAMILY)
114     family_xref = family_xreftags[void_ptr_to_int(self)];
115   output(1, "Family end, xref is %s\n", family_xref);
116 }
117
118 Gedcom_ctxt submit_start(Gedcom_rec rec, int level, Gedcom_val xref, char *tag,
119                          char *raw_value, int tag_value,
120                          Gedcom_val parsed_value)
121 {
122   output(1, "Submitter, xref is %s\n", GEDCOM_XREF_PTR(xref)->string);
123   return (Gedcom_ctxt)10000;
124 }
125
126 Gedcom_ctxt source_start(Gedcom_elt elt, Gedcom_ctxt parent, int level,
127                          char *tag, char* raw_value,
128                          int tag_value, Gedcom_val parsed_value)
129 {
130   Gedcom_ctxt self = (Gedcom_ctxt)(void_ptr_to_int(parent) + 1000);
131   output(1, "Source is %s (ctxt is %ld, parent is %ld)\n",
132          GEDCOM_STRING(parsed_value), void_ptr_to_int(self),
133          void_ptr_to_int(parent));
134   return self;
135 }
136
137 void source_end(Gedcom_elt elt, Gedcom_ctxt parent, Gedcom_ctxt self,
138                 Gedcom_val parsed_value)
139 {
140   output(1, "Source context %ld in parent %ld\n",
141          void_ptr_to_int(self), void_ptr_to_int(parent));
142 }
143
144 Gedcom_ctxt head_note_start(Gedcom_elt elt, Gedcom_ctxt parent, int level,
145                             char *tag, char* raw_value,
146                             int tag_value, Gedcom_val parsed_value)
147 {
148   Gedcom_ctxt self = (Gedcom_ctxt)(void_ptr_to_int(parent));
149   output(1, "Note: %s (ctxt is %ld, parent is %ld)\n",
150          GEDCOM_STRING(parsed_value), void_ptr_to_int(self),
151          void_ptr_to_int(parent));
152   return self;
153 }
154
155 void head_note_end(Gedcom_elt elt, Gedcom_ctxt parent, Gedcom_ctxt self,
156                    Gedcom_val parsed_value)
157 {
158   output(1, "Complete note:\n%s\n", GEDCOM_STRING(parsed_value));
159 }
160
161 Gedcom_ctxt date_start(Gedcom_elt elt, Gedcom_ctxt parent, int level,
162                        char *tag, char* raw_value,
163                        int tag_value, Gedcom_val parsed_value)
164 {
165   struct date_value dv;
166   Gedcom_ctxt self = (Gedcom_ctxt)(void_ptr_to_int(parent) + 1000);
167   dv = GEDCOM_DATE(parsed_value);
168   output(1, "Contents of the date_value:\n");
169   output(1, "  raw value: %s\n", raw_value);
170   output(1, "  type: %d\n", dv.type);
171   output(1, "  date1:\n");
172   output(1, "    calendar type: %d\n", dv.date1.cal);
173   output(1, "    day: %s\n", dv.date1.day_str);
174   output(1, "    month: %s\n", dv.date1.month_str);
175   output(1, "    year: %s\n", dv.date1.year_str);
176   output(1, "    date type: %d\n", dv.date1.type);
177   output(1, "    sdn1: %ld\n", dv.date1.sdn1);
178   output(1, "    sdn2: %ld\n", dv.date1.sdn2);
179   output(1, "  date2:\n");
180   output(1, "    calendar type: %d\n", dv.date2.cal);
181   output(1, "    day: %s\n", dv.date2.day_str);
182   output(1, "    month: %s\n", dv.date2.month_str);
183   output(1, "    year: %s\n", dv.date2.year_str);
184   output(1, "    date type: %d\n", dv.date2.type);
185   output(1, "    sdn1: %ld\n", dv.date2.sdn1);
186   output(1, "    sdn2: %ld\n", dv.date2.sdn2);
187   output(1, "  phrase: %s\n", dv.phrase);
188   return self;
189 }
190
191 Gedcom_ctxt age_start(Gedcom_elt elt, Gedcom_ctxt parent, int level,
192                       char *tag, char *raw_value,
193                       int tag_value, Gedcom_val parsed_value)
194 {
195   struct age_value age;
196   Gedcom_ctxt self = (Gedcom_ctxt)(void_ptr_to_int(parent) + 1000);
197   age = GEDCOM_AGE(parsed_value);
198   output(1, "Contents of the age_value:\n");
199   output(1, "  raw value: %s\n", raw_value);
200   output(1, "  type: %d\n", age.type);
201   output(1, "  modifier: %d\n", age.mod);
202   output(1, "  years: %d\n", age.years);
203   output(1, "  months: %d\n", age.months);
204   output(1, "  days: %d\n", age.days);
205   output(1, "  phrase: %s\n", age.phrase);
206   return self;
207 }
208
209 void default_cb(Gedcom_elt elt, Gedcom_ctxt ctxt, int level, char *tag,
210                 char *raw_value, int tag_value)
211 {
212   char   *converted = NULL;
213   int    conv_fails = 0;
214   if (raw_value)
215     converted = convert_utf8_to_locale(raw_value, &conv_fails);
216   output(0, "== %d %s (%d) %s (ctxt is %ld, conversion failures: %d)\n",
217          level, tag, tag_value, converted, void_ptr_to_int(ctxt), conv_fails);
218   total_conv_fails += conv_fails;
219 }
220
221 void subscribe_callbacks()
222 {
223   gedcom_subscribe_to_record(REC_HEAD, header_start, header_end);
224   gedcom_subscribe_to_record(REC_FAM,  family_start, family_end);
225   gedcom_subscribe_to_record(REC_INDI, rec_start, NULL);
226   gedcom_subscribe_to_record(REC_OBJE, rec_start, NULL);
227   gedcom_subscribe_to_record(REC_NOTE, note_start, NULL);
228   gedcom_subscribe_to_record(REC_REPO, rec_start, NULL);
229   gedcom_subscribe_to_record(REC_SOUR, rec_start, NULL);
230   gedcom_subscribe_to_record(REC_SUBN, rec_start, NULL);
231   gedcom_subscribe_to_record(REC_SUBM, submit_start, NULL);
232   gedcom_subscribe_to_record(REC_USER, rec_start, NULL);
233   gedcom_subscribe_to_element(ELT_HEAD_SOUR, source_start, source_end);
234   gedcom_subscribe_to_element(ELT_HEAD_NOTE, head_note_start, head_note_end);
235   gedcom_subscribe_to_element(ELT_SOUR_DATA_EVEN_DATE,
236                               date_start, NULL);
237   gedcom_subscribe_to_element(ELT_SUB_EVT_DATE, date_start, NULL);
238   gedcom_subscribe_to_element(ELT_SUB_FAM_EVT_AGE, age_start, NULL);
239 }
240
241 void gedcom_message_handler(Gedcom_msg_type type, char *msg)
242 {
243   if (type == MESSAGE)
244     output(1, "MESSAGE: ");
245   else if (type == WARNING)
246     output(1, "WARNING: ");
247   else if (type == ERROR)
248     output(1, "ERROR: ");
249   output(1, "%s\n", msg);
250 }
251
252 int main(int argc, char* argv[])
253 {
254   Gedcom_err_mech mech = IMMED_FAIL;
255   int compat_enabled = 1;
256   int debug_level = 0;
257   int run_times   = 1;
258   int bogus       = 0;
259   int result      = 0;
260   char* outfilename = NULL;
261   char* file_name = NULL;
262
263   if (argc > 1) {
264     int i;
265     for (i=1; i<argc; i++) {
266       if (!strncmp(argv[i], "-da", 4))
267         debug_level = 2;
268       else if (!strncmp(argv[i], "-dg", 4))
269         debug_level = 1;
270       else if (!strncmp(argv[i], "-fi", 4))
271         mech = IMMED_FAIL;
272       else if (!strncmp(argv[i], "-fd", 4))
273         mech = DEFER_FAIL;
274       else if (!strncmp(argv[i], "-fn", 4))
275         mech = IGNORE_ERRORS;
276       else if (!strncmp(argv[i], "-nc", 4))
277         compat_enabled = 0;
278       else if (!strncmp(argv[i], "-h", 3)) {
279         show_help();
280         exit(1);
281       }
282       else if (!strncmp(argv[i], "-2", 3)) {
283         run_times = 2;
284       }
285       else if (!strncmp(argv[i], "-3", 3)) {
286         run_times = 3;
287       }
288       else if (!strncmp(argv[i], "-b", 3)) {
289         bogus = 1;
290       }
291       else if (!strncmp(argv[i], "-q", 3)) {
292         output_set_quiet(1);
293       }
294       else if (!strncmp(argv[i], "-o", 3)) {
295         i++;
296         if (i < argc) {
297           outfilename = argv[i];
298         }
299         else {
300           printf ("Missing output file name\n");
301           show_help();
302           exit(1);
303         }
304       }
305       else if (strncmp(argv[i], "-", 1)) {
306         file_name = argv[i];
307         break;
308       }
309       else {
310         printf ("Unrecognized option: %s\n", argv[i]);
311         show_help();
312         exit(1);
313       }
314     }
315   }
316   
317   if (!file_name) {
318     printf("No file name given\n");
319     show_help();
320     exit(1);
321   }
322
323   gedcom_init();
324   setlocale(LC_ALL, "");
325   gedcom_set_debug_level(debug_level, NULL);
326   gedcom_set_compat_handling(compat_enabled);
327   gedcom_set_error_handling(mech);
328   gedcom_set_message_handler(gedcom_message_handler);
329   gedcom_set_default_callback(default_cb);
330   
331   subscribe_callbacks();
332   output_open(outfilename);
333   if (bogus) {
334     output(0, "\n=== Parsing bogus file %s\n", BOGUS_FILE_NAME);
335     gedcom_parse_file(BOGUS_FILE_NAME);
336   }
337   while (run_times-- > 0) {
338     output(0, "\n=== Parsing file %s\n", simple_base_name(file_name));
339     result |= gedcom_parse_file(file_name);
340     output(0, "\n=== Total conversion failures: %d\n", total_conv_fails);
341   }
342   if (result == 0) {
343     output(1, "Parse succeeded\n");
344   }
345   else {
346     output(1, "Parse failed\n");
347   }
348   output_close();
349   return result;
350 }