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.
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.
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.
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
24 #include "gedcom_internal.h"
27 #include "encoding_state.h"
30 #include "utf8tools.h"
32 #include <sys/types.h>
36 #define MAXWRITELEN MAXGEDCLINELEN
38 struct Gedcom_write_struct {
43 int ctxt_stack[MAXGEDCLEVEL+1];
47 void cleanup_write_buffer();
48 struct safe_buffer write_buffer = { NULL, 0, NULL, 0, cleanup_write_buffer };
50 void cleanup_convert_at_buffer();
51 struct safe_buffer convert_at_buffer = { NULL, 0, NULL, 0,
52 cleanup_convert_at_buffer };
54 void cleanup_write_buffer()
56 cleanup_buffer(&write_buffer);
59 void cleanup_convert_at_buffer()
61 cleanup_buffer(&convert_at_buffer);
64 int write_simple(Gedcom_write_hndl hndl,
65 int level, const char* xref, const char* tag,
75 reset_buffer(&write_buffer);
76 res = safe_buf_append(&write_buffer, "%d", level);
78 res += safe_buf_append(&write_buffer, " %s", xref);
79 res += safe_buf_append(&write_buffer, " %s", tag);
81 res += safe_buf_append(&write_buffer, " %s", value);
82 res += safe_buf_append(&write_buffer, hndl->term);
84 if (utf8_strlen(get_buf_string(&write_buffer)) > MAXGEDCLINELEN) {
85 gedcom_error(_("Line too long"));
88 converted = convert_from_utf8(hndl->conv, get_buf_string(&write_buffer),
89 &conv_fails, &outlen);
91 if (converted && (conv_fails == 0)) {
93 write(hndl->filedesc, converted, outlen);
96 hndl->total_conv_fails += conv_fails;
98 (_("Error converting output string: %s (%d conversion failures)"),
99 strerror(errno), conv_fails);
106 int write_encoding_value(Gedcom_write_hndl hndl,
107 int level, const char* xref, const char* tag,
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);
116 int supports_continuation(int elt_or_rec, int which_continuation)
118 return tag_data[elt_or_rec].options & which_continuation;
121 int write_long(Gedcom_write_hndl hndl, int elt_or_rec,
122 int level, const char* xref, const char* tag, const char* value)
124 int prefix_len, value_len = 0, term_len;
126 if (value) nl_pos = strchr(value, '\n');
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);
134 if (!nl_pos && prefix_len + value_len + term_len <= MAXWRITELEN)
135 write_simple(hndl, level, xref, tag, value);
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);
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++;
151 char* cont_tag = "CONT";
152 int line_len = (nl_pos && cont_supported
153 ? nl_pos - value_ptr : value_len);
155 if (prefix_len + line_len + term_len > MAXWRITELEN) {
156 line_len = MAXWRITELEN - prefix_len - term_len;
159 while (value_ptr[line_len] == ' '
160 || value_ptr[line_len-1] == ' ') {
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);
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') {
177 prefix_len = cont_prefix_len;
178 write_level = level + 1;
181 nl_pos = strchr(value_ptr, '\n');
192 /** The basic function for opening a GEDCOM file for writing.
194 \param filename The name of the file to write
196 \return A write handle, which needs to be used in the writing functions,
197 or \c NULL in case of errors.
199 Gedcom_write_hndl gedcom_write_open(const char *filename)
201 Gedcom_write_hndl hndl;
203 hndl = (Gedcom_write_hndl)malloc(sizeof(struct Gedcom_write_struct));
208 init_write_encoding();
209 init_write_terminator();
210 hndl->total_conv_fails = 0;
211 hndl->conv = initialize_utf8_conversion(write_encoding.encoding, 0);
213 gedcom_error(_("Could not open encoding '%s' for writing: %s"),
214 write_encoding.encoding, strerror(errno));
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);
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);
238 gedcom_warning(_("Byte order mark configured, but not relevant"));
247 /** The basic function for closing a GEDCOM file for writing.
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
254 \retval 0 in case of success
255 \retval >0 in case of failure.
257 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
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);
270 char* get_tag_string(int elt_or_rec, int tag)
272 int tagnum = tag_data[elt_or_rec].tag;
273 if (!tagnum) tagnum = tag;
276 if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END)
277 return tag_name[tagnum - TAG_NUM_START];
279 gedcom_error(_("Not a valid tag: %d"), tagnum);
284 gedcom_error(_("The element or record type '%s' requires a specific tag "
286 tag_data[elt_or_rec].elt_name);
291 int check_type(int elt_or_rec, Gedcom_val_type type)
293 int allowed = tag_data[elt_or_rec].allowed_types;
297 gedcom_error(_("Wrong data type for writing element or record type '%s'"),
298 tag_data[elt_or_rec].elt_name);
303 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
306 hndl->ctxt_level = 0;
309 while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
311 if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
315 gedcom_error(_("Parent %d not found during write of %d"),
320 hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
321 return hndl->ctxt_level;
324 char* convert_at(const char* input)
327 const char* ptr = input;
328 reset_buffer(&convert_at_buffer);
331 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
332 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
335 SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
339 return get_buf_string(&convert_at_buffer);
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)
351 char* tag_str = NULL;
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);
361 result = write_simple(hndl, level, xrefstr, tag_str, val);
367 /** Function for writing lines corresponding to standard records (i.e. on
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
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>.
380 \retval >0 on failure
382 int gedcom_write_record_str(Gedcom_write_hndl hndl,
383 Gedcom_rec rec, const char* xrefstr,
387 if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL)))
388 result = _gedcom_write_val(hndl, rec, 0, -1, xrefstr, convert_at(val));
392 /** Function for writing lines corresponding to standard elements (i.e. on
393 level bigger than 0), with a string as value.
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>
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,
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>.
415 \retval >0 on failure
417 int gedcom_write_element_str(Gedcom_write_hndl hndl,
418 Gedcom_elt elt, int tag, int parent_rec_or_elt,
422 if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL)))
423 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
428 /** Function for writing lines corresponding to standard elements (i.e. on
429 level bigger than 0), with a cross-reference as value.
431 See gedcom_write_element_str() for details.
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)
438 if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL)))
439 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
444 /** Function for writing lines corresponding to standard elements (i.e. on
445 level bigger than 0), with a date as value.
447 See gedcom_write_element_str() for details.
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)
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));
460 /** Function for writing lines corresponding to standard elements (i.e. on
461 level bigger than 0), with an age as value.
463 See gedcom_write_element_str() for details.
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)
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));
476 /** Function for writing lines corresponding to user-defined records and
477 elements, with a string as value.
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.
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.
491 \retval >0 on failure
493 int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, const char* tag,
494 const char* xrefstr, const char* value)
497 if (tag && tag[0] == '_')
498 result = write_simple(hndl, level, xrefstr, tag, convert_at(value));
502 /** Function for writing lines corresponding to user-defined records and
503 elements, with a cross-reference as value.
505 See gedcom_write_user_str() for details.
507 int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, const char* tag,
508 const char* xrefstr, const struct xref_value* val)
511 if (tag && tag[0] == '_')
512 result = write_simple(hndl, level, xrefstr, tag, val->string);