Recognize the UTF-8 byte order mark.
[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     return ONE_BYTE;
121   }
122   else {
123     gedcom_warning(_("Unknown encoding, falling back to one-byte"));
124     rewind_file(f);
125     return ONE_BYTE;
126   }
127 }
128
129 int init_called = 0;
130
131 int gedcom_init()
132 {
133   init_called = 1;
134   update_gconv_search_path();
135   init_encodings();
136   if (!setlocale(LC_ALL, "")
137       || ! bindtextdomain(PACKAGE, LOCALEDIR)
138       || ! bind_textdomain_codeset(PACKAGE, INTERNAL_ENCODING))
139     return 1;
140   else
141     return 0;
142 }
143
144 int gedcom_parse_file(const char* file_name)
145 {
146   Encoding enc;
147   int result = 1;
148   FILE* file;
149
150   if (!init_called) {
151     gedcom_error(_("Internal error: GEDCOM parser not initialized"));
152   }
153   else {
154     file = fopen(file_name, "r");
155     if (!file) {
156       gedcom_error(_("Could not open file '%s': %s"),
157                    file_name, strerror(errno));
158     }
159     else {
160       line_no = 1;
161       enc = determine_encoding(file);
162       
163       if (lexer_init(enc, file)) {
164         line_no = 0;
165         make_xref_table();
166         result = gedcom_parse();
167         line_no = 0;
168         if (result == 0)
169           result = check_xref_table();
170       }
171       lexer_close();
172       fclose(file);
173     }
174   }
175
176   return result;
177 }
178
179 int gedcom_new_model()
180 {
181   int result = 1;
182   FILE* file;
183
184   file = fopen(NEW_MODEL_FILE, "r");
185   if (file) {
186     fclose(file);
187     result = gedcom_parse_file(NEW_MODEL_FILE);
188   }
189   else {
190     char* filename = (char*) malloc(strlen(PKGDATADIR) + strlen(NEW_MODEL_FILE)
191                                     + 2);
192     if (!filename)
193       MEMORY_ERROR;
194     else {
195       sprintf(filename, "%s/%s", PKGDATADIR, NEW_MODEL_FILE);
196       result = gedcom_parse_file(filename);
197       free(filename);
198     }
199   }
200   return result;
201 }
202
203 int gedcom_check_version(int major, int minor, int patch)
204 {
205   if (major < GEDCOM_PARSE_VERSION_MAJOR)
206     return 1;
207   else if (major > GEDCOM_PARSE_VERSION_MAJOR)
208     return 0;
209   else if (minor < GEDCOM_PARSE_VERSION_MINOR)
210     return 1;
211   else if (minor > GEDCOM_PARSE_VERSION_MINOR)
212     return 0;
213   else if (patch <= GEDCOM_PARSE_VERSION_PATCH)
214     return 1;
215   else
216     return 0;
217 }