20221212
[devspec.git] / devspec.en_US / project / recutils / src / rec-writer.c
blob446b184a2600931bdcb11cdddb055b0d88bc2afa
1 /* -*- mode: C -*-
3 * File: rec-writer.c
4 * Date: Sat Dec 26 22:47:16 2009
6 * GNU recutils - Writer
8 */
10 /* Copyright (C) 2009-2019 Jose E. Marchesi */
12 /* This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <config.h>
28 #include <rec.h>
30 #include <stdlib.h>
31 #include <string.h>
33 #include <rec.h>
34 #include <rec-utils.h>
37 * Static functions defined in this file
39 static bool rec_writer_putc (rec_writer_t writer, char c);
40 static bool rec_writer_puts (rec_writer_t writer, const char *s);
42 /* Writer Data Structure
45 struct rec_writer_s
47 FILE *file_out; /* File stream used by the writer. */
48 rec_buf_t buf_out; /* Growable buffer used by the writer. */
50 bool eof;
51 int line; /* Current line number. */
53 /* The following flags and options can be accesssed using the
54 corresponding rec_writer_(get/set)_FLAG function. */
56 bool collapse_p; /* If true the writer won't introduce
57 separators between records. */
58 bool skip_comments_p; /* If true the writer won't print out
59 comments. */
60 enum rec_writer_mode_e mode; /* The mode in which the writer operates.
61 See the definition of the enumerated type
62 in rec.h for a list of allowed modes. */
66 static void
67 rec_writer_new_common (rec_writer_t writer)
69 writer->file_out = NULL;
70 writer->buf_out = NULL;
71 writer->line = 1;
72 writer->eof = false;
73 writer->collapse_p = false;
74 writer->skip_comments_p = false;
75 writer->mode = REC_WRITER_NORMAL;
78 rec_writer_t
79 rec_writer_new (FILE *file_out)
81 rec_writer_t new;
83 new = malloc (sizeof(struct rec_writer_s));
84 if (new)
86 rec_writer_new_common (new);
87 new->file_out = file_out;
90 return new;
93 rec_writer_t
94 rec_writer_new_str (char **str, size_t *str_size)
96 rec_writer_t new;
98 new = malloc (sizeof(struct rec_writer_s));
99 if (new)
101 rec_writer_new_common (new);
102 new->buf_out = rec_buf_new (str, str_size);
105 return new;
108 void
109 rec_writer_destroy (rec_writer_t writer)
111 if (writer)
113 if (writer->file_out)
115 fflush (writer->file_out);
117 if (writer->buf_out)
119 rec_buf_close (writer->buf_out);
122 free (writer);
126 bool
127 rec_write_comment (rec_writer_t writer,
128 rec_comment_t comment)
130 char *line;
131 char *str;
132 char *orig_str;
133 size_t i;
135 if (writer->mode == REC_WRITER_SEXP)
137 if (!rec_writer_puts (writer, "(comment "))
139 return false;
141 if (!rec_writer_putc (writer, '"'))
143 return false;
146 str = rec_comment_text (comment);
147 for (i = 0; i < strlen (str); i++)
149 if (str[i] == '\n')
151 if (!rec_writer_puts (writer, "\\n"))
153 return false;
156 else
158 if (!rec_writer_putc (writer, str[i]))
160 return false;
165 if (!rec_writer_puts (writer, "\")"))
167 return false;
170 else
172 /* Every line in the comment is written preceded by a '#'
173 character. The lines composing the comments are separated by
174 newline characters. */
176 bool first = true;
178 str = strdup (rec_comment_text (comment));
179 orig_str = str; /* Save a pointer to str to deallocate it later,
180 since strsep will modify the str
181 variable. */
182 line = strsep (&str, "\n");
185 if (!first)
187 if (!rec_writer_putc (writer, '\n'))
189 return false;
193 if (!rec_writer_putc (writer, '#')
194 || !rec_writer_puts (writer, line))
196 return false;
199 first = false;
201 while ((line = strsep (&str, "\n")));
203 free (orig_str);
206 return true;
209 bool
210 rec_write_field (rec_writer_t writer,
211 rec_field_t field)
213 size_t pos;
214 const char *fname;
215 const char *fvalue;
216 enum rec_writer_mode_e mode = writer->mode;
218 if (mode == REC_WRITER_SEXP)
220 if (!rec_writer_puts (writer, "(field "))
222 return false;
224 if (!rec_writer_puts (writer, rec_field_char_location_str (field)))
226 return false;
228 if (!rec_writer_putc (writer, ' '))
230 return false;
234 if ((mode != REC_WRITER_VALUES) && (mode != REC_WRITER_VALUES_ROW))
236 fname = rec_field_name (field);
237 if (!rec_write_field_name (writer, fname))
239 return false;
243 /* Write the field value */
244 if (mode == REC_WRITER_SEXP)
246 if (!rec_writer_putc (writer, ' '))
248 return false;
251 if (!rec_writer_putc (writer, '"'))
253 return false;
257 fvalue = rec_field_value (field);
259 if ((strlen (fvalue) > 0) && (mode == REC_WRITER_NORMAL))
261 if (!rec_writer_putc (writer, ' '))
263 return false;
267 for (pos = 0; pos < strlen (fvalue); pos++)
269 if (fvalue[pos] == '\n')
271 if (mode == REC_WRITER_SEXP)
273 if (!rec_writer_puts (writer, "\\n"))
275 return false;
278 else if (mode == REC_WRITER_NORMAL)
280 if (!rec_writer_puts (writer, "\n+ "))
282 return false;
285 else
287 if (!rec_writer_putc (writer, '\n'))
289 return false;
293 else if (((fvalue[pos] == '"') || (fvalue[pos] == '\\')) && (mode == REC_WRITER_SEXP))
295 if ((!rec_writer_putc (writer, '\\'))
296 || (!rec_writer_putc (writer, fvalue[pos])))
298 return false;
301 else
303 if (!rec_writer_putc (writer, fvalue[pos]))
305 /* EOF on output */
306 return false;
311 if (mode == REC_WRITER_SEXP)
313 if (!rec_writer_putc (writer, '"'))
315 return false;
319 if (mode == REC_WRITER_SEXP)
321 if (!rec_writer_puts (writer, ")"))
323 return false;
327 return true;
330 bool
331 rec_write_field_name (rec_writer_t writer,
332 const char *field_name)
334 /* Field names can be written in several formats, according to the
335 * desired mode:
337 * REC_WRITER_NORMAL
338 * The field name is written in rec format. i.e. NP:
339 * REC_WRITER_SEXP
340 * The field name is a string: "NP"
343 enum rec_writer_mode_e mode = writer->mode;
345 if (mode == REC_WRITER_SEXP)
347 if (!rec_writer_putc (writer, '"'))
349 return false;
353 if (!rec_writer_puts (writer, field_name))
355 return false;
358 if (mode == REC_WRITER_SEXP)
360 if (!rec_writer_putc (writer, '"'))
362 return false;
365 else
367 if (!rec_writer_putc (writer, ':'))
369 return false;
373 return true;
376 bool
377 rec_write_record (rec_writer_t writer,
378 rec_record_t record)
380 bool ret;
381 rec_mset_iterator_t iter;
382 rec_mset_elem_t elem;
383 char *data;
384 size_t num_field, num_elem, num_fields, num_elems;
385 enum rec_writer_mode_e mode = writer->mode;
387 ret = true;
389 if (mode == REC_WRITER_SEXP)
391 if (!rec_writer_puts (writer, "(record "))
392 return false;
393 if (!rec_writer_puts (writer, rec_record_char_location_str (record)))
394 return false;
395 if (!rec_writer_puts (writer, " (\n"))
396 return false;
399 num_elems = rec_record_num_elems (record);
400 num_fields = rec_record_num_fields (record);
401 num_field = 0;
402 num_elem = 0;
403 iter = rec_mset_iterator (rec_record_mset (record));
404 while (rec_mset_iterator_next (&iter, MSET_ANY, (const void **) &data, &elem))
406 if (rec_mset_elem_type (elem) == MSET_FIELD)
408 /* Write a field. */
409 rec_field_t field = (rec_field_t) data;
411 if (!rec_write_field (writer, field))
413 ret = false;
414 break;
417 /* Include a field separator. */
419 if ((mode == REC_WRITER_VALUES_ROW)
420 && (num_field != (num_fields - 1)))
422 if(mode == REC_WRITER_VALUES_ROW)
424 if (!rec_writer_putc (writer, ' '))
425 return false;
428 else if ((writer->skip_comments_p && (num_field != (num_fields - 1)))
429 || (!writer->skip_comments_p && (num_elem != (num_elems - 1))))
431 if (!rec_writer_putc (writer, '\n'))
432 return false;
435 num_field++;
437 else if (!writer->skip_comments_p)
439 /* Write a comment. */
441 rec_comment_t comment = (rec_comment_t) data;
443 if ((mode != REC_WRITER_VALUES) && (mode != REC_WRITER_VALUES_ROW))
445 if (!rec_write_comment (writer, comment))
447 ret = false;
448 break;
451 if (num_elem != (num_elems - 1))
453 if (!rec_writer_putc (writer, '\n'))
454 return false;
459 num_elem++;
462 rec_mset_iterator_free (&iter);
464 if (mode == REC_WRITER_SEXP)
466 if (!rec_writer_puts (writer, "))"))
468 return false;
472 return ret;
475 bool
476 rec_write_rset (rec_writer_t writer,
477 rec_rset_t rset)
479 bool ret;
480 rec_record_t descriptor;
481 bool wrote_descriptor;
482 size_t position;
483 size_t descriptor_pos;
484 rec_mset_iterator_t iter;
485 rec_mset_elem_t elem;
486 void *data;
487 enum rec_writer_mode_e mode = writer->mode;
489 ret = true;
490 wrote_descriptor = false;
491 position = 0;
492 descriptor_pos = rec_rset_descriptor_pos (rset);
493 descriptor = rec_rset_descriptor (rset);
495 /* Special case: record set containing just the record
496 descriptor. */
497 if ((rec_rset_num_elems (rset) == 0) && descriptor)
499 rec_write_record (writer,
500 rec_rset_descriptor (rset));
501 rec_writer_putc (writer, '\n');
503 return true;
506 iter = rec_mset_iterator (rec_rset_mset (rset));
507 while (rec_mset_iterator_next (&iter, MSET_ANY, (const void **) &data, &elem))
509 if (position != 0)
511 if (!rec_writer_putc (writer, '\n'))
513 ret = false;
517 if (position == descriptor_pos)
519 if (descriptor
520 && (!(wrote_descriptor = rec_write_record (writer,
521 rec_rset_descriptor (rset)))))
523 ret = false;
525 else
527 if (wrote_descriptor)
529 if (!rec_writer_puts (writer, "\n\n"))
531 ret = false;
537 if (rec_mset_elem_type (elem) == MSET_RECORD)
539 ret = rec_write_record (writer, (rec_record_t) data);
541 else if (!writer->skip_comments_p)
543 ret = rec_write_comment (writer, (rec_comment_t) data);
546 if (!writer->collapse_p || (position == (rec_rset_num_elems (rset) - 1)))
548 if (!rec_writer_putc (writer, '\n'))
550 ret = false;
554 if (!ret)
556 break;
559 position++;
562 rec_mset_iterator_free (&iter);
564 /* Special case:
566 * # comment 1
568 * # comment 2
569 * ...
570 * %rec: foo
572 if (!wrote_descriptor
573 && (descriptor_pos >= rec_rset_num_elems (rset))
574 && rec_rset_descriptor (rset))
576 if (!rec_writer_putc (writer, '\n'))
578 ret = false;
580 if (!rec_write_record (writer, rec_rset_descriptor (rset)))
582 ret = false;
584 if (!rec_writer_putc (writer, '\n'))
586 ret = false;
590 return ret;
593 bool
594 rec_write_db (rec_writer_t writer,
595 rec_db_t db)
597 bool ret;
598 int i;
600 ret = true;
601 for (i = 0; i < rec_db_size (db); i++)
603 rec_rset_t rset = rec_db_get_rset (db, i);
605 if (i != 0)
607 if (!rec_writer_putc (writer, '\n'))
609 ret = false;
610 break;
614 if (!rec_write_rset (writer, rset))
616 ret = false;
617 break;
621 return ret;
624 char *
625 rec_write_field_str (rec_field_t field,
626 rec_writer_mode_t mode)
628 rec_writer_t writer;
629 char *result;
630 size_t result_size;
632 result = NULL;
633 writer = rec_writer_new_str (&result, &result_size);
634 if (writer)
636 rec_writer_set_mode (writer, mode);
637 rec_write_field (writer, field);
638 rec_writer_destroy (writer);
641 return result;
644 char *
645 rec_write_field_name_str (const char *field_name,
646 rec_writer_mode_t mode)
648 rec_writer_t writer;
649 char *result;
650 size_t result_size;
652 result = NULL;
653 writer = rec_writer_new_str (&result, &result_size);
654 if (writer)
656 rec_writer_set_mode (writer, mode);
657 rec_write_field_name (writer, field_name);
658 rec_writer_destroy (writer);
661 return result;
664 char *
665 rec_write_comment_str (rec_comment_t comment,
666 rec_writer_mode_t mode)
668 rec_writer_t writer;
669 char *result;
670 size_t result_size;
672 result = NULL;
673 writer = rec_writer_new_str (&result, &result_size);
674 if (writer)
676 rec_writer_set_mode (writer, mode);
677 rec_write_comment (writer, comment);
678 rec_writer_destroy (writer);
681 return result;
684 bool
685 rec_write_string (rec_writer_t writer,
686 const char *str)
688 return rec_writer_puts (writer, str);
691 void
692 rec_writer_set_collapse (rec_writer_t writer,
693 bool value)
695 writer->collapse_p = value;
698 void
699 rec_writer_set_skip_comments (rec_writer_t writer,
700 bool value)
702 writer->skip_comments_p = value;
705 void
706 rec_writer_set_mode (rec_writer_t writer,
707 enum rec_writer_mode_e mode)
709 writer->mode = mode;
713 * Private functions
716 static bool
717 rec_writer_putc (rec_writer_t writer, char c)
719 bool ret;
721 ret = false;
722 if (writer->file_out)
724 ret = (fputc (c, writer->file_out) != EOF);
726 if (writer->buf_out)
728 ret = (rec_buf_putc (c, writer->buf_out) != EOF);
731 return ret;
734 static bool
735 rec_writer_puts (rec_writer_t writer, const char *s)
737 bool ret;
739 ret = false;
740 if (writer->file_out)
742 ret = (fputs (s, writer->file_out) != EOF);
744 if (writer->buf_out)
746 ret = (rec_buf_puts (s, writer->buf_out) != EOF);
749 return ret;
752 /* End of rec-writer.c */