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;
159 while (value_ptr[line_len] == ' '
160 || value_ptr[line_len-1] == ' ') {
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 if (*value_ptr == '\n') {
177 prefix_len = cont_prefix_len;
178 write_level = level + 1;
181 nl_pos = strchr(value_ptr, '\n');
192 Gedcom_write_hndl gedcom_write_open(const char *filename)
194 Gedcom_write_hndl hndl;
196 hndl = (Gedcom_write_hndl)malloc(sizeof(struct Gedcom_write_struct));
201 init_write_encoding();
202 init_write_terminator();
203 hndl->total_conv_fails = 0;
204 hndl->conv = initialize_utf8_conversion(write_encoding.encoding, 0);
206 gedcom_error(_("Could not open encoding '%s' for writing: %s"),
207 write_encoding.encoding, strerror(errno));
212 hndl->filedesc = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
213 if (!hndl->filedesc) {
214 gedcom_error(_("Could not open file '%s' for writing: %s"),
215 filename, strerror(errno));
216 cleanup_utf8_conversion(hndl->conv);
221 hndl->term = write_encoding.terminator;
222 hndl->ctxt_level = -1;
223 if (write_encoding.bom == WITH_BOM) {
224 if (write_encoding.width == TWO_BYTE_HILO)
225 write(hndl->filedesc, "\xFE\xFF", 2);
226 else if (write_encoding.width == TWO_BYTE_LOHI)
227 write(hndl->filedesc, "\xFF\xFE", 2);
228 else if (!strcmp(write_encoding.encoding, "UTF-8"))
229 write(hndl->filedesc, "\xEF\xBB\xBF", 3);
231 gedcom_warning(_("Byte order mark configured, but not relevant"));
240 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
244 write_simple(hndl, 0, NULL, "TRLR", NULL);
245 if (total_conv_fails) *total_conv_fails = hndl->total_conv_fails;
246 result = close(hndl->filedesc);
247 cleanup_utf8_conversion(hndl->conv);
253 char* get_tag_string(int elt_or_rec, int tag)
255 int tagnum = tag_data[elt_or_rec].tag;
256 if (!tagnum) tagnum = tag;
259 if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END)
260 return tag_name[tagnum - TAG_NUM_START];
262 gedcom_error(_("Not a valid tag: %d"), tagnum);
267 gedcom_error(_("The element or record type '%s' requires a specific tag "
269 tag_data[elt_or_rec].elt_name);
274 int check_type(int elt_or_rec, Gedcom_val_type type)
276 int allowed = tag_data[elt_or_rec].allowed_types;
280 gedcom_error(_("Wrong data type for writing element or record type '%s'"),
281 tag_data[elt_or_rec].elt_name);
286 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
289 hndl->ctxt_level = 0;
292 while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
294 if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
298 gedcom_error(_("Parent %d not found during write of %d"),
303 hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
304 return hndl->ctxt_level;
307 char* convert_at(const char* input)
310 const char* ptr = input;
311 reset_buffer(&convert_at_buffer);
314 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
315 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
318 SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
322 return get_buf_string(&convert_at_buffer);
328 int _gedcom_write_val(Gedcom_write_hndl hndl,
329 int rec_or_elt, int tag, int parent_rec_or_elt,
330 const char* xrefstr, const char* val)
334 char* tag_str = NULL;
336 tag_str = get_tag_string(rec_or_elt, tag);
337 level = get_level(hndl, rec_or_elt, parent_rec_or_elt);
338 if (tag_str && (level != -1)) {
339 if (rec_or_elt == ELT_HEAD_CHAR)
340 result = write_encoding_value(hndl, level, xrefstr, tag_str, val);
341 else if (supports_continuation(rec_or_elt, OPT_CONT|OPT_CONC))
342 result = write_long(hndl, rec_or_elt, level, xrefstr, tag_str, val);
344 result = write_simple(hndl, level, xrefstr, tag_str, val);
350 int gedcom_write_record_str(Gedcom_write_hndl hndl,
351 Gedcom_rec rec, const char* xrefstr,
355 if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL)))
356 result = _gedcom_write_val(hndl, rec, 0, -1, xrefstr, convert_at(val));
360 int gedcom_write_element_str(Gedcom_write_hndl hndl,
361 Gedcom_elt elt, int tag, int parent_rec_or_elt,
365 if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL)))
366 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
371 int gedcom_write_element_xref(Gedcom_write_hndl hndl,
372 Gedcom_elt elt, int tag, int parent_rec_or_elt,
373 const struct xref_value* val)
376 if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL)))
377 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
382 int gedcom_write_element_date(Gedcom_write_hndl hndl,
383 Gedcom_elt elt, int tag, int parent_rec_or_elt,
384 const struct date_value* val)
387 if (check_type(elt, (val ? GV_DATE_VALUE : GV_NULL)))
388 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
389 gedcom_date_to_string(val));
393 int gedcom_write_element_age(Gedcom_write_hndl hndl,
394 Gedcom_elt elt, int tag, int parent_rec_or_elt,
395 const struct age_value* val)
398 if (check_type(elt, (val ? GV_AGE_VALUE : GV_NULL)))
399 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
400 gedcom_age_to_string(val));
404 int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, const char* tag,
405 const char* xrefstr, const char* value)
408 if (tag && tag[0] == '_')
409 result = write_simple(hndl, level, xrefstr, tag, convert_at(value));
413 int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, const char* tag,
414 const char* xrefstr, const struct xref_value* val)
417 if (tag && tag[0] == '_')
418 result = write_simple(hndl, level, xrefstr, tag, val->string);