renamed the package to libgedcom-dev
[gedcom-parse.git] / gedcom / multilex.c
1 /* The lexer multiplexer for Gedcom.
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_internal.h"
25 #include "multilex.h"
26 #include "encoding.h"
27 #include "encoding_state.h"
28 #include "xref.h"
29
30 int line_no = 0;
31
32 typedef int (*lex_func)(void);
33 lex_func lf;
34
35 #define NEW_MODEL_FILE "new.ged"
36
37 int lexer_init(Encoding enc, FILE* f)
38 {
39   if (enc == ONE_BYTE) {
40     lf  = &gedcom_1byte_lex;
41     gedcom_1byte_myinit(f);
42     set_read_encoding_width(enc);
43     return open_conv_to_internal("ASCII");
44   }
45   else if (enc == TWO_BYTE_HILO) {
46     lf  = &gedcom_hilo_lex;
47     gedcom_hilo_myinit(f);
48     set_read_encoding_width(enc);
49     return open_conv_to_internal("UNICODE");
50   }
51   else if (enc == TWO_BYTE_LOHI) {
52     lf  = &gedcom_lohi_lex;
53     gedcom_lohi_myinit(f);
54     set_read_encoding_width(enc);
55     return open_conv_to_internal("UNICODE");
56   }
57   else {
58     return 0;
59   }
60 }
61
62 void lexer_close()
63 {
64   close_conv_to_internal();
65 }
66
67 int gedcom_lex()
68 {
69   return (*lf)();
70 }
71
72 void rewind_file(FILE* f)
73 {
74   if (fseek(f, 0, 0) != 0)
75     gedcom_warning(_("Error positioning input file: %s"), strerror(errno));
76 }
77
78 int determine_encoding(FILE* f)
79 {
80   char first[2];
81   int read;
82
83   set_read_encoding_bom(WITHOUT_BOM);
84   read = fread(first, 1, 2, f);
85   if (read != 2) {
86     gedcom_warning(_("Error reading from input file: %s"), strerror(errno));
87     rewind_file(f);
88     return ONE_BYTE;
89   }
90   else if ((first[0] == '0') && (first[1] == ' ')) {
91     gedcom_debug_print("One-byte encoding");
92     rewind_file(f);
93     return ONE_BYTE;
94   }
95   else if ((first[0] == '\0') && (first[1] == '0')) {
96     gedcom_debug_print("Two-byte encoding, high-low");
97     rewind_file(f);
98     return TWO_BYTE_HILO;
99   }
100   else if ((first[0] == '\xFE') && (first[1] == '\xFF')) {
101     gedcom_debug_print("Two-byte encoding, high-low, with BOM");
102     set_read_encoding_bom(WITH_BOM);
103     return TWO_BYTE_HILO;
104   }
105   else if ((first[0] == '0') && (first[1] == '\0')) {
106     gedcom_debug_print("Two-byte encoding, low-high");
107     rewind_file(f);
108     return TWO_BYTE_LOHI;
109   }
110   else if ((first[0] == '\xFF') && (first[1] == '\xFE')) {
111     gedcom_debug_print("Two-byte encoding, low-high, with BOM");
112     set_read_encoding_bom(WITH_BOM);
113     return TWO_BYTE_LOHI;
114   }
115   else if ((first[0] == '\xEF') && (first[1] == '\xBB')) {
116     read = fread(first, 1, 1, f);
117     if (read != 1) {
118       gedcom_warning(_("Error reading from input file: %s"), strerror(errno));
119       rewind_file(f);
120     }
121     else if (first[0] == '\xBF') {
122       set_read_encoding_bom(WITH_BOM);
123       gedcom_debug_print("UTF-8 encoding, with BOM");
124     }
125     else {
126       gedcom_warning(_("Unknown encoding, falling back to one-byte"));
127       rewind_file(f);
128     }
129     return ONE_BYTE;
130   }
131   else {
132     gedcom_warning(_("Unknown encoding, falling back to one-byte"));
133     rewind_file(f);
134     return ONE_BYTE;
135   }
136 }
137
138 int init_called = 0;
139
140 /** This function initializes the Gedcom parser library and must be called
141     before any other function in this library.
142
143     The function also initializes locale handling by calling
144     <tt> setlocale(LC_ALL, "") </tt>, in case the application would not do this
145     (it doesn't hurt for the application to do the same).
146
147     \attention This function should be called as early as possible.  The
148     requirement
149     is that it should come before the first call to \c iconv_open (part of the
150     generic character set conversion feature) in the program, either by your
151     program itself, or indirectly by the library calls it makes.
152     \attention Practically,
153     it should e.g. come before any calls to any GTK functions, because GTK
154     uses \c iconv_open in its initialization.
155
156     \retval 0 in case of success
157     \retval nonzero in case of failure (e.g. failure to set locale)
158  */
159 int gedcom_init()
160 {
161   init_called = 1;
162   update_gconv_search_path();
163   init_encodings();
164   if (!setlocale(LC_ALL, "")
165       || ! bindtextdomain(PACKAGE, LOCALEDIR)
166       || ! bind_textdomain_codeset(PACKAGE, INTERNAL_ENCODING))
167     return 1;
168   else
169     return 0;
170 }
171
172 /** This function parses the given file.  By itself, it doesn't provide any
173     other information than the parse result.
174
175     The function also empties the cross-reference table before parsing, and
176     checks the validity of the
177     cross-references if the parse was successful.
178     The following conditions can occur in the cross-reference table:
179       - An xref was defined, but not used (warning)
180       - An xref was used, but not defined (error)
181       - An xref was used as a different type than the defined type (error)
182
183     \param file_name The name of the Gedcom file to parse
184
185     \retval 0 if the parse was successful and no errors were found in the
186     cross-reference table
187     \retval nonzero on errors, which can include:
188             - \ref gedcom_init() was not called
189             - The given file was not found
190             - The parse of the given file failed
191             - There were errors found in the cross-reference table
192  */
193
194 int gedcom_parse_file(const char* file_name)
195 {
196   Encoding enc;
197   int result = 1;
198   FILE* file;
199
200   if (!init_called) {
201     gedcom_error(_("Internal error: GEDCOM parser not initialized"));
202   }
203   else {
204     file = fopen(file_name, "r");
205     if (!file) {
206       gedcom_error(_("Could not open file '%s': %s"),
207                    file_name, strerror(errno));
208     }
209     else {
210       line_no = 1;
211       enc = determine_encoding(file);
212       
213       if (lexer_init(enc, file)) {
214         line_no = 0;
215         make_xref_table();
216         result = gedcom_parse();
217         line_no = 0;
218         if (result == 0)
219           result = check_xref_table();
220       }
221       lexer_close();
222       fclose(file);
223     }
224   }
225
226   return result;
227 }
228
229 /** This function starts a new model.  It does this by parsing the \c new.ged
230     file in the data directory of the library (\c $PREFIX/share/gedcom-parse).
231     This can be used to start from an empty model, and to build up the model
232     by adding new records yourself.
233
234     \retval 0 on success
235     \retval nonzero on errors (mainly the errors from
236             \ref gedcom_parse_file()).
237  */
238
239 int gedcom_new_model()
240 {
241   int result = 1;
242   FILE* file;
243
244   file = fopen(NEW_MODEL_FILE, "r");
245   if (file) {
246     fclose(file);
247     result = gedcom_parse_file(NEW_MODEL_FILE);
248   }
249   else {
250     char* filename = (char*) malloc(strlen(PKGDATADIR) + strlen(NEW_MODEL_FILE)
251                                     + 2);
252     if (!filename)
253       MEMORY_ERROR;
254     else {
255       sprintf(filename, "%s/%s", PKGDATADIR, NEW_MODEL_FILE);
256       result = gedcom_parse_file(filename);
257       free(filename);
258     }
259   }
260   return result;
261 }
262
263 int gedcom_check_version(int major, int minor, int patch)
264 {
265   if (major < GEDCOM_PARSE_VERSION_MAJOR)
266     return 1;
267   else if (major > GEDCOM_PARSE_VERSION_MAJOR)
268     return 0;
269   else if (minor < GEDCOM_PARSE_VERSION_MINOR)
270     return 1;
271   else if (minor > GEDCOM_PARSE_VERSION_MINOR)
272     return 0;
273   else if (patch <= GEDCOM_PARSE_VERSION_PATCH)
274     return 1;
275   else
276     return 0;
277 }