Implemented an encoding state.
[gedcom-parse.git] / gedcom / write.c
1 /* Write functions 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 "gedcom.h"
26 #include "encoding.h"
27 #include "tag_data.h"
28 #include "buffer.h"
29 #include "utf8tools.h"
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34
35 #define MAXWRITELEN MAXGEDCLINELEN
36
37 /* SYS_NEWLINE is defined in config.h */
38 struct encoding_state write_encoding =
39 { "ASCII", "ASCII", ONE_BYTE, WITHOUT_BOM, SYS_NEWLINE };
40
41 struct Gedcom_write_struct {
42   int       filedesc;
43   convert_t conv;
44   int       total_conv_fails;
45   const char* term;
46   int       ctxt_stack[MAXGEDCLEVEL+1];
47   int       ctxt_level;
48 };
49
50 const char* default_encoding[] = {
51   /* ONE_BYTE */      "ASCII",
52   /* TWO_BYTE_HILO */ "UCS-2BE",
53   /* TWO_BYTE_LOHI */ "UCS-2LE"
54 };
55
56 const char* terminator[] = {
57   /* END_CR */     "\x0D",
58   /* END_LF */     "\x0A",
59   /* END_CR_LF */  "\x0D\x0A",
60   /* END_LF_CR */  "\x0A\x0D"
61 };
62
63 void cleanup_write_buffer();
64 struct safe_buffer write_buffer = { NULL, 0, NULL, 0, cleanup_write_buffer };
65
66 void cleanup_convert_at_buffer();
67 struct safe_buffer convert_at_buffer = { NULL, 0, NULL, 0,
68                                          cleanup_convert_at_buffer };
69
70 void cleanup_write_buffer()
71 {
72   cleanup_buffer(&write_buffer);
73 }
74
75 void cleanup_convert_at_buffer()
76 {
77   cleanup_buffer(&convert_at_buffer);
78 }
79
80 int write_simple(Gedcom_write_hndl hndl,
81                  int level, const char* xref, const char* tag,
82                  const char* value)
83 {
84   int res;
85   
86   if (hndl) {
87     char* converted;
88     int conv_fails;
89     size_t outlen;
90     
91     reset_buffer(&write_buffer);
92     res = safe_buf_append(&write_buffer, "%d", level);
93     if (xref)
94       res += safe_buf_append(&write_buffer, " %s", xref);
95     res += safe_buf_append(&write_buffer, " %s", tag);
96     if (value)
97       res += safe_buf_append(&write_buffer, " %s", value);
98     res += safe_buf_append(&write_buffer, hndl->term);
99
100     if (utf8_strlen(get_buf_string(&write_buffer)) > MAXGEDCLINELEN) {
101       gedcom_error(_("Line too long"));
102     }
103     else {
104       converted = convert_from_utf8(hndl->conv, get_buf_string(&write_buffer),
105                                     &conv_fails, &outlen);
106       
107       if (converted && (conv_fails == 0)) {
108         line_no++;
109         write(hndl->filedesc, converted, outlen);
110       }
111       else {
112         hndl->total_conv_fails += conv_fails;
113         gedcom_error
114           (_("Error converting output string: %s (%d conversion failures)"),
115            strerror(errno), conv_fails);
116       }
117     }
118   }
119   return 0;
120 }
121
122 int write_encoding_value(Gedcom_write_hndl hndl,
123                          int level, char* xref, char* tag, char* value)
124 {
125   if (strcmp(value, write_encoding.charset))
126     gedcom_warning(_("Forcing HEAD.CHAR value to '%s'"),
127                    write_encoding.charset);
128   return write_simple(hndl, level, xref, tag, write_encoding.charset);
129 }
130
131 int supports_continuation(int elt_or_rec, int which_continuation)
132 {
133   return tag_data[elt_or_rec].options & which_continuation;
134 }
135
136 int write_long(Gedcom_write_hndl hndl, int elt_or_rec,
137                int level, const char* xref, const char* tag, const char* value)
138 {
139   int prefix_len, value_len = 0, term_len;
140   char* nl_pos = NULL;
141   if (value) nl_pos = strchr(value, '\n');
142
143   prefix_len = utf8_strlen(tag) + 3;  /* for e.g. "0 INDI " */
144   if (level > 9) prefix_len++;
145   if (xref)      prefix_len += utf8_strlen(xref) + 1;
146   if (value)     value_len  = utf8_strlen(value);
147   term_len   = strlen(hndl->term);
148
149   if (!nl_pos && prefix_len + value_len + term_len <= MAXWRITELEN)
150     write_simple(hndl, level, xref, tag, value);
151   else {
152     const char* value_ptr = value;
153     int cont_supported = supports_continuation(elt_or_rec, OPT_CONT);
154     int cont_as_conc   = supports_continuation(elt_or_rec, OPT_CONT_AS_CONC);
155     if (nl_pos && !cont_supported) {
156       gedcom_error (_("The tag %s doesn't support newlines"), tag);
157       return 1;
158     }
159     else {
160       char value_part[MAXWRITELEN];
161       int cont_prefix_len, write_level = level;
162       cont_prefix_len = utf8_strlen("CONT") + 3;
163       if (level + 1 > 9) cont_prefix_len++;
164
165       while (value_ptr) {
166         char* cont_tag = "CONT";
167         int line_len = (nl_pos && cont_supported
168                         ? nl_pos - value_ptr : value_len);
169
170         if (prefix_len + line_len + term_len > MAXWRITELEN) {
171           line_len = MAXWRITELEN - prefix_len - term_len;
172           if (!cont_as_conc)
173             cont_tag = "CONC";
174         }
175         
176         memset(value_part, 0, sizeof(value_part));
177         strncpy(value_part, value_ptr, line_len);
178         write_simple(hndl, write_level, xref, tag, value_part);
179         
180         if (line_len < value_len) {
181           value_ptr   = value_ptr + line_len;
182           value_len   = value_len - line_len;
183           while (*value_ptr == '\n') {
184             value_ptr++;
185             value_len--;
186           }
187           prefix_len  = cont_prefix_len;
188           write_level = level + 1;
189           xref        = NULL;
190           tag         = cont_tag;
191           nl_pos      = strchr(value_ptr, '\n');
192         }
193         else
194           value_ptr = NULL;
195       }
196     }
197   }
198   
199   return 0;
200 }
201
202 int gedcom_write_set_encoding(const char* new_charset,
203                               Encoding width, Enc_bom bom)
204 {
205   char* new_encoding = NULL;
206   if (!strcmp(new_charset, "UNICODE")) {
207     if (width == ONE_BYTE) {
208       gedcom_error(_("Unicode cannot be encoded into one byte"));
209       return 1;
210     }
211     else {
212       new_encoding = get_encoding(new_charset, width);
213       if (new_encoding) {
214         write_encoding.encoding = new_encoding;
215         write_encoding.width = width;
216         write_encoding.bom   = bom;
217         strncpy(write_encoding.charset, new_charset, MAX_CHARSET_LEN);
218       }
219       else
220         return 1;
221     }
222   }
223   else {
224     new_encoding = get_encoding(new_charset, ONE_BYTE);
225     if (new_encoding) {
226       write_encoding.encoding = new_encoding;
227       write_encoding.width = ONE_BYTE;
228       write_encoding.bom   = bom;
229       strncpy(write_encoding.charset, new_charset, MAX_CHARSET_LEN);
230     }
231     else
232       return 1;
233   }
234   return 0;
235 }
236
237 int gedcom_write_set_line_terminator(Enc_line_end end)
238 {
239   strncpy(write_encoding.terminator, terminator[end], MAX_TERMINATOR_LEN);
240   return 0;
241 }
242
243 Gedcom_write_hndl gedcom_write_open(const char *filename)
244 {
245   Gedcom_write_hndl hndl;
246
247   hndl = (Gedcom_write_hndl)malloc(sizeof(struct Gedcom_write_struct));
248
249   if (!hndl)
250     MEMORY_ERROR;
251   else {
252     hndl->total_conv_fails = 0;
253     hndl->conv = initialize_utf8_conversion(write_encoding.encoding, 0);
254     if (!hndl->conv) {
255       gedcom_error(_("Could not open encoding '%s' for writing: %s"),
256                    write_encoding.encoding, strerror(errno));
257       free(hndl);
258       hndl = NULL;
259     }
260     else {
261       hndl->filedesc = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
262       if (!hndl->filedesc) {
263         gedcom_error(_("Could not open file '%s' for writing: %s"),
264                      filename, strerror(errno));
265         cleanup_utf8_conversion(hndl->conv);
266         free(hndl);
267         hndl = NULL;
268       }
269       else {
270         hndl->term = write_encoding.terminator;
271         hndl->ctxt_level = -1;
272         if (write_encoding.bom == WITH_BOM) {
273           if (write_encoding.width == TWO_BYTE_HILO)
274             write(hndl->filedesc, "\xFE\xFF", 2);
275           else if (write_encoding.width == TWO_BYTE_LOHI)
276             write(hndl->filedesc, "\xFF\xFE", 2);
277           else if (!strcmp(write_encoding.encoding, "UTF-8"))
278             write(hndl->filedesc, "\xEF\xBB\xBF", 3);
279           else
280             gedcom_warning(_("Byte order mark configured, but not relevant"));
281         }
282       }
283     }
284   }
285
286   return hndl;
287 }
288
289 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
290 {
291   int result = 0;
292   if (hndl) {
293     write_simple(hndl, 0, NULL, "TRLR", NULL);
294     if (total_conv_fails)  *total_conv_fails = hndl->total_conv_fails;
295     result = close(hndl->filedesc);
296     cleanup_utf8_conversion(hndl->conv);
297     free(hndl);
298   }
299   return result;
300 }
301
302 char* get_tag_string(int elt_or_rec, int tag)
303 {
304   int tagnum = tag_data[elt_or_rec].tag;
305   if (!tagnum) tagnum = tag;
306
307   if (tagnum) {
308     if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END)
309       return tag_name[tagnum - TAG_NUM_START];
310     else {
311       gedcom_error(_("Not a valid tag: %d"), tagnum);
312       return NULL;
313     }
314   }
315   else {
316     gedcom_error(_("The element or record type '%s' requires a specific tag "
317                    "for writing"),
318                  tag_data[elt_or_rec].elt_name);
319     return NULL;
320   }
321 }
322
323 int check_type(int elt_or_rec, Gedcom_val_type type)
324 {
325   int allowed = tag_data[elt_or_rec].allowed_types;
326   if (allowed & type)
327     return 1;
328   else {
329     gedcom_error(_("Wrong data type for writing element or record type '%s'"),
330                  tag_data[elt_or_rec].elt_name);
331     return 0;
332   }
333 }
334
335 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
336 {
337   if (parent == -1) {
338     hndl->ctxt_level = 0;
339   }
340   else {
341     while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
342       hndl->ctxt_level--;
343     if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
344       hndl->ctxt_level++;
345     }
346     else {
347       gedcom_error(_("Parent %d not found during write of %d"),
348                    parent, elt_or_rec);
349       return -1;
350     }
351   }
352   hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
353   return hndl->ctxt_level;
354 }
355
356 char* convert_at(const char* input)
357 {
358   if (input) {
359     const char* ptr = input;
360     reset_buffer(&convert_at_buffer);
361     while (*ptr) {
362       if (*ptr == '@') {
363         SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
364         SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
365       }
366       else {
367         SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
368       }
369       ptr++;
370     }
371     return get_buf_string(&convert_at_buffer);
372   }
373   else
374     return NULL;
375 }
376
377 int _gedcom_write_val(Gedcom_write_hndl hndl,
378                       int rec_or_elt, int tag, int parent_rec_or_elt,
379                       char* xrefstr, char* val)
380 {
381   int result = 1;
382   int level = 0;
383   char* tag_str = NULL;
384
385   tag_str = get_tag_string(rec_or_elt, tag);
386   level   = get_level(hndl, rec_or_elt, parent_rec_or_elt);
387   if (tag_str && (level != -1)) {
388     if (rec_or_elt == ELT_HEAD_CHAR)
389       result = write_encoding_value(hndl, level, xrefstr, tag_str, val);
390     else if (supports_continuation(rec_or_elt, OPT_CONT|OPT_CONC))
391       result = write_long(hndl, rec_or_elt, level, xrefstr, tag_str, val);
392     else
393       result = write_simple(hndl, level, xrefstr, tag_str, val);
394   }
395
396   return result;
397 }
398
399 int gedcom_write_record_str(Gedcom_write_hndl hndl,
400                             Gedcom_rec rec, char* xrefstr, char* val)
401 {
402   int result = 1;
403   if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL)))
404     result = _gedcom_write_val(hndl, rec, 0, -1, xrefstr, convert_at(val));
405   return result;
406 }
407
408 int gedcom_write_element_str(Gedcom_write_hndl hndl,
409                              Gedcom_elt elt, int tag, int parent_rec_or_elt,
410                              char* val)
411 {
412   int result = 1;
413   if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL)))
414     result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
415                                convert_at(val));
416   return result;
417 }
418
419 int gedcom_write_element_xref(Gedcom_write_hndl hndl,
420                               Gedcom_elt elt, int tag, int parent_rec_or_elt,
421                               struct xref_value* val)
422 {
423   int result = 1;
424   if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL)))
425     result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
426                                val->string);
427   return result;
428 }
429
430 int gedcom_write_element_date(Gedcom_write_hndl hndl,
431                               Gedcom_elt elt, int tag, int parent_rec_or_elt,
432                               struct date_value* val)
433 {
434   int result = 1;
435   if (check_type(elt, (val ? GV_DATE_VALUE : GV_NULL)))
436     result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
437                                gedcom_date_to_string(val));
438   return result;
439 }
440
441 int gedcom_write_element_age(Gedcom_write_hndl hndl,
442                              Gedcom_elt elt, int tag, int parent_rec_or_elt,
443                              struct age_value* val)
444 {
445   int result = 1;
446   if (check_type(elt, (val ? GV_AGE_VALUE : GV_NULL)))
447     result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
448                                gedcom_age_to_string(val));
449   return result;
450 }
451
452 int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, char* tag,
453                           char* xrefstr, char* value)
454 {
455   int result = 1;
456   if (tag && tag[0] == '_')
457     result = write_simple(hndl, level, xrefstr, tag, convert_at(value));
458   return result;
459 }
460
461 int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, char* tag,
462                            char* xrefstr, struct xref_value* val)
463 {
464   int result = 1;
465   if (tag && tag[0] == '_')
466     result = write_simple(hndl, level, xrefstr, tag, val->string);
467   return result;
468 }