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, char* xref, char* tag, char* value)
109 if (strcmp(value, write_encoding.charset))
110 gedcom_warning(_("Forcing HEAD.CHAR value to '%s'"),
111 write_encoding.charset);
112 return write_simple(hndl, level, xref, tag, write_encoding.charset);
115 int supports_continuation(int elt_or_rec, int which_continuation)
117 return tag_data[elt_or_rec].options & which_continuation;
120 int write_long(Gedcom_write_hndl hndl, int elt_or_rec,
121 int level, const char* xref, const char* tag, const char* value)
123 int prefix_len, value_len = 0, term_len;
125 if (value) nl_pos = strchr(value, '\n');
127 prefix_len = utf8_strlen(tag) + 3; /* for e.g. "0 INDI " */
128 if (level > 9) prefix_len++;
129 if (xref) prefix_len += utf8_strlen(xref) + 1;
130 if (value) value_len = utf8_strlen(value);
131 term_len = strlen(hndl->term);
133 if (!nl_pos && prefix_len + value_len + term_len <= MAXWRITELEN)
134 write_simple(hndl, level, xref, tag, value);
136 const char* value_ptr = value;
137 int cont_supported = supports_continuation(elt_or_rec, OPT_CONT);
138 int cont_as_conc = supports_continuation(elt_or_rec, OPT_CONT_AS_CONC);
139 if (nl_pos && !cont_supported) {
140 gedcom_error (_("The tag %s doesn't support newlines"), tag);
144 char value_part[MAXWRITELEN];
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 && cont_supported
152 ? nl_pos - value_ptr : value_len);
154 if (prefix_len + line_len + term_len > MAXWRITELEN) {
155 line_len = MAXWRITELEN - prefix_len - term_len;
160 memset(value_part, 0, sizeof(value_part));
161 strncpy(value_part, value_ptr, line_len);
162 write_simple(hndl, write_level, xref, tag, value_part);
164 if (line_len < value_len) {
165 value_ptr = value_ptr + line_len;
166 value_len = value_len - line_len;
167 while (*value_ptr == '\n') {
171 prefix_len = cont_prefix_len;
172 write_level = level + 1;
175 nl_pos = strchr(value_ptr, '\n');
186 Gedcom_write_hndl gedcom_write_open(const char *filename)
188 Gedcom_write_hndl hndl;
190 hndl = (Gedcom_write_hndl)malloc(sizeof(struct Gedcom_write_struct));
195 init_write_encoding();
196 init_write_terminator();
197 hndl->total_conv_fails = 0;
198 hndl->conv = initialize_utf8_conversion(write_encoding.encoding, 0);
200 gedcom_error(_("Could not open encoding '%s' for writing: %s"),
201 write_encoding.encoding, strerror(errno));
206 hndl->filedesc = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
207 if (!hndl->filedesc) {
208 gedcom_error(_("Could not open file '%s' for writing: %s"),
209 filename, strerror(errno));
210 cleanup_utf8_conversion(hndl->conv);
215 hndl->term = write_encoding.terminator;
216 hndl->ctxt_level = -1;
217 if (write_encoding.bom == WITH_BOM) {
218 if (write_encoding.width == TWO_BYTE_HILO)
219 write(hndl->filedesc, "\xFE\xFF", 2);
220 else if (write_encoding.width == TWO_BYTE_LOHI)
221 write(hndl->filedesc, "\xFF\xFE", 2);
222 else if (!strcmp(write_encoding.encoding, "UTF-8"))
223 write(hndl->filedesc, "\xEF\xBB\xBF", 3);
225 gedcom_warning(_("Byte order mark configured, but not relevant"));
234 int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
238 write_simple(hndl, 0, NULL, "TRLR", NULL);
239 if (total_conv_fails) *total_conv_fails = hndl->total_conv_fails;
240 result = close(hndl->filedesc);
241 cleanup_utf8_conversion(hndl->conv);
247 char* get_tag_string(int elt_or_rec, int tag)
249 int tagnum = tag_data[elt_or_rec].tag;
250 if (!tagnum) tagnum = tag;
253 if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END)
254 return tag_name[tagnum - TAG_NUM_START];
256 gedcom_error(_("Not a valid tag: %d"), tagnum);
261 gedcom_error(_("The element or record type '%s' requires a specific tag "
263 tag_data[elt_or_rec].elt_name);
268 int check_type(int elt_or_rec, Gedcom_val_type type)
270 int allowed = tag_data[elt_or_rec].allowed_types;
274 gedcom_error(_("Wrong data type for writing element or record type '%s'"),
275 tag_data[elt_or_rec].elt_name);
280 int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
283 hndl->ctxt_level = 0;
286 while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
288 if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
292 gedcom_error(_("Parent %d not found during write of %d"),
297 hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
298 return hndl->ctxt_level;
301 char* convert_at(const char* input)
304 const char* ptr = input;
305 reset_buffer(&convert_at_buffer);
308 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
309 SAFE_BUF_ADDCHAR(&convert_at_buffer, '@');
312 SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr);
316 return get_buf_string(&convert_at_buffer);
322 int _gedcom_write_val(Gedcom_write_hndl hndl,
323 int rec_or_elt, int tag, int parent_rec_or_elt,
324 char* xrefstr, char* val)
328 char* tag_str = NULL;
330 tag_str = get_tag_string(rec_or_elt, tag);
331 level = get_level(hndl, rec_or_elt, parent_rec_or_elt);
332 if (tag_str && (level != -1)) {
333 if (rec_or_elt == ELT_HEAD_CHAR)
334 result = write_encoding_value(hndl, level, xrefstr, tag_str, val);
335 else if (supports_continuation(rec_or_elt, OPT_CONT|OPT_CONC))
336 result = write_long(hndl, rec_or_elt, level, xrefstr, tag_str, val);
338 result = write_simple(hndl, level, xrefstr, tag_str, val);
344 int gedcom_write_record_str(Gedcom_write_hndl hndl,
345 Gedcom_rec rec, char* xrefstr, char* val)
348 if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL)))
349 result = _gedcom_write_val(hndl, rec, 0, -1, xrefstr, convert_at(val));
353 int gedcom_write_element_str(Gedcom_write_hndl hndl,
354 Gedcom_elt elt, int tag, int parent_rec_or_elt,
358 if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL)))
359 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
364 int gedcom_write_element_xref(Gedcom_write_hndl hndl,
365 Gedcom_elt elt, int tag, int parent_rec_or_elt,
366 struct xref_value* val)
369 if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL)))
370 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
375 int gedcom_write_element_date(Gedcom_write_hndl hndl,
376 Gedcom_elt elt, int tag, int parent_rec_or_elt,
377 struct date_value* val)
380 if (check_type(elt, (val ? GV_DATE_VALUE : GV_NULL)))
381 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
382 gedcom_date_to_string(val));
386 int gedcom_write_element_age(Gedcom_write_hndl hndl,
387 Gedcom_elt elt, int tag, int parent_rec_or_elt,
388 struct age_value* val)
391 if (check_type(elt, (val ? GV_AGE_VALUE : GV_NULL)))
392 result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL,
393 gedcom_age_to_string(val));
397 int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, char* tag,
398 char* xrefstr, char* value)
401 if (tag && tag[0] == '_')
402 result = write_simple(hndl, level, xrefstr, tag, convert_at(value));
406 int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, char* tag,
407 char* xrefstr, struct xref_value* val)
410 if (tag && tag[0] == '_')
411 result = write_simple(hndl, level, xrefstr, tag, val->string);