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
36 #define MAXCHARSETLEN 32
38 char charset[MAXCHARSETLEN+1] = "ASCII";
39 const char* encoding = "ASCII";
40 int write_encoding_details = ONE_BYTE;
41 /* SYS_NEWLINE is defined in config.h */
42 const char* write_terminator = SYS_NEWLINE;
44 struct Gedcom_write_struct {
49 int ctxt_stack[MAXGEDCLEVEL+1];
53 const char* default_encoding[] = {
54 /* ONE_BYTE */ "ASCII",
55 /* TWO_BYTE_HILO */ "UCS-2BE",
56 /* TWO_BYTE_LOHI */ "UCS-2LE"
59 const char* terminator[] = {
62 /* END_CR_LF */ "\x0D\x0A",
63 /* END_LF_CR */ "\x0A\x0D"
66 void cleanup_write_buffer();
67 struct safe_buffer write_buffer = { NULL, 0, NULL, 0, cleanup_write_buffer };
69 void cleanup_convert_at_buffer();
70 struct safe_buffer convert_at_buffer = { NULL, 0, NULL, 0,
71 cleanup_convert_at_buffer };
73 void cleanup_write_buffer()
75 cleanup_buffer(&write_buffer);
78 void cleanup_convert_at_buffer()
80 cleanup_buffer(&convert_at_buffer);
83 int write_simple(Gedcom_write_hndl hndl,
84 int level, const char* xref, const char* tag,
94 reset_buffer(&write_buffer);
95 res = safe_buf_append(&write_buffer, "%d", level);
97 res += safe_buf_append(&write_buffer, " %s", xref);
98 res += safe_buf_append(&write_buffer, " %s", tag);
100 res += safe_buf_append(&write_buffer, " %s", value);
101 res += safe_buf_append(&write_buffer, hndl->term);
103 if (utf8_strlen(get_buf_string(&write_buffer)) > MAXGEDCLINELEN) {
104 gedcom_error(_("Line too long"));
107 converted = convert_from_utf8(hndl->conv, get_buf_string(&write_buffer),
108 &conv_fails, &outlen);
110 if (converted && (conv_fails == 0)) {
112 write(hndl->filedesc, converted, outlen);
115 hndl->total_conv_fails += conv_fails;
117 (_("Error converting output string: %s (%d conversion failures)"),
118 strerror(errno), conv_fails);
125 int write_encoding(Gedcom_write_hndl hndl,
126 int level, char* xref, char* tag, char* value)
128 if (strcmp(value, charset))
129 gedcom_warning(_("Forcing HEAD.CHAR value to '%s'"), charset);
130 return write_simple(hndl, level, xref, tag, 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(const char* new_charset,
205 Encoding width, Enc_bom bom)
207 char* new_encoding = NULL;
208 if (!strcmp(new_charset, "UNICODE")) {
209 if (width == ONE_BYTE) {
210 gedcom_error(_("Unicode cannot be encoded into one byte"));
214 new_encoding = get_encoding(new_charset, width);
216 encoding = new_encoding;
217 write_encoding_details = width | bom;
218 strncpy(charset, new_charset, MAXCHARSETLEN);
225 new_encoding = get_encoding(new_charset, ONE_BYTE);
227 encoding = new_encoding;
228 write_encoding_details = ONE_BYTE;
229 strncpy(charset, new_charset, MAXCHARSETLEN);
237 int gedcom_write_set_line_terminator(Enc_line_end end)
239 write_terminator = terminator[end];
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(encoding, 0);
255 gedcom_error(_("Could not open encoding '%s' for writing: %s"),
256 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_terminator;
271 hndl->ctxt_level = -1;
272 if (write_encoding_details & WITH_BOM) {
273 if (write_encoding_details & TWO_BYTE_HILO)
274 write(hndl->filedesc, "\xFE\xFF", 2);
275 else if (write_encoding_details & TWO_BYTE_LOHI)
276 write(hndl->filedesc, "\xFF\xFE", 2);
278 gedcom_warning(_("Byte order mark configured, but no Unicode"));
287 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
291 write_simple(hndl, 0, NULL, "TRLR", NULL);
292 if (total_conv_fails) *total_conv_fails = hndl->total_conv_fails;
293 result = close(hndl->filedesc);
294 cleanup_utf8_conversion(hndl->conv);
300 char* get_tag_string(int elt_or_rec, int tag)
302 int tagnum = tag_data[elt_or_rec].tag;
303 if (!tagnum) tagnum = tag;
306 if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END)
307 return tag_name[tagnum - TAG_NUM_START];
309 gedcom_error(_("Not a valid tag: %d"), tagnum);
314 gedcom_error(_("The element or record type '%s' requires a specific tag "
316 tag_data[elt_or_rec].elt_name);
321 int check_type(int elt_or_rec, Gedcom_val_type type)
323 int allowed = tag_data[elt_or_rec].allowed_types;
327 gedcom_error(_("Wrong data type for writing element or record type '%s'"),
328 tag_data[elt_or_rec].elt_name);
333 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
336 hndl->ctxt_level = 0;
339 while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
341 if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
345 gedcom_error(_("Parent %d not found during write of %d"),
350 hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
351 return hndl->ctxt_level;
354 char* convert_at(const char* input)
357 const char* ptr = input;
358 reset_buffer(&convert_at_buffer);
361 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
362 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
365 SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
369 return get_buf_string(&convert_at_buffer);
375 int _gedcom_write_val(Gedcom_write_hndl hndl,
376 int rec_or_elt, int tag, int parent_rec_or_elt,
377 char* xrefstr, char* val)
381 char* tag_str = NULL;
383 tag_str = get_tag_string(rec_or_elt, tag);
384 level = get_level(hndl, rec_or_elt, parent_rec_or_elt);
385 if (tag_str && (level != -1)) {
386 if (rec_or_elt == ELT_HEAD_CHAR)
387 result = write_encoding(hndl, level, xrefstr, tag_str, val);
388 else if (supports_continuation(rec_or_elt, OPT_CONT|OPT_CONC))
389 result = write_long(hndl, rec_or_elt, level, xrefstr, tag_str, val);
391 result = write_simple(hndl, level, xrefstr, tag_str, val);
397 int gedcom_write_record_str(Gedcom_write_hndl hndl,
398 Gedcom_rec rec, char* xrefstr, char* val)
401 if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL)))
402 result = _gedcom_write_val(hndl, rec, 0, -1, xrefstr, convert_at(val));
406 int gedcom_write_element_str(Gedcom_write_hndl hndl,
407 Gedcom_elt elt, int tag, int parent_rec_or_elt,
411 if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL)))
412 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
417 int gedcom_write_element_xref(Gedcom_write_hndl hndl,
418 Gedcom_elt elt, int tag, int parent_rec_or_elt,
419 struct xref_value* val)
422 if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL)))
423 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
428 int gedcom_write_element_date(Gedcom_write_hndl hndl,
429 Gedcom_elt elt, int tag, int parent_rec_or_elt,
430 struct date_value* val)
433 if (check_type(elt, (val ? GV_DATE_VALUE : GV_NULL)))
434 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
435 gedcom_date_to_string(val));
439 int gedcom_write_element_age(Gedcom_write_hndl hndl,
440 Gedcom_elt elt, int tag, int parent_rec_or_elt,
441 struct age_value* val)
444 if (check_type(elt, (val ? GV_AGE_VALUE : GV_NULL)))
445 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
446 gedcom_age_to_string(val));
450 int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, char* tag,
451 char* xrefstr, char* value)
454 if (tag && tag[0] == '_')
455 result = write_simple(hndl, level, xrefstr, tag, convert_at(value));
459 int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, char* tag,
460 char* xrefstr, struct xref_value* val)
463 if (tag && tag[0] == '_')
464 result = write_simple(hndl, level, xrefstr, tag, val->string);