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"
31 #include <sys/types.h>
35 const char* encoding = "ASCII";
36 int write_encoding_details = ONE_BYTE;
37 /* SYS_NEWLINE is defined in config.h */
38 const char* write_terminator = SYS_NEWLINE;
40 struct Gedcom_write_struct {
45 int ctxt_stack[MAXGEDCLEVEL+1];
49 const char* default_encoding[] = {
50 /* ONE_BYTE */ "ASCII",
51 /* TWO_BYTE_HILO */ "UCS-2BE",
52 /* TWO_BYTE_LOHI */ "UCS-2LE"
55 const char* terminator[] = {
58 /* END_CR_LF */ "\x0D\x0A",
59 /* END_LF_CR */ "\x0A\x0D"
62 void cleanup_write_buffer();
63 struct safe_buffer write_buffer = { NULL, 0, NULL, 0, cleanup_write_buffer };
65 void cleanup_convert_at_buffer();
66 struct safe_buffer convert_at_buffer = { NULL, 0, NULL, 0,
67 cleanup_convert_at_buffer };
69 void cleanup_write_buffer()
71 cleanup_buffer(&write_buffer);
74 void cleanup_convert_at_buffer()
76 cleanup_buffer(&convert_at_buffer);
79 int write_simple(Gedcom_write_hndl hndl,
80 int level, char* xref, char* tag, char* value)
89 reset_buffer(&write_buffer);
90 res = safe_buf_append(&write_buffer, "%d", level);
92 res += safe_buf_append(&write_buffer, " %s", xref);
93 res += safe_buf_append(&write_buffer, " %s", tag);
95 res += safe_buf_append(&write_buffer, " %s", value);
96 res += safe_buf_append(&write_buffer, hndl->term);
98 if (utf8_strlen(get_buf_string(&write_buffer)) > MAXGEDCLINELEN) {
99 gedcom_error(_("Line too long"));
102 converted = convert_from_utf8(hndl->conv, get_buf_string(&write_buffer),
103 &conv_fails, &outlen);
105 if (converted && (conv_fails == 0))
106 write(hndl->filedesc, converted, outlen);
108 hndl->total_conv_fails += conv_fails;
110 (_("Error converting output string: %s (%d conversion failures)"),
111 strerror(errno), conv_fails);
118 int supports_continuation(int elt_or_rec, int which_continuation)
120 return tag_data[elt_or_rec].options & which_continuation;
123 int write_long(Gedcom_write_hndl hndl, int elt_or_rec,
124 int level, char* xref, char* tag, char* value)
126 int prefix_len, value_len, term_len;
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 value_len = utf8_strlen(value);
132 term_len = strlen(hndl->term);
134 if (prefix_len + value_len + term_len <= MAXGEDCLINELEN)
135 write_simple(hndl, level, xref, tag, value);
137 char* value_ptr = value;
138 char* nl_pos = strchr(value, '\n');
139 if (nl_pos && !supports_continuation(elt_or_rec, OPT_CONT)) {
140 gedcom_error (_("The tag %s doesn't support newlines\n"), tag);
144 char value_part[MAXGEDCLINELEN];
145 int cont_prefix_len, write_level = level;
146 cont_prefix_len = utf8_strlen("CONT") + 3;
147 if (level + 1 > 9) cont_prefix_len++;
150 char* cont_tag = "CONT";
151 int line_len = (nl_pos ? nl_pos - value_ptr : value_len);
153 if (prefix_len + line_len + term_len > MAXGEDCLINELEN) {
154 line_len = MAXGEDCLINELEN - prefix_len - term_len;
158 memset(value_part, 0, sizeof(value_part));
159 strncpy(value_part, value_ptr, line_len);
160 write_simple(hndl, write_level, xref, tag, value_part);
162 if (line_len < value_len) {
163 value_ptr = value_ptr + line_len;
164 value_len = value_len - line_len;
165 while (*value_ptr == '\n') {
169 prefix_len = cont_prefix_len;
170 write_level = level + 1;
173 nl_pos = strchr(value_ptr, '\n');
184 int gedcom_write_set_encoding(const char* charset,
185 Encoding width, Enc_bom bom)
187 char* new_encoding = NULL;
188 if (!strcmp(charset, "UNICODE")) {
189 if (width == ONE_BYTE) {
190 gedcom_error(_("Unicode cannot be encoded into one byte"));
194 new_encoding = get_encoding(charset, width);
196 encoding = new_encoding;
197 write_encoding_details = width | bom;
204 new_encoding = get_encoding(charset, ONE_BYTE);
206 encoding = new_encoding;
207 write_encoding_details = ONE_BYTE;
215 int gedcom_write_set_line_terminator(Enc_line_end end)
217 write_terminator = terminator[end];
221 Gedcom_write_hndl gedcom_write_open(const char *filename)
223 Gedcom_write_hndl hndl;
225 hndl = (Gedcom_write_hndl)malloc(sizeof(struct Gedcom_write_struct));
230 hndl->total_conv_fails = 0;
231 hndl->conv = initialize_utf8_conversion(encoding, 0);
233 gedcom_error(_("Could not open encoding '%s' for writing: %s"),
234 encoding, strerror(errno));
239 hndl->filedesc = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
240 if (!hndl->filedesc) {
241 gedcom_error(_("Could not open file '%s' for writing: %s"),
242 filename, strerror(errno));
243 cleanup_utf8_conversion(hndl->conv);
248 hndl->term = write_terminator;
249 hndl->ctxt_level = -1;
250 if (write_encoding_details & WITH_BOM) {
251 if (write_encoding_details & TWO_BYTE_HILO)
252 write(hndl->filedesc, "\xFE\xFF", 2);
253 else if (write_encoding_details & TWO_BYTE_LOHI)
254 write(hndl->filedesc, "\xFF\xFE", 2);
256 gedcom_warning(_("Byte order mark configured, but no Unicode"));
265 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
269 write_simple(hndl, 0, NULL, "TRLR", NULL);
270 if (total_conv_fails) *total_conv_fails = hndl->total_conv_fails;
271 result = close(hndl->filedesc);
272 cleanup_utf8_conversion(hndl->conv);
278 char* get_tag_string(int elt_or_rec, char* tag)
280 char* result = tag_data[elt_or_rec].tag_name;
287 gedcom_error(_("The element or record type '%s' requires a specific tag"
289 tag_data[elt_or_rec].elt_name);
294 int check_type(int elt_or_rec, Gedcom_val_type type)
296 int allowed = tag_data[elt_or_rec].allowed_types;
300 gedcom_error(_("Wrong data type for writing element or record type '%s'"),
301 tag_data[elt_or_rec].elt_name);
306 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
309 hndl->ctxt_level = 0;
312 while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
314 if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
318 gedcom_error(_("Parent %d not found during write of %d"),
323 hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
324 return hndl->ctxt_level;
327 char* convert_at(const char* input)
330 const char* ptr = input;
331 reset_buffer(&convert_at_buffer);
334 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
335 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
338 SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
342 return get_buf_string(&convert_at_buffer);
348 int gedcom_write_record_str(Gedcom_write_hndl hndl,
349 Gedcom_rec rec, char* tag,
350 struct xref_value* xref, char* val)
354 char* tag_str = NULL;
355 char* xref_str = NULL;
357 tag_str = get_tag_string(rec, tag);
358 level = get_level(hndl, rec, -1);
359 if (tag_str && check_type(rec, (val ? GV_CHAR_PTR : GV_NULL))) {
361 xref_str = xref->string;
362 if (supports_continuation(rec, OPT_CONT | OPT_CONC))
363 result = write_long(hndl, rec, level, xref_str, tag_str,
366 result = write_simple(hndl, level, xref_str, tag_str, convert_at(val));
372 int gedcom_write_element_str(Gedcom_write_hndl hndl,
373 Gedcom_elt elt, char* tag, int parent_rec_or_elt,
378 char* tag_str = NULL;
380 tag_str = get_tag_string(elt, tag);
381 level = get_level(hndl, elt, parent_rec_or_elt);
382 if (tag_str && (level != -1)
383 && check_type(elt, (val ? GV_CHAR_PTR : GV_NULL))) {
384 if (supports_continuation(elt, OPT_CONT | OPT_CONC))
385 result = write_long(hndl, elt, level, NULL, tag_str, convert_at(val));
387 result = write_simple(hndl, level, NULL, tag_str, convert_at(val));