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 #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, term_len;
131 char* nl_pos = strchr(value, '\n');
133 prefix_len = utf8_strlen(tag) + 3; /* for e.g. "0 INDI " */
134 if (level > 9) prefix_len++;
135 if (xref) prefix_len += utf8_strlen(xref) + 1;
136 value_len = utf8_strlen(value);
137 term_len = strlen(hndl->term);
139 if (!nl_pos && prefix_len + value_len + term_len <= MAXWRITELEN)
140 write_simple(hndl, level, xref, tag, value);
142 char* value_ptr = value;
143 int cont_supported = supports_continuation(elt_or_rec, OPT_CONT);
144 int cont_as_conc = supports_continuation(elt_or_rec, OPT_CONT_AS_CONC);
145 if (nl_pos && !cont_supported) {
146 gedcom_error (_("The tag %s doesn't support newlines\n"), tag);
150 char value_part[MAXWRITELEN];
151 int cont_prefix_len, write_level = level;
152 cont_prefix_len = utf8_strlen("CONT") + 3;
153 if (level + 1 > 9) cont_prefix_len++;
156 char* cont_tag = "CONT";
157 int line_len = (nl_pos && cont_supported
158 ? nl_pos - value_ptr : value_len);
160 if (prefix_len + line_len + term_len > MAXWRITELEN) {
161 line_len = MAXWRITELEN - prefix_len - term_len;
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 while (*value_ptr == '\n') {
177 prefix_len = cont_prefix_len;
178 write_level = level + 1;
181 nl_pos = strchr(value_ptr, '\n');
192 int gedcom_write_set_encoding(const char* charset,
193 Encoding width, Enc_bom bom)
195 char* new_encoding = NULL;
196 if (!strcmp(charset, "UNICODE")) {
197 if (width == ONE_BYTE) {
198 gedcom_error(_("Unicode cannot be encoded into one byte"));
202 new_encoding = get_encoding(charset, width);
204 encoding = new_encoding;
205 write_encoding_details = width | bom;
212 new_encoding = get_encoding(charset, ONE_BYTE);
214 encoding = new_encoding;
215 write_encoding_details = ONE_BYTE;
223 int gedcom_write_set_line_terminator(Enc_line_end end)
225 write_terminator = terminator[end];
229 Gedcom_write_hndl gedcom_write_open(const char *filename)
231 Gedcom_write_hndl hndl;
233 hndl = (Gedcom_write_hndl)malloc(sizeof(struct Gedcom_write_struct));
238 hndl->total_conv_fails = 0;
239 hndl->conv = initialize_utf8_conversion(encoding, 0);
241 gedcom_error(_("Could not open encoding '%s' for writing: %s"),
242 encoding, strerror(errno));
247 hndl->filedesc = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
248 if (!hndl->filedesc) {
249 gedcom_error(_("Could not open file '%s' for writing: %s"),
250 filename, strerror(errno));
251 cleanup_utf8_conversion(hndl->conv);
256 hndl->term = write_terminator;
257 hndl->ctxt_level = -1;
258 if (write_encoding_details & WITH_BOM) {
259 if (write_encoding_details & TWO_BYTE_HILO)
260 write(hndl->filedesc, "\xFE\xFF", 2);
261 else if (write_encoding_details & TWO_BYTE_LOHI)
262 write(hndl->filedesc, "\xFF\xFE", 2);
264 gedcom_warning(_("Byte order mark configured, but no Unicode"));
273 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
277 write_simple(hndl, 0, NULL, "TRLR", NULL);
278 if (total_conv_fails) *total_conv_fails = hndl->total_conv_fails;
279 result = close(hndl->filedesc);
280 cleanup_utf8_conversion(hndl->conv);
286 char* get_tag_string(int elt_or_rec, int tag)
288 int tagnum = tag_data[elt_or_rec].tag;
289 if (!tagnum) tagnum = tag;
292 if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END)
293 return tag_name[tagnum - TAG_NUM_START];
295 gedcom_error(_("Not a valid tag: %d"), tagnum);
300 gedcom_error(_("The element or record type '%s' requires a specific tag"
302 tag_data[elt_or_rec].elt_name);
307 int check_type(int elt_or_rec, Gedcom_val_type type)
309 int allowed = tag_data[elt_or_rec].allowed_types;
313 gedcom_error(_("Wrong data type for writing element or record type '%s'"),
314 tag_data[elt_or_rec].elt_name);
319 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
322 hndl->ctxt_level = 0;
325 while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
327 if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
331 gedcom_error(_("Parent %d not found during write of %d"),
336 hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
337 return hndl->ctxt_level;
340 char* convert_at(const char* input)
343 const char* ptr = input;
344 reset_buffer(&convert_at_buffer);
347 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
348 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
351 SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
355 return get_buf_string(&convert_at_buffer);
361 int _gedcom_write_val(Gedcom_write_hndl hndl,
362 int rec_or_elt, int tag, int parent_rec_or_elt,
363 char* xrefstr, char* val)
367 char* tag_str = NULL;
369 tag_str = get_tag_string(rec_or_elt, tag);
370 level = get_level(hndl, rec_or_elt, parent_rec_or_elt);
371 if (tag_str && (level != -1)) {
372 if (supports_continuation(rec_or_elt, OPT_CONT|OPT_CONC|OPT_CONT_AS_CONC))
373 result = write_long(hndl, rec_or_elt, level, xrefstr, tag_str, val);
375 result = write_simple(hndl, level, xrefstr, tag_str, val);
381 int gedcom_write_record_str(Gedcom_write_hndl hndl,
382 Gedcom_rec rec, int tag,
383 char* xrefstr, char* val)
386 if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL)))
387 result = _gedcom_write_val(hndl, rec, tag, -1, xrefstr, convert_at(val));
391 int gedcom_write_element_str(Gedcom_write_hndl hndl,
392 Gedcom_elt elt, int tag, int parent_rec_or_elt,
396 if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL)))
397 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
402 int gedcom_write_record_xref(Gedcom_write_hndl hndl,
403 Gedcom_rec rec, int tag,
404 char* xrefstr, struct xref_value* val)
407 if (check_type(rec, (val ? GV_XREF_PTR : GV_NULL)))
408 result = _gedcom_write_val(hndl, rec, tag, -1, xrefstr, val->string);
412 int gedcom_write_element_xref(Gedcom_write_hndl hndl,
413 Gedcom_elt elt, int tag, int parent_rec_or_elt,
414 struct xref_value* val)
417 if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL)))
418 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
423 int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, char* tag,
424 char* xrefstr, char* value)
427 if (tag && tag[0] == '_')
428 result = write_simple(hndl, level, xrefstr, tag, convert_at(value));
432 int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, char* tag,
433 char* xrefstr, struct xref_value* val)
436 if (tag && tag[0] == '_')
437 result = write_simple(hndl, level, xrefstr, tag, val->string);