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 const char* encoding = "ASCII";
38 int write_encoding_details = ONE_BYTE;
39 /* SYS_NEWLINE is defined in config.h */
40 const char* write_terminator = SYS_NEWLINE;
42 struct Gedcom_write_struct {
47 int ctxt_stack[MAXGEDCLEVEL+1];
51 const char* default_encoding[] = {
52 /* ONE_BYTE */ "ASCII",
53 /* TWO_BYTE_HILO */ "UCS-2BE",
54 /* TWO_BYTE_LOHI */ "UCS-2LE"
57 const char* terminator[] = {
60 /* END_CR_LF */ "\x0D\x0A",
61 /* END_LF_CR */ "\x0A\x0D"
64 void cleanup_write_buffer();
65 struct safe_buffer write_buffer = { NULL, 0, NULL, 0, cleanup_write_buffer };
67 void cleanup_convert_at_buffer();
68 struct safe_buffer convert_at_buffer = { NULL, 0, NULL, 0,
69 cleanup_convert_at_buffer };
71 void cleanup_write_buffer()
73 cleanup_buffer(&write_buffer);
76 void cleanup_convert_at_buffer()
78 cleanup_buffer(&convert_at_buffer);
81 int write_simple(Gedcom_write_hndl hndl,
82 int level, char* xref, char* tag, char* value)
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 supports_continuation(int elt_or_rec, int which_continuation)
124 return tag_data[elt_or_rec].options & which_continuation;
127 int write_long(Gedcom_write_hndl hndl, int elt_or_rec,
128 int level, char* xref, char* tag, char* value)
130 int prefix_len, value_len = 0, term_len;
132 if (value) nl_pos = strchr(value, '\n');
134 prefix_len = utf8_strlen(tag) + 3; /* for e.g. "0 INDI " */
135 if (level > 9) prefix_len++;
136 if (xref) prefix_len += utf8_strlen(xref) + 1;
137 if (value) value_len = utf8_strlen(value);
138 term_len = strlen(hndl->term);
140 if (!nl_pos && prefix_len + value_len + term_len <= MAXWRITELEN)
141 write_simple(hndl, level, xref, tag, value);
143 char* value_ptr = value;
144 int cont_supported = supports_continuation(elt_or_rec, OPT_CONT);
145 int cont_as_conc = supports_continuation(elt_or_rec, OPT_CONT_AS_CONC);
146 if (nl_pos && !cont_supported) {
147 gedcom_error (_("The tag %s doesn't support newlines\n"), tag);
151 char value_part[MAXWRITELEN];
152 int cont_prefix_len, write_level = level;
153 cont_prefix_len = utf8_strlen("CONT") + 3;
154 if (level + 1 > 9) cont_prefix_len++;
157 char* cont_tag = "CONT";
158 int line_len = (nl_pos && cont_supported
159 ? nl_pos - value_ptr : value_len);
161 if (prefix_len + line_len + term_len > MAXWRITELEN) {
162 line_len = MAXWRITELEN - prefix_len - term_len;
167 memset(value_part, 0, sizeof(value_part));
168 strncpy(value_part, value_ptr, line_len);
169 write_simple(hndl, write_level, xref, tag, value_part);
171 if (line_len < value_len) {
172 value_ptr = value_ptr + line_len;
173 value_len = value_len - line_len;
174 while (*value_ptr == '\n') {
178 prefix_len = cont_prefix_len;
179 write_level = level + 1;
182 nl_pos = strchr(value_ptr, '\n');
193 int gedcom_write_set_encoding(const char* charset,
194 Encoding width, Enc_bom bom)
196 char* new_encoding = NULL;
197 if (!strcmp(charset, "UNICODE")) {
198 if (width == ONE_BYTE) {
199 gedcom_error(_("Unicode cannot be encoded into one byte"));
203 new_encoding = get_encoding(charset, width);
205 encoding = new_encoding;
206 write_encoding_details = width | bom;
213 new_encoding = get_encoding(charset, ONE_BYTE);
215 encoding = new_encoding;
216 write_encoding_details = ONE_BYTE;
224 int gedcom_write_set_line_terminator(Enc_line_end end)
226 write_terminator = terminator[end];
230 Gedcom_write_hndl gedcom_write_open(const char *filename)
232 Gedcom_write_hndl hndl;
234 hndl = (Gedcom_write_hndl)malloc(sizeof(struct Gedcom_write_struct));
239 hndl->total_conv_fails = 0;
240 hndl->conv = initialize_utf8_conversion(encoding, 0);
242 gedcom_error(_("Could not open encoding '%s' for writing: %s"),
243 encoding, strerror(errno));
248 hndl->filedesc = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
249 if (!hndl->filedesc) {
250 gedcom_error(_("Could not open file '%s' for writing: %s"),
251 filename, strerror(errno));
252 cleanup_utf8_conversion(hndl->conv);
257 hndl->term = write_terminator;
258 hndl->ctxt_level = -1;
259 if (write_encoding_details & WITH_BOM) {
260 if (write_encoding_details & TWO_BYTE_HILO)
261 write(hndl->filedesc, "\xFE\xFF", 2);
262 else if (write_encoding_details & TWO_BYTE_LOHI)
263 write(hndl->filedesc, "\xFF\xFE", 2);
265 gedcom_warning(_("Byte order mark configured, but no Unicode"));
274 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
278 write_simple(hndl, 0, NULL, "TRLR", NULL);
279 if (total_conv_fails) *total_conv_fails = hndl->total_conv_fails;
280 result = close(hndl->filedesc);
281 cleanup_utf8_conversion(hndl->conv);
287 char* get_tag_string(int elt_or_rec, int tag)
289 int tagnum = tag_data[elt_or_rec].tag;
290 if (!tagnum) tagnum = tag;
293 if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END)
294 return tag_name[tagnum - TAG_NUM_START];
296 gedcom_error(_("Not a valid tag: %d"), tagnum);
301 gedcom_error(_("The element or record type '%s' requires a specific tag"
303 tag_data[elt_or_rec].elt_name);
308 int check_type(int elt_or_rec, Gedcom_val_type type)
310 int allowed = tag_data[elt_or_rec].allowed_types;
314 gedcom_error(_("Wrong data type for writing element or record type '%s'"),
315 tag_data[elt_or_rec].elt_name);
320 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
323 hndl->ctxt_level = 0;
326 while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
328 if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
332 gedcom_error(_("Parent %d not found during write of %d"),
337 hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
338 return hndl->ctxt_level;
341 char* convert_at(const char* input)
344 const char* ptr = input;
345 reset_buffer(&convert_at_buffer);
348 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
349 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
352 SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
356 return get_buf_string(&convert_at_buffer);
362 int _gedcom_write_val(Gedcom_write_hndl hndl,
363 int rec_or_elt, int tag, int parent_rec_or_elt,
364 char* xrefstr, char* val)
368 char* tag_str = NULL;
370 tag_str = get_tag_string(rec_or_elt, tag);
371 level = get_level(hndl, rec_or_elt, parent_rec_or_elt);
372 if (tag_str && (level != -1)) {
373 if (supports_continuation(rec_or_elt, OPT_CONT|OPT_CONC|OPT_CONT_AS_CONC))
374 result = write_long(hndl, rec_or_elt, level, xrefstr, tag_str, val);
376 result = write_simple(hndl, level, xrefstr, tag_str, val);
382 int gedcom_write_record_str(Gedcom_write_hndl hndl,
383 Gedcom_rec rec, int tag,
384 char* xrefstr, char* val)
387 if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL)))
388 result = _gedcom_write_val(hndl, rec, tag, -1, xrefstr, convert_at(val));
392 int gedcom_write_element_str(Gedcom_write_hndl hndl,
393 Gedcom_elt elt, int tag, int parent_rec_or_elt,
397 if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL)))
398 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
403 int gedcom_write_record_xref(Gedcom_write_hndl hndl,
404 Gedcom_rec rec, int tag,
405 char* xrefstr, struct xref_value* val)
408 if (check_type(rec, (val ? GV_XREF_PTR : GV_NULL)))
409 result = _gedcom_write_val(hndl, rec, tag, -1, xrefstr, val->string);
413 int gedcom_write_element_xref(Gedcom_write_hndl hndl,
414 Gedcom_elt elt, int tag, int parent_rec_or_elt,
415 struct xref_value* val)
418 if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL)))
419 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
424 int gedcom_write_element_date(Gedcom_write_hndl hndl,
425 Gedcom_elt elt, int tag, int parent_rec_or_elt,
426 struct date_value* val)
429 if (check_type(elt, (val ? GV_DATE_VALUE : GV_NULL)))
430 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
431 gedcom_date_to_string(val));
435 int gedcom_write_element_age(Gedcom_write_hndl hndl,
436 Gedcom_elt elt, int tag, int parent_rec_or_elt,
437 struct age_value* val)
440 if (check_type(elt, (val ? GV_AGE_VALUE : GV_NULL)))
441 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
442 gedcom_age_to_string(val));
446 int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, char* tag,
447 char* xrefstr, char* value)
450 if (tag && tag[0] == '_')
451 result = write_simple(hndl, level, xrefstr, tag, convert_at(value));
455 int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, char* tag,
456 char* xrefstr, struct xref_value* val)
459 if (tag && tag[0] == '_')
460 result = write_simple(hndl, level, xrefstr, tag, val->string);