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"
29 #include "utf8tools.h"
31 #include <sys/types.h>
35 #define MAXWRITELEN MAXGEDCLINELEN
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;
43 struct Gedcom_write_struct {
48 int ctxt_stack[MAXGEDCLEVEL+1];
52 const char* default_encoding[] = {
53 /* ONE_BYTE */ "ASCII",
54 /* TWO_BYTE_HILO */ "UCS-2BE",
55 /* TWO_BYTE_LOHI */ "UCS-2LE"
58 const char* terminator[] = {
61 /* END_CR_LF */ "\x0D\x0A",
62 /* END_LF_CR */ "\x0A\x0D"
65 void cleanup_write_buffer();
66 struct safe_buffer write_buffer = { NULL, 0, NULL, 0, cleanup_write_buffer };
68 void cleanup_convert_at_buffer();
69 struct safe_buffer convert_at_buffer = { NULL, 0, NULL, 0,
70 cleanup_convert_at_buffer };
72 void cleanup_write_buffer()
74 cleanup_buffer(&write_buffer);
77 void cleanup_convert_at_buffer()
79 cleanup_buffer(&convert_at_buffer);
82 int write_simple(Gedcom_write_hndl hndl,
83 int level, const char* xref, const char* tag,
93 reset_buffer(&write_buffer);
94 res = safe_buf_append(&write_buffer, "%d", level);
96 res += safe_buf_append(&write_buffer, " %s", xref);
97 res += safe_buf_append(&write_buffer, " %s", tag);
99 res += safe_buf_append(&write_buffer, " %s", value);
100 res += safe_buf_append(&write_buffer, hndl->term);
102 if (utf8_strlen(get_buf_string(&write_buffer)) > MAXGEDCLINELEN) {
103 gedcom_error(_("Line too long"));
106 converted = convert_from_utf8(hndl->conv, get_buf_string(&write_buffer),
107 &conv_fails, &outlen);
109 if (converted && (conv_fails == 0)) {
111 write(hndl->filedesc, converted, outlen);
114 hndl->total_conv_fails += conv_fails;
116 (_("Error converting output string: %s (%d conversion failures)"),
117 strerror(errno), conv_fails);
124 int write_encoding_value(Gedcom_write_hndl hndl,
125 int level, char* xref, char* tag, char* value)
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);
133 int supports_continuation(int elt_or_rec, int which_continuation)
135 return tag_data[elt_or_rec].options & which_continuation;
138 int write_long(Gedcom_write_hndl hndl, int elt_or_rec,
139 int level, const char* xref, const char* tag, const char* value)
141 int prefix_len, value_len = 0, term_len;
143 if (value) nl_pos = strchr(value, '\n');
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);
151 if (!nl_pos && prefix_len + value_len + term_len <= MAXWRITELEN)
152 write_simple(hndl, level, xref, tag, value);
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);
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++;
168 char* cont_tag = "CONT";
169 int line_len = (nl_pos && cont_supported
170 ? nl_pos - value_ptr : value_len);
172 if (prefix_len + line_len + term_len > MAXWRITELEN) {
173 line_len = MAXWRITELEN - prefix_len - term_len;
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);
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') {
189 prefix_len = cont_prefix_len;
190 write_level = level + 1;
193 nl_pos = strchr(value_ptr, '\n');
204 int gedcom_write_set_encoding(Enc_from from, const char* new_charset,
205 Encoding width, Enc_bom bom)
207 char* new_encoding = NULL;
208 if (from == ENC_FROM_SYS) {
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"));
219 new_encoding = get_encoding(new_charset, width);
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);
231 new_encoding = get_encoding(new_charset, ONE_BYTE);
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);
245 void copy_write_encoding_from_file()
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;
255 int gedcom_write_set_line_terminator(Enc_from from, Enc_line_end end)
257 const char* new_term = NULL;
258 write_terminator_from = from;
259 if (from == ENC_FROM_SYS) {
260 new_term = SYS_NEWLINE;
262 else if (from == ENC_MANUAL) {
263 new_term = terminator[end];
266 strncpy(write_encoding.terminator, new_term, MAX_TERMINATOR_LEN);
270 void copy_write_terminator_from_file()
272 if (read_encoding.terminator[0] != '\0') {
273 strncpy(write_encoding.terminator, read_encoding.terminator,
278 Gedcom_write_hndl gedcom_write_open(const char *filename)
280 Gedcom_write_hndl hndl;
282 hndl = (Gedcom_write_hndl)malloc(sizeof(struct Gedcom_write_struct));
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);
294 gedcom_error(_("Could not open encoding '%s' for writing: %s"),
295 write_encoding.encoding, strerror(errno));
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);
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);
319 gedcom_warning(_("Byte order mark configured, but not relevant"));
328 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
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);
341 char* get_tag_string(int elt_or_rec, int tag)
343 int tagnum = tag_data[elt_or_rec].tag;
344 if (!tagnum) tagnum = tag;
347 if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END)
348 return tag_name[tagnum - TAG_NUM_START];
350 gedcom_error(_("Not a valid tag: %d"), tagnum);
355 gedcom_error(_("The element or record type '%s' requires a specific tag "
357 tag_data[elt_or_rec].elt_name);
362 int check_type(int elt_or_rec, Gedcom_val_type type)
364 int allowed = tag_data[elt_or_rec].allowed_types;
368 gedcom_error(_("Wrong data type for writing element or record type '%s'"),
369 tag_data[elt_or_rec].elt_name);
374 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
377 hndl->ctxt_level = 0;
380 while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
382 if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
386 gedcom_error(_("Parent %d not found during write of %d"),
391 hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
392 return hndl->ctxt_level;
395 char* convert_at(const char* input)
398 const char* ptr = input;
399 reset_buffer(&convert_at_buffer);
402 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
403 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
406 SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
410 return get_buf_string(&convert_at_buffer);
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)
422 char* tag_str = NULL;
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);
432 result = write_simple(hndl, level, xrefstr, tag_str, val);
438 int gedcom_write_record_str(Gedcom_write_hndl hndl,
439 Gedcom_rec rec, char* xrefstr, char* val)
442 if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL)))
443 result = _gedcom_write_val(hndl, rec, 0, -1, xrefstr, convert_at(val));
447 int gedcom_write_element_str(Gedcom_write_hndl hndl,
448 Gedcom_elt elt, int tag, int parent_rec_or_elt,
452 if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL)))
453 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
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)
463 if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL)))
464 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
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)
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));
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)
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));
491 int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, char* tag,
492 char* xrefstr, char* value)
495 if (tag && tag[0] == '_')
496 result = write_simple(hndl, level, xrefstr, tag, convert_at(value));
500 int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, char* tag,
501 char* xrefstr, struct xref_value* val)
504 if (tag && tag[0] == '_')
505 result = write_simple(hndl, level, xrefstr, tag, val->string);