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