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 };
41 struct Gedcom_write_struct {
46 int ctxt_stack[MAXGEDCLEVEL+1];
50 const char* default_encoding[] = {
51 /* ONE_BYTE */ "ASCII",
52 /* TWO_BYTE_HILO */ "UCS-2BE",
53 /* TWO_BYTE_LOHI */ "UCS-2LE"
56 const char* terminator[] = {
59 /* END_CR_LF */ "\x0D\x0A",
60 /* END_LF_CR */ "\x0A\x0D"
63 void cleanup_write_buffer();
64 struct safe_buffer write_buffer = { NULL, 0, NULL, 0, cleanup_write_buffer };
66 void cleanup_convert_at_buffer();
67 struct safe_buffer convert_at_buffer = { NULL, 0, NULL, 0,
68 cleanup_convert_at_buffer };
70 void cleanup_write_buffer()
72 cleanup_buffer(&write_buffer);
75 void cleanup_convert_at_buffer()
77 cleanup_buffer(&convert_at_buffer);
80 int write_simple(Gedcom_write_hndl hndl,
81 int level, const char* xref, const char* tag,
91 reset_buffer(&write_buffer);
92 res = safe_buf_append(&write_buffer, "%d", level);
94 res += safe_buf_append(&write_buffer, " %s", xref);
95 res += safe_buf_append(&write_buffer, " %s", tag);
97 res += safe_buf_append(&write_buffer, " %s", value);
98 res += safe_buf_append(&write_buffer, hndl->term);
100 if (utf8_strlen(get_buf_string(&write_buffer)) > MAXGEDCLINELEN) {
101 gedcom_error(_("Line too long"));
104 converted = convert_from_utf8(hndl->conv, get_buf_string(&write_buffer),
105 &conv_fails, &outlen);
107 if (converted && (conv_fails == 0)) {
109 write(hndl->filedesc, converted, outlen);
112 hndl->total_conv_fails += conv_fails;
114 (_("Error converting output string: %s (%d conversion failures)"),
115 strerror(errno), conv_fails);
122 int write_encoding_value(Gedcom_write_hndl hndl,
123 int level, char* xref, char* tag, char* value)
125 if (strcmp(value, write_encoding.charset))
126 gedcom_warning(_("Forcing HEAD.CHAR value to '%s'"),
127 write_encoding.charset);
128 return write_simple(hndl, level, xref, tag, write_encoding.charset);
131 int supports_continuation(int elt_or_rec, int which_continuation)
133 return tag_data[elt_or_rec].options & which_continuation;
136 int write_long(Gedcom_write_hndl hndl, int elt_or_rec,
137 int level, const char* xref, const char* tag, const char* value)
139 int prefix_len, value_len = 0, term_len;
141 if (value) nl_pos = strchr(value, '\n');
143 prefix_len = utf8_strlen(tag) + 3; /* for e.g. "0 INDI " */
144 if (level > 9) prefix_len++;
145 if (xref) prefix_len += utf8_strlen(xref) + 1;
146 if (value) value_len = utf8_strlen(value);
147 term_len = strlen(hndl->term);
149 if (!nl_pos && prefix_len + value_len + term_len <= MAXWRITELEN)
150 write_simple(hndl, level, xref, tag, value);
152 const char* value_ptr = value;
153 int cont_supported = supports_continuation(elt_or_rec, OPT_CONT);
154 int cont_as_conc = supports_continuation(elt_or_rec, OPT_CONT_AS_CONC);
155 if (nl_pos && !cont_supported) {
156 gedcom_error (_("The tag %s doesn't support newlines"), tag);
160 char value_part[MAXWRITELEN];
161 int cont_prefix_len, write_level = level;
162 cont_prefix_len = utf8_strlen("CONT") + 3;
163 if (level + 1 > 9) cont_prefix_len++;
166 char* cont_tag = "CONT";
167 int line_len = (nl_pos && cont_supported
168 ? nl_pos - value_ptr : value_len);
170 if (prefix_len + line_len + term_len > MAXWRITELEN) {
171 line_len = MAXWRITELEN - prefix_len - term_len;
176 memset(value_part, 0, sizeof(value_part));
177 strncpy(value_part, value_ptr, line_len);
178 write_simple(hndl, write_level, xref, tag, value_part);
180 if (line_len < value_len) {
181 value_ptr = value_ptr + line_len;
182 value_len = value_len - line_len;
183 while (*value_ptr == '\n') {
187 prefix_len = cont_prefix_len;
188 write_level = level + 1;
191 nl_pos = strchr(value_ptr, '\n');
202 int gedcom_write_set_encoding(const char* new_charset,
203 Encoding width, Enc_bom bom)
205 char* new_encoding = NULL;
206 if (!strcmp(new_charset, "UNICODE")) {
207 if (width == ONE_BYTE) {
208 gedcom_error(_("Unicode cannot be encoded into one byte"));
212 new_encoding = get_encoding(new_charset, width);
214 write_encoding.encoding = new_encoding;
215 write_encoding.width = width;
216 write_encoding.bom = bom;
217 strncpy(write_encoding.charset, new_charset, MAX_CHARSET_LEN);
224 new_encoding = get_encoding(new_charset, ONE_BYTE);
226 write_encoding.encoding = new_encoding;
227 write_encoding.width = ONE_BYTE;
228 write_encoding.bom = bom;
229 strncpy(write_encoding.charset, new_charset, MAX_CHARSET_LEN);
237 int gedcom_write_set_line_terminator(Enc_line_end end)
239 strncpy(write_encoding.terminator, terminator[end], MAX_TERMINATOR_LEN);
243 Gedcom_write_hndl gedcom_write_open(const char *filename)
245 Gedcom_write_hndl hndl;
247 hndl = (Gedcom_write_hndl)malloc(sizeof(struct Gedcom_write_struct));
252 hndl->total_conv_fails = 0;
253 hndl->conv = initialize_utf8_conversion(write_encoding.encoding, 0);
255 gedcom_error(_("Could not open encoding '%s' for writing: %s"),
256 write_encoding.encoding, strerror(errno));
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);
270 hndl->term = write_encoding.terminator;
271 hndl->ctxt_level = -1;
272 if (write_encoding.bom == WITH_BOM) {
273 if (write_encoding.width == TWO_BYTE_HILO)
274 write(hndl->filedesc, "\xFE\xFF", 2);
275 else if (write_encoding.width == TWO_BYTE_LOHI)
276 write(hndl->filedesc, "\xFF\xFE", 2);
277 else if (!strcmp(write_encoding.encoding, "UTF-8"))
278 write(hndl->filedesc, "\xEF\xBB\xBF", 3);
280 gedcom_warning(_("Byte order mark configured, but not relevant"));
289 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
293 write_simple(hndl, 0, NULL, "TRLR", NULL);
294 if (total_conv_fails) *total_conv_fails = hndl->total_conv_fails;
295 result = close(hndl->filedesc);
296 cleanup_utf8_conversion(hndl->conv);
302 char* get_tag_string(int elt_or_rec, int tag)
304 int tagnum = tag_data[elt_or_rec].tag;
305 if (!tagnum) tagnum = tag;
308 if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END)
309 return tag_name[tagnum - TAG_NUM_START];
311 gedcom_error(_("Not a valid tag: %d"), tagnum);
316 gedcom_error(_("The element or record type '%s' requires a specific tag "
318 tag_data[elt_or_rec].elt_name);
323 int check_type(int elt_or_rec, Gedcom_val_type type)
325 int allowed = tag_data[elt_or_rec].allowed_types;
329 gedcom_error(_("Wrong data type for writing element or record type '%s'"),
330 tag_data[elt_or_rec].elt_name);
335 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
338 hndl->ctxt_level = 0;
341 while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
343 if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
347 gedcom_error(_("Parent %d not found during write of %d"),
352 hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
353 return hndl->ctxt_level;
356 char* convert_at(const char* input)
359 const char* ptr = input;
360 reset_buffer(&convert_at_buffer);
363 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
364 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
367 SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
371 return get_buf_string(&convert_at_buffer);
377 int _gedcom_write_val(Gedcom_write_hndl hndl,
378 int rec_or_elt, int tag, int parent_rec_or_elt,
379 char* xrefstr, char* val)
383 char* tag_str = NULL;
385 tag_str = get_tag_string(rec_or_elt, tag);
386 level = get_level(hndl, rec_or_elt, parent_rec_or_elt);
387 if (tag_str && (level != -1)) {
388 if (rec_or_elt == ELT_HEAD_CHAR)
389 result = write_encoding_value(hndl, level, xrefstr, tag_str, val);
390 else if (supports_continuation(rec_or_elt, OPT_CONT|OPT_CONC))
391 result = write_long(hndl, rec_or_elt, level, xrefstr, tag_str, val);
393 result = write_simple(hndl, level, xrefstr, tag_str, val);
399 int gedcom_write_record_str(Gedcom_write_hndl hndl,
400 Gedcom_rec rec, char* xrefstr, char* val)
403 if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL)))
404 result = _gedcom_write_val(hndl, rec, 0, -1, xrefstr, convert_at(val));
408 int gedcom_write_element_str(Gedcom_write_hndl hndl,
409 Gedcom_elt elt, int tag, int parent_rec_or_elt,
413 if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL)))
414 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
419 int gedcom_write_element_xref(Gedcom_write_hndl hndl,
420 Gedcom_elt elt, int tag, int parent_rec_or_elt,
421 struct xref_value* val)
424 if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL)))
425 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
430 int gedcom_write_element_date(Gedcom_write_hndl hndl,
431 Gedcom_elt elt, int tag, int parent_rec_or_elt,
432 struct date_value* val)
435 if (check_type(elt, (val ? GV_DATE_VALUE : GV_NULL)))
436 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
437 gedcom_date_to_string(val));
441 int gedcom_write_element_age(Gedcom_write_hndl hndl,
442 Gedcom_elt elt, int tag, int parent_rec_or_elt,
443 struct age_value* val)
446 if (check_type(elt, (val ? GV_AGE_VALUE : GV_NULL)))
447 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
448 gedcom_age_to_string(val));
452 int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, char* tag,
453 char* xrefstr, char* value)
456 if (tag && tag[0] == '_')
457 result = write_simple(hndl, level, xrefstr, tag, convert_at(value));
461 int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, char* tag,
462 char* xrefstr, struct xref_value* val)
465 if (tag && tag[0] == '_')
466 result = write_simple(hndl, level, xrefstr, tag, val->string);