Completed doxygen conversion of the documentation for the libgedcom part.
[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 "encoding_state.h"
28 #include "tag_data.h"
29 #include "buffer.h"
30 #include "utf8tools.h"
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35
36 #define MAXWRITELEN MAXGEDCLINELEN
37
38 struct Gedcom_write_struct {
39   int       filedesc;
40   convert_t conv;
41   int       total_conv_fails;
42   const char* term;
43   int       ctxt_stack[MAXGEDCLEVEL+1];
44   int       ctxt_level;
45 };
46
47 void cleanup_write_buffer();
48 struct safe_buffer write_buffer = { NULL, 0, NULL, 0, cleanup_write_buffer };
49
50 void cleanup_convert_at_buffer();
51 struct safe_buffer convert_at_buffer = { NULL, 0, NULL, 0,
52                                          cleanup_convert_at_buffer };
53
54 void cleanup_write_buffer()
55 {
56   cleanup_buffer(&write_buffer);
57 }
58
59 void cleanup_convert_at_buffer()
60 {
61   cleanup_buffer(&convert_at_buffer);
62 }
63
64 int write_simple(Gedcom_write_hndl hndl,
65                  int level, const char* xref, const char* tag,
66                  const char* value)
67 {
68   int res;
69   
70   if (hndl) {
71     char* converted;
72     int conv_fails;
73     size_t outlen;
74     
75     reset_buffer(&write_buffer);
76     res = safe_buf_append(&write_buffer, "%d", level);
77     if (xref)
78       res += safe_buf_append(&write_buffer, " %s", xref);
79     res += safe_buf_append(&write_buffer, " %s", tag);
80     if (value)
81       res += safe_buf_append(&write_buffer, " %s", value);
82     res += safe_buf_append(&write_buffer, hndl->term);
83
84     if (utf8_strlen(get_buf_string(&write_buffer)) > MAXGEDCLINELEN) {
85       gedcom_error(_("Line too long"));
86     }
87     else {
88       converted = convert_from_utf8(hndl->conv, get_buf_string(&write_buffer),
89                                     &conv_fails, &outlen);
90       
91       if (converted && (conv_fails == 0)) {
92         line_no++;
93         write(hndl->filedesc, converted, outlen);
94       }
95       else {
96         hndl->total_conv_fails += conv_fails;
97         gedcom_error
98           (_("Error converting output string: %s (%d conversion failures)"),
99            strerror(errno), conv_fails);
100       }
101     }
102   }
103   return 0;
104 }
105
106 int write_encoding_value(Gedcom_write_hndl hndl,
107                          int level, const char* xref, const char* tag,
108                          const char* value)
109 {
110   if (strcmp(value, write_encoding.charset))
111     gedcom_warning(_("Forcing HEAD.CHAR value to '%s'"),
112                    write_encoding.charset);
113   return write_simple(hndl, level, xref, tag, write_encoding.charset);
114 }
115
116 int supports_continuation(int elt_or_rec, int which_continuation)
117 {
118   return tag_data[elt_or_rec].options & which_continuation;
119 }
120
121 int write_long(Gedcom_write_hndl hndl, int elt_or_rec,
122                int level, const char* xref, const char* tag, const char* value)
123 {
124   int prefix_len, value_len = 0, term_len;
125   char* nl_pos = NULL;
126   if (value) nl_pos = strchr(value, '\n');
127
128   prefix_len = utf8_strlen(tag) + 3;  /* for e.g. "0 INDI " */
129   if (level > 9) prefix_len++;
130   if (xref)      prefix_len += utf8_strlen(xref) + 1;
131   if (value)     value_len  = utf8_strlen(value);
132   term_len   = strlen(hndl->term);
133
134   if (!nl_pos && prefix_len + value_len + term_len <= MAXWRITELEN)
135     write_simple(hndl, level, xref, tag, value);
136   else {
137     const char* value_ptr = value;
138     int cont_supported = supports_continuation(elt_or_rec, OPT_CONT);
139     int cont_as_conc   = supports_continuation(elt_or_rec, OPT_CONT_AS_CONC);
140     if (nl_pos && !cont_supported) {
141       gedcom_error (_("The tag %s doesn't support newlines"), tag);
142       return 1;
143     }
144     else {
145       char value_part[MAXWRITELEN];
146       int cont_prefix_len, write_level = level;
147       cont_prefix_len = utf8_strlen("CONT") + 3;
148       if (level + 1 > 9) cont_prefix_len++;
149
150       while (value_ptr) {
151         char* cont_tag = "CONT";
152         int line_len = (nl_pos && cont_supported
153                         ? nl_pos - value_ptr : value_len);
154
155         if (prefix_len + line_len + term_len > MAXWRITELEN) {
156           line_len = MAXWRITELEN - prefix_len - term_len;
157           if (!cont_as_conc) {
158             cont_tag = "CONC";
159             while (value_ptr[line_len] == ' '
160                    || value_ptr[line_len-1] == ' ') {
161               line_len--;
162             }
163           }
164         }
165         
166         memset(value_part, 0, sizeof(value_part));
167         strncpy(value_part, value_ptr, line_len);
168         write_simple(hndl, write_level, xref, tag, value_part);
169         
170         if (line_len < value_len) {
171           value_ptr   = value_ptr + line_len;
172           value_len   = value_len - line_len;
173           if (*value_ptr == '\n') {
174             value_ptr++;
175             value_len--;
176           }
177           prefix_len  = cont_prefix_len;
178           write_level = level + 1;
179           xref        = NULL;
180           tag         = cont_tag;
181           nl_pos      = strchr(value_ptr, '\n');
182         }
183         else
184           value_ptr = NULL;
185       }
186     }
187   }
188   
189   return 0;
190 }
191
192 /** The basic function for opening a GEDCOM file for writing.
193
194     \param filename  The name of the file to write
195
196     \return A write handle, which needs to be used in the writing functions,
197     or \c NULL in case of errors.
198  */
199 Gedcom_write_hndl gedcom_write_open(const char *filename)
200 {
201   Gedcom_write_hndl hndl;
202
203   hndl = (Gedcom_write_hndl)malloc(sizeof(struct Gedcom_write_struct));
204
205   if (!hndl)
206     MEMORY_ERROR;
207   else {
208     init_write_encoding();
209     init_write_terminator();
210     hndl->total_conv_fails = 0;
211     hndl->conv = initialize_utf8_conversion(write_encoding.encoding, 0);
212     if (!hndl->conv) {
213       gedcom_error(_("Could not open encoding '%s' for writing: %s"),
214                    write_encoding.encoding, strerror(errno));
215       free(hndl);
216       hndl = NULL;
217     }
218     else {
219       hndl->filedesc = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
220       if (!hndl->filedesc) {
221         gedcom_error(_("Could not open file '%s' for writing: %s"),
222                      filename, strerror(errno));
223         cleanup_utf8_conversion(hndl->conv);
224         free(hndl);
225         hndl = NULL;
226       }
227       else {
228         hndl->term = write_encoding.terminator;
229         hndl->ctxt_level = -1;
230         if (write_encoding.bom == WITH_BOM) {
231           if (write_encoding.width == TWO_BYTE_HILO)
232             write(hndl->filedesc, "\xFE\xFF", 2);
233           else if (write_encoding.width == TWO_BYTE_LOHI)
234             write(hndl->filedesc, "\xFF\xFE", 2);
235           else if (!strcmp(write_encoding.encoding, "UTF-8"))
236             write(hndl->filedesc, "\xEF\xBB\xBF", 3);
237           else
238             gedcom_warning(_("Byte order mark configured, but not relevant"));
239         }
240       }
241     }
242   }
243
244   return hndl;
245 }
246
247 /** The basic function for closing a GEDCOM file for writing.
248
249     \param hndl  The write handle as returned by gedcom_write_open().
250     \param total_conv_fails  If you pass an actual integer pointer for this,
251     the function will write in it the total number of conversion failures;
252     you can pass \c NULL if you're not interested
253
254     \retval 0 in case of success
255     \retval >0 in case of failure.
256  */
257 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
258 {
259   int result = 0;
260   if (hndl) {
261     write_simple(hndl, 0, NULL, "TRLR", NULL);
262     if (total_conv_fails)  *total_conv_fails = hndl->total_conv_fails;
263     result = close(hndl->filedesc);
264     cleanup_utf8_conversion(hndl->conv);
265     free(hndl);
266   }
267   return result;
268 }
269
270 char* get_tag_string(int elt_or_rec, int tag)
271 {
272   int tagnum = tag_data[elt_or_rec].tag;
273   if (!tagnum) tagnum = tag;
274
275   if (tagnum) {
276     if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END)
277       return tag_name[tagnum - TAG_NUM_START];
278     else {
279       gedcom_error(_("Not a valid tag: %d"), tagnum);
280       return NULL;
281     }
282   }
283   else {
284     gedcom_error(_("The element or record type '%s' requires a specific tag "
285                    "for writing"),
286                  tag_data[elt_or_rec].elt_name);
287     return NULL;
288   }
289 }
290
291 int check_type(int elt_or_rec, Gedcom_val_type type)
292 {
293   int allowed = tag_data[elt_or_rec].allowed_types;
294   if (allowed & type)
295     return 1;
296   else {
297     gedcom_error(_("Wrong data type for writing element or record type '%s'"),
298                  tag_data[elt_or_rec].elt_name);
299     return 0;
300   }
301 }
302
303 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
304 {
305   if (parent == -1) {
306     hndl->ctxt_level = 0;
307   }
308   else {
309     while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
310       hndl->ctxt_level--;
311     if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
312       hndl->ctxt_level++;
313     }
314     else {
315       gedcom_error(_("Parent %d not found during write of %d"),
316                    parent, elt_or_rec);
317       return -1;
318     }
319   }
320   hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
321   return hndl->ctxt_level;
322 }
323
324 char* convert_at(const char* input)
325 {
326   if (input) {
327     const char* ptr = input;
328     reset_buffer(&convert_at_buffer);
329     while (*ptr) {
330       if (*ptr == '@') {
331         SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
332         SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
333       }
334       else {
335         SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
336       }
337       ptr++;
338     }
339     return get_buf_string(&convert_at_buffer);
340   }
341   else
342     return NULL;
343 }
344
345 int _gedcom_write_val(Gedcom_write_hndl hndl,
346                       int rec_or_elt, int tag, int parent_rec_or_elt,
347                       const char* xrefstr, const char* val)
348 {
349   int result = 1;
350   int level = 0;
351   char* tag_str = NULL;
352
353   tag_str = get_tag_string(rec_or_elt, tag);
354   level   = get_level(hndl, rec_or_elt, parent_rec_or_elt);
355   if (tag_str && (level != -1)) {
356     if (rec_or_elt == ELT_HEAD_CHAR)
357       result = write_encoding_value(hndl, level, xrefstr, tag_str, val);
358     else if (supports_continuation(rec_or_elt, OPT_CONT|OPT_CONC))
359       result = write_long(hndl, rec_or_elt, level, xrefstr, tag_str, val);
360     else
361       result = write_simple(hndl, level, xrefstr, tag_str, val);
362   }
363
364   return result;
365 }
366
367 /** Function for writing lines corresponding to standard records (i.e. on
368     level 0).
369
370     \param hndl The write handle that was returned by gedcom_write_open().
371     \param rec  One of the identifiers given in the first column in
372     <a href=interface.html#Record_identifiers>this table</a> (except REC_USER).
373     \param xrefstr The cross-reference key of the record (something like
374     \c "@FAM01@".
375     \param val  The value of the record line, which should be \c NULL for some
376     record types, according to
377     <a href=interface.html#Record_identifiers>this table</a>.
378
379     \retval 0 on success
380     \retval >0 on failure
381 */  
382 int gedcom_write_record_str(Gedcom_write_hndl hndl,
383                             Gedcom_rec rec, const char* xrefstr,
384                             const char* val)
385 {
386   int result = 1;
387   if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL)))
388     result = _gedcom_write_val(hndl, rec, 0, -1, xrefstr, convert_at(val));
389   return result;
390 }
391
392 /** Function for writing lines corresponding to standard elements (i.e. on
393     level bigger than 0), with a string as value.
394
395     \param hndl The write handle that was returned by gedcom_write_open().
396     \param elt  One of the identifiers given in the first column in
397     <a href=interface.html#Element_identifiers>this table</a>
398     (except ELT_USER).
399     \param tag Some of the \c elt identifiers can actually stand for different
400     tags.  For this reason, the \c tag has to be passed for some of them.  This
401     parsed tag is the same as was returned by the callback functions, and is
402     an identifier of the form <code>TAG_<em>name</em></code>.  This parameter
403     is needed whenever the second column in 
404     <a href=interface.html#Element_identifiers>this table</a> shows several
405     possible tags (this is e.g. the case for \c ELT_SUB_FAM_EVT).  Otherwise,
406     you can pass 0.
407     \param parent_rec_or_elt The corresponding \c rec or \c elt identifier of
408     the logically enclosing statement: this will determine the level number
409     written on the line, as the level number of the parent + 1.
410     \param val  The value of the element line, which should be \c NULL for some
411     element types, according to
412     <a href=interface.html#Element_identifiers>this table</a>.
413
414     \retval 0 on success
415     \retval >0 on failure
416 */  
417 int gedcom_write_element_str(Gedcom_write_hndl hndl,
418                              Gedcom_elt elt, int tag, int parent_rec_or_elt,
419                              const char* val)
420 {
421   int result = 1;
422   if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL)))
423     result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
424                                convert_at(val));
425   return result;
426 }
427
428 /** Function for writing lines corresponding to standard elements (i.e. on
429     level bigger than 0), with a cross-reference as value.
430
431     See gedcom_write_element_str() for details.
432 */  
433 int gedcom_write_element_xref(Gedcom_write_hndl hndl,
434                               Gedcom_elt elt, int tag, int parent_rec_or_elt,
435                               const struct xref_value* val)
436 {
437   int result = 1;
438   if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL)))
439     result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
440                                val->string);
441   return result;
442 }
443
444 /** Function for writing lines corresponding to standard elements (i.e. on
445     level bigger than 0), with a date as value.
446
447     See gedcom_write_element_str() for details.
448 */  
449 int gedcom_write_element_date(Gedcom_write_hndl hndl,
450                               Gedcom_elt elt, int tag, int parent_rec_or_elt,
451                               const struct date_value* val)
452 {
453   int result = 1;
454   if (check_type(elt, (val ? GV_DATE_VALUE : GV_NULL)))
455     result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
456                                gedcom_date_to_string(val));
457   return result;
458 }
459
460 /** Function for writing lines corresponding to standard elements (i.e. on
461     level bigger than 0), with an age as value.
462
463     See gedcom_write_element_str() for details.
464 */  
465 int gedcom_write_element_age(Gedcom_write_hndl hndl,
466                              Gedcom_elt elt, int tag, int parent_rec_or_elt,
467                              const struct age_value* val)
468 {
469   int result = 1;
470   if (check_type(elt, (val ? GV_AGE_VALUE : GV_NULL)))
471     result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
472                                gedcom_age_to_string(val));
473   return result;
474 }
475
476 /** Function for writing lines corresponding to user-defined records and
477     elements, with a string as value.
478
479     In the case of user-defined tags, the
480     level and tag string are passed verbatim (not controlled by the library).
481     This allows to write any extra data that doesn't use a standard tag, but
482     is only allowed for tags starting with an underscore.
483
484     \param hndl The write handle that was returned by gedcom_write_open().
485     \param level  The integer level of the GEDCOM line
486     \param tag  The tag, as a literal string
487     \param xrefstr An optional cross-reference of the record or element.
488     \param value The value of the record or element line.
489
490     \retval 0 on success
491     \retval >0 on failure
492 */  
493 int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, const char* tag,
494                           const char* xrefstr, const char* value)
495 {
496   int result = 1;
497   if (tag && tag[0] == '_')
498     result = write_simple(hndl, level, xrefstr, tag, convert_at(value));
499   return result;
500 }
501
502 /** Function for writing lines corresponding to user-defined records and
503     elements, with a cross-reference as value.
504
505     See gedcom_write_user_str() for details.
506 */  
507 int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, const char* tag,
508                            const char* xrefstr, const struct xref_value* val)
509 {
510   int result = 1;
511   if (tag && tag[0] == '_')
512     result = write_simple(hndl, level, xrefstr, tag, val->string);
513   return result;
514 }