Implemented an encoding state.
[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 "xref.h"
28
29 int line_no = 0;
30
31 typedef int (*lex_func)(void);
32 lex_func lf;
33
34 #define NEW_MODEL_FILE "new.ged"
35
36 int lexer_init(Encoding enc, FILE* f)
37 {
38   if (enc == ONE_BYTE) {
39     lf  = &gedcom_1byte_lex;
40     gedcom_1byte_myinit(f);
41     set_encoding_width(enc);
42     return open_conv_to_internal("ASCII");
43   }
44   else if (enc == TWO_BYTE_HILO) {
45     lf  = &gedcom_hilo_lex;
46     gedcom_hilo_myinit(f);
47     set_encoding_width(enc);
48     return open_conv_to_internal("UNICODE");
49   }
50   else if (enc == TWO_BYTE_LOHI) {
51     lf  = &gedcom_lohi_lex;
52     gedcom_lohi_myinit(f);
53     set_encoding_width(enc);
54     return open_conv_to_internal("UNICODE");
55   }
56   else {
57     return 0;
58   }
59 }
60
61 void lexer_close()
62 {
63   close_conv_to_internal();
64 }
65
66 int gedcom_lex()
67 {
68   return (*lf)();
69 }
70
71 void rewind_file(FILE* f)
72 {
73   if (fseek(f, 0, 0) != 0)
74     gedcom_warning(_("Error positioning input file: %s"), strerror(errno));
75 }
76
77 int determine_encoding(FILE* f)
78 {
79   char first[2];
80   int read;
81
82   read = fread(first, 1, 2, f);
83   if (read != 2) {
84     gedcom_warning(_("Error reading from input file: %s"), strerror(errno));
85     rewind_file(f);
86     return ONE_BYTE;
87   }
88   else if ((first[0] == '0') && (first[1] == ' ')) {
89     gedcom_debug_print(_("One-byte encoding"));
90     rewind_file(f);
91     return ONE_BYTE;
92   }
93   else if ((first[0] == '\0') && (first[1] == '0')) {
94     gedcom_debug_print(_("Two-byte encoding, high-low"));
95     rewind_file(f);
96     return TWO_BYTE_HILO;
97   }
98   else if ((first[0] == '\xFE') && (first[1] == '\xFF')) {
99     gedcom_debug_print(_("Two-byte encoding, high-low, with BOM"));
100     return TWO_BYTE_HILO;
101   }
102   else if ((first[0] == '0') && (first[1] == '\0')) {
103     gedcom_debug_print(_("Two-byte encoding, low-high"));
104     rewind_file(f);
105     return TWO_BYTE_LOHI;
106   }
107   else if ((first[0] == '\xFF') && (first[1] == '\xFE')) {
108     gedcom_debug_print(_("Two-byte encoding, low-high, with BOM"));
109     return TWO_BYTE_LOHI;
110   }
111   else if ((first[0] == '\xEF') && (first[1] == '\xBB')) {
112     read = fread(first, 1, 1, f);
113     if (read != 1) {
114       gedcom_warning(_("Error reading from input file: %s"), strerror(errno));
115       rewind_file(f);
116     }
117     else if (first[0] == '\xBF') {
118       gedcom_debug_print(_("UTF-8 encoding, with BOM"));
119     }
120     else {
121       gedcom_warning(_("Unknown encoding, falling back to one-byte"));
122       rewind_file(f);
123     }
124     return ONE_BYTE;
125   }
126   else {
127     gedcom_warning(_("Unknown encoding, falling back to one-byte"));
128     rewind_file(f);
129     return ONE_BYTE;
130   }
131 }
132
133 int init_called = 0;
134
135 int gedcom_init()
136 {
137   init_called = 1;
138   update_gconv_search_path();
139   init_encodings();
140   if (!setlocale(LC_ALL, "")
141       || ! bindtextdomain(PACKAGE, LOCALEDIR)
142       || ! bind_textdomain_codeset(PACKAGE, INTERNAL_ENCODING))
143     return 1;
144   else
145     return 0;
146 }
147
148 int gedcom_parse_file(const char* file_name)
149 {
150   Encoding enc;
151   int result = 1;
152   FILE* file;
153
154   if (!init_called) {
155     gedcom_error(_("Internal error: GEDCOM parser not initialized"));
156   }
157   else {
158     file = fopen(file_name, "r");
159     if (!file) {
160       gedcom_error(_("Could not open file '%s': %s"),
161                    file_name, strerror(errno));
162     }
163     else {
164       line_no = 1;
165       enc = determine_encoding(file);
166       
167       if (lexer_init(enc, file)) {
168         line_no = 0;
169         make_xref_table();
170         result = gedcom_parse();
171         line_no = 0;
172         if (result == 0)
173           result = check_xref_table();
174       }
175       lexer_close();
176       fclose(file);
177     }
178   }
179
180   return result;
181 }
182
183 int gedcom_new_model()
184 {
185   int result = 1;
186   FILE* file;
187
188   file = fopen(NEW_MODEL_FILE, "r");
189   if (file) {
190     fclose(file);
191     result = gedcom_parse_file(NEW_MODEL_FILE);
192   }
193   else {
194     char* filename = (char*) malloc(strlen(PKGDATADIR) + strlen(NEW_MODEL_FILE)
195                                     + 2);
196     if (!filename)
197       MEMORY_ERROR;
198     else {
199       sprintf(filename, "%s/%s", PKGDATADIR, NEW_MODEL_FILE);
200       result = gedcom_parse_file(filename);
201       free(filename);
202     }
203   }
204   return result;
205 }
206
207 int gedcom_check_version(int major, int minor, int patch)
208 {
209   if (major < GEDCOM_PARSE_VERSION_MAJOR)
210     return 1;
211   else if (major > GEDCOM_PARSE_VERSION_MAJOR)
212     return 0;
213   else if (minor < GEDCOM_PARSE_VERSION_MINOR)
214     return 1;
215   else if (minor > GEDCOM_PARSE_VERSION_MINOR)
216     return 0;
217   else if (patch <= GEDCOM_PARSE_VERSION_PATCH)
218     return 1;
219   else
220     return 0;
221 }