Initial work on icalendar import
This commit is contained in:
parent
2a873eafb3
commit
a126904b7e
13
ChangeLog
13
ChangeLog
@ -1,3 +1,16 @@
|
|||||||
|
2008-09-15 Frederic Culot <frederic@culot.org>
|
||||||
|
|
||||||
|
* src/io.h: import_type_t added, export_mode_t changed to
|
||||||
|
io_mode_t
|
||||||
|
|
||||||
|
* src/utils.c (str_toupper): new function
|
||||||
|
|
||||||
|
* src/io.c (ical_chk_header, ical_datetime2long)
|
||||||
|
(ical_durtime2long, ical_durlong, ical_read_rrule, ical_add_exc)
|
||||||
|
(ical_read_exdate, ical_read_note, ical_read_event)
|
||||||
|
(ical_read_todo, io_import_data): new functions to handle
|
||||||
|
icalendar import
|
||||||
|
|
||||||
2008-08-28 Frederic Culot <frederic@culot.org>
|
2008-08-28 Frederic Culot <frederic@culot.org>
|
||||||
|
|
||||||
* === Released 2.2 ===
|
* === Released 2.2 ===
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $calcurse: args.c,v 1.38 2008/08/12 15:53:17 culot Exp $ */
|
/* $calcurse: args.c,v 1.39 2008/09/15 20:40:22 culot Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calcurse - text-based organizer
|
* Calcurse - text-based organizer
|
||||||
@ -754,7 +754,7 @@ parse_args (int argc, char **argv, conf_t *conf)
|
|||||||
notify_init_vars ();
|
notify_init_vars ();
|
||||||
custom_load_conf (conf, 0);
|
custom_load_conf (conf, 0);
|
||||||
io_load_todo ();
|
io_load_todo ();
|
||||||
io_export_data (IO_EXPORT_NONINTERACTIVE, xfmt, conf);
|
io_export_data (IO_MODE_NONINTERACTIVE, xfmt, conf);
|
||||||
non_interactive = 1;
|
non_interactive = 1;
|
||||||
return (non_interactive);
|
return (non_interactive);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $calcurse: calcurse.c,v 1.65 2008/08/10 09:24:46 culot Exp $ */
|
/* $calcurse: calcurse.c,v 1.66 2008/09/15 20:40:22 culot Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calcurse - text-based organizer
|
* Calcurse - text-based organizer
|
||||||
@ -392,11 +392,11 @@ main (int argc, char **argv)
|
|||||||
{
|
{
|
||||||
case 'I':
|
case 'I':
|
||||||
case 'i':
|
case 'i':
|
||||||
io_export_data (IO_EXPORT_INTERACTIVE, IO_EXPORT_ICAL, &conf);
|
io_export_data (IO_MODE_INTERACTIVE, IO_EXPORT_ICAL, &conf);
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
case 'p':
|
case 'p':
|
||||||
io_export_data (IO_EXPORT_INTERACTIVE, IO_EXPORT_PCAL, &conf);
|
io_export_data (IO_MODE_INTERACTIVE, IO_EXPORT_PCAL, &conf);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
wins_reset ();
|
wins_reset ();
|
||||||
|
750
src/io.c
750
src/io.c
@ -1,4 +1,4 @@
|
|||||||
/* $calcurse: io.c,v 1.33 2008/08/18 07:30:07 culot Exp $ */
|
/* $calcurse: io.c,v 1.34 2008/09/15 20:40:22 culot Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calcurse - text-based organizer
|
* Calcurse - text-based organizer
|
||||||
@ -43,6 +43,8 @@
|
|||||||
#define ICALDATEFMT "%Y%m%d"
|
#define ICALDATEFMT "%Y%m%d"
|
||||||
#define ICALDATETIMEFMT "%Y%m%dT%H%M%S"
|
#define ICALDATETIMEFMT "%Y%m%dT%H%M%S"
|
||||||
|
|
||||||
|
#define STRING_BUILD(str) {str, sizeof (str) - 1}
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
PROGRESS_BAR_SAVE,
|
PROGRESS_BAR_SAVE,
|
||||||
@ -50,6 +52,24 @@ typedef enum
|
|||||||
PROGRESS_BAR_EXPORT
|
PROGRESS_BAR_EXPORT
|
||||||
} progress_bar_t;
|
} progress_bar_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *str;
|
||||||
|
const int len;
|
||||||
|
} string_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
UNDEFINED,
|
||||||
|
APPOINTMENT,
|
||||||
|
EVENT
|
||||||
|
} ical_vevent_e;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
recur_types_t type;
|
||||||
|
int freq;
|
||||||
|
long until;
|
||||||
|
unsigned count;
|
||||||
|
} ical_rpt_t;
|
||||||
|
|
||||||
/* Type definition for callbacks to multiple-mode export functions. */
|
/* Type definition for callbacks to multiple-mode export functions. */
|
||||||
typedef void (*cb_export_t)(FILE *);
|
typedef void (*cb_export_t)(FILE *);
|
||||||
typedef void (*cb_dump_t)(FILE *, long, long, char *);
|
typedef void (*cb_dump_t)(FILE *, long, long, char *);
|
||||||
@ -1213,7 +1233,7 @@ io_startup_screen (bool skip_dialogs, int no_data_file)
|
|||||||
|
|
||||||
/* Export calcurse data. */
|
/* Export calcurse data. */
|
||||||
void
|
void
|
||||||
io_export_data (export_mode_t mode, export_type_t type, conf_t *conf)
|
io_export_data (io_mode_t mode, export_type_t type, conf_t *conf)
|
||||||
{
|
{
|
||||||
FILE *stream;
|
FILE *stream;
|
||||||
char *wrong_mode = _("FATAL ERROR in io_export_data: wrong export mode\n");
|
char *wrong_mode = _("FATAL ERROR in io_export_data: wrong export mode\n");
|
||||||
@ -1228,10 +1248,10 @@ io_export_data (export_mode_t mode, export_type_t type, conf_t *conf)
|
|||||||
}
|
}
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case IO_EXPORT_NONINTERACTIVE:
|
case IO_MODE_NONINTERACTIVE:
|
||||||
stream = stdout;
|
stream = stdout;
|
||||||
break;
|
break;
|
||||||
case IO_EXPORT_INTERACTIVE:
|
case IO_MODE_INTERACTIVE:
|
||||||
stream = get_export_stream (type);
|
stream = get_export_stream (type);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -1245,17 +1265,17 @@ io_export_data (export_mode_t mode, export_type_t type, conf_t *conf)
|
|||||||
|
|
||||||
cb_export_header[type] (stream);
|
cb_export_header[type] (stream);
|
||||||
|
|
||||||
if (!conf->skip_progress_bar && mode == IO_EXPORT_INTERACTIVE)
|
if (!conf->skip_progress_bar && mode == IO_MODE_INTERACTIVE)
|
||||||
progress_bar (PROGRESS_BAR_EXPORT, 0);
|
progress_bar (PROGRESS_BAR_EXPORT, 0);
|
||||||
cb_export_recur_events[type] (stream);
|
cb_export_recur_events[type] (stream);
|
||||||
cb_export_events[type] (stream);
|
cb_export_events[type] (stream);
|
||||||
|
|
||||||
if (!conf->skip_progress_bar && mode == IO_EXPORT_INTERACTIVE)
|
if (!conf->skip_progress_bar && mode == IO_MODE_INTERACTIVE)
|
||||||
progress_bar (PROGRESS_BAR_EXPORT, 1);
|
progress_bar (PROGRESS_BAR_EXPORT, 1);
|
||||||
cb_export_recur_apoints[type] (stream);
|
cb_export_recur_apoints[type] (stream);
|
||||||
cb_export_apoints[type] (stream);
|
cb_export_apoints[type] (stream);
|
||||||
|
|
||||||
if (!conf->skip_progress_bar && mode == IO_EXPORT_INTERACTIVE)
|
if (!conf->skip_progress_bar && mode == IO_MODE_INTERACTIVE)
|
||||||
progress_bar (PROGRESS_BAR_EXPORT, 2);
|
progress_bar (PROGRESS_BAR_EXPORT, 2);
|
||||||
cb_export_todo[type] (stream);
|
cb_export_todo[type] (stream);
|
||||||
|
|
||||||
@ -1264,7 +1284,7 @@ io_export_data (export_mode_t mode, export_type_t type, conf_t *conf)
|
|||||||
if (stream != stdout)
|
if (stream != stdout)
|
||||||
fclose (stream);
|
fclose (stream);
|
||||||
|
|
||||||
if (!conf->skip_system_dialogs && mode == IO_EXPORT_INTERACTIVE)
|
if (!conf->skip_system_dialogs && mode == IO_MODE_INTERACTIVE)
|
||||||
{
|
{
|
||||||
status_mesg (success, enter);
|
status_mesg (success, enter);
|
||||||
wgetch (win[STA].p);
|
wgetch (win[STA].p);
|
||||||
@ -1294,3 +1314,717 @@ io_export_bar (void)
|
|||||||
wmove (win[STA].p, 0, 0);
|
wmove (win[STA].p, 0, 0);
|
||||||
doupdate ();
|
doupdate ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ical_chk_header (FILE *fd)
|
||||||
|
{
|
||||||
|
const char *icalheader = "BEGIN:VCALENDAR";
|
||||||
|
const int headerlen = strlen (icalheader);
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
|
||||||
|
fgets (buf, BUFSIZ, fd);
|
||||||
|
if (buf == NULL
|
||||||
|
|| strncmp (str_toupper (buf), icalheader, headerlen) != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "The ical file seems to be malformed.\n"
|
||||||
|
"Header does not begin with \"%s\". Aborting...\n", icalheader);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const int AWAITED = 1;
|
||||||
|
float version;
|
||||||
|
int read;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (fgets (buf, BUFSIZ, fd) == NULL)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "The ical file seems to be malformed.\n"
|
||||||
|
"iCalendar specification version was not found. "
|
||||||
|
"Aborting...\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
read = sscanf (buf, "VERSION:%f", &version);
|
||||||
|
}
|
||||||
|
while (read != AWAITED);
|
||||||
|
fprintf (stdout, "Found ical file, version %.1f\n", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* iCalendar date-time format is based on the ISO 8601 complete
|
||||||
|
* representation. It should be something like : DATE 'T' TIME
|
||||||
|
* where DATE is 'YYYYMMDD' and TIME is 'HHMMSS'.
|
||||||
|
* The time and 'T' separator are optional (in the case of an day-long event).
|
||||||
|
*
|
||||||
|
* Optionnaly, if the type pointer is given, specify if it is an event
|
||||||
|
* (no time is given, meaning it is an all-day event), or an appointment
|
||||||
|
* (time is given).
|
||||||
|
*
|
||||||
|
* The timezone is not yet handled by calcurse.
|
||||||
|
*/
|
||||||
|
static long
|
||||||
|
ical_datetime2long (char *datestr, ical_vevent_e *type)
|
||||||
|
{
|
||||||
|
const int NOTFOUND = -1, APPOINT_AWAITED = 5, EVENT_AWAITED = 3;
|
||||||
|
date_t date;
|
||||||
|
unsigned hour, min;
|
||||||
|
long datelong;
|
||||||
|
|
||||||
|
if (strchr (datestr, 'T') == NULL)
|
||||||
|
{
|
||||||
|
if (type)
|
||||||
|
*type = EVENT;
|
||||||
|
if (sscanf (datestr, "%04u%02u%02u",
|
||||||
|
&date.yyyy, &date.mm, &date.dd) != EVENT_AWAITED)
|
||||||
|
datelong = NOTFOUND;
|
||||||
|
else
|
||||||
|
datelong = date2sec (date, 0, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (type)
|
||||||
|
*type = APPOINTMENT;
|
||||||
|
if (sscanf (datestr, "%04u%02u%02uT%02u%02u",
|
||||||
|
&date.yyyy, &date.mm, &date.dd, &hour, &min)
|
||||||
|
!= APPOINT_AWAITED)
|
||||||
|
datelong = NOTFOUND;
|
||||||
|
else
|
||||||
|
datelong = date2sec (date, hour, min);
|
||||||
|
}
|
||||||
|
return datelong;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long
|
||||||
|
ical_durtime2long (char *timestr)
|
||||||
|
{
|
||||||
|
long timelong;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
if ((p = strchr (timestr, 'T')) == NULL)
|
||||||
|
timelong = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int nbmatch;
|
||||||
|
struct {
|
||||||
|
unsigned hour, min, sec;
|
||||||
|
} time;
|
||||||
|
|
||||||
|
p++;
|
||||||
|
bzero (&time, sizeof time);
|
||||||
|
nbmatch = sscanf (p, "%uH%uM%uS", &time.hour, &time.min, &time.sec);
|
||||||
|
if (nbmatch < 1 || nbmatch > 3)
|
||||||
|
timelong = 0;
|
||||||
|
else
|
||||||
|
timelong = time.hour * HOURINSEC + time.min * MININSEC + time.sec;
|
||||||
|
}
|
||||||
|
return timelong;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract from RFC2445:
|
||||||
|
*
|
||||||
|
* Value Name: DURATION
|
||||||
|
*
|
||||||
|
* Purpose: This value type is used to identify properties that contain
|
||||||
|
* duration of time.
|
||||||
|
*
|
||||||
|
* Formal Definition: The value type is defined by the following
|
||||||
|
* notation:
|
||||||
|
*
|
||||||
|
* dur-value = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
|
||||||
|
* dur-date = dur-day [dur-time]
|
||||||
|
* dur-time = "T" (dur-hour / dur-minute / dur-second)
|
||||||
|
* dur-week = 1*DIGIT "W"
|
||||||
|
* dur-hour = 1*DIGIT "H" [dur-minute]
|
||||||
|
* dur-minute = 1*DIGIT "M" [dur-second]
|
||||||
|
* dur-second = 1*DIGIT "S"
|
||||||
|
* dur-day = 1*DIGIT "D"
|
||||||
|
*
|
||||||
|
* Example: A duration of 15 days, 5 hours and 20 seconds would be:
|
||||||
|
* P15DT5H0M20S
|
||||||
|
* A duration of 7 weeks would be:
|
||||||
|
* P7W
|
||||||
|
*/
|
||||||
|
static long
|
||||||
|
ical_dur2long (char *durstr)
|
||||||
|
{
|
||||||
|
const int NOTFOUND = -1;
|
||||||
|
long durlong;
|
||||||
|
char *p;
|
||||||
|
struct {
|
||||||
|
unsigned week, day;
|
||||||
|
} date;
|
||||||
|
|
||||||
|
bzero (&date, sizeof date);
|
||||||
|
if ((p = strchr (durstr, 'P')) == NULL)
|
||||||
|
durlong = NOTFOUND;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p++;
|
||||||
|
if (*p == '-')
|
||||||
|
return NOTFOUND;
|
||||||
|
else if (*p == '+')
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (*p == 'T') /* dur-time */
|
||||||
|
durlong = ical_durtime2long (p);
|
||||||
|
else if (sscanf (p, "%uW", &date.week) == 1) /* dur-week */
|
||||||
|
durlong = date.week * WEEKINDAYS * DAYINSEC;
|
||||||
|
else /* dur-date */
|
||||||
|
{
|
||||||
|
if (sscanf (p, "%uD", &date.day) == 1)
|
||||||
|
{
|
||||||
|
durlong = date.day * DAYINSEC;
|
||||||
|
durlong += ical_durtime2long (p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
durlong = NOTFOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return durlong;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read a recurrence rule from an iCalendar RRULE string.
|
||||||
|
*
|
||||||
|
* Value Name: RECUR
|
||||||
|
*
|
||||||
|
* Purpose: This value type is used to identify properties that contain
|
||||||
|
* a recurrence rule specification.
|
||||||
|
*
|
||||||
|
* Formal Definition: The value type is defined by the following
|
||||||
|
* notation:
|
||||||
|
*
|
||||||
|
* recur = "FREQ"=freq *(
|
||||||
|
*
|
||||||
|
* ; either UNTIL or COUNT may appear in a 'recur',
|
||||||
|
* ; but UNTIL and COUNT MUST NOT occur in the same 'recur'
|
||||||
|
*
|
||||||
|
* ( ";" "UNTIL" "=" enddate ) /
|
||||||
|
* ( ";" "COUNT" "=" 1*DIGIT ) /
|
||||||
|
*
|
||||||
|
* ; the rest of these keywords are optional,
|
||||||
|
* ; but MUST NOT occur more than
|
||||||
|
* ; once
|
||||||
|
*
|
||||||
|
* ( ";" "INTERVAL" "=" 1*DIGIT ) /
|
||||||
|
* ( ";" "BYSECOND" "=" byseclist ) /
|
||||||
|
* ( ";" "BYMINUTE" "=" byminlist ) /
|
||||||
|
* ( ";" "BYHOUR" "=" byhrlist ) /
|
||||||
|
* ( ";" "BYDAY" "=" bywdaylist ) /
|
||||||
|
* ( ";" "BYMONTHDAY" "=" bymodaylist ) /
|
||||||
|
* ( ";" "BYYEARDAY" "=" byyrdaylist ) /
|
||||||
|
* ( ";" "BYWEEKNO" "=" bywknolist ) /
|
||||||
|
* ( ";" "BYMONTH" "=" bymolist ) /
|
||||||
|
* ( ";" "BYSETPOS" "=" bysplist ) /
|
||||||
|
* ( ";" "WKST" "=" weekday ) /
|
||||||
|
* ( ";" x-name "=" text )
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ical_read_rrule (char *rrulestr, ical_rpt_t *rpt, unsigned *noskipped)
|
||||||
|
{
|
||||||
|
const int HAS_RECURRENCE = 1, NO_RECURRENCE = 0;
|
||||||
|
const string_t daily = STRING_BUILD ("DAILY");
|
||||||
|
const string_t weekly = STRING_BUILD ("WEEKLY");
|
||||||
|
const string_t monthly = STRING_BUILD ("MONTHLY");
|
||||||
|
const string_t yearly = STRING_BUILD ("YEARLY");
|
||||||
|
unsigned interval;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
if ((p = strchr (rrulestr, ':')) != NULL)
|
||||||
|
{
|
||||||
|
char freqstr[BUFSIZ], untilstr[BUFSIZ];
|
||||||
|
|
||||||
|
p++;
|
||||||
|
if (sscanf (p, "FREQ=%s;", freqstr) != 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Warning: recurrence frequence not found. "
|
||||||
|
"Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
return NO_RECURRENCE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strncmp (freqstr, daily.str, daily.len) == 0)
|
||||||
|
rpt->type = RECUR_DAILY;
|
||||||
|
else if (strncmp (freqstr, weekly.str, weekly.len) == 0)
|
||||||
|
rpt->type = RECUR_WEEKLY;
|
||||||
|
else if (strncmp (freqstr, monthly.str, monthly.len) == 0)
|
||||||
|
rpt->type = RECUR_MONTHLY;
|
||||||
|
else if (strncmp (freqstr, yearly.str, yearly.len) == 0)
|
||||||
|
rpt->type = RECUR_YEARLY;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Warning: recurrence frequence not recognized. "
|
||||||
|
"Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
return NO_RECURRENCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
The UNTIL rule part defines a date-time value which bounds the
|
||||||
|
recurrence rule in an inclusive manner. If not present, and the
|
||||||
|
COUNT rule part is also not present, the RRULE is considered to
|
||||||
|
repeat forever.
|
||||||
|
|
||||||
|
The COUNT rule part defines the number of occurrences at which to
|
||||||
|
range-bound the recurrence. The "DTSTART" property value, if
|
||||||
|
specified, counts as the first occurrence.
|
||||||
|
*/
|
||||||
|
if (sscanf (p, "UNTIL=%s;", untilstr) == 1)
|
||||||
|
{
|
||||||
|
rpt->until = ical_datetime2long (untilstr, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned count;
|
||||||
|
|
||||||
|
if (sscanf (p, "COUNT=%u;", &count) != 1)
|
||||||
|
{
|
||||||
|
rpt->until = 0; /* endless repetition */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rpt->count = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sscanf (p, "INTERVAL=%u;", &interval) == 1)
|
||||||
|
{
|
||||||
|
rpt->freq = interval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Warning: recurrence rule malformed. Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
return NO_RECURRENCE;
|
||||||
|
}
|
||||||
|
return HAS_RECURRENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ical_add_exc (days_t **exc_head, long date)
|
||||||
|
{
|
||||||
|
if (date == 0)
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct days_s *exc;
|
||||||
|
|
||||||
|
exc = malloc (sizeof (struct days_s));
|
||||||
|
exc->st = date;
|
||||||
|
exc->next = *exc_head;
|
||||||
|
*exc_head = exc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This property defines the list of date/time exceptions for a
|
||||||
|
* recurring calendar component.
|
||||||
|
*/
|
||||||
|
static days_t *
|
||||||
|
ical_read_exdate (char *exstr, unsigned *noskipped)
|
||||||
|
{
|
||||||
|
days_t *exc;
|
||||||
|
char *p, *q;
|
||||||
|
long date;
|
||||||
|
|
||||||
|
exc = NULL;
|
||||||
|
if ((p = strchr (exstr, ':')) != NULL)
|
||||||
|
{
|
||||||
|
p++;
|
||||||
|
while ((q = strchr (p, ',')) != NULL)
|
||||||
|
{
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
const int buflen = q - p;
|
||||||
|
|
||||||
|
strncpy (buf, p, buflen);
|
||||||
|
buf[buflen] = '\0';
|
||||||
|
date = ical_datetime2long (buf, NULL);
|
||||||
|
ical_add_exc (&exc, date);
|
||||||
|
p = ++q;
|
||||||
|
}
|
||||||
|
date = ical_datetime2long (p, NULL);
|
||||||
|
ical_add_exc (&exc, date);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Warning: recurrence exception dates malformed. "
|
||||||
|
"Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
}
|
||||||
|
return exc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
ical_read_note (char *first_line, FILE *fdi, FILE *fdo, unsigned *noskipped)
|
||||||
|
{
|
||||||
|
const int CHAR_SPACE = 32, CHAR_TAB = 9;
|
||||||
|
char *p, *note, buf[BUFSIZ];
|
||||||
|
int c;
|
||||||
|
|
||||||
|
/* TODO: creer un fichier temporaire et le renvoyer en fin de methode.
|
||||||
|
Attention a liberer le nom du fichier temporaire si foirade !
|
||||||
|
*/
|
||||||
|
note = NULL;
|
||||||
|
if ((p = strchr (first_line, ':')) != NULL)
|
||||||
|
{
|
||||||
|
p++;
|
||||||
|
fprintf (fdo, "%s", p);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
c = getc (fdi);
|
||||||
|
if (c == CHAR_SPACE || c == CHAR_TAB)
|
||||||
|
{
|
||||||
|
if (fgets (buf, BUFSIZ, fdi) != NULL)
|
||||||
|
{
|
||||||
|
fprintf (fdo, "%s", buf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Warning: could not get entire description. "
|
||||||
|
"Skipping...\n");
|
||||||
|
/* Free temporary note name */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ungetc (c, fdi);
|
||||||
|
return NULL; /* Ne pas renvoyer NULL mais le nom du fichier temp! */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Warning: description malformed. Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ical_read_event (FILE *fdi, FILE *fdo, unsigned *noevents, unsigned *noapoints,
|
||||||
|
unsigned *noskipped)
|
||||||
|
{
|
||||||
|
const int NOTFOUND = -1;
|
||||||
|
const string_t endevent = STRING_BUILD ("END:VEVENT");
|
||||||
|
const string_t summary = STRING_BUILD ("SUMMARY:");
|
||||||
|
const string_t dtstart = STRING_BUILD ("DTSTART");
|
||||||
|
const string_t dtend = STRING_BUILD ("DTEND");
|
||||||
|
const string_t duration = STRING_BUILD ("DURATION:");
|
||||||
|
const string_t rrule = STRING_BUILD ("RRULE");
|
||||||
|
const string_t exdate = STRING_BUILD ("EXDATE");
|
||||||
|
const string_t alarm = STRING_BUILD ("BEGIN:VALARM");
|
||||||
|
const string_t endalarm = STRING_BUILD ("END:VALARM");
|
||||||
|
const string_t desc = STRING_BUILD ("DESCRIPTION");
|
||||||
|
ical_vevent_e vevent_type;
|
||||||
|
char *p, buf[BUFSIZ], buf_upper[BUFSIZ];
|
||||||
|
struct {
|
||||||
|
days_t *exc;
|
||||||
|
ical_rpt_t rpt;
|
||||||
|
char mesg[BUFSIZ], *note;
|
||||||
|
long start, end, dur;
|
||||||
|
int has_summary, has_alarm, has_recurrence;
|
||||||
|
} vevent;
|
||||||
|
int skip_alarm;
|
||||||
|
|
||||||
|
vevent_type = UNDEFINED;
|
||||||
|
bzero (&vevent, sizeof vevent);
|
||||||
|
skip_alarm = 0;
|
||||||
|
while (fgets (buf, BUFSIZ, fdi) != NULL)
|
||||||
|
{
|
||||||
|
memcpy (buf_upper, buf, strlen (buf));
|
||||||
|
str_toupper (buf_upper);
|
||||||
|
if (skip_alarm)
|
||||||
|
{
|
||||||
|
/* Need to skip VALARM properties because some keywords could
|
||||||
|
interfere, such as DURATION, SUMMARY,.. */
|
||||||
|
if (strncmp (buf_upper, endalarm.str, endalarm.len) == 0)
|
||||||
|
skip_alarm = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strncmp (buf_upper, endevent.str, endevent.len) == 0)
|
||||||
|
{
|
||||||
|
if (vevent.has_summary)
|
||||||
|
{
|
||||||
|
switch (vevent_type)
|
||||||
|
{
|
||||||
|
case APPOINTMENT:
|
||||||
|
if (vevent.start == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Warning: VEVENT appointment has "
|
||||||
|
"no start time. Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vevent.dur == 0)
|
||||||
|
{
|
||||||
|
if (vevent.end == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Warning: could not compute "
|
||||||
|
"VEVENT duration (no end time). "
|
||||||
|
"Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (vevent.start == vevent.end)
|
||||||
|
{
|
||||||
|
vevent_type = EVENT;
|
||||||
|
(*noevents)++;
|
||||||
|
ical_store_event (vevent.mesg, vevent.start,
|
||||||
|
&vevent.rpt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
vevent.dur = vevent.start - vevent.end;
|
||||||
|
}
|
||||||
|
ical_store_apoint (vevent.mesg, vevent.start, vevent.end,
|
||||||
|
vevent.dur, &vevent.rpt);
|
||||||
|
(*noapoints)++;
|
||||||
|
break;
|
||||||
|
case EVENT:
|
||||||
|
ical_store_event (vevent.mesg, vevent.start, &vevent.rpt);
|
||||||
|
(*noevents)++;
|
||||||
|
break;
|
||||||
|
case UNDEFINED:
|
||||||
|
fprintf (stderr, "Warning: VEVENT could not be identified. "
|
||||||
|
"Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr,
|
||||||
|
"Warning: VEVENT has no summary. Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strncmp (buf_upper, dtstart.str, dtstart.len) == 0)
|
||||||
|
{
|
||||||
|
if ((p = strchr (buf, ':')) == NULL)
|
||||||
|
vevent.start = NOTFOUND;
|
||||||
|
else
|
||||||
|
vevent.start = ical_datetime2long (++p, &vevent_type);
|
||||||
|
if (vevent.start == NOTFOUND)
|
||||||
|
{
|
||||||
|
fprintf (stderr,
|
||||||
|
"Warning: could not retrieve event start time. "
|
||||||
|
"Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strncmp (buf_upper, dtend.str, dtend.len) == 0)
|
||||||
|
{
|
||||||
|
if ((p = strchr (buf, ':')) == NULL)
|
||||||
|
vevent.end = NOTFOUND;
|
||||||
|
else
|
||||||
|
vevent.end = ical_datetime2long (++p, &vevent_type);
|
||||||
|
if (vevent.end == NOTFOUND)
|
||||||
|
{
|
||||||
|
fprintf (stderr,
|
||||||
|
"Warning: could not retrieve event end time. "
|
||||||
|
"Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strncmp (buf_upper, duration.str, duration.len) == 0)
|
||||||
|
{
|
||||||
|
if ((vevent.dur = ical_dur2long (buf)) <= 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,
|
||||||
|
"Warning: vevent duration malformed. Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strncmp (buf_upper, rrule.str, rrule.len) == 0)
|
||||||
|
{
|
||||||
|
vevent.has_recurrence =
|
||||||
|
ical_read_rrule (buf, &vevent.rpt, noskipped);
|
||||||
|
}
|
||||||
|
else if (strncmp (buf_upper, exdate.str, exdate.len) == 0)
|
||||||
|
{
|
||||||
|
vevent.exc = ical_read_exdate (buf, noskipped);
|
||||||
|
}
|
||||||
|
else if (strncmp (buf_upper, summary.str, summary.len) == 0)
|
||||||
|
{
|
||||||
|
const int sumlen = strlen (buf) - summary.len - 1;
|
||||||
|
memcpy (vevent.mesg, buf + summary.len, sumlen);
|
||||||
|
vevent.mesg[sumlen - 1] = '\0';
|
||||||
|
vevent.has_summary = 1;
|
||||||
|
}
|
||||||
|
else if (strncmp (buf_upper, alarm.str, alarm.len) == 0)
|
||||||
|
{
|
||||||
|
skip_alarm = 1;
|
||||||
|
vevent.has_alarm = 1;
|
||||||
|
}
|
||||||
|
else if (strncmp (buf_upper, desc.str, desc.len) == 0)
|
||||||
|
{
|
||||||
|
vevent.note = ical_read_note (buf, fdi, fdo, noskipped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf (stderr, "The ical file seems to be malformed.\n"
|
||||||
|
"The end of VEVENT item was not found. Aborting...");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ical_read_todo (FILE *fdi, FILE *fdo, unsigned *notodos, unsigned *noskipped)
|
||||||
|
{
|
||||||
|
const string_t endtodo = STRING_BUILD ("END:VTODO");
|
||||||
|
const string_t summary = STRING_BUILD ("SUMMARY:");
|
||||||
|
const string_t alarm = STRING_BUILD ("BEGIN:VALARM");
|
||||||
|
const string_t endalarm = STRING_BUILD ("END:VALARM");
|
||||||
|
const string_t desc = STRING_BUILD ("DESCRIPTION");
|
||||||
|
const int LOWEST = 9;
|
||||||
|
char buf[BUFSIZ], buf_upper[BUFSIZ];
|
||||||
|
struct {
|
||||||
|
char mesg[BUFSIZ], *note;
|
||||||
|
int has_priority, has_summary, priority;
|
||||||
|
} vtodo;
|
||||||
|
int skip_alarm;
|
||||||
|
|
||||||
|
bzero (&vtodo, sizeof vtodo);
|
||||||
|
skip_alarm = 0;
|
||||||
|
while (fgets (buf, BUFSIZ, fdi) != NULL)
|
||||||
|
{
|
||||||
|
memcpy (buf_upper, buf, strlen (buf));
|
||||||
|
str_toupper (buf_upper);
|
||||||
|
if (skip_alarm)
|
||||||
|
{
|
||||||
|
/* Need to skip VALARM properties because some keywords could
|
||||||
|
interfere, such as DURATION, SUMMARY,.. */
|
||||||
|
if (strncmp (buf_upper, endalarm.str, endalarm.len) == 0)
|
||||||
|
skip_alarm = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strncmp (buf_upper, endtodo.str, endtodo.len) == 0)
|
||||||
|
{
|
||||||
|
if (!vtodo.has_priority)
|
||||||
|
vtodo.priority = LOWEST;
|
||||||
|
if (vtodo.has_summary)
|
||||||
|
{
|
||||||
|
ical_store_todo (vtodo.priority, vtodo.mesg);
|
||||||
|
(*notodos)++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr,
|
||||||
|
"Warning: VTODO item has no summary. Skipping...\n");
|
||||||
|
(*noskipped)++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int tmpint;
|
||||||
|
|
||||||
|
if (sscanf (buf_upper, "PRIORITY:%d", &tmpint) == 1)
|
||||||
|
{
|
||||||
|
if (tmpint <= 9 && tmpint >= 1)
|
||||||
|
{
|
||||||
|
vtodo.priority = tmpint;
|
||||||
|
vtodo.has_priority = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr,
|
||||||
|
"Warning: VTODO item priority is not acceptable\n"
|
||||||
|
"(must be between 1 and 9 while it is %d).\n",
|
||||||
|
tmpint);
|
||||||
|
vtodo.priority = LOWEST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strncmp (buf_upper, summary.str, summary.len) == 0)
|
||||||
|
{
|
||||||
|
const int sumlen = strlen (buf) - summary.len - 1;
|
||||||
|
memcpy (vtodo.mesg, buf + summary.len, sumlen);
|
||||||
|
vtodo.mesg[sumlen - 1] = '\0';
|
||||||
|
vtodo.has_summary = 1;
|
||||||
|
}
|
||||||
|
else if (strncmp (buf_upper, alarm.str, alarm.len) == 0)
|
||||||
|
{
|
||||||
|
skip_alarm = 1;
|
||||||
|
}
|
||||||
|
else if (strncmp (buf_upper, desc.str, desc.len) == 0)
|
||||||
|
{
|
||||||
|
vtodo.note = ical_read_note (buf, fdi, fdo, noskipped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf (stderr, "The ical file seems to be malformed.\n"
|
||||||
|
"The end of VTODO item was not found. Aborting...");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
io_import_data (char *infile, char *outfile, io_mode_t mode, import_type_t type,
|
||||||
|
conf_t *conf)
|
||||||
|
{
|
||||||
|
const char *wrong_mode =
|
||||||
|
_("FATAL ERROR in io_import_data: wrong import mode\n");
|
||||||
|
const char *wrong_type =
|
||||||
|
_("FATAL ERROR in io_import_data: unknown import type\n");
|
||||||
|
const char *vevent = "BEGIN:VEVENT";
|
||||||
|
const char *vtodo = "BEGIN:VTODO";
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
FILE *fdi, *fdo, *stream;
|
||||||
|
struct {
|
||||||
|
unsigned events, apoints, todos, lines, skipped;
|
||||||
|
} stats;
|
||||||
|
|
||||||
|
if (type < 0 || type >= IO_IMPORT_NBTYPES)
|
||||||
|
{
|
||||||
|
fputs (wrong_type, stderr);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case IO_MODE_NONINTERACTIVE:
|
||||||
|
stream = stdout;
|
||||||
|
break;
|
||||||
|
case IO_MODE_INTERACTIVE:
|
||||||
|
stream = get_import_stream (type);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fputs (wrong_mode, stderr);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
fdi = fopen (infile, "r");
|
||||||
|
exitonerr (fdi != NULL, "Could not open %s", infile);
|
||||||
|
fdo = fopen (outfile, "w");
|
||||||
|
exitonerr (fdo != NULL, "Could not open %s", outfile);
|
||||||
|
ical_chk_header (fdi);
|
||||||
|
bzero (&stats, sizeof stats);
|
||||||
|
while (fgets (buf, BUFSIZ, fdi) != NULL)
|
||||||
|
{
|
||||||
|
stats.lines++;
|
||||||
|
str_toupper (buf);
|
||||||
|
printf ("Number of lines read: %04d "
|
||||||
|
"(apoints: %d / events: %d / todos: %d / skipped: %d)\r",
|
||||||
|
stats.lines, stats.apoints, stats.events, stats.todos,
|
||||||
|
stats.skipped);
|
||||||
|
if (strncmp (buf, vevent, strlen (vevent)) == 0)
|
||||||
|
ical_read_event (fdi, fdo, &stats.events, &stats.apoints,
|
||||||
|
&stats.skipped);
|
||||||
|
else if (strncmp (buf, vtodo, strlen (vtodo)) == 0)
|
||||||
|
ical_read_todo (fdi, fdo, &stats.todos, &stats.skipped);
|
||||||
|
}
|
||||||
|
printf ("\n");
|
||||||
|
fclose (fdi);
|
||||||
|
fclose (fdo);
|
||||||
|
}
|
||||||
|
19
src/io.h
19
src/io.h
@ -1,4 +1,4 @@
|
|||||||
/* $calcurse: io.h,v 1.11 2008/08/10 09:24:46 culot Exp $ */
|
/* $calcurse: io.h,v 1.12 2008/09/15 20:40:22 culot Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calcurse - text-based organizer
|
* Calcurse - text-based organizer
|
||||||
@ -31,10 +31,16 @@
|
|||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
IO_EXPORT_NONINTERACTIVE,
|
IO_MODE_NONINTERACTIVE,
|
||||||
IO_EXPORT_INTERACTIVE,
|
IO_MODE_INTERACTIVE,
|
||||||
IO_EXPORT_NBMODES
|
IO_NBMODES
|
||||||
} export_mode_t;
|
} io_mode_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
IO_IMPORT_ICAL,
|
||||||
|
IO_IMPORT_NBTYPES
|
||||||
|
} import_type_t;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
@ -50,7 +56,8 @@ void io_load_app (void);
|
|||||||
void io_load_todo (void);
|
void io_load_todo (void);
|
||||||
int io_check_data_files (void);
|
int io_check_data_files (void);
|
||||||
void io_startup_screen (bool, int);
|
void io_startup_screen (bool, int);
|
||||||
void io_export_data (export_mode_t, export_type_t, conf_t *);
|
void io_export_data (io_mode_t, export_type_t, conf_t *);
|
||||||
void io_export_bar (void);
|
void io_export_bar (void);
|
||||||
|
void io_import_data (char *, char *, io_mode_t, import_type_t, conf_t *);
|
||||||
|
|
||||||
#endif /* CALCURSE_IO_H */
|
#endif /* CALCURSE_IO_H */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $calcurse: recur.h,v 1.20 2008/08/10 09:24:46 culot Exp $ */
|
/* $calcurse: recur.h,v 1.21 2008/09/15 20:40:22 culot Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calcurse - text-based organizer
|
* Calcurse - text-based organizer
|
||||||
@ -37,11 +37,11 @@ typedef enum
|
|||||||
}
|
}
|
||||||
recur_types_t;
|
recur_types_t;
|
||||||
|
|
||||||
struct days_s
|
typedef struct days_s
|
||||||
{
|
{
|
||||||
struct days_s *next;
|
struct days_s *next;
|
||||||
long st; /* beggining of the considered day, in seconds */
|
long st; /* beggining of the considered day, in seconds */
|
||||||
};
|
} days_t;
|
||||||
|
|
||||||
struct rpt_s
|
struct rpt_s
|
||||||
{
|
{
|
||||||
|
12
src/utils.c
12
src/utils.c
@ -1,4 +1,4 @@
|
|||||||
/* $calcurse: utils.c,v 1.48 2008/08/11 18:08:45 culot Exp $ */
|
/* $calcurse: utils.c,v 1.49 2008/09/15 20:40:22 culot Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calcurse - text-based organizer
|
* Calcurse - text-based organizer
|
||||||
@ -964,3 +964,13 @@ parse_date (char *date_string, int datefmt, int *year, int *month, int *day)
|
|||||||
*day = lday;
|
*day = lday;
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
str_toupper (char *s)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
|
||||||
|
for (len = 0; s && s[len]; len++)
|
||||||
|
s[len] = toupper (s[len]);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $calcurse: utils.h,v 1.32 2008/08/11 18:08:45 culot Exp $ */
|
/* $calcurse: utils.h,v 1.33 2008/09/15 20:40:22 culot Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calcurse - text-based organizer
|
* Calcurse - text-based organizer
|
||||||
@ -107,5 +107,6 @@ void print_option_incolor (WINDOW *, bool, int, int);
|
|||||||
char *new_tempfile (const char *, int);
|
char *new_tempfile (const char *, int);
|
||||||
void erase_note (char **, erase_flag_e);
|
void erase_note (char **, erase_flag_e);
|
||||||
int parse_date (char *, int, int *, int *, int *);
|
int parse_date (char *, int, int *, int *, int *);
|
||||||
|
char *str_toupper (char *);
|
||||||
|
|
||||||
#endif /* CALCURSE_UTILS_H */
|
#endif /* CALCURSE_UTILS_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user