992 lines
28 KiB
C
992 lines
28 KiB
C
/************************************************************************
|
|
* Copyright (C) 2001 Thomas Schulz *
|
|
* */
|
|
static char rcsid[] =
|
|
"$Id: ic35mgr.c,v 1.20 2001/11/20 23:08:35 thosch Exp $"; /*
|
|
* *
|
|
* IC35 manager *
|
|
* *
|
|
*************************************************************************
|
|
* *
|
|
* ??? 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 ic35mgr -h or see function usage() below. *
|
|
* *
|
|
************************************************************************/
|
|
|
|
#include <stdio.h> /* printf(), .. */
|
|
#include <stdlib.h> /* malloc(), free() */
|
|
#include <string.h> /* memcpy(), strcpy() ..*/
|
|
#include <ctype.h> /* islower(), .. */
|
|
#include <getopt.h> /* getopt(), optarg, .. */
|
|
#include <time.h> /* struct tm, time() .. */
|
|
#include <utime.h> /* struct utimbuf, .. */
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
|
|
#include "util.h" /* FALSE, .. */
|
|
#include "comio.h" /* COM_SIMINIT(), .. */
|
|
#include "mgrtrans.h" /* IC35mgr transactions */
|
|
NOTUSED(rcsid)
|
|
|
|
|
|
extern char * pkgvers; /* these are all */
|
|
extern char * pkgdate; /* in versinfo.c, which is */
|
|
extern char * bldinfo; /* auto-generated by Makefile */
|
|
|
|
|
|
/* ==================================== */
|
|
/* IC35 manager commands */
|
|
/* ==================================== */
|
|
|
|
/* report IC35 status
|
|
* ------------------
|
|
*/
|
|
static int
|
|
ic35mgrstat( char * devname, char * statfname )
|
|
{
|
|
int mmcnum;
|
|
int rval;
|
|
char label[11+1];
|
|
|
|
if ( (rval = mconnect( devname, statfname )) != OK )
|
|
return rval;
|
|
|
|
for ( mmcnum = 1; mmcnum <= 2; ++mmcnum ) {
|
|
if ( (rval = mmc_status( mmcnum )) < 0 )
|
|
break;
|
|
if ( rval != 0 ) {
|
|
if ( (rval = mmc_label( mmcnum, label )) < 0 )
|
|
break;
|
|
message( "MMCard%d found, label: \"%s\"", mmcnum, label );
|
|
} else {
|
|
message( "MMCard%d not present", mmcnum );
|
|
}
|
|
}
|
|
|
|
mdisconnect();
|
|
return rval;
|
|
}
|
|
|
|
|
|
/* backup IC35 database to file
|
|
* ----------------------------
|
|
*/
|
|
static int
|
|
ic35backup( char * devname, char * fname )
|
|
{
|
|
int rval;
|
|
|
|
if ( (rval = mconnect( devname, NULL )) != OK )
|
|
return rval;
|
|
|
|
rval = readdatabase( fname );
|
|
|
|
mdisconnect();
|
|
return rval;
|
|
}
|
|
|
|
/* restore IC35 database from file
|
|
* -------------------------------
|
|
*/
|
|
static int
|
|
ic35restore( char * devname, char * fname )
|
|
{
|
|
int rval;
|
|
|
|
if ( (rval = mconnect( devname, NULL )) != OK )
|
|
return rval;
|
|
|
|
rval = writedatabase( fname );
|
|
|
|
mdisconnect();
|
|
return rval;
|
|
}
|
|
|
|
|
|
/* list IC35 MMCard(s) directories
|
|
* -------------------------------
|
|
* list looks like:
|
|
* MMCardn "Label"
|
|
* size filetimestamp attr filepath
|
|
* nnnnnnnnnn yyyy-mm-dd hh:mm:ss xxx MMCard1\subdir\filename.ext
|
|
*/
|
|
static int
|
|
mmcdirlist( char * dirpath )
|
|
{
|
|
MMCDIR * dirp;
|
|
MMCDIRENT * dirent;
|
|
size_t pfxlen;
|
|
char * subdir;
|
|
char attrtext[4+1];
|
|
int rval, rc;
|
|
|
|
if ( (dirp = mmc_opendir( dirpath )) == NULL )
|
|
return ERR;
|
|
pfxlen = strlen( dirpath ) + 1;
|
|
rval = OK;
|
|
while ( (dirent = mmc_readdir( dirp )) != NULL ) {
|
|
switch ( dirent->attr ) {
|
|
case MMCattrDirectory: strcpy( attrtext, "dir" ); break;
|
|
case MMCattrArchive: strcpy( attrtext, "arc" ); break;
|
|
default: sprintf( attrtext, "0x%02X", dirent->attr & 0xFF );
|
|
}
|
|
printf( "%10ld %s %4s %s\\%s\n",
|
|
dirent->size, mmctstampstr( dirent->tstamp ),
|
|
attrtext, dirpath, dirent->name );
|
|
fflush( stdout );
|
|
if ( dirent->attr & MMCattrDirectory
|
|
&& strcmp( dirent->name, ".." ) != 0
|
|
&& (subdir = malloc( pfxlen + strlen(dirent->name) + 1 )) != NULL ) {
|
|
sprintf( subdir, "%s\\%s", dirpath, dirent->name );
|
|
rc = mmcdirlist( subdir );
|
|
if ( rval == OK ) rval = rc;
|
|
free( subdir );
|
|
}
|
|
}
|
|
rc = mmc_closedir( dirp );
|
|
if ( rval == OK ) rval = rc;
|
|
return rval; /* errcode of first failure or success OK */
|
|
}
|
|
static int
|
|
ic35mmcdir( char * devname )
|
|
{
|
|
int mmcnum;
|
|
int rval;
|
|
char label[11+1];
|
|
char dirpath[7+1];
|
|
|
|
if ( (rval = mconnect( devname, NULL )) != OK )
|
|
return rval;
|
|
|
|
for ( mmcnum = 1; mmcnum <= 2; ++mmcnum ) {
|
|
if ( (rval = mmc_status( mmcnum )) < 0 )
|
|
break;
|
|
if ( rval == 0 ) {
|
|
message( "MMCard%d not present", mmcnum );
|
|
continue;
|
|
}
|
|
if ( (rval = mmc_label( 1, label )) < 0 )
|
|
break;
|
|
printf( "MMCard%d \"%s\"\n", mmcnum, label );
|
|
fflush( stdout );
|
|
sprintf( dirpath, "MMCard%d", mmcnum );
|
|
if ( (rval = mmcdirlist( dirpath )) < 0 )
|
|
break;
|
|
}
|
|
|
|
mdisconnect();
|
|
return rval;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ==================================== */
|
|
/* IC35 MMCard file commands */
|
|
/* ==================================== */
|
|
|
|
#define MINBLKSZ 1 /* min.blocksize */
|
|
#define MAXBLKSZ 16350 /* max.blocksize (found by experiments) */
|
|
|
|
#define ERR_mmcpfx -1
|
|
#define ERR_mmcnum -2
|
|
#define ERR_mmcslash -3
|
|
#define ERR_mmcbadpath -4
|
|
|
|
static char * MMCard = "MMCard";
|
|
|
|
static int blocksize; /* read/write buffer size mmcget/mmcput */
|
|
|
|
/* check,convert MMCard path to IC35 format & get MMCard number
|
|
* ------------------------------------------------------
|
|
* the ic35path will be checked (slash means / or \):
|
|
* - prefix must be "MMCard" in lowercase or uppercase or mixed
|
|
* - MMCard number after prefix must be '1' or '2'
|
|
* - slash after MMCard number (unless the path ends there)
|
|
* - no adjacent slashes
|
|
* - at most one dot between slashes
|
|
* - max. 8 characters after slash until dot or slash or end
|
|
* - max. 3 characters after dot until slash or end
|
|
* the ic35path will be translated to match IC35 requirements:
|
|
* translate '/' to '\' dir separators and lowercase to upper.
|
|
* the translation is done in place, i.e. the argument string
|
|
* is modified.
|
|
* returns:
|
|
* -1 ERR, missing "MMCard" prefix
|
|
* -2 ERR, bad MMCard number (not 1,2)
|
|
* -3 ERR, missing slash/backslash after "MMCard[12]"
|
|
* -4 ERR, bad path component (too long, >1 dot, adjacent slashes)
|
|
*
|
|
* parameter mode: if 1 create directories
|
|
*/
|
|
static int
|
|
_ic35mmcpath( char * ic35path , int mode)
|
|
{
|
|
MMCDIR * mdirp;
|
|
int mmcnum;
|
|
char * ptr;
|
|
char * pdot;
|
|
char * pslash;
|
|
|
|
/* check/set "MMCard" prefix in ic35fpath */
|
|
if ( strncasecmp( ic35path, MMCard, strlen(MMCard) ) != 0
|
|
|| ! isdigit( ic35path[strlen(MMCard)] ) )
|
|
return ERR_mmcpfx; /* error: missing MMCard prefix */
|
|
memcpy( ic35path, MMCard, strlen(MMCard) );
|
|
/* check MMCard number and slash/backslash after prefix */
|
|
ptr = ic35path + strlen(MMCard);
|
|
if ( *ptr != '1' && *ptr != '2' )
|
|
return ERR_mmcnum; /* error: bad MMCard number */
|
|
mmcnum = *ptr - '0';
|
|
++ptr;
|
|
if ( *ptr == 0 ) return mmcnum; /* allow just MMCard<n> */
|
|
if ( *ptr == '/' ) *ptr = '\\';
|
|
if ( *ptr != '\\' )
|
|
return ERR_mmcslash; /* error: miss slash/backslash */
|
|
/* check path components for 8+3, too many dots, etc. */
|
|
pslash = ptr++; pdot = NULL;
|
|
do {
|
|
switch ( *ptr ) {
|
|
case '/':
|
|
case '\\':
|
|
if (mode == 1) {
|
|
/* create directory if it does not yet exist */
|
|
*ptr = 0;
|
|
if ((mdirp = mmc_opendir(ic35path)) == NULL)
|
|
mdirp = mmc_createdir(ic35path);
|
|
mmc_closedir(mdirp);
|
|
}
|
|
*ptr = '\\';
|
|
/* fall through */
|
|
case '\0':
|
|
if ( ptr - (pslash+1) == 0 ) {
|
|
if (*ptr == 0) *pslash = 0; /* remove trailing slash */
|
|
else return ERR_mmcbadpath; /* error: adjacent slashes */
|
|
}
|
|
if ( (pdot != NULL && ptr - (pdot+1) > 3)
|
|
|| (pdot == NULL && ptr - (pslash+1) > 8) )
|
|
return ERR_mmcbadpath; /* error: too long since slash */
|
|
pslash = ptr; pdot = NULL;
|
|
break;
|
|
case '.':
|
|
if ( pdot != NULL )
|
|
return ERR_mmcbadpath; /* error: too many dots */
|
|
if ( ptr - (pslash+1) > 8 )
|
|
return ERR_mmcbadpath; /* error: too long before dot */
|
|
pdot = ptr;
|
|
break;
|
|
}
|
|
} while ( *ptr++ );
|
|
/* convert ic35path '/' to '\\', lowercase to uppercase */
|
|
for ( ptr = ic35path+strlen(MMCard); *ptr; ++ptr ) {
|
|
if ( islower( *ptr ) )
|
|
*ptr = (char)toupper( *ptr );
|
|
}
|
|
return mmcnum;
|
|
}
|
|
|
|
/* output MMCard path error message
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
_mmcpatherror( int errnum, char * ic35path )
|
|
{
|
|
switch ( errnum ) {
|
|
case ERR_mmcpfx:
|
|
error( "missing \"%s\" prefix in IC35 filepath: %s", MMCard, ic35path );
|
|
break;
|
|
case ERR_mmcnum:
|
|
error( "bad MMCard number in IC35 filepath: %s", ic35path );
|
|
break;
|
|
case ERR_mmcslash:
|
|
error( "missing \\ or / in IC35 filepath: %s", ic35path );
|
|
break;
|
|
case ERR_mmcbadpath:
|
|
default:
|
|
error( "bad path component in IC35 filepath: %s", ic35path );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* get filename from IC35 MMCard path
|
|
* ----------------------------------
|
|
* derive local filename from base filename in ic35path, i.e. leading
|
|
* directory components removed, and translate to lowercase.
|
|
* the buffer for the local filename is strdup()'d and shall be freed
|
|
* by caller.
|
|
*/
|
|
static char *
|
|
_dupic35fname( char * ic35path, int mode)
|
|
{
|
|
char * ptr;
|
|
char * fname;
|
|
|
|
/* create copy */
|
|
if ( (fname = strdup( ic35path )) == NULL )
|
|
return NULL;
|
|
|
|
/* convert to unix filename, creating dirs on the way */
|
|
ptr = fname;
|
|
while (*ptr) {
|
|
if (*ptr == '\\' || *ptr == '/') {
|
|
if ( mode == 1 ) {
|
|
/* create subdir */
|
|
*ptr = 0;
|
|
if (mkdir(fname, 0700) < 0
|
|
&& errno != EEXIST ) {
|
|
perror(fname);
|
|
}
|
|
}
|
|
*ptr++ = '/';
|
|
} else {
|
|
*ptr++ = tolower( *ptr );
|
|
}
|
|
}
|
|
return fname;
|
|
}
|
|
|
|
static void progress( int mode, char* ipath, char *upath, ulong curr, ulong total)
|
|
{
|
|
fprintf( stderr, "\r%s %s %s %s %ld",
|
|
mode? "read": "write", ipath,
|
|
mode? "->" : "<-", upath, (curr+511)/1024);
|
|
if (total) fprintf( stderr, "/%ldk, %ld%% ", (total+511)/1024,
|
|
curr*100/total);
|
|
else fprintf( stderr, "k ");
|
|
}
|
|
|
|
/* read file from IC35 MMCard
|
|
* --------------------------
|
|
* the ic35path will be translated to match IC35 requirements:
|
|
* translate '/' to '\' dir separators and lowercase to upper.
|
|
* on absent local filename (NULL or empty string) derive it from
|
|
* base filename in ic35path, i.e. leading directory components
|
|
* removed, translated to lowercase.
|
|
* e.g. mmcard1/Ic35\App\Reversi.APP will be translated
|
|
* to MMCard1\IC35\APP\REVERSI.APP and local file reversi.app
|
|
* the local file's timestamp will be set to the IC35 file's.
|
|
* (but IC35 seems to errorneously set the IC35 file's timestamp
|
|
* to the time of read, although the IC35 file is not modified!?)
|
|
*
|
|
*/
|
|
|
|
static int
|
|
mmcget( char * ic35path, char * a_fname )
|
|
{
|
|
int rbuffsize = 5 * 1024;
|
|
char * fname;
|
|
MMCFILE * mmcfp;
|
|
MMCDIRENT * mmcfstat;
|
|
FILE * fp;
|
|
struct utimbuf ftime;
|
|
uchar * buff;
|
|
size_t blen;
|
|
int rlen;
|
|
ulong frlen;
|
|
int rval = OK;
|
|
ulong size;
|
|
|
|
if ( MINBLKSZ <= blocksize && blocksize <= MAXBLKSZ )
|
|
rbuffsize = blocksize;
|
|
|
|
else if ( blocksize != 0 )
|
|
error( "bad buffer size %d, using default %d", blocksize, rbuffsize );
|
|
|
|
/* create fname from basename(ic35path) if absent */
|
|
if ( a_fname && *a_fname ) {
|
|
fname = a_fname;
|
|
} else
|
|
if ( (fname = _dupic35fname( ic35path, 1 )) == NULL ) {
|
|
error( "no memory for local filename from %s", ic35path );
|
|
return ERR;
|
|
}
|
|
|
|
if ( (mmcfp = mmc_openfile( ic35path, MMCopenexist )) != NULL ) {
|
|
if ( (mmcfstat = mmc_statfile( mmcfp )) != NULL ) {
|
|
size = mmcfstat->size;
|
|
if ( (buff = malloc( blen = rbuffsize )) != NULL ) {
|
|
if ( (fp = fopen( fname, "w" )) != NULL ) {
|
|
rval = OK;
|
|
frlen = 0;
|
|
progress( 1, ic35path, fname, frlen, size );
|
|
while ( (rlen = mmc_readfile( mmcfp, buff, blen )) > 0 ) {
|
|
frlen += rlen;
|
|
progress( 1, ic35path, fname, frlen, size );
|
|
if ( fwrite( buff, 1, rlen, fp ) != rlen ) {
|
|
error( "write error %s: %s (%d)",
|
|
fname, strerror(errno), errno );
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
}
|
|
fclose( fp );
|
|
if ( rlen == 0 && rval == OK ) {
|
|
ftime.actime = time( NULL );
|
|
ftime.modtime = mmctstampunixtime( mmcfstat->tstamp );
|
|
utime( fname, &ftime ); /* set file modtime from IC35 */
|
|
fprintf( stderr, "\n" );
|
|
} else /* mmc_readfile() or fwrite() failed */
|
|
rval = ERR;
|
|
} else /* (fp = fopen()) == NULL */
|
|
error( "cannot open %s: %s (%d)",
|
|
fname, strerror(errno), errno );
|
|
free( buff );
|
|
} else /* (buff = malloc()) == NULL */
|
|
error( "no memory for read buffer (%d)", rbuffsize );
|
|
} else /* mmc_statfile() failed */
|
|
rval = ERR;
|
|
mmc_closefile( mmcfp );
|
|
} else /* mmc_openfile() failed */
|
|
rval = ERR;
|
|
if ( fname != a_fname )
|
|
free( fname );
|
|
return rval;
|
|
}
|
|
|
|
static int
|
|
ic35mmcget( char * devname, char * ic35path, char * a_fname )
|
|
{
|
|
int mmcnum;
|
|
int rval;
|
|
|
|
/* check ic35path, check/set "MMCard" prefix and */
|
|
/* convert '/' to '\\', lowercase to uppercase */
|
|
if ( (mmcnum = _ic35mmcpath( ic35path, 0 )) < 0 ) {
|
|
_mmcpatherror( mmcnum, ic35path );
|
|
return ERR;
|
|
}
|
|
/* connect to IC35 in SyncStation via serial device */
|
|
if ( (rval = mconnect( devname, NULL )) != OK )
|
|
return rval;
|
|
/* get MMCard status (else other MMCard ops would fail) */
|
|
if ( (rval = mmc_status( mmcnum )) <= 0 ) {
|
|
if ( rval == 0 )
|
|
message( "MMCard%d not present", mmcnum );
|
|
mdisconnect();
|
|
return ERR;
|
|
}
|
|
|
|
/* read file from IC35 MMCard */
|
|
rval = mmcget( ic35path, a_fname);
|
|
|
|
/* cleanup and disconnect from IC35 */
|
|
mdisconnect();
|
|
return rval;
|
|
}
|
|
|
|
/* get complete tree from IC35 MMCard(s)
|
|
* -------------------------------
|
|
* dirpath = subdir from which to start
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
static int
|
|
mmcgettree( char * dirpath )
|
|
{
|
|
MMCDIR * dirp;
|
|
MMCDIRENT * dirent;
|
|
size_t pfxlen;
|
|
char * subdir;
|
|
char * ic35path;
|
|
int rval, rc;
|
|
|
|
if ( (dirp = mmc_opendir( dirpath )) == NULL )
|
|
return ERR;
|
|
pfxlen = strlen( dirpath ) + 1;
|
|
rval = OK;
|
|
while ( (dirent = mmc_readdir( dirp )) != NULL ) {
|
|
switch ( dirent->attr ) {
|
|
case MMCattrDirectory:
|
|
if (strcmp(dirent->name, ".." ) != 0
|
|
&& (subdir = malloc( pfxlen + strlen(dirent->name) + 1 )) != NULL ) {
|
|
sprintf(subdir, "%s\\%s", dirpath, dirent->name );
|
|
rc = mmcgettree(subdir);
|
|
if ( rval == OK ) rval = rc;
|
|
free(subdir);
|
|
}
|
|
break;
|
|
case MMCattrArchive:
|
|
ic35path = malloc(pfxlen + strlen(dirent->name) + 1 );
|
|
sprintf(ic35path, "%s\\%s", dirpath, dirent->name);
|
|
mmcget(ic35path, NULL);
|
|
free(ic35path);
|
|
break;
|
|
default:
|
|
}
|
|
}
|
|
rc = mmc_closedir( dirp );
|
|
if ( rval == OK ) rval = rc;
|
|
return rval; /* errcode of first failure or success OK */
|
|
}
|
|
|
|
static int
|
|
ic35mmcgettree( char * devname, char * ic35path)
|
|
{
|
|
int mmcnum;
|
|
int rval;
|
|
char dirpath[7+1];
|
|
|
|
if (ic35path) {
|
|
/* check ic35path, check/set "MMCard" prefix and */
|
|
/* convert '/' to '\\', lowercase to uppercase */
|
|
if ( (mmcnum = _ic35mmcpath( ic35path, 0 )) < 0 ) {
|
|
_mmcpatherror( mmcnum, ic35path );
|
|
return ERR;
|
|
}
|
|
if ( (rval = mconnect( devname, NULL )) != OK )
|
|
return rval;
|
|
if ( (rval = mmc_status( mmcnum )) == 0 ) {
|
|
message( "MMCard%d not present", mmcnum );
|
|
rval = ERR;
|
|
} else if (rval > 0)
|
|
rval = mmcgettree( ic35path );
|
|
}
|
|
else {
|
|
if ( (rval = mconnect( devname, NULL )) != OK )
|
|
return rval;
|
|
for ( mmcnum = 1; mmcnum <= 2; ++mmcnum ) {
|
|
if ( (rval = mmc_status( mmcnum )) < 0 )
|
|
break;
|
|
if ( rval == 0 ) {
|
|
message( "MMCard%d not present", mmcnum );
|
|
continue;
|
|
}
|
|
sprintf( dirpath, "MMCard%d", mmcnum );
|
|
if ( (rval = mmcgettree( dirpath )) < 0 )
|
|
break;
|
|
}
|
|
}
|
|
mdisconnect();
|
|
return rval;
|
|
}
|
|
|
|
/* write file to IC35 MMCard
|
|
* -------------------------
|
|
* see ic35mmcget() comment about ic35path conversions and deriving
|
|
* fname from it if a_fname is NULL or empty.
|
|
*/
|
|
|
|
static int
|
|
mmcput(char * ic35path, char * a_fname )
|
|
{
|
|
int wbuffsize = 2 * 1024;
|
|
MMCFILE * mmcfp;
|
|
FILE * fp;
|
|
uchar * buff;
|
|
size_t blen;
|
|
char * fname;
|
|
int rlen;
|
|
ulong fwlen;
|
|
ulong size;
|
|
int rval = OK;
|
|
struct stat statbuf;
|
|
|
|
if ( MINBLKSZ <= blocksize && blocksize <= MAXBLKSZ )
|
|
wbuffsize = blocksize;
|
|
else if ( blocksize != 0 )
|
|
error( "bad buffer size %d, using default %d", blocksize, wbuffsize );
|
|
|
|
/* create fname from basename(ic35path) if absent */
|
|
if ( a_fname && *a_fname ) {
|
|
fname = a_fname;
|
|
} else
|
|
if ( (fname = _dupic35fname( ic35path, 0 )) == NULL ) {
|
|
error( "no memory for local filename from %s", ic35path );
|
|
return ERR;
|
|
}
|
|
|
|
/* write file to IC35 MMCard */
|
|
if ( (mmcfp = mmc_openfile( ic35path, MMCcreatrunc )) != NULL ) {
|
|
if ( (buff = malloc( blen = wbuffsize )) != NULL ) {
|
|
if ( (fp = fopen( fname, "r" )) != NULL ) {
|
|
rval = OK;
|
|
fwlen = 0;
|
|
stat(fname, &statbuf);
|
|
size = statbuf.st_size;
|
|
progress( 0, ic35path, fname, fwlen, size );
|
|
while ( (rlen = fread( buff, 1, blen, fp )) > 0 ) {
|
|
if ( mmc_writefile( mmcfp, buff, rlen ) != rlen ) {
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
fwlen += rlen;
|
|
progress( 0, ic35path, fname, fwlen, size );
|
|
}
|
|
if ( rlen <= 0 && feof( fp ) && rval == OK ) {
|
|
fprintf( stderr, "\n" );
|
|
} else { /* fread() or mmc_writefile() failed */
|
|
if ( rval != ERR )
|
|
error( "read error %s: %s (%d)",
|
|
fname, strerror(errno), errno );
|
|
rval = ERR;
|
|
}
|
|
fclose( fp );
|
|
} else /* (fp = fopen()) == NULL */
|
|
error( "cannot open %s: %s (%d)",
|
|
fname, strerror(errno), errno );
|
|
free( buff );
|
|
} else /* (buff = malloc()) == NULL */
|
|
error( "no memory for write buffer (%d)", wbuffsize );
|
|
mmc_closefile( mmcfp );
|
|
} else /* mmc_openfile() failed */
|
|
rval = ERR;
|
|
if ( fname != a_fname )
|
|
free( fname );
|
|
return rval;
|
|
}
|
|
|
|
static int
|
|
ic35mmcput( char * devname, char * ic35path, char * a_fname )
|
|
{
|
|
int mmcnum;
|
|
int rval;
|
|
|
|
/* check ic35path, check/set "MMCard" prefix and */
|
|
/* convert '/' to '\\', lowercase to uppercase */
|
|
if ( (mmcnum = _ic35mmcpath( ic35path, 0 )) < 0 ) {
|
|
_mmcpatherror( mmcnum, ic35path );
|
|
return ERR;
|
|
}
|
|
/* connect to IC35 in SyncStation via serial device */
|
|
if ( (rval = mconnect( devname, NULL )) != OK )
|
|
return rval;
|
|
/* get MMCard status (else other MMCard ops would fail) */
|
|
if ( (rval = mmc_status( mmcnum )) <= 0 ) {
|
|
if ( rval == 0 )
|
|
message( "MMCard%d not present", mmcnum );
|
|
mdisconnect();
|
|
return ERR;
|
|
}
|
|
rval = mmcput(ic35path, a_fname );
|
|
|
|
/* cleanup and disconnect from IC35 */
|
|
mdisconnect();
|
|
return rval;
|
|
}
|
|
|
|
/* put complete tree to IC35 MMCard(s)
|
|
* -------------------------------
|
|
* dirpath = subdir from which to start
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
static int
|
|
mmcputtree( char * dirpath )
|
|
{
|
|
DIR * dirp;
|
|
struct dirent * dirent;
|
|
size_t pfxlen;
|
|
char * path;
|
|
int rval, rc;
|
|
int mmcnum;
|
|
struct stat statbuf;
|
|
|
|
if ( (dirp = opendir( dirpath )) == NULL )
|
|
return ERR;
|
|
pfxlen = strlen( dirpath ) + 1;
|
|
rval = OK;
|
|
while ( (dirent = readdir( dirp )) != NULL ) {
|
|
if (strcmp(dirent->d_name, ".." ) == 0) continue;
|
|
if (strcmp(dirent->d_name, "." ) == 0) continue;
|
|
path = malloc(pfxlen + strlen(dirent->d_name) + 1 );
|
|
if (path == NULL) {
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
sprintf(path, "%s/%s", dirpath, dirent->d_name);
|
|
if (stat(path, &statbuf) == -1) {
|
|
rval = ERR;
|
|
break;
|
|
}
|
|
if (S_ISDIR(statbuf.st_mode)) {
|
|
rval = mmcputtree(path);
|
|
} else if (S_ISREG(statbuf.st_mode)) {
|
|
/* convert to IC35 name */
|
|
if ( (mmcnum = _ic35mmcpath( path, 1)) < 0 ) {
|
|
_mmcpatherror( mmcnum, path );
|
|
return ERR;
|
|
}
|
|
mmcput(path, NULL);
|
|
}
|
|
free(path);
|
|
}
|
|
rc = closedir( dirp );
|
|
if ( rval == OK ) rval = rc;
|
|
return rval; /* errcode of first failure or success OK */
|
|
}
|
|
|
|
static int
|
|
ic35mmcputtree( char * devname, char * ic35path)
|
|
{
|
|
int mmcnum;
|
|
int rval;
|
|
char * tmp;
|
|
|
|
/* check ic35path, check/set "MMCard" prefix and */
|
|
/* convert '/' to '\\', lowercase to uppercase */
|
|
tmp = strdup(ic35path);
|
|
mmcnum = _ic35mmcpath( tmp, 0 );
|
|
free(tmp);
|
|
if ( mmcnum < 0 ) {
|
|
_mmcpatherror( mmcnum, tmp );
|
|
return ERR;
|
|
}
|
|
if ( (rval = mconnect( devname, NULL )) != OK )
|
|
return rval;
|
|
if ( (rval = mmc_status( mmcnum )) == 0 ) {
|
|
message( "MMCard%d not present", mmcnum );
|
|
rval = ERR;
|
|
} else if (rval > 0)
|
|
rval = mmcputtree( ic35path );
|
|
mdisconnect();
|
|
return rval;
|
|
}
|
|
|
|
/* delete file on IC35 MMCard
|
|
* -------------------------
|
|
*/
|
|
static int
|
|
ic35mmcdel( char * devname, char * ic35path )
|
|
{
|
|
int mmcnum;
|
|
int rval;
|
|
|
|
/* check ic35path, check/set "MMCard" prefix and */
|
|
/* convert '/' to '\\', lowercase to uppercase */
|
|
if ( (mmcnum = _ic35mmcpath( ic35path, 0 )) < 0 ) {
|
|
_mmcpatherror( mmcnum, ic35path );
|
|
return ERR;
|
|
}
|
|
|
|
/* connect to IC35 in SyncStation via serial device */
|
|
if ( (rval = mconnect( devname, NULL )) != OK )
|
|
return rval;
|
|
/* get MMCard status (else delete MMCard file fails) */
|
|
if ( (rval = mmc_status( mmcnum )) > 0 ) {
|
|
message( "delete %s", ic35path );
|
|
rval = mmc_delfile( ic35path ); /* delete IC35 MMCard file */
|
|
} else if ( rval == 0 ) {
|
|
message( "MMCard%d not present", mmcnum );
|
|
rval = ERR;
|
|
}
|
|
/* disconnect from IC35 */
|
|
mdisconnect();
|
|
return rval;
|
|
}
|
|
|
|
|
|
/* -------------------- M A I N - program ----------------------------- */
|
|
static void
|
|
versinfo( void )
|
|
{
|
|
printf( "IC35manager for GNU/Linux %s (%s)\n", pkgvers, pkgdate );
|
|
printf( "%s\n", bldinfo );
|
|
printf(
|
|
"Copyright (C) 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: ic35mgr [option..] command [argument..]",
|
|
"commands and arguments are:",
|
|
" status [FILE] report IC35 status (and write to FILE)",
|
|
" backup FILE backup IC35 database to FILE",
|
|
" restore FILE restore IC35 database from FILE",
|
|
" mmcdir list contents of IC35 MMCard(s)",
|
|
" mmcdel IC35PATH delete MMCard file IC35PATH",
|
|
" mmcget IC35PATH [FILE] read FILE from IC35PATH on IC35 MMCard",
|
|
" mmcgettree [IC35PATH] read file tree starting at IC35PATH on IC35 MMCard",
|
|
" mmcput IC35PATH [FILE] write FILE to IC35PATH on IC35 MMCard",
|
|
" mmcputtree IC35PATH write file tree starting at IC35PATH to IC35 MMCard",
|
|
" IC35PATH e.g. MMCard1/ic35/app/reversi.app will be",
|
|
" converted to MMCard1\\IC35\\APP\\REVERSI.APP on IC35",
|
|
" filename FILE is taken from IC35PATH if absent",
|
|
"options in short and long form are:",
|
|
" -d DEV --device=DEV serial device with IC35 connected, default /dev/ic35",
|
|
" -b SIZE --blocksize=SIZE block size for mmcget / mmcput, default 5120 / 2048",
|
|
" -n NINC --nice=NINC lower process priority for restore,mmcput, default 2",
|
|
#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' },
|
|
{ "blocksize", required_argument, NULL, 'b' },
|
|
{ "nice", required_argument, NULL, 'n' },
|
|
#ifndef NO_LOGSIM
|
|
{ "logfile", required_argument, NULL, 'l' },
|
|
{ "simfile", required_argument, NULL, 's' },
|
|
#endif
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
char * devname = "/dev/ic35";
|
|
char * nicearg = "2";
|
|
char * command = 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:b:hV", long_opts, NULL ) ) {
|
|
default: /* invalid option */
|
|
fprintf( stderr, "use 'ic35mgr --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 'b': /* buffer size for mmcget,put */
|
|
blocksize = atoi( optarg );
|
|
continue;
|
|
case 'n': /* nice processs priority */
|
|
nicearg = 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 ) {
|
|
fatal( "missing command\n"
|
|
"use 'ic35mgr --help' for more information" );
|
|
return 1;
|
|
}
|
|
if ( atoi( nicearg ) == 0 && !isdigit(*nicearg) ) {
|
|
fatal( "non-integer nice argument: %s", nicearg );
|
|
return 1;
|
|
}
|
|
com_waitnice( atoi( nicearg ) );
|
|
|
|
/*
|
|
* initial info to logfile: date,time, version, command line
|
|
* if 'logfname' is NULL or empty, nothing will be logged.
|
|
*/
|
|
LOG_INIT(( logfname, "ic35mgr", 0 ));
|
|
LOG_PROGINFO(( "ic35mgr", pkgvers, pkgdate, bldinfo ));
|
|
LOG_ARGSINFO(( argc, argv ));
|
|
|
|
/*
|
|
* setup simulated communication if requested
|
|
*/
|
|
COM_SIMINIT(( simfname ));
|
|
|
|
/*
|
|
* process command
|
|
*/
|
|
command = argv[optind]; /* note command */
|
|
argv += optind, argc -= optind; /* skip options */
|
|
rval = 1;
|
|
if ( strcmp( command, "status" ) == 0 ) {
|
|
if ( argc <= 2 )
|
|
rval = ic35mgrstat( devname, argc > 1 ? argv[1] /*???*/
|
|
: "/tmp/ic35.stat" );
|
|
else
|
|
error( "too many arguments" );
|
|
} else if ( strcmp( command, "backup" ) == 0 ) {
|
|
if ( argc == 2 )
|
|
rval = ic35backup( devname, argv[1] );
|
|
else
|
|
error( argc < 2 ? "missing backup file"
|
|
: "too many arguments" );
|
|
} else if ( strcmp( command, "restore" ) == 0 ) {
|
|
if ( argc == 2 )
|
|
rval = ic35restore( devname, argv[1] );
|
|
else
|
|
error( argc < 2 ? "missing restore file"
|
|
: "too many arguments" );
|
|
} else if ( strcmp( command, "mmcdir" ) == 0 ) {
|
|
if ( argc == 1 )
|
|
rval = ic35mmcdir( devname ) == OK ? 0 : 1;
|
|
else
|
|
error( "too many arguments" );
|
|
} else if ( strcmp( command, "mmcdel" ) == 0 ) {
|
|
if ( argc == 2 )
|
|
rval = ic35mmcdel( devname, argv[1] ) == OK ? 0 : 1;
|
|
else
|
|
error( argc < 2 ? "missing ic35path"
|
|
: "too many arguments" );
|
|
} else if ( strcmp( command, "mmcget" ) == 0 ) {
|
|
if ( argc == 2 || argc == 3 )
|
|
rval = ic35mmcget( devname, argv[1], argc == 3 ? argv[2] : NULL );
|
|
else
|
|
error( argc < 2 ? "missing ic35path"
|
|
: "too many arguments" );
|
|
} else if ( strcmp( command, "mmcgettree" ) == 0 ) {
|
|
if ( argc == 1 || argc == 2 )
|
|
rval = ic35mmcgettree( devname, argc == 2 ? argv[1] : NULL );
|
|
else
|
|
error( "too many arguments" );
|
|
} else if ( strcmp( command, "mmcput" ) == 0 ) {
|
|
if ( argc == 2 || argc == 3 )
|
|
rval = ic35mmcput( devname, argv[1], argc == 3 ? argv[2] : NULL );
|
|
else
|
|
error( argc < 2 ? "missing ic35path,file"
|
|
: "too many arguments" );
|
|
} else if ( strcmp( command, "mmcputtree" ) == 0 ) {
|
|
if ( argc == 2 )
|
|
rval = ic35mmcputtree( devname, argv[1]);
|
|
else
|
|
error( argc < 2 ? "missing ic35path,file"
|
|
: "too many arguments" );
|
|
} else {
|
|
error( "unsupported command: %s", command );
|
|
rval = 1;
|
|
}
|
|
if ( rval != OK )
|
|
rval = 1;
|
|
|
|
LOG_CLOSE(());
|
|
return rval;
|
|
}
|