2
* Pilot Datebook processing utility
4
* (c) 2000, Matthias Hessler <pilot-datebook@mhessler.de>
6
* This program is free software; you can redistribute it and/or modify it
7
* under the terms of the GNU General Public License as published by the
8
* Free Software Foundation; either version 2 of the License, or (at your
9
* option) any later version.
11
* This program is distributed in the hope that it will be useful, but
12
* WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14
* Public License for more details.
16
* You should have received a copy of the GNU General Public License along
17
* with this program; if not, write to the Free Software Foundation, Inc.,
18
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
#include "pilot-datebook-longtxt.h"
25
/* longtxt is a lossless file format, and designed to be human-readable.
26
* Since it may change over time, it is not guaranteed to read in files
27
* written in earlier versions of longtxt.
29
* It is actually the best output format, as it will capture even more data
30
* than can be stored in pdb files (fields index, misc_flag).
31
* Therefore best results for reading are achieved when reading via hotsync
32
* and writing longtxt.
36
/* Read/write header format */
37
/* Due to usage of scanf, can not suround strings with any delimiters
38
* other than white space or end of line
40
const char LONGTXT_HEADER_NAME_FORMAT[] = "name=%s\n";
41
const char LONGTXT_HEADER_FLAGS_FORMAT[] = "flags=0x%x%s%s%s%s%s%s%s\n";
42
const char LONGTXT_HEADER_MISC_FLAGS_FORMAT[] = "misc_flags=0x%x\n";
43
const char LONGTXT_HEADER_TYPE_FORMAT[] = "type=%s\n";
44
const char LONGTXT_HEADER_CREATOR_FORMAT[] = "creator=%s\n";
45
const char LONGTXT_HEADER_VERSION_FORMAT[] = "version=%d\n";
46
const char LONGTXT_HEADER_MODIFICATION_FORMAT[] = "modification_number=%ld\n";
47
const char LONGTXT_HEADER_CREATE_TIME_FORMAT[] = "creation_time=%s%s%s%s\n";
48
const char LONGTXT_HEADER_MODIFY_TIME_FORMAT[] = "modified_time=%s%s%s%s\n";
49
const char LONGTXT_HEADER_BACKUP_TIME_FORMAT[] = "backup_time=%s%s%s%s\n";
50
const char LONGTXT_HEADER_INDEX_FORMAT[] = "index=%u\n";
51
const char LONGTXT_HEADER_CATEGORY_HEADER1_FORMAT[] = "Categories\n";
52
const char LONGTXT_HEADER_CATEGORY_HEADER2_FORMAT[] = "#\tID\tRenamed\tName\n";
53
const char LONGTXT_HEADER_CATEGORY_ROW_FORMAT[] = "%d\t%u\t%s\t%s%s\n";
54
const char LONGTXT_HEADER_RENAMED_YES[] = "Yes";
55
const char LONGTXT_HEADER_RENAMED_NO[] = "No";
56
const char LONGTXT_HEADER_RENAMED_UNUSED[] = "(unused)";
58
const char LONGTXT_HEADER_LAST_UNIQUE_ID_FORMAT[] = "Last unique ID=%u\n";
59
const char LONGTXT_HEADER_START_OF_WEEK_FORMAT[] = "Start of week: %d\n";
60
const char LONGTXT_HEADER_NO_APP_INFO[] = "No application info found.\n";
61
const char LONGTXT_HEADER_NO_SORT_INFO[] = "No sort info found.\n";
64
/* Read/write row format */
65
/* Due to more manual processing, data fields are surrounded by
66
* greater/less than signs, like this: <data_field>
68
const char LONGTXT_ROW_UNTIMED1_FORMAT[] = "UNTIMED_EVENT <";
69
const char LONGTXT_ROW_UNTIMED2_FORMAT[] = "> on <";
70
const char LONGTXT_ROW_UNTIMED3_FORMAT[] = ">:";
71
const char LONGTXT_ROW_APPOINTMENT1_FORMAT[] = "APPOINTMENT <";
72
const char LONGTXT_ROW_APPOINTMENT2_FORMAT[] = "> from <";
73
const char LONGTXT_ROW_APPOINTMENT3_FORMAT[] = "> to <";
74
const char LONGTXT_ROW_APPOINTMENT4_FORMAT[] = ">:";
75
const char LONGTXT_ROW_ATTRIBUTES_FORMAT[] = "attributes=0x%x%s%s%s%s%s\n";
76
const char LONGTXT_ROW_CATEGORY_FORMAT[] = "category=%d (%s)\n";
78
const char LONGTXT_ROW_REPEAT1_FORMAT[] = "REPEAT from <";
79
const char LONGTXT_ROW_REPEAT2_FORMAT[] = "> until <";
80
const char LONGTXT_ROW_REPEAT3_FORMAT[] = ">: <";
81
const char LONGTXT_ROW_REPEAT4_FORMAT[] = ">";
82
const char LONGTXT_ROW_REPEAT_FOREVER_CONST[] = "forever";
83
const char LONGTXT_ROW_REPEAT_DAILY_CONST[] = "daily";
84
const char LONGTXT_ROW_REPEAT_WEEKLY_CONST[] = "weekly";
85
const char LONGTXT_ROW_REPEAT_MONTHLY_LAST_WEEK_CONST[] = "monthly/last_weekday";
86
const char LONGTXT_ROW_REPEAT_MONTHLY_WEEKDAY_CONST[] = "monthly/weekday";
87
const char LONGTXT_ROW_REPEAT_MONTHLY_CONST[] = "monthly";
88
const char LONGTXT_ROW_REPEAT_YEARLY_CONST[] = "yearly";
89
const char LONGTXT_ROW_REPEAT_UNKNOWN_CONST[] = "unknown";
91
const char LONGTXT_ROW_REPEAT_WEEKLY_FORMAT[] = "%s%s%s%s%s%s%s";
92
const char LONGTXT_ROW_REPEAT_MONTHLY_FORMAT[] = "%d";
93
const char LONGTXT_ROW_REPEAT_MONTHLY_LAST_WEEK_FORMAT[] = "%s %d";
94
const char LONGTXT_ROW_REPEAT_MONTHLY_WEEKDAY_FORMAT[] = "%s ge %d";
96
const char LONGTXT_ROW_REPEAT_YEARLY_FORMAT[] = "%s %d";
97
const char LONGTXT_ROW_REPEAT_PARAM1_FORMAT[] = " on <";
98
const char LONGTXT_ROW_REPEAT_PARAM2_FORMAT[] = ">";
99
const char LONGTXT_ROW_REPEAT_WEEKSTART1_FORMAT[] = " week starts <";
100
const char LONGTXT_ROW_REPEAT_WEEKSTART2_FORMAT[] = ">";
102
const char LONGTXT_ROW_REPEAT_FREQUENCY1_FORMAT[] = " every <";
103
const char LONGTXT_ROW_REPEAT_FREQUENCY2_FORMAT[] = "> times";
105
const char LONGTXT_ROW_REPEAT_OMIT1_FORMAT[] = " OMIT: <";
106
const char LONGTXT_ROW_REPEAT_OMIT2_FORMAT[] = "> <";
107
const char LONGTXT_ROW_REPEAT_OMIT3_FORMAT[] = ">";
109
const char LONGTXT_ROW_ALARM1_FORMAT[] = "ALARM <";
110
const char LONGTXT_ROW_ALARM2_FORMAT[] = "%d %s before>";
111
const char LONGTXT_ROW_ALARM_MIN_CONST[] = "min";
112
const char LONGTXT_ROW_ALARM_HOURS_CONST[] = "hours";
113
const char LONGTXT_ROW_ALARM_DAYS_CONST[] = "days";
114
const char LONGTXT_ROW_ALARM_UNKNOWN_CONST[] = "unknown_unit";
116
const char LONGTXT_ROW_NOTE_FORMAT[] = "NOTE:\n";
120
/* Public functions */
124
/* Initialize read data structure */
126
longtxt_init_read (struct longtxt_file_data * in_file)
130
debug_message("Entering longtxt_init_read\n");
132
/* Init own data structure */
133
in_file->filename = NULL;
134
in_file->file = NULL;
135
in_file->file_is_open = FALSE;
136
in_file->line_buffer = NULL;
137
in_file->line_no = 0;
138
in_file->num_recs = 0;
139
in_file->next_rec = 0;
140
in_file->lines_read = 0;
141
in_file->lines_written = 0;
142
in_file->records_read = 0;
143
in_file->records_written = 0;
146
debug_message("Leaving longtxt_init_read\n");
152
/* Initialize write data structure */
154
longtxt_init_write (struct longtxt_file_data * out_file)
158
debug_message("Entering longtxt_init_write\n");
160
/* Init own data structure */
161
out_file->filename = NULL;
162
out_file->file = NULL;
163
out_file->file_is_open = FALSE;
164
out_file->line_buffer = NULL;
165
out_file->line_no = 0;
166
out_file->num_recs = 0;
167
out_file->next_rec = 0;
168
out_file->lines_read = 0;
169
out_file->lines_written = 0;
170
out_file->records_read = 0;
171
out_file->records_written = 0;
174
debug_message("Leaving longtxt_init_write\n");
180
/* Destroy read data structure */
182
longtxt_exit_read (struct longtxt_file_data * in_file)
186
debug_message("Entering longtxt_exit_read\n");
189
if (in_file->filename)
190
free (in_file->filename);
193
debug_message("Leaving longtxt_exit_read\n");
197
/* Destroy write data structure */
199
longtxt_exit_write (struct longtxt_file_data * out_file)
203
debug_message("Entering longtxt_exit_write\n");
206
if (out_file->filename)
207
free (out_file->filename);
210
debug_message("Leaving longtxt_exit_write\n");
214
/* Set read command line option */
216
longtxt_set_read_option (struct longtxt_file_data * in_file, char opt, char * opt_arg)
222
debug_message("Entering longtxt_set_read_option\n");
229
&& *opt_arg != '\0') {
230
if (in_file->filename)
231
free (in_file->filename);
232
in_file->filename = strdup(opt_arg);
237
fprintf(stderr, "Can not process read option <%c> for input file\n",
242
debug_message("Leaving longtxt_set_read_option\n");
248
/* Set write command line option */
250
longtxt_set_write_option (struct longtxt_file_data * out_file, char opt, char * opt_arg)
256
debug_message("Entering longtxt_set_write_option\n");
263
&& *opt_arg != '\0') {
264
if (out_file->filename)
265
free (out_file->filename);
266
out_file->filename = strdup(opt_arg);
271
fprintf(stderr, "Can not process write option <%c> for input file\n",
276
debug_message("Leaving longtxt_set_write_option\n");
283
/* For opening & closing */
285
/* Open input data file for reading */
287
longtxt_open_read (struct longtxt_file_data * in_file, struct header_data * header)
291
debug_message("Entering longtxt_open_read\n");
294
/* Read from stdin if file name not given */
295
if (!in_file->filename) {
296
/* No open is needed when writing to stdout */
297
in_file->file = stdin;
300
in_file->file = fopen (in_file->filename, "r");
302
error_message("Can not open file <%s> for reading\n",
304
in_file->file_is_open = TRUE;
308
in_file->line_buffer = NULL;
309
in_file->line_no = 0;
310
in_file->num_recs = 0;
311
in_file->next_rec = 0;
313
/* Read header data */
314
longtxt_read_header (in_file, header);
317
debug_message("Leaving longtxt_open_read\n");
321
/* Open output data file for writing */
323
longtxt_open_write (struct longtxt_file_data * out_file, struct header_data * header)
327
debug_message("Entering longtxt_open_write\n");
330
/* Write to stdout if file name not given or "-" */
331
if (!out_file->filename) {
332
/* No open is needed when writing to stdout */
333
out_file->file = stdout;
336
out_file->file = fopen(out_file->filename, "w");
338
error_message("Can not open file <%s> for writing\n",
340
out_file->file_is_open = TRUE;
344
out_file->line_buffer = NULL;
345
out_file->line_no = 0;
346
out_file->num_recs = 0;
347
out_file->next_rec = 0;
350
longtxt_write_header (out_file, header);
353
debug_message("Leaving longtxt_open_write\n");
357
/* Close input file at end of processing */
359
longtxt_close_read (struct longtxt_file_data * in_file, struct header_data * header)
363
debug_message("Entering longtxt_close_read\n");
366
if (in_file->file_is_open == TRUE) {
367
if (fclose (in_file->file))
368
warn_message("Can not close input file\n");
369
in_file->file_is_open = FALSE;
372
/* Update statistics */
373
in_file->lines_read = in_file->line_no;
375
/* clear data structures */
376
in_file->file = NULL;
377
in_file->line_buffer = NULL;
378
in_file->line_no = 0;
379
in_file->num_recs = 0;
380
in_file->next_rec = 0;
383
debug_message("Leaving longtxt_close_read\n");
387
/* Close output file at end of processing */
389
longtxt_close_write (struct longtxt_file_data * out_file, struct header_data * header)
393
debug_message("Entering longtxt_close_write\n");
396
if (out_file->file_is_open == TRUE) {
397
if (fclose (out_file->file))
398
warn_message("Can not close output file\n");
399
out_file->file_is_open = FALSE;
402
/* Update statistics */
403
out_file->lines_written = out_file->line_no;
405
/* Clear data structures */
406
out_file->file = NULL;
407
out_file->line_buffer = NULL;
408
out_file->line_no = 0;
409
out_file->num_recs = 0;
410
out_file->next_rec = 0;
413
debug_message("Leaving longtxt_close_write\n");
417
/* Close input file in case of an error */
419
longtxt_abort_read (struct longtxt_file_data * in_file)
423
debug_message("Entering longtxt_abort_read\n");
426
if (in_file->file_is_open == TRUE) {
427
if (fclose (in_file->file))
428
warn_message("Can not close input file\n");
429
in_file->file_is_open = FALSE;
432
/* No special error processing needed */
434
/* Update statistics */
435
in_file->lines_read = in_file->line_no;
437
/* clear data structures */
438
in_file->file = NULL;
439
in_file->line_buffer = NULL;
440
in_file->line_no = 0;
441
in_file->num_recs = 0;
442
in_file->next_rec = 0;
445
debug_message("Leaving longtxt_abort_read\n");
449
/* Close output file in case of an error */
451
longtxt_abort_write (struct longtxt_file_data * out_file)
455
debug_message("Entering longtxt_abort_write\n");
458
if (out_file->file_is_open == TRUE) {
459
if (fclose (out_file->file))
460
warn_message("Can not close output file\n");
461
out_file->file_is_open = FALSE;
464
/* Remove incompletely written file */
465
if (out_file->filename) {
466
info_message("Removing incompletely written output file <%s>\n",
468
if (out_file->filename)
469
unlink(out_file->filename);
472
/* Update statistics */
473
out_file->lines_written = out_file->line_no;
475
/* Clear data structures */
476
out_file->file = NULL;
477
out_file->line_buffer = NULL;
478
out_file->line_no = 0;
479
out_file->num_recs = 0;
480
out_file->next_rec = 0;
483
debug_message("Leaving longtxt_abort_write\n");
490
/* Identify end of file */
492
longtxt_read_eof (struct longtxt_file_data * in_file)
496
debug_message("Entering & Leaving longtxt_read_eof\n");
498
return feof(in_file->file);
502
/* read routine for appointment in current record */
504
longtxt_read_row (struct longtxt_file_data * in_file, struct row_data * row)
510
const char * p1 = NULL;
518
int repeat_last_week = 0;
519
char repeatparam[0xffff];
521
char date_buffer[50];
524
char buffer1[0xffff];
525
char buffer2[0xffff];
526
char buffer3[0xffff];
527
char buffer4[0xffff];
528
char buffer5[0xffff];
532
struct Appointment a;
535
unsigned long uid = 0;
541
debug_message("Entering longtxt_read_row\n");
543
/* Ideally, this should be reprogrammed to a real parser (maybe using
544
* yacc or so). A parser would be much faster and safer.
545
* Unfortunately, I don't know how to do that, so I had
546
* to sweat through this hell here.
547
* If someone else knows how to do it better...
551
/* Init data structures */
552
memset(&a, 0, sizeof(a));
553
memset(&tm, 0, sizeof(tm));
558
/* Skip empty lines */
559
longtxt_read_line(buffer, sizeof(buffer), in_file);
560
while (buffer[0] == '\n') {
561
longtxt_read_line(buffer, sizeof(buffer), in_file);
563
if (feof(in_file->file))
564
error_message("Read unexpected end of input file at line %d\n\n",
567
/* Now parse; per default assume that reading will fail */
568
setRowIsValid(row, FALSE);
570
/* Now expect UNTIMED_EVENT or APPOINTMENT */
572
p1 = LONGTXT_ROW_UNTIMED1_FORMAT;
573
len = sizeof(LONGTXT_ROW_UNTIMED1_FORMAT) -1;
574
if(!strncmp(b1, p1, len)) {
577
/* Found UNTIMED_EVENT */
581
uid = strtoul(b1, &b2, 0);
584
/* Find event date */
585
p1 = LONGTXT_ROW_UNTIMED2_FORMAT;
586
len = sizeof(LONGTXT_ROW_UNTIMED2_FORMAT) -1;
587
if(strncmp(b1, p1, len)) {
588
warn_message("Can not find event date from input file line %d\n\n",
595
* (assuming here that the first character of the next text string will
596
* always contain the separator)
598
b2 = data_index(b1, LONGTXT_ROW_UNTIMED3_FORMAT[0]);
600
if (len >= sizeof(date_buffer))
601
error_message("Date_buffer size: Can not parse event date from input file line %d\n\n",
603
strncpy(date_buffer, b1, len);
604
date_buffer[len] = '\0';
605
t = read_iso_date_str1 (date_buffer);
607
warn_message("Can not parse start date from input file line %d\n\n",
612
a.begin = *localtime(&t);
614
/* Find end of line */
615
p1 = LONGTXT_ROW_UNTIMED3_FORMAT;
616
len = sizeof(LONGTXT_ROW_UNTIMED3_FORMAT) -1;
617
if(strncmp(b1, p1, len)) {
618
warn_message("Unexpected end of line reading from input file line %d\n\n",
627
/* Check for Appointment */
629
p1 = LONGTXT_ROW_APPOINTMENT1_FORMAT;
630
len = sizeof(LONGTXT_ROW_APPOINTMENT1_FORMAT) -1;
631
if(strncmp(b1, p1, len)) {
632
warn_message("Could not parse input file line %d, expected begin of appointment\n\n",
639
/* Found APPOINTMENT */
643
uid = strtoul(b1, &b2, 0);
646
/* Find event start date & time */
647
p1 = LONGTXT_ROW_APPOINTMENT2_FORMAT;
648
len = sizeof(LONGTXT_ROW_APPOINTMENT2_FORMAT) -1;
649
if(strncmp(b1, p1, len)) {
650
warn_message("Can not find event start date & time from input file line %d\n\n",
656
/* Read event start date & time
657
* (assuming here that the first character of the next text string will
658
* always contain the separator)
660
b2 = data_index(b1, LONGTXT_ROW_APPOINTMENT3_FORMAT[0]);
662
if (len >= sizeof(date_buffer))
663
error_message("Date_buffer size: Can not read event start date & time from input file line %d\n\n",
665
strncpy(date_buffer, b1, len);
666
date_buffer[len] = '\0';
667
t = read_iso_time_str1 (date_buffer);
669
warn_message("Can not parse event start date & time time from input file line %d\n\n",
674
a.begin = *localtime(&t);
677
/* Find event end date & time */
678
p1 = LONGTXT_ROW_APPOINTMENT3_FORMAT;
679
len = sizeof(LONGTXT_ROW_APPOINTMENT3_FORMAT) -1;
680
if(strncmp(b1, p1, len)) {
681
warn_message("Can not find event end date & time from input file line %d\n\n",
687
/* Read event end date & time
688
* (assuming here that the first character of the next text string will
689
* always contain the separator)
691
b2 = data_index(b1, LONGTXT_ROW_APPOINTMENT4_FORMAT[0]);
693
if (len >= sizeof(date_buffer))
694
error_message("Date_buffer size: Can not read event end date & time from input file line %d\n\n",
696
strncpy(date_buffer, b1, len);
697
date_buffer[len] = '\0';
698
t = read_iso_time_str1 (date_buffer);
700
warn_message("Can not parse event end date & time from input file line %d\n\n",
705
a.end = *localtime(&t);
707
/* Find end of line */
708
p1 = LONGTXT_ROW_APPOINTMENT4_FORMAT;
709
len = sizeof(LONGTXT_ROW_APPOINTMENT4_FORMAT) -1;
710
if(strncmp(b1, p1, len)) {
711
warn_message("Unexpected end of line reading from input file line %d\n\n",
716
} /* else Appointment */
718
} /* else UNTIMED_EVENT or Appointment */
720
/* Read attributes, if present */
721
longtxt_read_line(buffer, sizeof(buffer), in_file);
722
num_read = sscanf (buffer, LONGTXT_ROW_ATTRIBUTES_FORMAT,
730
/* Get next line, if we successfully processed this line */
731
longtxt_read_line(buffer, sizeof(buffer), in_file);
734
/* Could not read attributes? Then assume default */
738
/* Read category, if present */
739
num_read = sscanf (buffer, LONGTXT_ROW_CATEGORY_FORMAT,
742
/* Ignoring buffer1 for now, which contains the category name
743
* plus the closing paranthesis - could rebuild or verify category
744
* index table from encountered category id and name
747
/* Get next line, if we successfully processed this line */
748
longtxt_read_line(buffer, sizeof(buffer), in_file);
751
/* Could not read category? Then assume default */
756
/* Read description until encountering REPEAT, ALARM, NOTE,
757
* UNTIMED_EVENT, APPOINTMENT, or two empty lines.
761
while (!feof(in_file->file)
762
&& empty_lines < 2) {
763
/* Test for REPEAT */
765
p1 = LONGTXT_ROW_REPEAT1_FORMAT;
766
len = sizeof(LONGTXT_ROW_REPEAT1_FORMAT) -1;
767
if(!strncmp(b1, p1, len)) {
773
p1 = LONGTXT_ROW_ALARM1_FORMAT;
774
len = sizeof(LONGTXT_ROW_ALARM1_FORMAT) -1;
775
if(!strncmp(b1, p1, len)) {
781
p1 = LONGTXT_ROW_NOTE_FORMAT;
782
len = sizeof(LONGTXT_ROW_NOTE_FORMAT) -1;
783
if(!strncmp(b1, p1, len)) {
787
/* Test for UNTIMED_EVENT */
789
p1 = LONGTXT_ROW_UNTIMED1_FORMAT;
790
len = sizeof(LONGTXT_ROW_UNTIMED1_FORMAT) -1;
791
if(!strncmp(b1, p1, len)) {
795
/* Test for APOINTMENT */
797
p1 = LONGTXT_ROW_APPOINTMENT1_FORMAT;
798
len = sizeof(LONGTXT_ROW_APPOINTMENT1_FORMAT) -1;
799
if(!strncmp(b1, p1, len)) {
803
/* If we are here, then this line is still part of the description */
805
/* Check for empty line
806
* (check is done after exit tests, to allow to remove trailing
809
if (buffer[0] == '\n') {
816
/* Add line to description */
817
strncat(buffer1, buffer, sizeof(buffer1)-strlen(buffer1)-1);
820
longtxt_read_line(buffer, sizeof(buffer), in_file);
822
/* Remove up to 2 trailing newlines from description
824
/* Less then 2 empty lines? Then end without newline */
826
*(buffer1 + strlen(buffer1) - empty_lines -1) = '\0';
828
*(buffer1 + strlen(buffer1) - empty_lines) = '\0';
830
if (strlen(buffer1)<=0) {
831
warn_message("Description was empty when reading from input file line %d\n\n",
835
a.description = strdup(buffer1);
836
if (a.description == NULL)
837
error_message("Out of memory: strdup failed when reading description from input file line %d\n\n",
841
/* Test for REPEAT */
843
p1 = LONGTXT_ROW_REPEAT1_FORMAT;
844
len = sizeof(LONGTXT_ROW_REPEAT1_FORMAT) -1;
845
if (feof(in_file->file)
846
|| strncmp(b1, p1, len)) {
848
a.repeatType = (enum repeatTypes) 0;
850
a.repeatFrequency = 0;
851
a.repeatDay = (enum DayOfMonthType) 0;
854
a.repeatWeekstart = 0;
860
/* Init repeat data */
861
a.repeatType = (enum repeatTypes) 0;
863
a.repeatFrequency = 0;
864
a.repeatDay = (enum DayOfMonthType) 0;
867
a.repeatWeekstart = 0;
869
/* Read repeat start date
870
* (assuming here that the first character of the next text string will
871
* always contain the separator)
873
b2 = data_index(b1, LONGTXT_ROW_REPEAT2_FORMAT[0]);
875
if (len >= sizeof(date_buffer))
876
error_message("Date_buffer size: Can not read repeat start date from input file line %d\n\n",
878
strncpy(date_buffer, b1, len);
879
date_buffer[len] = '\0';
880
t = read_iso_date_str1 (date_buffer);
882
warn_message("Can not parse repeat start date from input file line %d\n\n",
888
if (tm.tm_year != a.begin.tm_year
889
|| tm.tm_mon != a.begin.tm_mon
890
|| tm.tm_mday != a.begin.tm_mday) {
891
warn_message("Repeat start date deviates from event start date, input file line %d\n\n",
896
/* Find repeat end date */
897
p1 = LONGTXT_ROW_REPEAT2_FORMAT;
898
len = sizeof(LONGTXT_ROW_REPEAT2_FORMAT) -1;
899
if(strncmp(b1, p1, len)) {
900
warn_message("Can not find repeat end date from input file line %d\n\n",
906
/* Read repeat end date
907
* (assuming here that the first character of the next text string will
908
* always contain the separator)
910
b2 = data_index(b1, LONGTXT_ROW_REPEAT3_FORMAT[0]);
912
if (len >= sizeof(date_buffer))
913
error_message("Date_buffer size: Can not parse repeat end date from input file line %d\n\n",
915
if (!strncmp(b1, LONGTXT_ROW_REPEAT_FOREVER_CONST, len)) {
921
/* Repeat until end date */
923
strncpy(date_buffer, b1, len);
924
date_buffer[len] = '\0';
925
t = read_iso_date_str1 (date_buffer);
927
warn_message("Can not parse repeat end date from input file line %d\n\n",
932
a.repeatEnd = *localtime(&t);
935
/* Find repeat type */
936
p1 = LONGTXT_ROW_REPEAT3_FORMAT;
937
len = sizeof(LONGTXT_ROW_REPEAT3_FORMAT) -1;
938
if(strncmp(b1, p1, len)) {
939
warn_message("Can not find repeat type in input file line %d\n\n",
946
* (assuming here that the first character of the next text string will
947
* always contain the separator)
949
if (!strncmp(b1, LONGTXT_ROW_REPEAT_DAILY_CONST,
950
sizeof(LONGTXT_ROW_REPEAT_DAILY_CONST) -1)) {
952
a.repeatType = repeatDaily;
953
b1 += sizeof(LONGTXT_ROW_REPEAT_DAILY_CONST) -1;
955
else if (!strncmp(b1, LONGTXT_ROW_REPEAT_WEEKLY_CONST,
956
sizeof(LONGTXT_ROW_REPEAT_WEEKLY_CONST) -1)) {
958
a.repeatType = repeatWeekly;
959
b1 += sizeof(LONGTXT_ROW_REPEAT_WEEKLY_CONST) -1;
961
else if (!strncmp(b1, LONGTXT_ROW_REPEAT_MONTHLY_LAST_WEEK_CONST,
962
sizeof(LONGTXT_ROW_REPEAT_MONTHLY_LAST_WEEK_CONST) -1)) {
963
/* Repeat monthly on a fixed weekday in the last week of the month */
964
a.repeatType = repeatMonthlyByDay;
965
repeat_last_week = TRUE;
966
b1 += sizeof(LONGTXT_ROW_REPEAT_MONTHLY_LAST_WEEK_CONST) -1;
968
else if (!strncmp(b1, LONGTXT_ROW_REPEAT_MONTHLY_WEEKDAY_CONST,
969
sizeof(LONGTXT_ROW_REPEAT_MONTHLY_WEEKDAY_CONST) -1)) {
970
/* Repeat monthly on a fixed weekday */
971
a.repeatType = repeatMonthlyByDay;
972
repeat_last_week = FALSE;
973
b1 += sizeof(LONGTXT_ROW_REPEAT_MONTHLY_WEEKDAY_CONST) -1;
975
else if (!strncmp(b1, LONGTXT_ROW_REPEAT_MONTHLY_CONST,
976
sizeof(LONGTXT_ROW_REPEAT_MONTHLY_CONST) -1)) {
978
a.repeatType = repeatMonthlyByDate;
979
b1 += sizeof(LONGTXT_ROW_REPEAT_MONTHLY_CONST) -1;
981
else if (!strncmp(b1, LONGTXT_ROW_REPEAT_YEARLY_CONST,
982
sizeof(LONGTXT_ROW_REPEAT_YEARLY_CONST) -1)) {
984
a.repeatType = repeatYearly;
985
b1 += sizeof(LONGTXT_ROW_REPEAT_YEARLY_CONST) -1;
988
warn_message("Encountered unknown repeat type from input file line %d\n\n",
993
/* Find end of repeat type */
994
p1 = LONGTXT_ROW_REPEAT4_FORMAT;
995
len = sizeof(LONGTXT_ROW_REPEAT4_FORMAT) -1;
996
if(strncmp(b1, p1, len)) {
997
warn_message("Can not find end of repeat type from input file line %d\n\n",
1004
/* Read repeat type specific parameters */
1007
* Means on proceeding days:
1008
* Today, tomorrow,...
1010
if(a.repeatType == repeatDaily) {
1011
/* On the specified day... */
1012
/* No parameters to read */
1015
/* All other repeat types have repeat type specific data */
1016
/* Find start of repeat type specific data */
1017
p1 = LONGTXT_ROW_REPEAT_PARAM1_FORMAT;
1018
len = sizeof(LONGTXT_ROW_REPEAT_PARAM1_FORMAT) -1;
1019
if(strncmp(b1, p1, len)) {
1020
warn_message("Can not find repeat type specific data from input file line %d\n\n",
1026
b2 = data_index(b1, LONGTXT_ROW_REPEAT_PARAM2_FORMAT[0]);
1028
if (len >= sizeof(buffer1)) {
1029
warn_message("buffer1 size: Can not parse repeat type specific data from input file line %d\n\n",
1033
strncpy(repeatparam, b1, len);
1034
repeatparam[len] = '\0';
1037
/* Find end of repeatparams */
1038
p1 = LONGTXT_ROW_REPEAT_PARAM2_FORMAT;
1039
len = sizeof(LONGTXT_ROW_REPEAT_PARAM2_FORMAT) -1;
1040
if(strncmp(b1, p1, len)) {
1041
warn_message("Unexpected end of line reading from input file line %d\n\n",
1046
/* Now repeatparam contains the repeat type specific data */
1049
/* Repeat monthly by date
1051
* Means always on the same date number (e.g. 17) in every month:
1052
* 17th of January, 17th of February, 17th of March,...
1054
if(a.repeatType == repeatMonthlyByDate) {
1055
/* On the x of every month */
1057
/* Parsing repeat type specific data */
1058
num_read = sscanf (repeatparam,
1059
LONGTXT_ROW_REPEAT_MONTHLY_FORMAT,
1061
if (num_read != 1) {
1062
warn_message("Can not parse repeat type specific data from input file line %d\n\n",
1066
if (mday != a.begin.tm_mday) {
1067
warn_message("Monthly repeat day differs from start day reading from input file line %d\n\n",
1076
* Means always on the same weekday in every week:
1077
* Tuesday this week, Tuesday next week,...
1079
else if(a.repeatType == repeatWeekly)
1080
/* On the chosen days of the week */
1084
/* Parsing repeat type specific data */
1086
len = strlen(repeatparam);
1089
a.repeatDays[k] = 0;
1091
/* process whole repeat param string */
1095
/* skip separator */
1100
/* Found week day name, now trying to find index */
1101
k = weekday2int(b2);
1103
/* Found index of week day */
1104
a.repeatDays[k] = 1;
1110
/* Unknown week day */
1111
strncpy(buffer1, b2, WEEKDAY_LEN);
1112
buffer1[WEEKDAY_LEN]= '\0';
1113
warn_message("Unknown week day <%s> found reading from input file line %d\n\n",
1119
} /* else repeat weekly */
1122
/* Repeat monthly by weekday
1124
* Means always on the same weekday in every month.
1125
* Comes in two flavors:
1126
* - weekday of last week
1127
* - weekday of first, second, third, or forth week
1129
else if(a.repeatType == repeatMonthlyByDay) {
1133
/* Repeat monthly by weekday for last week
1135
* Means always on the same weekday in the last week in every month:
1136
* Last Tuesday this month, last Tuesday next month,...
1138
if(repeat_last_week == TRUE) {
1139
num_read = sscanf (repeatparam,
1140
LONGTXT_ROW_REPEAT_MONTHLY_LAST_WEEK_FORMAT,
1145
warn_message("Can not parse weekday repeat specific data from input file line %d\n\n",
1149
weekday = weekday2int(buffer1);
1151
/* Unknown week day */
1152
strncpy(buffer1, b2, WEEKDAY_LEN);
1153
buffer1[WEEKDAY_LEN]= '\0';
1154
warn_message("Unknown week day <%s> found reading from input file line %d\n\n",
1159
/* day should be 1, since only the last week is possible,
1160
* but can not be bothered to check for this...
1162
a.repeatDay = domLastSun + weekday;
1166
/* Repeat monthly by weekday not in last week
1168
* Means always on the same weekday in the same week in every month:
1169
* Second Tuesday this month, second Tuesday next month,...
1172
num_read = sscanf (repeatparam,
1173
LONGTXT_ROW_REPEAT_MONTHLY_WEEKDAY_FORMAT,
1176
if (num_read != 2) {
1177
warn_message("Can not parse weekday repeat specific data from input file line %d\n\n",
1181
weekday = weekday2int(buffer1);
1183
/* Unknown week day */
1184
strncpy(buffer1, b2, WEEKDAY_LEN);
1185
buffer1[WEEKDAY_LEN]= '\0';
1186
warn_message("Unknown week day <%s> found reading from input file line %d\n\n",
1191
if (day <1 || day > 4) {
1192
warn_message("Unknown week number <%d> found reading from input file line %d\n\n",
1197
a.repeatDay = (day -1)*7 + weekday;
1198
} /* else repeat monthly by weekday not in last week */
1199
} /* else repeat monthly by weekday */
1204
* Means always on the same date in every year:
1205
* 27th October this year, 27th October next year,...
1207
else if(a.repeatType == repeatYearly) {
1208
/* On one day each year */
1209
t = read_iso_date_str1 (repeatparam);
1211
warn_message("Can not parse yearly repeat date from input file line %d\n\n",
1215
tm = *localtime(&t);
1216
if (tm.tm_mon != a.begin.tm_mon
1217
|| tm.tm_mday != a.begin.tm_mday) {
1218
warn_message("Yearly repeat date deviates from event start date, input file line %d\n\n",
1222
} /* else repeat yearly */
1224
} /* end else for repeat types with specific data */
1227
/* Test for repeat frequency */
1228
p1 = LONGTXT_ROW_REPEAT_FREQUENCY1_FORMAT;
1229
len = sizeof(LONGTXT_ROW_REPEAT_FREQUENCY1_FORMAT) -1;
1230
if(strncmp(b1, p1, len)) {
1231
/* No frequency found */
1232
/* Set default: frequency = 1 */
1233
a.repeatFrequency = 1;
1236
/* Found frequency */
1239
/* Read frequency */
1240
a.repeatFrequency = strtol(b1, &b2, 0);
1243
/* Find end of repeat frequency */
1244
p1 = LONGTXT_ROW_REPEAT_FREQUENCY2_FORMAT;
1245
len = sizeof(LONGTXT_ROW_REPEAT_FREQUENCY2_FORMAT) -1;
1246
if(strncmp(b1, p1, len)) {
1247
warn_message("Unexpected end of line reading frequency from input file line %d\n\n",
1255
/* Test for repeat weekstart */
1256
p1 = LONGTXT_ROW_REPEAT_WEEKSTART1_FORMAT;
1257
len = sizeof(LONGTXT_ROW_REPEAT_WEEKSTART1_FORMAT) -1;
1258
if(strncmp(b1, p1, len)) {
1259
/* No weekstart found */
1260
/* Set default: weekstart = 0 */
1261
a.repeatWeekstart = 0;
1264
/* Found weekstart */
1267
/* Read weekstart */
1268
a.repeatWeekstart = strtol(b1, &b2, 0);
1272
b2 = data_index(b1, LONGTXT_ROW_REPEAT_PARAM2_FORMAT[0]);
1274
if (len >= sizeof(buffer1))
1275
error_message("buffer1 size: Can not parse repeat type specific data from input file line %d\n\n",
1277
/* Found week day name, now trying to find index */
1278
a.repeatWeekstart = weekday2int(b1);
1279
if (a.repeatWeekstart<0) {
1280
/* Unknown week day */
1281
strncpy(buffer1, b2, WEEKDAY_LEN);
1282
buffer1[WEEKDAY_LEN]= '\0';
1283
warn_message("Unknown week day <%s> found reading from input file line %d\n\n",
1291
/* Find end of repeat weekstart */
1292
p1 = LONGTXT_ROW_REPEAT_WEEKSTART2_FORMAT;
1293
len = sizeof(LONGTXT_ROW_REPEAT_WEEKSTART2_FORMAT) -1;
1294
if(strncmp(b1, p1, len)) {
1295
warn_message("Unexpected end of line reading weekstart from input file line %d\n\n",
1304
/* Test for repeat exceptions (omissions) */
1305
p1 = LONGTXT_ROW_REPEAT_OMIT1_FORMAT;
1306
len = sizeof(LONGTXT_ROW_REPEAT_OMIT1_FORMAT) -1;
1307
if(strncmp(b1, p1, len)) {
1308
/* No exceptions for this event */
1313
/* Found exceptions */
1316
/* Find out how many exceptions for proper malloc */
1318
/* Look for separator */
1319
b2 = data_index(b1, LONGTXT_ROW_REPEAT_OMIT2_FORMAT[0]);
1323
/* Found end separator */
1324
/* Only count as success when end separator was found */
1327
/* More exceptions to be found ? */
1328
p1 = LONGTXT_ROW_REPEAT_OMIT2_FORMAT;
1329
len = sizeof(LONGTXT_ROW_REPEAT_OMIT2_FORMAT) -1;
1330
if(!strncmp(b2, p1, len)) {
1331
/* Found another exception */
1333
/* Now look for its end separator */
1334
b2 = data_index(b2 +1, LONGTXT_ROW_REPEAT_OMIT2_FORMAT[0]);
1336
/* No more separator */
1340
/* No more exceptions */
1347
a.exception = calloc(a.exceptions, sizeof(struct tm));
1349
/* Now really read exceptions */
1350
b2 = data_index(b1, LONGTXT_ROW_REPEAT_OMIT2_FORMAT[0]);
1353
/* Read exception dates */
1357
/* Found end separator */
1359
/* Read omission date */
1361
if (len >= sizeof(date_buffer))
1362
error_message("Date_buffer size: Can not read omit date from input file line %d\n\n",
1364
strncpy(date_buffer, b1, len);
1365
date_buffer[len] = '\0';
1366
t = read_iso_date_str1 (date_buffer);
1368
warn_message("Can not parse omit date <%s> from input file line %d\n\n",
1374
a.exception[j] = *localtime(&t);
1376
/* Only count as success when successfully processed */
1379
/* More exceptions to be found ? */
1380
p1 = LONGTXT_ROW_REPEAT_OMIT2_FORMAT;
1381
len = sizeof(LONGTXT_ROW_REPEAT_OMIT2_FORMAT) -1;
1382
if(!strncmp(b2, p1, len)) {
1383
/* Found another exception */
1386
/* Now look for its end separator */
1387
b2 = data_index(b2 +1, LONGTXT_ROW_REPEAT_OMIT2_FORMAT[0]);
1389
/* No more separator */
1393
/* No more exceptions */
1395
} /* if found one exception */
1398
/* Find end of repeat exceptions */
1399
p1 = LONGTXT_ROW_REPEAT_OMIT3_FORMAT;
1400
len = sizeof(LONGTXT_ROW_REPEAT_OMIT3_FORMAT) -1;
1401
if(strncmp(b1, p1, len)) {
1402
warn_message("Unexpected end of line reading omit dates from input file line %d\n\n",
1407
/* should be at end of line now */
1408
} /* End of test for repeat exceptions */
1411
/* Read next line */
1412
longtxt_read_line(buffer, sizeof(buffer), in_file);
1413
} /* end else for REPEAT */
1416
/* Test for ALARM */
1418
p1 = LONGTXT_ROW_ALARM1_FORMAT;
1419
len = sizeof(LONGTXT_ROW_ALARM1_FORMAT) -1;
1420
if(feof(in_file->file)
1421
|| strncmp(b1, p1, len)) {
1433
/* alarm how much before */
1434
num_read = sscanf (b1,
1435
LONGTXT_ROW_ALARM2_FORMAT,
1438
if (num_read != 2) {
1439
warn_message("Can not read alarm advance options from input file line %d\n\n",
1444
p1 = LONGTXT_ROW_ALARM_MIN_CONST;
1445
len = sizeof(LONGTXT_ROW_ALARM_MIN_CONST) -1;
1446
if(!strncmp(b2, p1, len)) {
1447
a.advanceUnits = advMinutes;
1450
p1 = LONGTXT_ROW_ALARM_HOURS_CONST;
1451
len = sizeof(LONGTXT_ROW_ALARM_HOURS_CONST) -1;
1452
if(!strncmp(b2, p1, len)) {
1453
a.advanceUnits = advHours;
1456
p1 = LONGTXT_ROW_ALARM_DAYS_CONST;
1457
len = sizeof(LONGTXT_ROW_ALARM_DAYS_CONST) -1;
1458
if(!strncmp(b2, p1, len)) {
1459
a.advanceUnits = advDays;
1462
warn_message("Can not understand alarm advance unit <%s> from input file line %d\n\n",
1466
} /* advance days */
1467
} /* advance hours */
1468
} /* advance minutes */
1471
/* Read next line */
1472
longtxt_read_line(buffer, sizeof(buffer), in_file);
1477
p1 = LONGTXT_ROW_NOTE_FORMAT;
1478
len = sizeof(LONGTXT_ROW_NOTE_FORMAT) -1;
1479
if(feof(in_file->file)
1480
|| strncmp(buffer, p1, len)) {
1486
/* should be at end of line now */
1488
/* Read next line */
1489
longtxt_read_line(buffer, sizeof(buffer), in_file);
1491
/* Read note until encountering two empty lines or
1492
* next UNTIMED_EVENT, or APPOINTMENT
1496
while (!feof(in_file->file)
1497
&& empty_lines < 2) {
1498
/* Test for UNTIMED_EVENT */
1500
p1 = LONGTXT_ROW_UNTIMED1_FORMAT;
1501
len = sizeof(LONGTXT_ROW_UNTIMED1_FORMAT) -1;
1502
if(!strncmp(b1, p1, len)) {
1506
/* Test for APPOINTMENT */
1508
p1 = LONGTXT_ROW_APPOINTMENT1_FORMAT;
1509
len = sizeof(LONGTXT_ROW_APPOINTMENT1_FORMAT) -1;
1510
if(!strncmp(b1, p1, len)) {
1514
/* If we are here, then this line is still part of the note */
1516
/* Check for empty line
1517
* (check is done after exit tests, to allow to remove trailing
1520
if (buffer[0] == '\n') {
1527
/* Add line to note */
1528
strncat(buffer1, buffer, sizeof(buffer1)-strlen(buffer1)-1);
1530
/* Read next line */
1531
longtxt_read_line(buffer, sizeof(buffer), in_file);
1534
/* Always remove at max two trailing newlines, since two newlines
1535
* separate the note from the following data.
1537
/* Less then 2 empty lines? Then end without newline */
1538
if (empty_lines < 2)
1539
*(buffer1 + strlen(buffer1) - empty_lines -1) = '\0';
1541
*(buffer1 + strlen(buffer1) - empty_lines) = '\0';
1543
if (strlen(buffer1)<=0) {
1544
warn_message("Note was empty when reading from input file line %d\n\n",
1548
a.note = strdup(buffer1);
1550
error_message("Out of memory: strdup failed when reading note from input file line %d\n\n",
1554
/* Skip empty lines to position for next row */
1555
while (!feof(in_file->file)
1556
&& buffer[0] == '\n') {
1557
longtxt_read_line(buffer, sizeof(buffer), in_file);
1560
/* Put last line back, to be read on next read attempt */
1561
if (!feof(in_file->file))
1562
longtxt_pushback_line(buffer, in_file);
1566
/* Set datebook row data */
1567
setRowRecordNum(row, record_num);
1568
setRowUid(row, uid);
1569
setRowAttributes(row, attributes);
1570
setRowCategory(row, category);
1571
setRowAppointment(row, a);
1572
setRowIsValid(row, TRUE);
1574
/* increment counters */
1575
in_file->num_recs++;
1576
in_file->next_rec++;
1578
/* Update statistics */
1579
in_file->records_read++;
1583
debug_message("Leaving longtxt_read_row\n");
1590
/* Write an appointment record */
1592
longtxt_write_row (struct longtxt_file_data * out_file, struct header_data * header, struct row_data * row)
1595
struct AppointmentAppInfo aai;
1596
struct Appointment a;
1602
char date1_buffer[50];
1603
char time1_buffer[50];
1604
char date2_buffer[50];
1605
char time2_buffer[50];
1606
char repeatType[30];
1607
char repeatparam[30];
1608
char alarmparam[80];
1609
int repeatparam_len = 0;
1613
debug_message("Entering longtxt_write_row\n");
1615
if (!getRowIsValid(row))
1616
error_message("Can not write invalid row.\n");
1618
/* Get datebook header data */
1621
/* Get datebook row data */
1622
record_num = getRowRecordNum(row);
1623
uid = getRowUid(row);
1624
attributes = getRowAttributes(row);
1625
category = getRowCategory(row);
1626
a = getRowAppointment(row);
1629
/* Write datebook row data */
1630
/* Event type/time */
1632
write_human_date_str (mktime(&a.begin), date1_buffer, sizeof(date1_buffer));
1633
longtxt_write(out_file, "%s%lu%s%s%s",
1634
LONGTXT_ROW_UNTIMED1_FORMAT,
1636
LONGTXT_ROW_UNTIMED2_FORMAT,
1638
LONGTXT_ROW_UNTIMED3_FORMAT);
1639
longtxt_write_str(out_file, "\n");
1642
write_human_full_time_str (mktime(&a.begin), time1_buffer, sizeof(time1_buffer));
1643
write_human_full_time_str (mktime(&a.end), time2_buffer, sizeof(time2_buffer));
1644
longtxt_write(out_file, "%s%lu%s%s%s%s%s",
1645
LONGTXT_ROW_APPOINTMENT1_FORMAT,
1647
LONGTXT_ROW_APPOINTMENT2_FORMAT,
1649
LONGTXT_ROW_APPOINTMENT3_FORMAT,
1651
LONGTXT_ROW_APPOINTMENT4_FORMAT);
1652
longtxt_write_str(out_file, "\n");
1656
if (attributes > 0) {
1657
longtxt_write(out_file, LONGTXT_ROW_ATTRIBUTES_FORMAT,
1659
(attributes & dlpRecAttrDeleted) ? " DELETED" : "",
1660
(attributes & dlpRecAttrDirty) ? " DIRTY" : "",
1661
(attributes & dlpRecAttrBusy) ? " LOCKED" : "",
1662
(attributes & dlpRecAttrSecret) ? " PRIVATE" : "",
1663
(attributes & dlpRecAttrArchived) ? " ARCHIVED" : "");
1668
if (header->isValid)
1669
longtxt_write(out_file, LONGTXT_ROW_CATEGORY_FORMAT,
1671
aai.category.name[category]);
1673
longtxt_write(out_file, LONGTXT_ROW_CATEGORY_FORMAT,
1679
/* Print it this way to properly count lines, and description is non-empty */
1681
longtxt_write_str(out_file, a.description);
1683
longtxt_write_str(out_file, " ");
1684
longtxt_write_str(out_file, "\n");
1687
if(a.repeatType == repeatNone) {
1688
/* One-time event (no repeats) */
1692
write_human_date_str (mktime(&a.begin), date1_buffer, sizeof(date1_buffer));
1694
if (a.repeatForever)
1695
strncpy(date2_buffer, LONGTXT_ROW_REPEAT_FOREVER_CONST, sizeof(date2_buffer));
1697
write_human_date_str (mktime(&a.repeatEnd), date2_buffer, sizeof(date2_buffer));
1699
if(a.repeatType == repeatDaily) {
1700
strncpy(repeatType, LONGTXT_ROW_REPEAT_DAILY_CONST, sizeof(repeatType));
1701
} else if(a.repeatType == repeatWeekly) {
1702
strncpy(repeatType, LONGTXT_ROW_REPEAT_WEEKLY_CONST, sizeof(repeatType));
1703
} else if(a.repeatType == repeatMonthlyByDay) {
1704
if(a.repeatDay>=domLastSun) {
1705
strncpy(repeatType, LONGTXT_ROW_REPEAT_MONTHLY_LAST_WEEK_CONST, sizeof(repeatType));
1707
strncpy(repeatType, LONGTXT_ROW_REPEAT_MONTHLY_WEEKDAY_CONST, sizeof(repeatType));
1709
} else if(a.repeatType == repeatMonthlyByDate) {
1710
strncpy(repeatType, LONGTXT_ROW_REPEAT_MONTHLY_CONST, sizeof(repeatType));
1711
} else if(a.repeatType == repeatYearly) {
1712
strncpy(repeatType, LONGTXT_ROW_REPEAT_YEARLY_CONST, sizeof(repeatType));
1714
strncpy(repeatType, LONGTXT_ROW_REPEAT_UNKNOWN_CONST, sizeof(repeatType));
1717
longtxt_write(out_file, "%s%s%s%s%s%s%s",
1718
LONGTXT_ROW_REPEAT1_FORMAT,
1720
LONGTXT_ROW_REPEAT2_FORMAT,
1722
LONGTXT_ROW_REPEAT3_FORMAT,
1724
LONGTXT_ROW_REPEAT4_FORMAT);
1727
if(a.repeatType == repeatDaily) {
1728
/* On the specified day... */
1729
/* No more parameters */
1731
/* Repeat monthly by date */
1732
} else if(a.repeatType == repeatMonthlyByDate) {
1733
/* On the x of every month */
1734
snprintf (repeatparam, sizeof(repeatparam),
1735
LONGTXT_ROW_REPEAT_MONTHLY_FORMAT,
1737
longtxt_write(out_file, "%s%s%s",
1738
LONGTXT_ROW_REPEAT_PARAM1_FORMAT,
1740
LONGTXT_ROW_REPEAT_PARAM2_FORMAT);
1743
} else if(a.repeatType == repeatWeekly) {
1745
/* On the chosen days of the week */
1747
repeatparam[0] ='\0';
1748
repeatparam_len = 0;
1750
if(a.repeatDays[k]) {
1751
/* Use comma as separator between weekdays */
1752
if (repeatparam_len > 0) {
1753
repeatparam[repeatparam_len] = ',';
1755
repeatparam[repeatparam_len] = '\0';
1758
strncat(repeatparam + repeatparam_len,
1760
sizeof(repeatparam)-repeatparam_len-1);
1761
repeatparam_len = strlen(repeatparam);
1765
longtxt_write(out_file, "%s%s%s",
1766
LONGTXT_ROW_REPEAT_PARAM1_FORMAT,
1768
LONGTXT_ROW_REPEAT_PARAM2_FORMAT);
1770
/* Repeat monthly by weekday */
1771
} else if(a.repeatType == repeatMonthlyByDay) {
1775
if(a.repeatDay>=domLastSun) {
1777
weekday = a.repeatDay % 7;
1778
snprintf (repeatparam, sizeof(repeatparam),
1779
LONGTXT_ROW_REPEAT_MONTHLY_LAST_WEEK_FORMAT,
1780
int2weekday(weekday),
1783
/* day contains the week number */
1784
day = a.repeatDay / 7 +1;
1785
weekday = a.repeatDay % 7;
1786
snprintf (repeatparam, sizeof(repeatparam),
1787
LONGTXT_ROW_REPEAT_MONTHLY_WEEKDAY_FORMAT,
1788
int2weekday(weekday),
1791
longtxt_write(out_file, "%s%s%s",
1792
LONGTXT_ROW_REPEAT_PARAM1_FORMAT,
1794
LONGTXT_ROW_REPEAT_PARAM2_FORMAT);
1797
} else if(a.repeatType == repeatYearly) {
1798
/* On one day each year */
1799
snprintf (repeatparam, sizeof(repeatparam),
1800
LONGTXT_ROW_REPEAT_YEARLY_FORMAT,
1801
int2month(a.begin.tm_mon),
1803
longtxt_write(out_file, "%s%s%s",
1804
LONGTXT_ROW_REPEAT_PARAM1_FORMAT,
1806
LONGTXT_ROW_REPEAT_PARAM2_FORMAT);
1809
/* Repeat frequency */
1810
if(a.repeatFrequency != 1) {
1811
longtxt_write(out_file, "%s%d%s",
1812
LONGTXT_ROW_REPEAT_FREQUENCY1_FORMAT,
1814
LONGTXT_ROW_REPEAT_FREQUENCY2_FORMAT);
1818
/* Repeat week start
1820
* The user-decided day which starts the week
1821
* (0 = Sunday, 1 = Monday).
1822
* For some reason only being used for weekly events?
1824
if(a.repeatWeekstart != 0) {
1825
longtxt_write(out_file, "%s%s%s",
1826
LONGTXT_ROW_REPEAT_WEEKSTART1_FORMAT,
1827
int2weekday(a.repeatWeekstart),
1828
LONGTXT_ROW_REPEAT_WEEKSTART2_FORMAT);
1832
/* List all exceptions of a repeating event */
1834
write_human_date_str (mktime(&a.exception[0]),
1835
date1_buffer, sizeof(date1_buffer));
1836
longtxt_write(out_file, "%s%s",
1837
LONGTXT_ROW_REPEAT_OMIT1_FORMAT,
1839
/* Start from 1, since first exception has already been printed */
1840
for(j=1;j<a.exceptions;j++) {
1841
write_human_date_str (mktime(&a.exception[j]),
1842
date1_buffer, sizeof(date1_buffer));
1843
longtxt_write(out_file, "%s%s",
1844
LONGTXT_ROW_REPEAT_OMIT2_FORMAT,
1847
longtxt_write_str(out_file, LONGTXT_ROW_REPEAT_OMIT3_FORMAT);
1848
} /* if exceptions */
1849
longtxt_write_str(out_file, "\n");
1854
/* alarm how much before */
1855
snprintf(alarmparam, sizeof(alarmparam), LONGTXT_ROW_ALARM2_FORMAT,
1857
(a.advanceUnits == advMinutes) ? LONGTXT_ROW_ALARM_MIN_CONST :
1858
(a.advanceUnits == advHours) ? LONGTXT_ROW_ALARM_HOURS_CONST :
1859
(a.advanceUnits == advDays) ? LONGTXT_ROW_ALARM_DAYS_CONST :
1860
LONGTXT_ROW_ALARM_UNKNOWN_CONST);
1861
longtxt_write(out_file, "%s%s",
1862
LONGTXT_ROW_ALARM1_FORMAT,
1864
longtxt_write_str(out_file, "\n");
1869
/* Print it this way to properly count lines */
1870
longtxt_write_str(out_file, LONGTXT_ROW_NOTE_FORMAT);
1871
longtxt_write_str(out_file, a.note);
1872
longtxt_write_str(out_file, "\n");
1875
/* Have at least one empty line between rows */
1876
longtxt_write_str(out_file, "\n");
1878
/* Increase counters */
1879
out_file->num_recs++;
1880
out_file->next_rec++;
1882
/* Update statistics */
1883
out_file->records_written++;
1886
debug_message("Leaving longtxt_write_row\n");
1891
/* For statistics */
1893
/* Show input statistics */
1895
longtxt_show_read_statistics (struct longtxt_file_data * in_file)
1899
debug_message("Entering longtxt_show_read_statistics\n");
1901
info_message("Input file <%s>, format <%s>:\n",
1902
(in_file->filename) ? in_file->filename : "stdin",
1903
dataformat2txt(DATA_FORMAT_LONGTXT));
1904
info_message("Lines read: %d\n",
1905
in_file->lines_read);
1906
info_message("Records read: %d\n",
1907
in_file->records_read);
1910
debug_message("Leaving longtxt_show_read_statistics\n");
1914
/* Show output statistics */
1916
longtxt_show_write_statistics (struct longtxt_file_data * out_file)
1920
debug_message("Entering longtxt_show_write_statistics\n");
1922
info_message("Output file <%s>, format <%s>:\n",
1923
(out_file->filename) ? out_file->filename : "stdout",
1924
dataformat2txt(DATA_FORMAT_LONGTXT));
1925
info_message("Lines written: %d\n",
1926
out_file->lines_written);
1927
info_message("Records written: %d\n",
1928
out_file->records_written);
1931
debug_message("Leaving longtxt_show_write_statistics\n");
1936
/* Private functions */
1938
/* Read datebook file header */
1940
longtxt_read_header (struct longtxt_file_data * in_file, struct header_data * header)
1943
struct AppointmentAppInfo aai;
1950
char buffer[0xffff];
1951
char buffer1[0xffff];
1952
char buffer2[0xffff];
1953
char buffer3[0xffff];
1954
char buffer4[0xffff];
1955
char buffer5[0xffff];
1956
char buffer6[0xffff];
1957
char buffer7[0xffff];
1959
char long_buffer[50];
1967
debug_message("Entering longtxt_read_header\n");
1969
/* Init data structures */
1970
memset(&ip, 0, sizeof(ip));
1971
memset(&aai, 0, sizeof(aai));
1973
/* Read general database header data */
1975
/* Skip empty lines */
1976
longtxt_read_line(buffer, sizeof(buffer), in_file);
1977
while (buffer[0] == '\n') {
1978
longtxt_read_line(buffer, sizeof(buffer), in_file);
1980
if (feof(in_file->file))
1981
error_message("Read unexpected end of input file at line %d\n\n",
1984
/* Check for UNTIMED_EVENT or APPOINTMENT */
1986
LONGTXT_ROW_UNTIMED1_FORMAT,
1987
sizeof(LONGTXT_ROW_UNTIMED1_FORMAT) -1)) {
1988
/* Found UNTIMED_EVENT => No header present */
1989
longtxt_pushback_line(buffer, in_file);
1992
else if(!strncmp(buffer,
1993
LONGTXT_ROW_APPOINTMENT1_FORMAT,
1994
sizeof(LONGTXT_ROW_APPOINTMENT1_FORMAT) -1)) {
1995
/* Found APPOINTMENT => No header present */
1996
longtxt_pushback_line(buffer, in_file);
2000
/* Read database name */
2001
num_read = sscanf (buffer, LONGTXT_HEADER_NAME_FORMAT,
2003
if (num_read != 1) {
2004
warn_message("Can not read header name from input file line %d\n\n",
2010
/* Read database flags */
2011
longtxt_read_line(buffer, sizeof(buffer), in_file);
2012
num_read = sscanf (buffer, LONGTXT_HEADER_FLAGS_FORMAT,
2021
if (num_read <= 0) {
2022
warn_message("Can not read flags from input file line %d\n\n",
2026
if (ip.flags & dlpDBFlagResource) {
2027
warn_message("Input file is not a Datebook file, resource flag is set!\n\n");
2032
/* Read database misc flags */
2033
longtxt_read_line(buffer, sizeof(buffer), in_file);
2034
num_read = sscanf (buffer, LONGTXT_HEADER_MISC_FLAGS_FORMAT,
2036
if (num_read != 1) {
2037
warn_message("Can not read misc flags from input file line %d\n\n",
2043
/* Read database type */
2044
longtxt_read_line(buffer, sizeof(buffer), in_file);
2045
num_read = sscanf (buffer, LONGTXT_HEADER_TYPE_FORMAT,
2047
if (num_read != 1) {
2048
warn_message("Can not read database type from input file line %d\n\n",
2052
ip.type = makelong(long_buffer);
2055
/* Read database creator */
2056
longtxt_read_line(buffer, sizeof(buffer), in_file);
2057
num_read = sscanf (buffer, LONGTXT_HEADER_CREATOR_FORMAT,
2059
if (num_read != 1) {
2060
warn_message("Can not read creator from input file line %d\n\n",
2064
ip.creator = makelong(long_buffer);
2067
/* Read database version */
2068
longtxt_read_line(buffer, sizeof(buffer), in_file);
2069
num_read = sscanf (buffer, LONGTXT_HEADER_VERSION_FORMAT,
2071
if (num_read != 1) {
2072
warn_message("Can not read version from input file line %d\n\n",
2078
/* Read database modification number */
2079
longtxt_read_line(buffer, sizeof(buffer), in_file);
2080
num_read = sscanf (buffer, LONGTXT_HEADER_MODIFICATION_FORMAT,
2082
if (num_read != 1) {
2083
warn_message("Can not read modification number from input file line %d\n\n",
2089
/* Read database creation time */
2090
longtxt_read_line(buffer, sizeof(buffer), in_file);
2091
num_read = sscanf (buffer, LONGTXT_HEADER_CREATE_TIME_FORMAT,
2092
&buffer1, &buffer2, &buffer3, &buffer4);
2093
if (num_read < 2 || num_read > 4) {
2094
warn_message("Can not read creation time from input file line %d\n\n",
2098
t = read_iso_time_str4n (num_read, buffer1, buffer2, buffer3, buffer4);
2100
info_message("Can not parse creation time from input file line %d\n\n",
2105
/* Read database modification time */
2106
longtxt_read_line(buffer, sizeof(buffer), in_file);
2107
num_read = sscanf (buffer, LONGTXT_HEADER_MODIFY_TIME_FORMAT,
2108
&buffer1, &buffer2, &buffer3, &buffer4);
2109
if (num_read < 2 || num_read > 4) {
2110
warn_message("Can not read modification time from input file line %d\n\n",
2114
t = read_iso_time_str4n (num_read, buffer1, buffer2, buffer3, buffer4);
2116
info_message("Can not parse modification time from input file line %d\n\n",
2121
/* Read database backup time */
2122
longtxt_read_line(buffer, sizeof(buffer), in_file);
2123
num_read = sscanf (buffer, LONGTXT_HEADER_BACKUP_TIME_FORMAT,
2124
&buffer1, &buffer2, &buffer3, &buffer4);
2125
if (num_read < 2 || num_read > 4) {
2126
warn_message("Can not read backup time from input file line %d\n\n",
2130
t = read_iso_time_str4n (num_read, buffer1, buffer2, buffer3, buffer4);
2132
info_message("Can not parse backup time from input file line %d\n\n",
2137
/* Read database index */
2138
longtxt_read_line(buffer, sizeof(buffer), in_file);
2139
num_read = sscanf (buffer, LONGTXT_HEADER_INDEX_FORMAT,
2141
if (num_read != 1) {
2142
warn_message("Can not read index from input file line %d\n\n",
2149
/* Read datebook application information header data */
2151
/* Skip empty lines, then check for category header */
2152
longtxt_read_line(buffer, sizeof(buffer), in_file);
2153
while (buffer[0] == '\n') {
2154
longtxt_read_line(buffer, sizeof(buffer), in_file);
2156
if (!strncmp(buffer, LONGTXT_HEADER_NO_APP_INFO,
2157
sizeof(LONGTXT_HEADER_NO_APP_INFO))) {
2158
/* No application header found */
2162
/* Found application header */
2163
if (strncmp(buffer, LONGTXT_HEADER_CATEGORY_HEADER1_FORMAT,
2164
sizeof(LONGTXT_HEADER_CATEGORY_HEADER1_FORMAT))) {
2165
warn_message("Can not read category header 1 from input file line %d\n\n",
2169
longtxt_read_line(buffer, sizeof(buffer), in_file);
2170
if (strncmp(buffer, LONGTXT_HEADER_CATEGORY_HEADER2_FORMAT,
2171
sizeof(LONGTXT_HEADER_CATEGORY_HEADER2_FORMAT))) {
2172
warn_message("Can not read category header 2 from input file line %d\n\n",
2177
/* Read category rows */
2178
for(j=0;j<DATEBOOK_MAX_CATEGORIES;j++) {
2179
longtxt_read_line(buffer, sizeof(buffer), in_file);
2180
num_read = sscanf (buffer, LONGTXT_HEADER_CATEGORY_ROW_FORMAT,
2182
&((aai.category).ID[j]),
2184
&((aai.category).name[j]));
2185
if (num_read != 4) {
2186
/* Allow category 0 to be without name (Unfiled) */
2187
if (j != 0 || num_read != 3) {
2188
warn_message("Can not read category row %d from input file line %d\n\n",
2189
j, in_file->line_no);
2193
if (strncmp(buffer2, LONGTXT_HEADER_RENAMED_YES, sizeof(buffer2)))
2194
(aai.category).renamed[j] = 0;
2196
(aai.category).renamed[j] = 1;
2197
if (!strncmp((aai.category).name[j],
2198
LONGTXT_HEADER_RENAMED_UNUSED,
2199
sizeof(LONGTXT_HEADER_RENAMED_UNUSED))) {
2200
memset((aai.category).name[j], 0, sizeof((aai.category).name[j]));
2202
} /* for all categories */
2204
/* Skip empty lines, then read last unique category id */
2205
longtxt_read_line(buffer, sizeof(buffer), in_file);
2206
while (buffer[0] == '\n') {
2207
longtxt_read_line(buffer, sizeof(buffer), in_file);
2209
num_read = sscanf (buffer, LONGTXT_HEADER_LAST_UNIQUE_ID_FORMAT,
2210
&((aai.category).lastUniqueID));
2211
if (num_read != 1) {
2212
warn_message("Can not read last unique category id from input file line %d\n\n",
2218
/* Read start of week */
2219
longtxt_read_line(buffer, sizeof(buffer), in_file);
2220
num_read = sscanf (buffer, LONGTXT_HEADER_START_OF_WEEK_FORMAT,
2222
if (num_read != 1) {
2223
warn_message("Can not read start of Week from input file line %d\n\n",
2228
/* Set size of application information header data */
2229
app_info_size = sizeof(header->aai);
2230
} /* else (no application information) */
2233
/* Read datebook sort information header data */
2237
/* Skip empty lines, then read 'no sort info'
2238
* TODO: read in sort info according to hex dump written by write_header
2240
longtxt_read_line(buffer, sizeof(buffer), in_file);
2241
while (buffer[0] == '\n') {
2242
longtxt_read_line(buffer, sizeof(buffer), in_file);
2244
if (strncmp(buffer, LONGTXT_HEADER_NO_SORT_INFO,
2245
sizeof(LONGTXT_HEADER_NO_SORT_INFO))) {
2246
warn_message("Can not read hint on no sort info from input file line %d\n\n",
2252
/* Skip empty lines to find begin of row data */
2253
longtxt_read_line(buffer, sizeof(buffer), in_file);
2254
while (buffer[0] == '\n') {
2255
longtxt_read_line(buffer, sizeof(buffer), in_file);
2258
/* Put last line back, to be read on next read attempt */
2259
longtxt_pushback_line(buffer, in_file);
2262
/* Set datebook header data if header has not already been read.
2263
* If header has already been read earlier, then the longtxt read
2264
* header information is ignored - but we are properly positioned
2265
* for reading row data now.
2267
if (header->isValid) {
2268
info_message("Skip reading header data, since header has already been read.\n");
2273
header->app_info_size = app_info_size;
2274
header->sort_info = sort_info;
2275
header->sort_info_size = sort_info_size;
2276
header->isValid = TRUE;
2281
debug_message("Leaving longtxt_read_header\n");
2285
/* Write datebook file header */
2287
longtxt_write_header (struct longtxt_file_data * out_file, struct header_data * header)
2290
struct AppointmentAppInfo * aai;
2294
char time_buffer[50];
2300
debug_message("Entering longtxt_write_header\n");
2302
if (header->isValid) {
2303
/* Get datebook header data */
2304
ip = &(header->info);
2305
aai = &(header->aai);
2306
sort_info = header->sort_info;
2307
sort_info_size = header->sort_info_size;
2310
/* Print database header flags */
2311
longtxt_write(out_file, LONGTXT_HEADER_NAME_FORMAT,
2313
longtxt_write(out_file, LONGTXT_HEADER_FLAGS_FORMAT, ip->flags,
2314
(ip->flags & dlpDBFlagResource) ? " RESOURCE" : "",
2315
(ip->flags & dlpDBFlagReadOnly) ? " READ_ONLY" : "",
2316
(ip->flags & dlpDBFlagAppInfoDirty) ? " APP-INFO-DIRTY" : "",
2317
(ip->flags & dlpDBFlagBackup) ? " BACKUP" : "",
2318
(ip->flags & dlpDBFlagOpen) ? " OPEN" : "",
2319
(ip->flags & dlpDBFlagNewer) ? " NEWER" : "",
2320
(ip->flags & dlpDBFlagReset) ? " RESET" : "");
2321
longtxt_write(out_file, LONGTXT_HEADER_MISC_FLAGS_FORMAT,
2323
longtxt_write(out_file, LONGTXT_HEADER_TYPE_FORMAT,
2324
printlong(ip->type));
2325
longtxt_write(out_file, LONGTXT_HEADER_CREATOR_FORMAT,
2326
printlong(ip->creator));
2327
longtxt_write(out_file, LONGTXT_HEADER_VERSION_FORMAT,
2329
longtxt_write(out_file, LONGTXT_HEADER_MODIFICATION_FORMAT,
2332
write_human_full_time_str (ip->createDate, time_buffer, sizeof(time_buffer));
2333
longtxt_write(out_file, LONGTXT_HEADER_CREATE_TIME_FORMAT,
2334
time_buffer, "", "", "");
2336
write_human_full_time_str (ip->modifyDate, time_buffer, sizeof(time_buffer));
2337
longtxt_write(out_file, LONGTXT_HEADER_MODIFY_TIME_FORMAT,
2338
time_buffer, "", "", "");
2340
write_human_full_time_str (ip->backupDate, time_buffer, sizeof(time_buffer));
2341
longtxt_write(out_file, LONGTXT_HEADER_BACKUP_TIME_FORMAT,
2342
time_buffer, "", "", "");
2344
longtxt_write(out_file, LONGTXT_HEADER_INDEX_FORMAT,
2346
longtxt_write_str(out_file, "\n");
2349
/* Print datebook application header */
2350
if (header->app_info_size <= 0) {
2351
longtxt_write_str(out_file, "\n");
2352
longtxt_write_str(out_file, LONGTXT_HEADER_NO_APP_INFO);
2355
longtxt_write_str(out_file, "\n");
2356
longtxt_write_str(out_file, LONGTXT_HEADER_CATEGORY_HEADER1_FORMAT);
2357
longtxt_write_str(out_file, LONGTXT_HEADER_CATEGORY_HEADER2_FORMAT);
2358
for(j=0;j<DATEBOOK_MAX_CATEGORIES;j++) {
2359
longtxt_write(out_file, LONGTXT_HEADER_CATEGORY_ROW_FORMAT,
2361
(aai->category).ID[j],
2362
(aai->category).renamed[j]
2363
? LONGTXT_HEADER_RENAMED_YES : LONGTXT_HEADER_RENAMED_NO,
2364
(aai->category).name[j],
2365
(j>0 && (aai->category).ID[j] == 0)
2366
? LONGTXT_HEADER_RENAMED_UNUSED : "");
2368
longtxt_write(out_file, LONGTXT_HEADER_LAST_UNIQUE_ID_FORMAT,
2369
(aai->category).lastUniqueID);
2370
longtxt_write(out_file, LONGTXT_HEADER_START_OF_WEEK_FORMAT,
2372
longtxt_write_str(out_file, "\n");
2376
/* Print datebook sort header */
2377
if (sort_info_size > 0)
2379
/* Does this ever happen for a datebook database?
2380
* At least currently we will not be able to read the hexdump
2383
longtxt_write(out_file, "sort_info_size %d\n", sort_info_size);
2384
write_dump (out_file->file, sort_info, sort_info_size);
2385
longtxt_write_str(out_file, "\n");
2389
longtxt_write_str(out_file, LONGTXT_HEADER_NO_SORT_INFO);
2390
longtxt_write_str(out_file, "\n");
2392
/* Visually separate header from body */
2393
longtxt_write_str(out_file, "\n");
2396
debug_message("No valid header present => skip writing header.\n");
2400
debug_message("Leaving longtxt_write_header\n");
2407
/* Read one line from input file */
2409
longtxt_read_line (char * buffer, int buffer_size, struct longtxt_file_data * in_file)
2412
/* Check for unexpected end of file */
2413
if (feof(in_file->file))
2414
error_message("Unexpected end of input file, line %d\n\n",
2418
if (in_file->line_buffer != NULL) {
2419
/* Use pushback buffer, if filled */
2420
strncpy(buffer, in_file->line_buffer, buffer_size);
2421
free (in_file->line_buffer);
2422
in_file->line_buffer = NULL;
2426
/* Return empty string if fgets fails */
2429
/* Read from file */
2430
fgets(buffer, buffer_size, in_file->file);
2431
if (!feof(in_file->file)) {
2434
/* Check whether entire line could be read */
2435
if (buffer[strlen(buffer) -1] != '\n')
2436
error_message("Line <%d> in input file <%s> exceeds buffer size\n",
2438
(in_file->filename==NULL) ? "stdin" : in_file->filename);
2444
/* Push pack one line into input file */
2446
longtxt_pushback_line (char * buffer, struct longtxt_file_data * in_file)
2448
/* Pushback line buffer to allow for later re-reading */
2449
in_file->line_buffer = strdup(buffer);
2454
/* Write one single string to output file
2456
* Especially useful for printing multi-line text, to properly count
2460
longtxt_write_str (struct longtxt_file_data * out_file, const char * out_string)
2466
fprintf (out_file->file, "%s", out_string);
2468
/* Count printed lines */
2469
newline_pos = data_index(out_string, '\n');
2470
while (newline_pos != NULL) {
2471
out_file->line_no++;
2472
newline_pos = data_index(newline_pos+1, '\n');
2477
/* Write to output file */
2479
longtxt_write (struct longtxt_file_data * out_file, const char * format, ...)
2481
va_list printf_args;
2485
/* Get variable arguments */
2486
va_start(printf_args, format);
2489
vfprintf (out_file->file, format, printf_args);
2491
/* No more processing of variable arguments */
2492
va_end(printf_args);
2494
/* Count printed lines
2495
* (only accurate if newlines can be found in format parameter) */
2496
newline_pos = data_index(format, '\n');
2497
while (newline_pos != NULL) {
2498
out_file->line_no++;
2499
newline_pos = data_index(newline_pos+1, '\n');