f0b1529ca8ca9271d107dbf1cc69aa98bca241c6
[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 #define MAXCHARSETLEN 32
37
38 char charset[MAXCHARSETLEN+1]  = "ASCII";
39 const char* encoding = "ASCII";
40 int write_encoding_details = ONE_BYTE;
41 /* SYS_NEWLINE is defined in config.h */
42 const char* write_terminator = SYS_NEWLINE;
43
44 struct Gedcom_write_struct {
45   int       filedesc;
46   convert_t conv;
47   int       total_conv_fails;
48   const char* term;
49   int       ctxt_stack[MAXGEDCLEVEL+1];
50   int       ctxt_level;
51 };
52
53 const char* default_encoding[] = {
54   /* ONE_BYTE */      "ASCII",
55   /* TWO_BYTE_HILO */ "UCS-2BE",
56   /* TWO_BYTE_LOHI */ "UCS-2LE"
57 };
58
59 const char* terminator[] = {
60   /* END_CR */     "\x0D",
61   /* END_LF */     "\x0A",
62   /* END_CR_LF */  "\x0D\x0A",
63   /* END_LF_CR */  "\x0A\x0D"
64 };
65
66 void cleanup_write_buffer();
67 struct safe_buffer write_buffer = { NULL, 0, NULL, 0, cleanup_write_buffer };
68
69 void cleanup_convert_at_buffer();
70 struct safe_buffer convert_at_buffer = { NULL, 0, NULL, 0,
71                                          cleanup_convert_at_buffer };
72
73 void cleanup_write_buffer()
74 {
75   cleanup_buffer(&write_buffer);
76 }
77
78 void cleanup_convert_at_buffer()
79 {
80   cleanup_buffer(&convert_at_buffer);
81 }
82
83 int write_simple(Gedcom_write_hndl hndl,
84                  int level, const char* xref, const char* tag,
85                  const char* value)
86 {
87   int res;
88   
89   if (hndl) {
90     char* converted;
91     int conv_fails;
92     size_t outlen;
93     
94     reset_buffer(&write_buffer);
95     res = safe_buf_append(&write_buffer, "%d", level);
96     if (xref)
97       res += safe_buf_append(&write_buffer, " %s", xref);
98     res += safe_buf_append(&write_buffer, " %s", tag);
99     if (value)
100       res += safe_buf_append(&write_buffer, " %s", value);
101     res += safe_buf_append(&write_buffer, hndl->term);
102
103     if (utf8_strlen(get_buf_string(&write_buffer)) > MAXGEDCLINELEN) {
104       gedcom_error(_("Line too long"));
105     }
106     else {
107       converted = convert_from_utf8(hndl->conv, get_buf_string(&write_buffer),
108                                     &conv_fails, &outlen);
109       
110       if (converted && (conv_fails == 0)) {
111         line_no++;
112         write(hndl->filedesc, converted, outlen);
113       }
114       else {
115         hndl->total_conv_fails += conv_fails;
116         gedcom_error
117           (_("Error converting output string: %s (%d conversion failures)"),
118            strerror(errno), conv_fails);
119       }
120     }
121   }
122   return 0;
123 }
124
125 int write_encoding(Gedcom_write_hndl hndl,
126                    int level, char* xref, char* tag, char* value)
127 {
128   if (strcmp(value, charset))
129     gedcom_warning(_("Forcing HEAD.CHAR value to '%s'"), charset);
130   return write_simple(hndl, level, xref, tag, 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(const char* new_charset,
205                               Encoding width, Enc_bom bom)
206 {
207   char* new_encoding = NULL;
208   if (!strcmp(new_charset, "UNICODE")) {
209     if (width == ONE_BYTE) {
210       gedcom_error(_("Unicode cannot be encoded into one byte"));
211       return 1;
212     }
213     else {
214       new_encoding = get_encoding(new_charset, width);
215       if (new_encoding) {
216         encoding = new_encoding;
217         write_encoding_details = width | bom;
218         strncpy(charset, new_charset, MAXCHARSETLEN);
219       }
220       else
221         return 1;
222     }
223   }
224   else {
225     new_encoding = get_encoding(new_charset, ONE_BYTE);
226     if (new_encoding) {
227       encoding = new_encoding;
228       write_encoding_details = ONE_BYTE;
229       strncpy(charset, new_charset, MAXCHARSETLEN);
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   write_terminator = terminator[end];
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(encoding, 0);
254     if (!hndl->conv) {
255       gedcom_error(_("Could not open encoding '%s' for writing: %s"),
256                    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_terminator;
271         hndl->ctxt_level = -1;
272         if (write_encoding_details & WITH_BOM) {
273           if (write_encoding_details & TWO_BYTE_HILO)
274             write(hndl->filedesc, "\xFE\xFF", 2);
275           else if (write_encoding_details & TWO_BYTE_LOHI)
276             write(hndl->filedesc, "\xFF\xFE", 2);
277           else
278             gedcom_warning(_("Byte order mark configured, but no Unicode"));
279         }
280       }
281     }
282   }
283
284   return hndl;
285 }
286
287 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
288 {
289   int result = 0;
290   if (hndl) {
291     write_simple(hndl, 0, NULL, "TRLR", NULL);
292     if (total_conv_fails)  *total_conv_fails = hndl->total_conv_fails;
293     result = close(hndl->filedesc);
294     cleanup_utf8_conversion(hndl->conv);
295     free(hndl);
296   }
297   return result;
298 }
299
300 char* get_tag_string(int elt_or_rec, int tag)
301 {
302   int tagnum = tag_data[elt_or_rec].tag;
303   if (!tagnum) tagnum = tag;
304
305   if (tagnum) {
306     if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END)
307       return tag_name[tagnum - TAG_NUM_START];
308     else {
309       gedcom_error(_("Not a valid tag: %d"), tagnum);
310       return NULL;
311     }
312   }
313   else {
314     gedcom_error(_("The element or record type '%s' requires a specific tag "
315                    "for writing"),
316                  tag_data[elt_or_rec].elt_name);
317     return NULL;
318   }
319 }
320
321 int check_type(int elt_or_rec, Gedcom_val_type type)
322 {
323   int allowed = tag_data[elt_or_rec].allowed_types;
324   if (allowed & type)
325     return 1;
326   else {
327     gedcom_error(_("Wrong data type for writing element or record type '%s'"),
328                  tag_data[elt_or_rec].elt_name);
329     return 0;
330   }
331 }
332
333 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
334 {
335   if (parent == -1) {
336     hndl->ctxt_level = 0;
337   }
338   else {
339     while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
340       hndl->ctxt_level--;
341     if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
342       hndl->ctxt_level++;
343     }
344     else {
345       gedcom_error(_("Parent %d not found during write of %d"),
346                    parent, elt_or_rec);
347       return -1;
348     }
349   }
350   hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
351   return hndl->ctxt_level;
352 }
353
354 char* convert_at(const char* input)
355 {
356   if (input) {
357     const char* ptr = input;
358     reset_buffer(&convert_at_buffer);
359     while (*ptr) {
360       if (*ptr == '@') {
361         SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
362         SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
363       }
364       else {
365         SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
366       }
367       ptr++;
368     }
369     return get_buf_string(&convert_at_buffer);
370   }
371   else
372     return NULL;
373 }
374
375 int _gedcom_write_val(Gedcom_write_hndl hndl,
376                       int rec_or_elt, int tag, int parent_rec_or_elt,
377                       char* xrefstr, char* val)
378 {
379   int result = 1;
380   int level = 0;
381   char* tag_str = NULL;
382
383   tag_str = get_tag_string(rec_or_elt, tag);
384   level   = get_level(hndl, rec_or_elt, parent_rec_or_elt);
385   if (tag_str && (level != -1)) {
386     if (rec_or_elt == ELT_HEAD_CHAR)
387       result = write_encoding(hndl, level, xrefstr, tag_str, val);
388     else if (supports_continuation(rec_or_elt, OPT_CONT|OPT_CONC))
389       result = write_long(hndl, rec_or_elt, level, xrefstr, tag_str, val);
390     else
391       result = write_simple(hndl, level, xrefstr, tag_str, val);
392   }
393
394   return result;
395 }
396
397 int gedcom_write_record_str(Gedcom_write_hndl hndl,
398                             Gedcom_rec rec, char* xrefstr, char* val)
399 {
400   int result = 1;
401   if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL)))
402     result = _gedcom_write_val(hndl, rec, 0, -1, xrefstr, convert_at(val));
403   return result;
404 }
405
406 int gedcom_write_element_str(Gedcom_write_hndl hndl,
407                              Gedcom_elt elt, int tag, int parent_rec_or_elt,
408                              char* val)
409 {
410   int result = 1;
411   if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL)))
412     result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
413                                convert_at(val));
414   return result;
415 }
416
417 int gedcom_write_element_xref(Gedcom_write_hndl hndl,
418                               Gedcom_elt elt, int tag, int parent_rec_or_elt,
419                               struct xref_value* val)
420 {
421   int result = 1;
422   if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL)))
423     result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
424                                val->string);
425   return result;
426 }
427
428 int gedcom_write_element_date(Gedcom_write_hndl hndl,
429                               Gedcom_elt elt, int tag, int parent_rec_or_elt,
430                               struct date_value* val)
431 {
432   int result = 1;
433   if (check_type(elt, (val ? GV_DATE_VALUE : GV_NULL)))
434     result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
435                                gedcom_date_to_string(val));
436   return result;
437 }
438
439 int gedcom_write_element_age(Gedcom_write_hndl hndl,
440                              Gedcom_elt elt, int tag, int parent_rec_or_elt,
441                              struct age_value* val)
442 {
443   int result = 1;
444   if (check_type(elt, (val ? GV_AGE_VALUE : GV_NULL)))
445     result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
446                                gedcom_age_to_string(val));
447   return result;
448 }
449
450 int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, char* tag,
451                           char* xrefstr, char* value)
452 {
453   int result = 1;
454   if (tag && tag[0] == '_')
455     result = write_simple(hndl, level, xrefstr, tag, convert_at(value));
456   return result;
457 }
458
459 int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, char* tag,
460                            char* xrefstr, struct xref_value* val)
461 {
462   int result = 1;
463   if (tag && tag[0] == '_')
464     result = write_simple(hndl, level, xrefstr, tag, val->string);
465   return result;
466 }