981 lines
29 KiB
C
981 lines
29 KiB
C
/************************************************************************
|
|
* Copyright (C) 2000,2001 Thomas Schulz *
|
|
* */
|
|
static char rcsid[] =
|
|
"$Id: ic35sync.c,v 1.19 2001/03/02 02:10:42 tsch Rel $"; /*
|
|
* *
|
|
* IC35 synchronize PIM data *
|
|
* *
|
|
*************************************************************************
|
|
* *
|
|
* ??? is "fixme" mark: sections of code needing fixes *
|
|
* *
|
|
* conditional compile on NO_LOGSIM: if #defined, logging of the *
|
|
* IC35 communications as well as simulated communication are NOT *
|
|
* supported, default WITH logging and com-simulation support. *
|
|
* *
|
|
* for usage run ic35sync -h or see function usage() below. *
|
|
* *
|
|
************************************************************************/
|
|
|
|
#include <stdio.h> /* printf(), .. */
|
|
#include <string.h> /* strcmp(), .. */
|
|
#include <getopt.h> /* getopt(), optarg, .. */
|
|
#include <time.h> /* struct tm, time() .. */
|
|
|
|
#include "util.h" /* FALSE, .. */
|
|
#include "comio.h" /* com_siminit() */
|
|
#include "syntrans.h" /* IC35sync transactions */
|
|
#include "ic35frec.h" /* FILEADDR, .. */
|
|
#include "dataio.h" /* IC35sync import/export */
|
|
NOTUSED(rcsid)
|
|
|
|
|
|
extern char * pkgvers; /* these are all */
|
|
extern char * pkgdate; /* in versinfo.c, which is */
|
|
extern char * bldinfo; /* auto-generated by Makefile */
|
|
|
|
|
|
static const int fileids[4] = {
|
|
FILEADDR,
|
|
FILEMEMO,
|
|
FILESCHED,
|
|
FILETODO
|
|
};
|
|
|
|
|
|
/* report IC35 status
|
|
* ------------------
|
|
* show firmware version, sysinfo (date+time), total and modified
|
|
* number of records per IC35 file.
|
|
*/
|
|
static int
|
|
ic35status( char * devname, char * passwd )
|
|
{
|
|
int i, fileid;
|
|
int fd;
|
|
char * fname;
|
|
int nrec, nmod, nspc;
|
|
int rval;
|
|
char ic35dtime[16+1];
|
|
|
|
if ( (rval = connect( devname, passwd, ic35dtime )) != OK )
|
|
return rval;
|
|
|
|
for ( i = 0; i < sizeof(fileids)/sizeof(fileids[0]); ++i ) {
|
|
fileid = fileids[i];
|
|
rval = ERR;
|
|
if ( (fname = ic35fname( fileid )) == NULL ) {
|
|
error( "status failed: bad fileid %02X", fileid );
|
|
break;
|
|
}
|
|
LPRINTF(( L_INFO, "status fid=%02X \"%s\" ..", fileid, fname ));
|
|
if ( (fd = open_file( ic35fname( fileid ) )) >= 0 ) {
|
|
if ( (nrec = get_flen( fd )) >= 0 ) {
|
|
if ( (nmod = get_mod_flen( fd )) >= 0 ) {
|
|
if ( (nspc = 10 - strlen( fname )) < 0 ) nspc = 0;
|
|
message( "status \"%s\"%*s total %2d modified %2d records",
|
|
fname, nspc, "", nrec, nmod );
|
|
rval = OK;
|
|
} else
|
|
error( "status %s failed: get_mod_flen failed", fname );
|
|
} else
|
|
error( "status %s failed: get_flen failed", fname );
|
|
close_file( fd );
|
|
} else
|
|
error( "status %s failed: open_file failed", fname );
|
|
LPRINTF(( L_INFO, "status fid=%02X \"%s\" %s", fileid, fname,
|
|
rval == OK ? "OK" : "ERR" ));
|
|
if ( rval != OK )
|
|
break;
|
|
}
|
|
|
|
disconnect();
|
|
return rval;
|
|
}
|
|
|
|
|
|
/* run IC35 export session
|
|
* -----------------------
|
|
* read all IC35 records and export (i.e. add/update) to PIMfile:
|
|
* - set modified+created time in PIMrecord if added IC35 record
|
|
* - set modified time in PIMrecord if updated from IC35 record
|
|
* - do not change modified time in PIMrecord if same as IC35 record
|
|
* - set PIMrecord status to CLEAN if added/updated/same as IC35
|
|
* - set IC35 record-ID to added/updated PIMfile records
|
|
* changeflags of records in IC35 will not be reset,
|
|
* records in IC35 will not be modified/deleted, and
|
|
* sysinfo (date+time) will not be written to IC35.
|
|
* IC35 overrides PIMfile, PIMfile records not in IC35 remain untouched.
|
|
*/
|
|
static int
|
|
exportfile( int fileid )
|
|
{
|
|
char * fname;
|
|
int fd, nrec, irec;
|
|
IC35REC * rec;
|
|
int rval;
|
|
|
|
if ( (fname = ic35fname( fileid )) == NULL ) {
|
|
error( "export failed: bad fileid %02X", fileid );
|
|
return ERR;
|
|
}
|
|
LPRINTF(( L_INFO, "exportfile(fid=%02X) \"%s\" ..", fileid, fname ));
|
|
if ( (rec = new_ic35rec()) == NULL ) {
|
|
error( "export %s failed: no memory", fname );
|
|
return ERR;
|
|
}
|
|
if ( (fd = open_file( ic35fname( fileid ) )) < 0 ) {
|
|
error( "export %s failed: open_file failed", fname );
|
|
del_ic35rec( rec );
|
|
return ERR;
|
|
}
|
|
if ( (nrec = get_flen( fd )) < 0 ) {
|
|
error( "export %s failed: get_flen failed", fname );
|
|
close_file( fd );
|
|
del_ic35rec( rec );
|
|
return ERR;
|
|
}
|
|
message( "export \"%s\", %d records", fname, nrec );
|
|
rval = OK;
|
|
for ( irec = 0; irec < nrec; ++irec ) {
|
|
if ( read_frec( fd, irec, rec ) < 0 ) {
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
pim_putic35rec( rec );
|
|
}
|
|
close_file( fd );
|
|
del_ic35rec( rec );
|
|
LPRINTF(( L_INFO, "exportfile(fid=%02X) \"%s\" %s",
|
|
fileid, fname, rval == OK ? "OK" : "ERR" ));
|
|
return rval;
|
|
}
|
|
static int
|
|
ic35export( char * devname, char * passwd )
|
|
{
|
|
int i, rval;
|
|
|
|
if ( (rval = connect( devname, passwd, NULL )) != 0 )
|
|
return rval;
|
|
|
|
for ( i = 0; i < sizeof(fileids)/sizeof(fileids[0]); ++i )
|
|
if ( (rval = exportfile( fileids[i] )) != OK )
|
|
break;
|
|
|
|
disconnect();
|
|
return rval;
|
|
}
|
|
|
|
|
|
/* maintain IC35 record table for import,sync
|
|
* ------------------------------------------
|
|
*/
|
|
enum ic35op { /* IC35 record operation */
|
|
IC35NOACT, /* 0 no action */
|
|
IC35DELETE, /* 1 delete record */
|
|
IC35WRITE, /* 2 write new record */
|
|
IC35UPDATE, /* 3 re-write existing record */
|
|
IC35COMMIT, /* 4 commit clears changeflag */
|
|
};
|
|
struct recop { /* IC35 record,op entry */
|
|
IC35REC * rec; /* ptr to IC35 record */
|
|
enum ic35op op; /* record operation to do */
|
|
void * pim; /* ptr to PIM record */
|
|
};
|
|
|
|
static struct recop * _ic35rectab;
|
|
static size_t _ic35rectablen;
|
|
|
|
#define ADD_CHUNKS 64
|
|
|
|
/* put IC35 record and action into table
|
|
* -------------------------------------
|
|
* if record already in table, just update action
|
|
* if free entry available, put record,action there
|
|
* if table full or not allocated yet, make it bigger
|
|
* in chunks of
|
|
*/
|
|
static void
|
|
_put_ic35recs( IC35REC * ic35rec, enum ic35op action, void * pimrec )
|
|
{
|
|
int i;
|
|
struct recop * ntab;
|
|
size_t nlen;
|
|
|
|
for ( i = 0; i < _ic35rectablen; ++i ) {
|
|
if ( _ic35rectab[i].rec == ic35rec ) {
|
|
_ic35rectab[i].op = action;
|
|
_ic35rectab[i].pim = pimrec;
|
|
return;
|
|
}
|
|
if ( _ic35rectab[i].rec == NULL )
|
|
break;
|
|
}
|
|
if ( i >= _ic35rectablen ) {
|
|
nlen = _ic35rectablen + ADD_CHUNKS;
|
|
if ( (ntab = realloc( _ic35rectab, nlen * sizeof(ntab[0]) )) == NULL )
|
|
return;
|
|
memset( ntab+_ic35rectablen, 0, ADD_CHUNKS * sizeof(ntab[0]) );
|
|
_ic35rectab = ntab;
|
|
_ic35rectablen = nlen;
|
|
}
|
|
_ic35rectab[i].rec = ic35rec;
|
|
_ic35rectab[i].op = action;
|
|
_ic35rectab[i].pim = pimrec;
|
|
}
|
|
/* get IC35 record and action from table
|
|
* -------------------------------------
|
|
* if non-zero 'recid' lookup IC35 record with that record-ID,
|
|
* otherwise just retrieve first non-NULL record in table.
|
|
* pass action associated with record to non-NULL 'paction'.
|
|
* the record will be removed from table !
|
|
*/
|
|
static IC35REC *
|
|
_get_ic35recs( ulong recid, enum ic35op * paction, void ** ppimrec )
|
|
{
|
|
int i;
|
|
IC35REC * rec;
|
|
|
|
for ( i = 0; i < _ic35rectablen; ++i ) {
|
|
if ( (rec = _ic35rectab[i].rec) == NULL )
|
|
continue;
|
|
if ( recid == 0 || ic35recid( rec ) == recid ) {
|
|
if ( paction ) *paction = _ic35rectab[i].op;
|
|
if ( ppimrec ) *ppimrec = _ic35rectab[i].pim;
|
|
_ic35rectab[i].op = IC35NOACT;
|
|
_ic35rectab[i].rec = NULL;
|
|
return rec;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
/* delete IC35 record table
|
|
* ------------------------
|
|
* release all non-NULL records in table, then release table
|
|
* return number of records yet in table, used by "import" to
|
|
* detect records in IC35 but not in PIMfile.
|
|
*/
|
|
static int
|
|
_del_ic35recs( void )
|
|
{
|
|
int i;
|
|
int only_ic35;
|
|
|
|
for ( i = only_ic35 = 0; i < _ic35rectablen; ++i )
|
|
if ( _ic35rectab[i].rec != NULL ) {
|
|
++only_ic35; /* IC35 record not in PIMfile */
|
|
del_ic35rec( _ic35rectab[i].rec );
|
|
}
|
|
free( _ic35rectab );
|
|
_ic35rectab = NULL;
|
|
_ic35rectablen = 0;
|
|
return only_ic35; /* num.of records not in PIMfile */
|
|
}
|
|
|
|
|
|
/* run IC35 import session
|
|
* -----------------------
|
|
* read all records from IC35 and compare with PIMfile
|
|
* - update PIMrecord to IC35 if same record-ID on IC35
|
|
* - write PIMrecord to IC35 if record not on IC35
|
|
* and write back new IC35 record-ID to PIMrecord
|
|
* - reset IC35 record changeflag if same as PIMrecord
|
|
* - set PIMrecord status to CLEAN
|
|
* - owner address data must be written with update_frec() and recID 1
|
|
* otherwise on IC35 in category "Address", but not as owner data
|
|
* do not delete records neither in IC35 nor in PIMfile
|
|
* write sysinfo (date+time) if all IC35 records match PIMfile
|
|
* PIMfile overrides IC35, IC35 records not in PIMfile remain untouched,
|
|
* sysinfo (date+time) is not written if any IC35 record not in PIMfile.
|
|
*/
|
|
static int
|
|
importfile( int fileid, char * ic35dtime )
|
|
{
|
|
char * fname;
|
|
int fd, nrec, irec;
|
|
IC35REC * ic35rec;
|
|
void * pimrec;
|
|
ulong recid;
|
|
int n_write, n_update, n_commit;
|
|
int ic35_not_in_PIM;
|
|
enum ic35op action;
|
|
char * actext;
|
|
int rc, rval;
|
|
|
|
if ( (fname = ic35fname( fileid )) == NULL ) {
|
|
error( "import failed: bad fileid %02X", fileid );
|
|
return ERR;
|
|
}
|
|
LPRINTF(( L_INFO, "importfile(fid=%02X) \"%s\" ..", fileid, fname ));
|
|
if ( (fd = open_file( ic35fname( fileid ) )) < 0 ) {
|
|
error( "import %s failed: open_file failed", fname );
|
|
return ERR;
|
|
}
|
|
if ( (nrec = get_flen( fd )) < 0 ) {
|
|
error( "import %s failed: get_flen failed", fname );
|
|
close_file( fd );
|
|
return ERR;
|
|
}
|
|
/*
|
|
* read all IC35-records and note them
|
|
*/
|
|
message( "import \"%s\", read %d records", fname, nrec );
|
|
rval = OK;
|
|
for ( irec = 0; irec < nrec; ++irec ) {
|
|
if ( (ic35rec = new_ic35rec()) == NULL ) {
|
|
error( "import %s failed: no memory for ic35rec", fname );
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
if ( read_frec( fd, irec, ic35rec ) < 0 ) {
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
_put_ic35recs( ic35rec, IC35NOACT, NULL );
|
|
}
|
|
if ( rval != OK ) {
|
|
close_file( fd );
|
|
LPRINTF(( L_INFO, "importfile(fid=%02X) \"%s\" rval=ERR", fileid,fname ));
|
|
return rval;
|
|
}
|
|
/*
|
|
* compare all PIM-records with IC35-records, and do:
|
|
* - if PIM-record not in IC35, write it to IC35
|
|
* and write back record-ID from IC35 to PIM-record,
|
|
* - if different update IC35-record with PIM-record,
|
|
* - if same commit to IC35 if non-zero changeflag in IC35.
|
|
* mark all PIM-records CLEAN as they are now same as IC35.
|
|
* collect IC35 records with actions to do.
|
|
*/
|
|
n_write = n_update = n_commit = 0;
|
|
pim_rewind();
|
|
while ( (pimrec = pim_getrec( fileid )) != NULL ) {
|
|
if ( (recid = pim_recid( pimrec )) != 0 /* PIM record-ID on IC35 */
|
|
&& (ic35rec = _get_ic35recs( recid, NULL, NULL )) != NULL ) {
|
|
if ( pim_cmpic35rec( ic35rec, pimrec ) == 0 ) {
|
|
if ( ic35recchg( ic35rec ) == IC35_CLEAN ) {
|
|
del_ic35rec( ic35rec ); /* same as PIM, unchanged */
|
|
continue; /* no need for commit */
|
|
}
|
|
action = IC35COMMIT; /* same as PIM, commit it */
|
|
++n_commit;
|
|
} else {
|
|
action = IC35UPDATE; /* different, update rec */
|
|
++n_update;
|
|
}
|
|
} else { /* PIMrecord not on IC35 */
|
|
ic35rec = new_ic35rec();
|
|
if ( recid == FileRecId(FILEADDR,1) ) {
|
|
action = IC35UPDATE; /* must update owner-addr */
|
|
++n_update;
|
|
} else {
|
|
action = IC35WRITE; /* write new IC35 record */
|
|
++n_write;
|
|
}
|
|
}
|
|
pim_updic35rec( ic35rec, pimrec ); /* PIM update/new to IC35 */
|
|
_put_ic35recs( ic35rec, action, pimrec ); /* add action and PIMrec */
|
|
}
|
|
/*
|
|
* execute actions on IC35 records collected above.
|
|
* count records with no action, i.e. only existing
|
|
* on IC35, but not in PIMfile.
|
|
*/
|
|
message( "import \"%s\", write %d update %d commit %d records",
|
|
fname, n_write, n_update, n_commit );
|
|
ic35_not_in_PIM = 0;
|
|
while ( (ic35rec = _get_ic35recs( 0, &action, &pimrec )) != NULL ) {
|
|
rc = 0; actext = "";
|
|
switch ( action ) {
|
|
case IC35COMMIT:
|
|
actext = "commit";
|
|
rc = commit_frec( fd, ic35recid( ic35rec ) );
|
|
break;
|
|
case IC35UPDATE:
|
|
actext = "update";
|
|
rc = update_frec( fd, ic35rec );
|
|
break;
|
|
case IC35WRITE:
|
|
actext = "write";
|
|
rc = write_frec( fd, ic35rec );
|
|
break;
|
|
default:
|
|
++ic35_not_in_PIM;
|
|
continue;
|
|
}
|
|
if ( rc >= 0 ) { /* commit/update/write success */
|
|
if ( pimrec != NULL ) {
|
|
pim_set_recid( pimrec, ic35recid( ic35rec ) );
|
|
pim_set_recstat( pimrec, PIM_CLEAN );
|
|
}
|
|
} else { /* commit/update/write failed */
|
|
error( "import %s failed: %s_frec failed", fname, actext );
|
|
rval = ERR;
|
|
}
|
|
del_ic35rec( ic35rec );
|
|
}
|
|
_del_ic35recs();
|
|
if ( rval == OK )
|
|
rval = ic35_not_in_PIM; /* any IC35 records not in PIMfile */
|
|
close_file( fd );
|
|
LPRINTF(( L_INFO, "importfile(fid=%02X) \"%s\" rval=%d",
|
|
fileid, fname, rval ));
|
|
return rval;
|
|
}
|
|
static int
|
|
ic35import( char * devname, char * passwd )
|
|
{
|
|
int i, rc, rval;
|
|
bool ic35_same_as_PIM;
|
|
char ic35dtime[16+1];
|
|
|
|
if ( (rval = connect( devname, passwd, ic35dtime )) != 0 )
|
|
return rval;
|
|
set_oldic35dt( ic35dtime );
|
|
|
|
rval = OK;
|
|
ic35_same_as_PIM = TRUE;
|
|
for ( i = 0; i < sizeof(fileids)/sizeof(fileids[0]); ++i )
|
|
if ( (rc = importfile( fileids[i], ic35dtime )) != OK ) {
|
|
ic35_same_as_PIM = FALSE;
|
|
if ( rc < 0 ) {
|
|
rval = rc;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ic35_same_as_PIM ) {
|
|
get_newic35dt( ic35dtime );
|
|
WriteSysInfo( ic35dtime );
|
|
}
|
|
|
|
disconnect();
|
|
return rval;
|
|
}
|
|
|
|
|
|
/* run IC35 sync session
|
|
* ---------------------
|
|
* update PIMfile with IC35 changes (modify/delete) and vice versa
|
|
* - phase-0: initial export if PIMfile does not contains any records
|
|
* otherwise phase-3 below would delete all unmodified records on IC35,
|
|
* as they are regarded deleted from PIMfile.
|
|
* - phase-1a: update records modified/deleted on IC35 to PIMfile
|
|
* read all modified records from IC35, if unchanged in PIMfile:
|
|
* - delete from PIMfile if deleted on IC35,
|
|
* - update from IC35 to PIMfile if modified on IC35.
|
|
* note IC35-recID for commit (commit now would disturb read_mod_frec)
|
|
* conflict if record changed in IC35 AND also in PIMfile, resolve:
|
|
* - PIMfile overrides IC35: update from PIMrecord to IC35record
|
|
* will be done in next phase below (now would disturb read_mod_frec),
|
|
* - IC35 overrides PIMfile: update from IC35record to PIMrecord
|
|
* ??? ? add changed record on IC35/PIM to PIM/IC35 with conflict mark
|
|
* - phase-1b: commit handled IC35 changes to IC35
|
|
* commits noted above to avoid disturbing read_mod_frec, now do them.
|
|
* - phase-2: update records modified in PIMfile to IC35
|
|
* - if record exists on IC35, i.e. has record-ID,
|
|
* read record by ID from IC35, update with PIMrecord
|
|
* write record to IC35
|
|
* - if record is new in PIMfile (no record-ID)
|
|
* create IC35 record from PIMrecord, write to IC35
|
|
* and writeback new IC35 record-ID to PIMrecord
|
|
* - phase-3a: records deleted from PIMfile are detected by reading
|
|
* all records from IC35 and check if record in PIMfile
|
|
* ??? sanity check IC35 changeflags clean
|
|
* - PIMfile overrides IC35: note IC35-recID for delete
|
|
* (delete now would disturb read_frec)
|
|
* - IC35 overrides PIMfile: re-create PIMrecord from IC35 record
|
|
* - phase-3b: delete records on IC35
|
|
* deletes were noted above to avoid disturbing read_frec, now do them.
|
|
* now there should be no more pending changes neither in IC35 nor in PIM.
|
|
* write sysinfo (date+time) as now all IC35 records (shall) match PIMfile
|
|
*/
|
|
static int
|
|
syncfile( int fileid, char * ic35dtime )
|
|
{
|
|
char * fname;
|
|
bool initexp;
|
|
int fd, nrec, irec;
|
|
IC35REC * ic35rec;
|
|
void * pimrec;
|
|
ulong recid;
|
|
enum ic35op action;
|
|
int n_commit, n_delete;
|
|
int rc, rval;
|
|
|
|
if ( (fname = ic35fname( fileid )) == NULL ) {
|
|
error( "sync failed: bad fileid %02X", fileid );
|
|
return ERR;
|
|
}
|
|
/*
|
|
* phase-0: initial export if PIMfile does not contains any records
|
|
* otherwise phase-3 below would delete all unmodified records on IC35
|
|
*/
|
|
initexp = FALSE;
|
|
pim_rewind();
|
|
if ( pim_getrec( fileid ) == NULL ) {
|
|
message( "sync \"%s\", no PIM records: initial export", fname );
|
|
if ( (rval = exportfile( fileid )) != OK )
|
|
return rval;
|
|
initexp = TRUE;
|
|
}
|
|
|
|
LPRINTF(( L_INFO, "syncfile(fid=%02X) \"%s\" ..", fileid, fname ));
|
|
if ( (fd = open_file( ic35fname( fileid ) )) < 0 ) {
|
|
error( "sync %s failed: open_file failed", fname );
|
|
return ERR;
|
|
}
|
|
/*
|
|
* phase-1a: update records modified/deleted on IC35 to PIMfile
|
|
* read all modified records from IC35, if unchanged in PIMfile:
|
|
* - delete from PIMfile if deleted on IC35,
|
|
* - update from IC35 to PIMfile if modified on IC35.
|
|
* note IC35-recID for commit (commit now would disturb read_mod_frec)
|
|
* conflict if record changed in IC35 AND also in PIMfile, resolve:
|
|
* - PIMfile overrides IC35: update from PIMrecord to IC35record
|
|
* will be done in next phase below (now would disturb read_mod_frec),
|
|
* - IC35 overrides PIMfile: update from IC35record to PIMrecord
|
|
* ? add changed record on IC35/PIM to PIM/IC35 with conflict mark ???
|
|
*/
|
|
if ( (nrec = get_mod_flen( fd )) < 0 ) {
|
|
error( "sync %s failed: get_mod_flen failed", fname );
|
|
close_file( fd );
|
|
return ERR;
|
|
}
|
|
message( "sync \"%s\", read %d modified records%s",
|
|
fname, nrec, nrec > 0 ? ", update PIM" : "" );
|
|
n_commit = 0;
|
|
rval = OK;
|
|
for ( irec = 0; irec < nrec; ++irec ) {
|
|
if ( (ic35rec = new_ic35rec()) == NULL ) {
|
|
error( "sync %s failed: no memory", fname );
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
if ( read_mod_frec( fd, ic35rec ) < 0 ) {
|
|
error( "sync %s failed: read_mod_frec failed", fname );
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
if ( (pimrec = pim_getrec_byID( ic35recid( ic35rec ) )) == NULL
|
|
|| pim_recstat( pimrec ) == PIM_CLEAN ) {
|
|
switch ( ic35recchg( ic35rec ) ) {
|
|
case IC35_NEW:
|
|
case IC35_MOD:
|
|
pim_putic35rec( ic35rec );
|
|
break;
|
|
case IC35_DEL:
|
|
if ( pimrec )
|
|
pim_delrec( pimrec );
|
|
break;
|
|
}
|
|
_put_ic35recs( ic35rec, IC35COMMIT, NULL );
|
|
++n_commit;
|
|
} else {
|
|
; /*??? CONFLICT: changed on both sides */
|
|
}
|
|
}
|
|
/*
|
|
* phase-1b: commit handled IC35 changes to IC35
|
|
* commits noted above to avoid disturbing read_mod_frec, now do them.
|
|
*/
|
|
if ( rval == OK && n_commit > 0 ) {
|
|
message( "sync \"%s\", commit %d IC35 records", fname, n_commit );
|
|
while ( (ic35rec = _get_ic35recs( 0, &action, NULL )) != NULL ) {
|
|
if ( action == IC35COMMIT
|
|
&& commit_frec( fd, ic35recid( ic35rec ) ) < 0 ) {
|
|
error( "sync %s failed: commit_frec failed", fname );
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
del_ic35rec( ic35rec );
|
|
}
|
|
}
|
|
_del_ic35recs();
|
|
if ( rval != OK ) {
|
|
close_file( fd );
|
|
LPRINTF(( L_INFO, "syncfile(fid=%02X) \"%s\" rval=ERR", fileid,fname ));
|
|
return rval;
|
|
}
|
|
/*
|
|
* if initial export was done, update from PIM to IC35 and
|
|
* check records removed from PIM and delete is not needed.
|
|
*/
|
|
if ( initexp ) {
|
|
close_file( fd );
|
|
LPRINTF(( L_INFO, "syncfile(fid=%02X) \"%s\" rval=OK", fileid, fname ));
|
|
return rval;
|
|
}
|
|
/*
|
|
* phase-2: update records modified in PIMfile to IC35
|
|
* - if record exists on IC35, i.e. has record-ID,
|
|
* read record by ID from IC35, update with PIMrecord
|
|
* write record to IC35
|
|
* - if record is new in PIMfile (no record-ID)
|
|
* create IC35 record from PIMrecord, write to IC35
|
|
* and writeback new IC35 record-ID to PIMrecord
|
|
*/
|
|
if ( (ic35rec = new_ic35rec()) == NULL ) {
|
|
close_file( fd );
|
|
error( "sync %s failed: no memory", fname );
|
|
return ERR;
|
|
}
|
|
message( "sync \"%s\", update IC35", fname );
|
|
pim_rewind();
|
|
while ( rval == OK && (pimrec = pim_getrec( fileid )) != NULL ) {
|
|
switch ( pim_recstat( pimrec ) ) {
|
|
case PIM_CLEAN:
|
|
continue;
|
|
case PIM_DEL:
|
|
if ( (recid = pim_recid( pimrec )) != 0 ) {
|
|
if ( delete_frec( fd, recid ) >= 0 ) {
|
|
pim_delrec( pimrec );
|
|
} else {
|
|
error( "sync %s failed: delete_frec failed", fname );
|
|
rval = ERR;
|
|
}
|
|
}
|
|
continue;
|
|
default:
|
|
error( "bad PIM record status: %d", pim_recstat( pimrec ) );
|
|
/* full through and handle as PIM_DIRTY */
|
|
case PIM_DIRTY:
|
|
if ( (recid = pim_recid( pimrec )) != 0 ) { /* PIMrec on IC35 */
|
|
if ( (rc = read_id_frec( fd, recid, ic35rec )) < 0 ) {
|
|
error( "sync %s failed: read_id_frec failed", fname );
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
if ( rc > 0 ) { /* PIMrec found on IC35 */
|
|
pim_updic35rec( ic35rec, pimrec );
|
|
if ( update_frec( fd, ic35rec ) >= 0 ) {
|
|
pim_set_recstat( pimrec, PIM_CLEAN );
|
|
} else {
|
|
error( "sync %s failed: update_frec failed", fname );
|
|
rval = ERR;
|
|
}
|
|
}
|
|
break;
|
|
} /* PIMrecord not on IC35 */
|
|
set_ic35recdata( ic35rec, NULL, 0 );
|
|
pim_updic35rec( ic35rec, pimrec );
|
|
if ( write_frec( fd, ic35rec ) >= 0 ) {
|
|
pim_set_recid( pimrec, ic35recid( ic35rec ) );
|
|
pim_set_recstat( pimrec, PIM_CLEAN );
|
|
} else {
|
|
error( "sync %s failed: write_frec failed", fname );
|
|
rval = ERR;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
del_ic35rec( ic35rec ); ic35rec = NULL;
|
|
if ( rval != OK ) {
|
|
close_file( fd );
|
|
LPRINTF(( L_INFO, "syncfile(fid=%02X) \"%s\" rval=ERR", fileid,fname ));
|
|
return rval;
|
|
}
|
|
/*
|
|
* phase-3a: records deleted from PIMfile are detected by reading
|
|
* all records from IC35 and check if record in PIMfile
|
|
* ? sanity check IC35 changeflags clean ???
|
|
* - PIMfile overrides IC35: note IC35-recID for delete
|
|
* (delete now would disturb read_frec)
|
|
* - IC35 overrides PIMfile: re-create PIMrecord from IC35 record
|
|
*/
|
|
if ( (nrec = get_flen( fd )) < 0 ) {
|
|
error( "sync %s failed: get_flen failed", fname );
|
|
close_file( fd );
|
|
return ERR;
|
|
}
|
|
message( "sync \"%s\", read %d records, check PIM deleted", fname, nrec );
|
|
n_delete = 0;
|
|
for ( irec = 0; irec < nrec; ++irec ) {
|
|
if ( (ic35rec = new_ic35rec()) == NULL ) {
|
|
error( "sync %s failed: no memory", fname );
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
if ( read_frec( fd, irec, ic35rec ) < 0 ) {
|
|
error( "sync %s failed: read_frec failed", fname );
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
if ( pim_getrec_byID( ic35recid( ic35rec ) ) ) {
|
|
del_ic35rec( ic35rec );
|
|
continue;
|
|
}
|
|
/*??? sanity check ic35recchg() == 0 ? */
|
|
/*??? if PIM overrides IC35 */
|
|
_put_ic35recs( ic35rec, IC35DELETE, NULL );
|
|
++n_delete;
|
|
/*??? if IC35 overrides PIM */
|
|
/*??? pim_putic35rec( ic35rec ); */
|
|
}
|
|
/*
|
|
* phase-3b: delete records on IC35
|
|
* deletes were noted above to avoid disturbing read_frec, now do them.
|
|
*/
|
|
if ( rval == OK && n_delete ) {
|
|
message( "sync \"%s\", delete %d IC35 records", fname, n_delete );
|
|
while ( (ic35rec = _get_ic35recs( 0, &action, NULL )) != NULL ) {
|
|
if ( action == IC35DELETE
|
|
&& delete_frec( fd, ic35recid( ic35rec ) ) < 0 ) {
|
|
error( "sync %s failed: delete_frec failed", fname );
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
del_ic35rec( ic35rec );
|
|
}
|
|
}
|
|
_del_ic35recs();
|
|
|
|
close_file( fd );
|
|
LPRINTF(( L_INFO, "syncfile(fid=%02X) \"%s\" rval=%d",
|
|
fileid, fname, rval ));
|
|
return rval;
|
|
}
|
|
static int
|
|
ic35sync( char * devname, char * passwd )
|
|
{
|
|
int i, rval;
|
|
char ic35dtime[16+1];
|
|
|
|
if ( (rval = connect( devname, passwd, ic35dtime )) != 0 )
|
|
return rval;
|
|
set_oldic35dt( ic35dtime );
|
|
|
|
for ( i = 0; i < sizeof(fileids)/sizeof(fileids[0]); ++i ) {
|
|
if ( (rval = syncfile( fileids[i], ic35dtime )) != OK )
|
|
break;
|
|
}
|
|
|
|
if ( rval == OK ) {
|
|
get_newic35dt( ic35dtime );
|
|
WriteSysInfo( ic35dtime );
|
|
}
|
|
|
|
disconnect();
|
|
return rval;
|
|
}
|
|
|
|
|
|
/* -------------------- M A I N - program ----------------------------- */
|
|
static void
|
|
versinfo( void )
|
|
{
|
|
printf( "IC35sync for GNU/Linux %s (%s)\n", pkgvers, pkgdate );
|
|
printf( "%s\n", bldinfo );
|
|
printf(
|
|
"Copyright (C) 2000,2001 Thomas Schulz\n"
|
|
"This is free software; see the GNU General Public Licence version 2 in\n"
|
|
"the file named COPYING for copying conditions. There is NO warranty.\n"
|
|
);
|
|
}
|
|
static void
|
|
usage( void )
|
|
{
|
|
static char * usetext[] = {
|
|
"usage: ic35sync [option..] command [pimfile..]",
|
|
"commands are:",
|
|
" sync synchronize IC35 with pimfile(s) data",
|
|
" export read all data from IC35 and write to pimfile(s)",
|
|
" import write to IC35 all data read from pimfile(s)",
|
|
" status report IC35 status (no pimfile access)",
|
|
"pimfile contains local Personal Information Management data,",
|
|
"e.g. in vCard/vCalendar format.",
|
|
"options in short and long form are:",
|
|
" -d DEV --device=DEV serial device with IC35 connected, default /dev/ic35",
|
|
" -p TEXT --password=TEXT password TEXT on IC35, default none",
|
|
" -f FORM --format=FORM format of pimfile(s), FORM and default pimfile(s):",
|
|
" vca (default) vCard,vCalendar,Memo, ic35.vcard ic35.vcal ic35.memo",
|
|
" txt (export only) plain text, ic35.txt",
|
|
" bin raw binary data, ic35.bin",
|
|
#ifndef NO_LOGSIM
|
|
" -l FILE --logfile=FILE log communications to FILE, default no logging",
|
|
" -s FILE --simfile=FILE simulate communication with IC35 using FILE",
|
|
" (create simulation FILE from logfile with 'ic35log')",
|
|
#endif
|
|
" -V --version show version information and exit",
|
|
" -h --help show this help and exit",
|
|
NULL
|
|
};
|
|
char ** lptr;
|
|
|
|
for ( lptr = usetext; *lptr != NULL; ++lptr )
|
|
printf( "%s\n", *lptr );
|
|
}
|
|
|
|
int
|
|
main( int argc, char *argv[] )
|
|
{
|
|
static struct option const long_opts[] = {
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "version", no_argument, NULL, 'V' },
|
|
{ "device", required_argument, NULL, 'd' },
|
|
{ "password", required_argument, NULL, 'p' },
|
|
{ "format", required_argument, NULL, 'f' },
|
|
#ifndef NO_LOGSIM
|
|
{ "logfile", required_argument, NULL, 'l' },
|
|
{ "simfile", required_argument, NULL, 's' },
|
|
#endif
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
char * passwd = "";
|
|
char * devname = "/dev/ic35";
|
|
char * pimfmtstr = "vca";
|
|
char * command = NULL;
|
|
char * pimaddrfname = NULL;
|
|
char * pimvcalfname = NULL;
|
|
char * pimmemofname = NULL;
|
|
#ifndef NO_LOGSIM
|
|
char * logfname = NULL;
|
|
char * simfname = NULL;
|
|
#endif
|
|
int rval;
|
|
|
|
/*
|
|
* parse command line
|
|
*/
|
|
for ( ; ; ) {
|
|
switch ( getopt_long( argc, argv,
|
|
#ifndef NO_LOGSIM
|
|
"l:s:"
|
|
#endif
|
|
"d:p:f:hV", long_opts, NULL ) ) {
|
|
default:
|
|
fprintf( stderr, "use 'ic35sync --help' for more information\n" );
|
|
return 1;
|
|
case 'h': /* show Help and exit */
|
|
usage();
|
|
return 0;
|
|
case 'V': /* show Version and exit */
|
|
versinfo();
|
|
return 0;
|
|
case 'd': /* serial Device where IC35 */
|
|
devname = optarg;
|
|
continue;
|
|
case 'p': /* password on IC35 */
|
|
passwd = optarg;
|
|
continue;
|
|
case 'f': /* format of PIMfile */
|
|
pimfmtstr = optarg;
|
|
continue;
|
|
#ifndef NO_LOGSIM
|
|
case 'l': /* communication logfile */
|
|
logfname = optarg;
|
|
continue;
|
|
case 's': /* simulate: no communication */
|
|
simfname = optarg;
|
|
continue;
|
|
#endif/*NO_LOGSIM*/
|
|
case -1: /* end of options */
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if ( argc < optind+1 ) {
|
|
error( "missing command\n"
|
|
"use 'ic35sync --help' for more information" );
|
|
return 1;
|
|
}
|
|
command = argv[optind];
|
|
|
|
/*
|
|
* initial info to logfile: date,time, version, command line
|
|
* if 'logfname' is NULL or empty, nothing will be logged.
|
|
*/
|
|
LOG_INIT(( logfname, "ic35sync", 0 ));
|
|
LOG_PROGINFO(( "ic35sync", pkgvers, pkgdate, bldinfo ));
|
|
LOG_ARGSINFO(( argc, argv ));
|
|
|
|
/*
|
|
* setup simulated communication if requested
|
|
*/
|
|
COM_SIMINIT(( simfname ));
|
|
|
|
/*
|
|
* status command does not use pimfile(s)
|
|
*/
|
|
if ( strcmp( command, "status" ) == 0 ) {
|
|
rval = ic35status( devname, passwd ) == OK ? 0 : 1;
|
|
LOG_CLOSE(());
|
|
return rval;
|
|
}
|
|
|
|
/*
|
|
* defaults for filenames are:
|
|
* - no filename
|
|
* use defaults depending on format
|
|
* - text format ic35.txt
|
|
* - binary format ic35.bin
|
|
* - vCard,vCal format ic35.vcard, ic35.vcal, ic35.memo
|
|
* - 1 filename
|
|
* Addresses, Schedule,ToDo, Memo all in 1 file
|
|
* - 2 filenames
|
|
* Addresses to 1st file, Schedule,ToDo,Memo to 2nd file
|
|
* - 3 filenames
|
|
* Addresses to 1st file, Schedule,ToDo to 2nd, Memo to 3rd
|
|
*/
|
|
if ( set_pim_format( pimfmtstr ) != OK ) {
|
|
error( "unknown format: %s", pimfmtstr );
|
|
return 1;
|
|
}
|
|
argv += optind, argc -= optind; /* skip to non-option arguments */
|
|
switch ( argc ) {
|
|
default:
|
|
error( "max. 3 pimfiles supported, have %d", argc - 1 );
|
|
return 1;
|
|
case 4: /* 3 filenames */
|
|
pimmemofname = argv[3];
|
|
/* fall through */
|
|
case 3: /* 2 filenames */
|
|
pimvcalfname = argv[2];
|
|
/* fall through */
|
|
case 2: /* 1 filename */
|
|
pimaddrfname = argv[1];
|
|
break;
|
|
case 1: /* no filename */
|
|
switch ( pim_format() ) {
|
|
case PIM_TXT:
|
|
pimaddrfname = "ic35.txt";
|
|
break;
|
|
case PIM_BIN:
|
|
pimaddrfname = "ic35.bin";
|
|
break;
|
|
case PIM_VCA:
|
|
pimaddrfname = "ic35.vcard";
|
|
pimvcalfname = "ic35.vcal";
|
|
pimmemofname = "ic35.memo";
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if ( pim_open( pimaddrfname, pimvcalfname, pimmemofname ) != OK ) {
|
|
error( "failed to open pimfile(s): %s %s %s\n",
|
|
pimaddrfname,
|
|
pimvcalfname ? pimvcalfname : "",
|
|
pimmemofname ? pimmemofname : "" );
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* process command
|
|
*/
|
|
if ( strcmp( command, "export" ) == 0 ) {
|
|
rval = ic35export( devname, passwd ) == OK ? 0 : 1;
|
|
} else if ( strcmp( command, "import" ) == 0 ) {
|
|
rval = ic35import( devname, passwd ) == OK ? 0 : 1;
|
|
} else if ( strcmp( command, "sync" ) == 0 ) {
|
|
rval = ic35sync( devname, passwd ) == OK ? 0 : 1;
|
|
} else {
|
|
error( "unsupported command: %s", command );
|
|
rval = 1;
|
|
}
|
|
|
|
pim_close();
|
|
LOG_CLOSE(());
|
|
return rval;
|
|
}
|