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"
27 #include "encoding_state.h"
30 #include "utf8tools.h"
32 #include <sys/types.h>
36 #define MAXWRITELEN MAXGEDCLINELEN
38 struct Gedcom_write_struct {
43 int ctxt_stack[MAXGEDCLEVEL+1];
47 void cleanup_write_buffer();
48 struct safe_buffer write_buffer = { NULL, 0, NULL, 0, cleanup_write_buffer };
50 void cleanup_convert_at_buffer();
51 struct safe_buffer convert_at_buffer = { NULL, 0, NULL, 0,
52 cleanup_convert_at_buffer };
54 void cleanup_write_buffer()
56 cleanup_buffer(&write_buffer);
59 void cleanup_convert_at_buffer()
61 cleanup_buffer(&convert_at_buffer);
64 int write_simple(Gedcom_write_hndl hndl,
65 int level, const char* xref, const char* tag,
75 reset_buffer(&write_buffer);
76 res = safe_buf_append(&write_buffer, "%d", level);
78 res += safe_buf_append(&write_buffer, " %s", xref);
79 res += safe_buf_append(&write_buffer, " %s", tag);
81 res += safe_buf_append(&write_buffer, " %s", value);
82 res += safe_buf_append(&write_buffer, hndl->term);
84 if (utf8_strlen(get_buf_string(&write_buffer)) > MAXGEDCLINELEN) {
85 gedcom_error(_("Line too long"));
88 converted = convert_from_utf8(hndl->conv, get_buf_string(&write_buffer),
89 &conv_fails, &outlen);
91 if (converted && (conv_fails == 0)) {
93 write(hndl->filedesc, converted, outlen);
96 hndl->total_conv_fails += conv_fails;
98 (_("Error converting output string: %s (%d conversion failures)"),
99 strerror(errno), conv_fails);
106 int write_encoding_value(Gedcom_write_hndl hndl,
107 int level, const char* xref, const char* tag,
110 if (strcmp(value, write_encoding.charset))
111 gedcom_warning(_("Forcing HEAD.CHAR value to '%s'"),
112 write_encoding.charset);
113 return write_simple(hndl, level, xref, tag, write_encoding.charset);
116 int supports_continuation(int elt_or_rec, int which_continuation)
118 return tag_data[elt_or_rec].options & which_continuation;
121 int write_long(Gedcom_write_hndl hndl, int elt_or_rec,
122 int level, const char* xref, const char* tag, const char* value)
124 int prefix_len, value_len = 0, term_len;
126 if (value) nl_pos = strchr(value, '\n');
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 if (value) value_len = utf8_strlen(value);
132 term_len = strlen(hndl->term);
134 if (!nl_pos && prefix_len + value_len + term_len <= MAXWRITELEN)
135 write_simple(hndl, level, xref, tag, value);
137 const char* value_ptr = value;
138 int cont_supported = supports_continuation(elt_or_rec, OPT_CONT);
139 int cont_as_conc = supports_continuation(elt_or_rec, OPT_CONT_AS_CONC);
140 if (nl_pos && !cont_supported) {
141 gedcom_error (_("The tag %s doesn't support newlines"), tag);
145 char value_part[MAXWRITELEN];
146 int cont_prefix_len, write_level = level;
147 cont_prefix_len = utf8_strlen("CONT") + 3;
148 if (level + 1 > 9) cont_prefix_len++;
151 char* cont_tag = "CONT";
152 int line_len = (nl_pos && cont_supported
153 ? nl_pos - value_ptr : value_len);
155 if (prefix_len + line_len + term_len > MAXWRITELEN) {
156 line_len = MAXWRITELEN - prefix_len - term_len;
161 memset(value_part, 0, sizeof(value_part));
162 strncpy(value_part, value_ptr, line_len);
163 write_simple(hndl, write_level, xref, tag, value_part);
165 if (line_len < value_len) {
166 value_ptr = value_ptr + line_len;
167 value_len = value_len - line_len;
168 while (*value_ptr == '\n') {
172 prefix_len = cont_prefix_len;
173 write_level = level + 1;
176 nl_pos = strchr(value_ptr, '\n');
187 Gedcom_write_hndl gedcom_write_open(const char *filename)
189 Gedcom_write_hndl hndl;
191 hndl = (Gedcom_write_hndl)malloc(sizeof(struct Gedcom_write_struct));
196 init_write_encoding();
197 init_write_terminator();
198 hndl->total_conv_fails = 0;
199 hndl->conv = initialize_utf8_conversion(write_encoding.encoding, 0);
201 gedcom_error(_("Could not open encoding '%s' for writing: %s"),
202 write_encoding.encoding, strerror(errno));
207 hndl->filedesc = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
208 if (!hndl->filedesc) {
209 gedcom_error(_("Could not open file '%s' for writing: %s"),
210 filename, strerror(errno));
211 cleanup_utf8_conversion(hndl->conv);
216 hndl->term = write_encoding.terminator;
217 hndl->ctxt_level = -1;
218 if (write_encoding.bom == WITH_BOM) {
219 if (write_encoding.width == TWO_BYTE_HILO)
220 write(hndl->filedesc, "\xFE\xFF", 2);
221 else if (write_encoding.width == TWO_BYTE_LOHI)
222 write(hndl->filedesc, "\xFF\xFE", 2);
223 else if (!strcmp(write_encoding.encoding, "UTF-8"))
224 write(hndl->filedesc, "\xEF\xBB\xBF", 3);
226 gedcom_warning(_("Byte order mark configured, but not relevant"));
235 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
239 write_simple(hndl, 0, NULL, "TRLR", NULL);
240 if (total_conv_fails) *total_conv_fails = hndl->total_conv_fails;
241 result = close(hndl->filedesc);
242 cleanup_utf8_conversion(hndl->conv);
248 char* get_tag_string(int elt_or_rec, int tag)
250 int tagnum = tag_data[elt_or_rec].tag;
251 if (!tagnum) tagnum = tag;
254 if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END)
255 return tag_name[tagnum - TAG_NUM_START];
257 gedcom_error(_("Not a valid tag: %d"), tagnum);
262 gedcom_error(_("The element or record type '%s' requires a specific tag "
264 tag_data[elt_or_rec].elt_name);
269 int check_type(int elt_or_rec, Gedcom_val_type type)
271 int allowed = tag_data[elt_or_rec].allowed_types;
275 gedcom_error(_("Wrong data type for writing element or record type '%s'"),
276 tag_data[elt_or_rec].elt_name);
281 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
284 hndl->ctxt_level = 0;
287 while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
289 if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
293 gedcom_error(_("Parent %d not found during write of %d"),
298 hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
299 return hndl->ctxt_level;
302 char* convert_at(const char* input)
305 const char* ptr = input;
306 reset_buffer(&convert_at_buffer);
309 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
310 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
313 SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
317 return get_buf_string(&convert_at_buffer);
323 int _gedcom_write_val(Gedcom_write_hndl hndl,
324 int rec_or_elt, int tag, int parent_rec_or_elt,
325 const char* xrefstr, const char* val)
329 char* tag_str = NULL;
331 tag_str = get_tag_string(rec_or_elt, tag);
332 level = get_level(hndl, rec_or_elt, parent_rec_or_elt);
333 if (tag_str && (level != -1)) {
334 if (rec_or_elt == ELT_HEAD_CHAR)
335 result = write_encoding_value(hndl, level, xrefstr, tag_str, val);
336 else if (supports_continuation(rec_or_elt, OPT_CONT|OPT_CONC))
337 result = write_long(hndl, rec_or_elt, level, xrefstr, tag_str, val);
339 result = write_simple(hndl, level, xrefstr, tag_str, val);
345 int gedcom_write_record_str(Gedcom_write_hndl hndl,
346 Gedcom_rec rec, const char* xrefstr,
350 if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL)))
351 result = _gedcom_write_val(hndl, rec, 0, -1, xrefstr, convert_at(val));
355 int gedcom_write_element_str(Gedcom_write_hndl hndl,
356 Gedcom_elt elt, int tag, int parent_rec_or_elt,
360 if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL)))
361 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
366 int gedcom_write_element_xref(Gedcom_write_hndl hndl,
367 Gedcom_elt elt, int tag, int parent_rec_or_elt,
368 const struct xref_value* val)
371 if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL)))
372 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
377 int gedcom_write_element_date(Gedcom_write_hndl hndl,
378 Gedcom_elt elt, int tag, int parent_rec_or_elt,
379 const struct date_value* val)
382 if (check_type(elt, (val ? GV_DATE_VALUE : GV_NULL)))
383 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
384 gedcom_date_to_string(val));
388 int gedcom_write_element_age(Gedcom_write_hndl hndl,
389 Gedcom_elt elt, int tag, int parent_rec_or_elt,
390 const struct age_value* val)
393 if (check_type(elt, (val ? GV_AGE_VALUE : GV_NULL)))
394 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
395 gedcom_age_to_string(val));
399 int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, const char* tag,
400 const char* xrefstr, const char* value)
403 if (tag && tag[0] == '_')
404 result = write_simple(hndl, level, xrefstr, tag, convert_at(value));
408 int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, const char* tag,
409 const char* xrefstr, const struct xref_value* val)
412 if (tag && tag[0] == '_')
413 result = write_simple(hndl, level, xrefstr, tag, val->string);