You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
408 lines
9.9 KiB
C
408 lines
9.9 KiB
C
/*
|
|
* $Id: pilot-read-ical.c,v 1.59 2007/02/04 23:06:02 desrod Exp $
|
|
*
|
|
* pilot-read-ical.c: Translate Palm ToDo and Datebook databases into ical
|
|
* 2.0 format
|
|
*
|
|
* Copyright (c) 1996, Kenneth Albanowski
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
* Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
#include "pi-source.h"
|
|
#include "pi-todo.h"
|
|
#include "pi-datebook.h"
|
|
#include "pi-dlp.h"
|
|
#include "pi-header.h"
|
|
#include "pi-userland.h"
|
|
|
|
char *tclquote(char *in)
|
|
{
|
|
int len;
|
|
char *out,
|
|
*pos;
|
|
static char *buffer = 0;
|
|
|
|
if (in == NULL) {
|
|
return buffer;
|
|
}
|
|
|
|
/* Skip leading bullet (and any whitespace after) */
|
|
if (in[0] == '\x95') {
|
|
++in;
|
|
while (in[0] == ' ' || in[0] == '\t') {
|
|
++in;
|
|
}
|
|
}
|
|
|
|
len = 3;
|
|
pos = in;
|
|
while (*pos) {
|
|
if ((*pos == '\\') || (*pos == '"') || (*pos == '[')
|
|
|| (*pos == '{')
|
|
|| (*pos == '$'))
|
|
len++;
|
|
len++;
|
|
pos++;
|
|
}
|
|
|
|
if (buffer)
|
|
free(buffer);
|
|
buffer = (char *) malloc(len);
|
|
out = buffer;
|
|
|
|
pos = in;
|
|
*out++ = '"';
|
|
while (*pos) {
|
|
if ((*pos == '\\') || (*pos == '"') || (*pos == '[')
|
|
|| (*pos == '{')
|
|
|| (*pos == '$'))
|
|
*out++ = '\\';
|
|
*out++ = *pos++;
|
|
}
|
|
*out++ = '"';
|
|
*out++ = '\0';
|
|
|
|
return buffer;
|
|
}
|
|
|
|
|
|
int main(int argc, const char *argv[])
|
|
{
|
|
int c, /* switch */
|
|
db,
|
|
sd = -1,
|
|
i,
|
|
read_todos = -1;
|
|
FILE *ical = NULL;
|
|
char cmd[255],
|
|
*ptext = NULL,
|
|
*icalfile = NULL,
|
|
*pubtext = NULL;
|
|
struct ToDoAppInfo tai;
|
|
pi_buffer_t *recbuf,
|
|
*appblock;
|
|
|
|
poptContext pc;
|
|
|
|
struct poptOption options[] = {
|
|
USERLAND_RESERVED_OPTIONS
|
|
{"datebook", 'd', POPT_ARG_VAL, &read_todos, 0,
|
|
"Datebook only, no ToDos", NULL},
|
|
{"pubtext", 't', POPT_ARG_STRING, &ptext, 0,
|
|
"Replace text of items not started with a bullet with <pubtext>",
|
|
"pubtext"},
|
|
{"file", 'f', POPT_ARG_STRING, &icalfile, 0,
|
|
"Write the ical formatted data to <file> (overwrites existing <file>)",
|
|
"file"},
|
|
POPT_TABLEEND
|
|
};
|
|
|
|
pc = poptGetContext("read-ical", argc, argv, options, 0);
|
|
poptSetOtherOptionHelp(pc,"\n\n"
|
|
" Dumps the DatebookDB and/or ToDo applications to ical format.\n"
|
|
" Requires the program 'ical'.\n\n");
|
|
|
|
if (argc < 2) {
|
|
poptPrintUsage(pc,stderr,0);
|
|
return 1;
|
|
}
|
|
|
|
while ((c = poptGetNextOpt(pc)) >= 0) {
|
|
fprintf(stderr," ERROR: Unhandled option %d.\n",c);
|
|
return 1;
|
|
}
|
|
|
|
if (c < -1) {
|
|
plu_badoption(pc,c);
|
|
return 1;
|
|
}
|
|
|
|
|
|
if (icalfile == NULL) {
|
|
fprintf(stderr, " ERROR: ical filename not specified. Please use the -f option.\n");
|
|
return 1;
|
|
}
|
|
|
|
sd = plu_connect();
|
|
if (sd < 0)
|
|
goto error;
|
|
|
|
unlink(icalfile);
|
|
sprintf(cmd, "ical -list -f - -calendar %s", icalfile);
|
|
ical = popen(cmd, "w");
|
|
if (ical == NULL) {
|
|
int e = errno;
|
|
fprintf(stderr," ERROR: cannot start communication with ical.\n"
|
|
" %s\n",strerror(e));
|
|
return 1;
|
|
}
|
|
fprintf(ical, "calendar cal $ical(calendar)\n");
|
|
|
|
if (dlp_OpenConduit(sd) < 0)
|
|
goto error_close;
|
|
|
|
|
|
if (read_todos) {
|
|
/* Open the ToDo database, store access handle in db */
|
|
if (dlp_OpenDB
|
|
(sd, 0, 0x80 | 0x40, "ToDoDB", &db) < 0) {
|
|
fprintf(stderr," ERROR: Unable to open ToDoDB on Palm.\n");
|
|
dlp_AddSyncLogEntry(sd, "Unable to open ToDoDB.\n");
|
|
goto error_close;
|
|
}
|
|
|
|
appblock = pi_buffer_new(0xffff);
|
|
dlp_ReadAppBlock(sd, db, 0, 0xffff, appblock);
|
|
unpack_ToDoAppInfo(&tai, appblock->data, appblock->used);
|
|
pi_buffer_free(appblock);
|
|
|
|
recbuf = pi_buffer_new (0xffff);
|
|
|
|
for (i = 0;; i++) {
|
|
int attr,
|
|
category;
|
|
char id_buf[255];
|
|
struct ToDo t;
|
|
recordid_t id_;
|
|
|
|
int len = dlp_ReadRecordByIndex(sd, db, i, recbuf, &id_, &attr, &category);
|
|
|
|
if (len < 0)
|
|
break;
|
|
|
|
/* Skip deleted records */
|
|
if ((attr & dlpRecAttrDeleted)
|
|
|| (attr & dlpRecAttrArchived))
|
|
continue;
|
|
|
|
unpack_ToDo(&t, recbuf, todo_v1);
|
|
|
|
fprintf(ical, "set n [notice]\n");
|
|
|
|
/* '\x95' is the "bullet" chcter */
|
|
fprintf(ical, "$n text %s\n", tclquote((pubtext && t.description[0] != '\x95') ? pubtext : t.description));
|
|
fprintf(ical, "$n date [date today]\n");
|
|
fprintf(ical, "$n todo 1\n");
|
|
fprintf(ical, "$n option Priority %d\n", t.priority);
|
|
sprintf(id_buf, "%lx", id_);
|
|
fprintf(ical, "$n option PilotRecordId %s\n", id_buf);
|
|
fprintf(ical, "$n done %d\n", t.complete ? 1 : 0);
|
|
fprintf(ical, "cal add $n\n");
|
|
|
|
free_ToDo(&t);
|
|
}
|
|
pi_buffer_free(recbuf);
|
|
|
|
/* Close the database */
|
|
dlp_CloseDB(sd, db);
|
|
|
|
dlp_AddSyncLogEntry(sd, "Successfully read todos from Palm.\nThank you for using pilot-link.\n");
|
|
}
|
|
|
|
/* Open the Datebook's database, store access handle in db */
|
|
if (dlp_OpenDB
|
|
(sd, 0, 0x80 | 0x40, "DatebookDB", &db) < 0) {
|
|
fprintf(stderr," ERROR: Unable to open DatebookDB on Palm.\n");
|
|
dlp_AddSyncLogEntry(sd, "Unable to open DatebookDB.\n");
|
|
goto error_close;
|
|
}
|
|
|
|
recbuf = pi_buffer_new (0xffff);
|
|
|
|
for (i = 0;; i++) {
|
|
int j,
|
|
attr;
|
|
char id_buf[255];
|
|
struct Appointment a;
|
|
recordid_t id_;
|
|
|
|
int len =
|
|
dlp_ReadRecordByIndex(sd, db, i, recbuf, &id_, &attr, 0);
|
|
|
|
if (len < 0)
|
|
break;
|
|
|
|
/* Skip deleted records */
|
|
if ((attr & dlpRecAttrDeleted)
|
|
|| (attr & dlpRecAttrArchived))
|
|
continue;
|
|
|
|
unpack_Appointment(&a, recbuf, datebook_v1);
|
|
|
|
if (a.event) {
|
|
fprintf(ical, "set i [notice]\n");
|
|
|
|
} else {
|
|
int start,
|
|
end;
|
|
|
|
fprintf(ical, "set i [appointment]\n");
|
|
|
|
start =
|
|
a.begin.tm_hour * 60 +
|
|
a.begin.tm_min;
|
|
end =
|
|
a.end.tm_hour * 60 +
|
|
a.end.tm_min;
|
|
|
|
fprintf(ical, "$i starttime %d\n", start);
|
|
fprintf(ical, "$i length %d\n", end - start);
|
|
}
|
|
|
|
/* Don't hilight private (secret) records */
|
|
if (attr & dlpRecAttrSecret) {
|
|
fprintf(ical, "$i hilite never\n");
|
|
}
|
|
|
|
/* Handle alarms */
|
|
if (a.alarm) {
|
|
if (a.event) {
|
|
if (a.advanceUnits == 2) {
|
|
fprintf(ical, "$i earlywarning %d\n", a.advance);
|
|
} else {
|
|
printf("Minute or hour alarm on untimed event ignored: %s\n", a.description);
|
|
}
|
|
} else {
|
|
switch (a.advanceUnits) {
|
|
case 0:
|
|
fprintf(ical, "$i alarms { %d }\n", a.advance);
|
|
break;
|
|
case 1:
|
|
fprintf(ical, "$i alarms { %d }\n", a.advance * 60);
|
|
break;
|
|
case 2:
|
|
fprintf(ical, "$i earlywarning %d\n", a.advance);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* '\x95' is the "bullet" chcter */
|
|
fprintf(ical, "$i text %s\n",
|
|
tclquote((pubtext
|
|
&& a.description[0] !=
|
|
'\x95') ? pubtext : a.
|
|
description));
|
|
|
|
fprintf(ical,
|
|
"set begin [date make %d %d %d]\n",
|
|
a.begin.tm_mday,
|
|
a.begin.tm_mon + 1,
|
|
a.begin.tm_year + 1900);
|
|
|
|
if (a.repeatFrequency) {
|
|
if (a.repeatType == repeatDaily) {
|
|
fprintf(ical,
|
|
"$i dayrepeat %d $begin\n", a.repeatFrequency);
|
|
} else if (a.repeatType == repeatMonthlyByDate) {
|
|
fprintf(ical, "$i month_day %d $begin %d\n", a.begin.tm_mon + 1, a.repeatFrequency);
|
|
} else if (a.repeatType == repeatMonthlyByDay) {
|
|
if (a.repeatDay >= domLastSun) {
|
|
fprintf(ical, "$i month_last_week_day %d 1 $begin %d\n", a.repeatDay % 7 + 1, a.repeatFrequency);
|
|
} else {
|
|
fprintf(ical, "$i month_week_day %d %d $begin %d\n", a.repeatDay % 7 + 1, a.repeatDay / 7 + 1, a.repeatFrequency);
|
|
}
|
|
} else if (a.repeatType == repeatWeekly) {
|
|
/*
|
|
* Handle the case where the user said weekly repeat, but
|
|
* really meant daily repeat every n*7 days. Note: We can't
|
|
* do days of the week and a repeat-frequency > 1, so do the
|
|
* best we can and go on.
|
|
*/
|
|
if (a.repeatFrequency > 1) {
|
|
int ii, found;
|
|
|
|
for (ii = 0, found = 0; ii < 7; ii++) {
|
|
if (a.repeatDays [ii])
|
|
found++;
|
|
}
|
|
if (found > 1) {
|
|
fprintf(stderr," WARNING: Incomplete translation of %s\n", a.description);
|
|
}
|
|
fprintf(ical, "$i dayrepeat %d $begin\n", a.repeatFrequency * 7);
|
|
} else {
|
|
int ii;
|
|
|
|
fprintf(ical, "$i weekdays ");
|
|
for (ii = 0; ii < 7; ii++)
|
|
if (a.repeatDays [ii])
|
|
fprintf(ical, "%d ", ii + 1);
|
|
fprintf(ical, "\n");
|
|
}
|
|
} else if (a.repeatType == repeatYearly) {
|
|
fprintf(ical, "$i monthrepeat %d $begin\n", 12 * a.repeatFrequency);
|
|
}
|
|
fprintf(ical, "$i start $begin\n");
|
|
if (!a.repeatForever)
|
|
fprintf(ical,
|
|
"$i finish [date make %d %d %d]\n",
|
|
a.repeatEnd.tm_mday, a.repeatEnd.tm_mon + 1, a.repeatEnd.tm_year + 1900);
|
|
if (a.exceptions)
|
|
for (j = 0; j < a.exceptions; j++)
|
|
fprintf(ical, "$i deletion [date make %d %d %d]\n", a.exception[j].tm_mday, a.exception[j].tm_mon + 1, a.exception[j].tm_year + 1900);
|
|
} else
|
|
fprintf(ical, "$i date $begin\n");
|
|
|
|
sprintf(id_buf, "%lx", id_);
|
|
fprintf(ical, "$i option PilotRecordId %s\n", id_buf);
|
|
fprintf(ical, "cal add $i\n");
|
|
|
|
free_Appointment(&a);
|
|
|
|
}
|
|
|
|
pi_buffer_free (recbuf);
|
|
|
|
fprintf(ical, "cal save [cal main]\n");
|
|
fprintf(ical, "exit\n");
|
|
|
|
pclose(ical);
|
|
|
|
/* Close the database */
|
|
dlp_CloseDB(sd, db);
|
|
|
|
dlp_AddSyncLogEntry(sd, "read-ical successfully read Datebook from Palm.\n"
|
|
"Thank you for using pilot-link.\n");
|
|
dlp_EndOfSync(sd, 0);
|
|
pi_close(sd);
|
|
return 0;
|
|
|
|
error_close:
|
|
if (ical) {
|
|
/* explicitly do NOT save on failure. since we've already unlinked
|
|
the original ical file, we've still destroyed something. */
|
|
fprintf(ical, "exit\n");
|
|
pclose(ical);
|
|
}
|
|
pi_close(sd);
|
|
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noexpandtab: cin */
|
|
/* ex: set tabstop=4 expandtab: */
|
|
/* Local Variables: */
|
|
/* indent-tabs-mode: t */
|
|
/* c-basic-offset: 8 */
|
|
/* End: */
|