photopc.c


/*
	Copyright (c) 1997,1998 Eugene G. Crosser
	Copyright (c) 1998 Bruce D. Lightner (DOS/Windows support)

	You may do virtually what you wish with this software, as long
	as the explicit reference to its original author is retained.

	THIS SOFTWARE IS PROVIDED AS IS AND COME WITH NO WARRANTY OF ANY
	KIND, EITHER EXPRESSED OR IMPLIED.  IN NO EVENT WILL THE
	COPYRIGHT HOLDER BE LIABLE FOR ANY DAMAGES RESULTING FROM THE
	USE OF THIS SOFTWARE.
*/

#include "eph_io.h"
#include "jscan.h"
#include "revision.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#ifdef HAVE_PRIOCTL
#include <sched.h>
#endif
#ifdef UNIX
#include <sys/utsname.h>
#include <unistd.h>
#else
#include "getopt.h"
#include "strcscmp.h"
#endif

#ifdef DOS
#define MS_PROGRAM_NAMES
#pragma warn -par
#pragma warn -sus
#endif

#ifdef MSWINDOWS
#define MS_PROGRAM_NAMES
#define ERRNO GetLastError()
#else
#define ERRNO errno
#endif

#define MAXFORMAT 200

#ifdef UNIX
#define WRITEMODE "w"
#define READMODE  "r"
#else
#define WRITEMODE "wb"
#define READMODE  "rb"
#endif

#ifndef S_ISDIR
#define S_ISDIR(st_mode) ((S_IFDIR & (st_mode)) ? 1 : 0)
#endif

#ifdef UNIX
static char *device="/dev/photopc";
#else
static char *device="COM1:";
#endif
static int quiet=0;
static int notimezone=0;
static unsigned long filesize=0L;
static long frame=0L;
static char *nameformat=NULL;
static int switchoff=0;
static FILE *fp = NULL;
static char *fname = NULL;

void file_abort_cleanup(void) {
	if (fp) {
		fclose(fp);
		fp=NULL;
	}
	if (fname) {
		printf("\ndeleting file %s\n", fname);
		unlink(fname);
		free(fname);
		fname=NULL;
	}
}

int probe(eph_iob *iob) {
	long ret;

	if (eph_getint(iob,1,&ret)) return -1;
	else return 0;
}

int setclock(eph_iob *iob,int argc,char *argv[]) {
	time_t now,new;

	(void)time(&now);

	if (eph_setint(iob,2,now)) return -1;
	if (eph_getint(iob,2,&new)) return -1;
	if (now != new) {
		fprintf(stderr,"time we tried to set does not match the result\n");
		return -1;
	} else {
		return 0;
	}
}

int resolution(eph_iob *iob,int argc,char *argv[]) {
	long val=0;

	if (strcasecmp(argv[1],"Lo") == 0) {
		val=1;
	} else if (strcasecmp(argv[1],"Hi") == 0) {
		val=2;
	} else if (strcasecmp(argv[1],"Ext") == 0) {
		val=3;
	} else {
		fprintf(stderr,"bad resolution `%s'\n",argv[1]);
		return -1;
	}

	return eph_setint(iob,1,val);
}

int shutter(eph_iob *iob,int argc,char *argv[]) {
	long val=0;

	if (strcasecmp(argv[1],"Auto") == 0) {
		val=0L;
	} else if ((strlen(argv[1]) > 2) && (strncmp(argv[1],"1/",2) == 0)) {
		val=atol(argv[1]+2);
		if (val <= 0) {
			fprintf(stderr,"bad shutter speed `%s'\n",argv[1]);
			return -1;
		} else {
			val=1000000L/val;
		}
	} else {
		if ((val=atol(argv[1])) <= 0) {
			fprintf(stderr,"bad shutter speed `%s'\n",argv[1]);
			return -1;
		}
	}

	return eph_setint(iob,3,val);
}

int flash(eph_iob *iob,int argc,char *argv[]) {
	long val=0;

	if (strcasecmp(argv[1],"Auto") == 0) {
		val=0;
	} else if (strcasecmp(argv[1],"Force") == 0) {
		val=1;
	} else if (strcasecmp(argv[1],"Off") == 0) {
		val=2;
	} else if (strcasecmp(argv[1],"Antiredeye") == 0) {
		val=3;
	} else {
		fprintf(stderr,"bad flash mode `%s'\n",argv[1]);
		return -1;
	}

	return eph_setint(iob,7,val);
}

int autoshut_host(eph_iob *iob,int argc,char *argv[]) {
	long val=0;

	if ((val=atol(argv[1])) <= 0) {
		fprintf(stderr,"bad timer value `%s'\n",argv[1]);
		return -1;
	}

	return eph_setint(iob,0x17,val);
}

int autoshut_field(eph_iob *iob,int argc,char *argv[]) {
	long val=0;

	if ((val=atol(argv[1])) <= 0) {
		fprintf(stderr,"bad timer value `%s'\n",argv[1]);
		return -1;
	}

	return eph_setint(iob,0x18,val);
}

int lcd_autoshut(eph_iob *iob,int argc,char *argv[]) {
	long val=0;

	if ((val=atol(argv[1])) <= 0) {
		fprintf(stderr,"bad timer value `%s'\n",argv[1]);
		return -1;
	}

	return eph_setint(iob,38,val);
}

int lcd_brightness(eph_iob *iob,int argc,char *argv[]) {
	long val=0;

	if (((val=atol(argv[1])) <= 0) || (val > 7)) {
		fprintf(stderr,"bad brighness value `%s'\n",argv[1]);
		return -1;
	}

	return eph_setint(iob,35,val);
}

int setid(eph_iob *iob,int argc,char *argv[]) {
	return eph_setvar(iob,0x16,argv[1],strlen(argv[1]));
}

int macro(eph_iob *iob,int argc,char *argv[]) {
	long val=0;

	if (strcasecmp(argv[1],"On") == 0) {
		val=1;
	} else if (strcasecmp(argv[1],"Off") == 0) {
		val=2;
	} else {
		fprintf(stderr,"bad macro mode `%s'\n",argv[1]);
		return -1;
	}

	return eph_setint(iob,33,val);
}

int color(eph_iob *iob,int argc,char *argv[]) {
	long val=0;

	if (strcasecmp(argv[1],"On") == 0) {
		val=1;
	} else if (strcasecmp(argv[1],"Off") == 0) {
		val=2;
	} else {
		fprintf(stderr,"bad color mode `%s'\n",argv[1]);
		return -1;
	}

	return eph_setint(iob,6,val);
}

int seti(eph_iob *iob,int argc,char *argv[]) {
	long reg;
	long val;

	if ((reg=atol(argv[1])) <= 0) {
		fprintf(stderr,"bad register value \"%s\"\n",argv[1]);
		return -1;
	}
	if (strspn(argv[2],"0123456789") != strlen(argv[2])) {
		fprintf(stderr,"bad set value \"%s\"\n",argv[2]);
		return -1;
	}
	val=atol(argv[2]);
	return eph_setint(iob,reg,val);
}

int setv(eph_iob *iob,int argc,char *argv[]) {
	long reg;

	if ((reg=atol(argv[1])) <= 0) {
		fprintf(stderr,"bad register value \"%s\"\n",argv[1]);
		return -1;
	}
	return eph_setvar(iob,reg,argv[2],strlen(argv[2]));
}

int snapshot(eph_iob *iob,int argc,char *argv[]) {
	char zero=0;

	if (eph_action(iob,2,&zero,1)) return -1;
	else return 0;
}

int erase(eph_iob *iob,int argc,char *argv[]) {
	char zero=0;
	long frame;

	if ((frame=atol(argv[1])) <= 0) {
		fprintf(stderr,"bad frame number %s\n",argv[1]);
		return -1;
	}

	if (eph_setint(iob,4,frame)) return -1;
	if (eph_action(iob,7,&zero,1)) return -1;
	else return 0;
}

int protect(eph_iob *iob,int argc,char *argv[]) {
	char pmode;
	long frame;

	if (strcasecmp(argv[1],"On") == 0) {
		pmode=1;
	} else if (strcasecmp(argv[1],"Off") == 0) {
		pmode=0;
	} else {
		fprintf(stderr,"bad protect mode `%s'\n",argv[1]);
		return -1;
	}
	if ((frame=atol(argv[2])) <= 0) {
		fprintf(stderr,"bad frame number %s\n",argv[1]);
		return -1;
	}

	if (eph_setint(iob,4,frame)) return -1;
	if (eph_action(iob,9,&pmode,1)) return -1;
	else return 0;
}

int eraseall(eph_iob *iob,int argc,char *argv[]) {
	char zero=0;

	if (eph_action(iob,1,&zero,1)) return -1;
	else return 0;
}

int cmd(eph_iob *iob,int argc,char *argv[]) {
	long ccode;
	int intarg=-1;
	char chararg;

	if ((ccode=atol(argv[1])) <= 0) {
		fprintf(stderr,"bad code value \"%s\"\n",argv[1]);
		return -1;
	}
	if (strspn(argv[2],"0123456789") == strlen(argv[2]))
		intarg=atol(argv[2]);
	if ((intarg >= 0) && (intarg <= 255)) {
		chararg=intarg;
		return eph_action(iob,ccode,&chararg,1);
	} else
		return eph_action(iob,ccode,argv[2],strlen(argv[2]));
}

char *ctimetz(time_t *clock) {
	static char tmbuf[80];
	struct tm *camtm;
	char *timefmt;

	camtm=localtime(clock);
#ifdef HAVE_STRFTIME
	if (notimezone) timefmt="%a %b %d %H:%M:%S %Y";
	else timefmt="%a %b %d %H:%M:%S %Y %Z";
	(void)strftime(tmbuf,sizeof(tmbuf),timefmt,camtm);
#else
	strncpy(tmbuf,asctime(camtm),sizeof(tmbuf));
	tmbuf[sizeof(tmbuf)-1]='\0'; /* paranoia */
	tmbuf[strlen(tmbuf)-1]='\0'; /* remove newline */
#endif
	return tmbuf;
}

static char *flashval[] = {"Auto","Force","Off","AntiRedeye"};
static char *resval[] = {"-bad-","Low","High","Extended"};

int query(eph_iob *iob,int argc,char *argv[]) {
	unsigned long result;
	char *buffer;
	off_t bufsize;
	int rc;

	buffer=malloc(2048);

	if ((rc=eph_getint(iob,1,&result)) == 0)
		printf("Resolution: %lu - %s\n",(unsigned long)result,
			(result < 4)?resval[result]:"Bad value");
	else if (rc == DC1)
		printf("Resulution unavailable\n");
	else goto failure;

	if ((rc=eph_getint(iob,2,&result)) == 0) {
		if ((result == 0L) || (result == (unsigned long)-1L)) {
			printf("Camera time: not set (%ld)\n",result);
		} else {
			printf("Camera time: %s\n",ctimetz((time_t*)&result));
		}
	} else if (rc == DC1)
		printf("Camera time unavailable\n");
	else goto failure;

	if ((rc=eph_getint(iob,3,&result)) == 0)
		printf("Shutter: %lu (1/%lu)\n",(unsigned long)result,
			(unsigned long)(result?1000000/result:0L));
	else if (rc == DC1)
		printf("Shutter unavailable\n");
	else goto failure;
/*
	if ((rc=eph_getint(iob,5,&result)) == 0)
		printf("Reg 5: %lu\n",(unsigned long)result);
	else if (rc == DC1)
		printf("Reg 5 unavailable\n");
	else goto failure;
*/
	if ((rc=eph_getint(iob,6,&result)) == 0)
		printf("Color mode: %lu - %s\n",result,
		(result == 1)?"Color":(result == 2)?"B/W":"Bad value");
	else if (rc == DC1)
		printf("Color mode unavailable\n");
	else goto failure;

	if ((rc=eph_getint(iob,7,&result)) == 0)
		printf("Flash: %lu - %s\n",(unsigned long)result,
			(result < 4)?flashval[result]:"Bad value");
	else if (rc == DC1)
		printf("Flash mode unavailable\n");
	else goto failure;

	if ((rc=eph_getint(iob,10,&result)) == 0)
		printf("Frames taken: %lu\n",(unsigned long)result);
	else if (rc == DC1)
		printf("Frames taken unavailable\n");
	else goto failure;

	if ((rc=eph_getint(iob,11,&result)) == 0)
		printf("Frames left: %lu\n",(unsigned long)result);
	else if (rc == DC1)
		printf("Frames left unavailable\n");
	else goto failure;

	if ((rc=eph_getint(iob,16,&result)) == 0)
		printf("Battery: %lu%%\n",(unsigned long)result);
	else if (rc == DC1)
		printf("Battery capacity unavailable\n");
	else goto failure;

	if ((rc=eph_getint(iob,23,&result)) == 0)
		printf("Autoshut on host: %lu sec\n",(unsigned long)result);
	else if (rc == DC1)
		printf("Autoshut on host timer unavailable\n");
	else goto failure;

	if ((rc=eph_getint(iob,24,&result)) == 0)
		printf("Autoshut on field: %lu sec\n",(unsigned long)result);
	else if (rc == DC1)
		printf("Autoshut on field timer unavailable\n");
	else goto failure;

	if ((rc=eph_getint(iob,28,&result)) == 0)
		printf("Free memory: %lu bytes\n",(unsigned long)result);
	else if (rc == DC1)
		printf("Free memory size unavailable\n");
	else goto failure;

	if ((rc=eph_getint(iob,33,&result)) == 0)
		printf("Macro mode: %lu - %s\n",result,
		(result == 1)?"On":(result == 2)?"Off":"Bad value");
	else if (rc == DC1)
		printf("Macro mode unavailable\n");
	else goto failure;

	if ((rc=eph_getint(iob,35,&result)) == 0)
		printf("LCD brightness: %lu (of 7)\n",(unsigned long)result);
	else if (rc == DC1)
		printf("LCD brightness unavailable\n");
	else goto failure;

	if ((rc=eph_getint(iob,38,&result)) == 0)
		printf("LCD autoshut: %lu sec\n",(unsigned long)result);
	else if (rc == DC1)
		printf("LCD autoshut timer unavailable\n");
	else goto failure;
/*
	bufsize=2048; buffer[0]='\0';
	if ((rc=eph_getvar(iob,0x08,&buffer,&bufsize)) == 0)
		printf("Reg 8: \"%s\"\n",buffer);
	else if (rc == DC1)
		printf("Reg 8 unavailable\n");
	else goto failure;
*/
	bufsize=2048; buffer[0]='\0';
	if ((rc=eph_getvar(iob,0x16,&buffer,&bufsize)) == 0)
		printf("Camera I.D.: \"%s\"\n",buffer);
	else if (rc == DC1)
		printf("Camera I.D. unavailable\n");
	else goto failure;

	bufsize=2048; buffer[0]='\0';
	if ((rc=eph_getvar(iob,0x19,&buffer,&bufsize)) == 0)
		printf("Serial No.: \"%s\"\n",buffer);
	else if (rc == DC1)
		printf("Serial No. unavailable\n");
	else goto failure;

	bufsize=2048; buffer[0]='\0';
	if ((rc=eph_getvar(iob,0x1a,&buffer,&bufsize)) == 0)
		printf("Version: \"%s\"\n",buffer);
	else if (rc == DC1)
		printf("Version unavailable\n");
	else goto failure;

	bufsize=2048; buffer[0]='\0';
	if ((rc=eph_getvar(iob,0x1b,&buffer,&bufsize)) == 0)
		printf("Model: \"%s\"\n",buffer);
	else if (rc == DC1)
		printf("Model unavailable\n");
	else goto failure;

	free(buffer);
	return 0;

failure:
	free(buffer);
	return -1;
}

int count(eph_iob *iob,int argc,char *argv[]) {
	unsigned long result;

	if (eph_getint(iob,0x0a,&result)) {
		printf("-1\n");
		return -1;
	}
	else printf("%lu\n",result);
	return 0;
}

int geti(eph_iob *iob,int argc,char *argv[]) {
	long res;
	long reg;

	if ((reg=atol(argv[1])) <= 0) {
		fprintf(stderr,"bad register value \"%s\"\n",argv[1]);
		return -1;
	}

	if (eph_getint(iob,reg,&res)) return -1;
	printf("Reg %ld=%ld\n",reg,res);
	return 0;
}

int getv(eph_iob *iob,int argc,char *argv[]) {
	char *buffer=malloc(2048);
	off_t bufsize=2048;
	int i;
	long reg;

	if ((reg=atol(argv[1])) <= 0) {
		fprintf(stderr,"bad register value \"%s\"\n",argv[1]);
		free(buffer);
		return -1;
	}

	buffer[0]='\0';
	if (eph_getvar(iob,reg,&buffer,&bufsize)) {
		free(buffer);
		return -1;
	}
	printf("Reg %ld=\"",reg);
	for (i=0;i<bufsize;i++) {
		if ((buffer[i] >= ' ') && (buffer[i] <= 'z')) {
			printf("%c",buffer[i]);
		} else {
			printf("\\%03o",(unsigned char)buffer[i]);
		}
	}
	printf("\"\n");
	free(buffer);
	return 0;
}

int allregs(eph_iob *iob,int argc,char *argv[]) {
	char *buffer;
	off_t bufsize;
	int i,rc;
	long j,max;
	long res;

	if ((max=atol(argv[1])) <= 0) {
		fprintf(stderr,"bad max register value \"%s\"\n",argv[1]);
		return -1;
	}

	for (j=0;j<=max;j++) {
		printf("%3ld ",j);
		if ((rc=eph_getint(iob,j,&res)) == DC1) {
			printf("N/A        ");
		} else if (rc) {
			printf("FAIL       ");
		} else {
			printf("%10ld ",res);
		}
		buffer=malloc(2048);
		buffer[0]='\0';
		bufsize=2048;
		if ((rc=eph_getvar(iob,j,&buffer,&bufsize)) == DC1) {
			strcpy(buffer,"N/A");
		} else if (rc) {
			strcpy(buffer,"FAIL");
		}
		printf("\"");
		for (i=0;(i<bufsize) && (i < 8);i++) {
			if ((buffer[i] >= ' ') && (buffer[i] <= 'z')) {
				printf("%c",buffer[i]);
			} else {
				printf("\\%03o",(unsigned char)buffer[i]);
			}
		}
		if (i < bufsize) printf("..");
		printf("\"\n");
		free(buffer);
	}
	return 0;
}

int list(eph_iob *iob,int argc,char *argv[]) {
	char *buffer,*p;
	unsigned char *ress;
	off_t bufsize,ressize;
	int rc;
	long j,max;
	long res;

	if (eph_getint(iob,10,&max)) {
		return -1;
	}
	printf("total %lu\n",max);
	printf("No. P Size  R F Shuttr Date and Time\n");

	for (j=1;j<=max;j++) {
		printf("%3ld ",j);
		if ((rc=eph_setint(iob,4,j)) == DC1) {
			printf("N/A\n");
			break;
		} else if (rc) {
			printf("FAIL\n");
			break;
		}
		if ((rc=eph_getint(iob,39,&res)) == DC1) {
			printf("N ");
		} else if (rc) {
			printf("F ");
		} else {
			printf("%c ",res?'P':'-');
		}
		if ((rc=eph_getint(iob,12,&res)) == DC1) {
			printf("N/A   ");
		} else if (rc) {
			printf("FAIL  ");
		} else {
			printf("%5lu ",res);
		}
		buffer=malloc(2048);
		buffer[0]='\0';
		bufsize=2048;
		if ((rc=eph_getvar(iob,15,&buffer,&bufsize)) == DC1) {
			printf("N/A\n");
			break;
		} else if (rc) {
			printf("FAIL\n");
			break;
		}
		ress=buffer;
		ressize=bufsize;
		jscan(&ress,&ressize);
		p=jsearch("Resolution",ress,ressize);
		res=-1L;
		if (p) {
			if (sscanf(p,"%lu",&res) != 1) res=-1L;
		}
		if (res != -1L) printf("%ld ",res);
		else printf("N ");
		p=jsearch("Flash",ress,ressize);
		res=-1L;
		if (p) {
			if (sscanf(p,"%lu",&res) != 1) res=-1L;
		}
		if (res != -1L) printf("%c ",res?'F':'-');
		else printf("N ");
		p=jsearch("Shutter",ress,ressize);
		res=-1L;
		if (p) {
			if (sscanf(p,"%lu",&res) != 1) res=-1L;
		}
		if (res != -1L) printf("1/%-4ld ",res?1000000L/res:0L);
		else printf("N/A   ");
		p=jsearch("TimeDate",ress,ressize);
		res=-1L;
		if (p) {
			if (sscanf(p,"%lu",&res) != 1) res=-1L;
		}
		if (res != -1L) printf("%8s\n",ctimetz(&res));
		else printf("N/A\n");
		free(buffer);
	}
	return 0;
}

static char *shortcut[] = {
	"%m%d_%%03d.jpg",
	"%y%m%d%%02d.jpg",
	"%Y_%m_%d-%H:%M:%S.jpg",
	"%Y/%m/%d/%H:%M:%S.jpg"
};
#define MAXSHORTCUT '4'
static int ftype=0;

void makename(char *fname,int flen,char *filenm,int thumbnail,
				time_t pictime,char *nameformat) {
	int count;
	struct stat st;
	struct tm *pictm;
	int hascount=0;
	int width=0;
	int maxcount;
	char *ifslash;
	char *p;

	if ((filenm[strlen(filenm)-1] == '\\') ||
	    (filenm[strlen(filenm)-1] == '/' )) {
		ifslash="";
	} else {
		ifslash="/";
	}

	pictm=localtime(&pictime);
	if (pictime == (time_t)-1) {
		pictm->tm_mon=-1;
		pictm->tm_mday=0;
	}

	if (nameformat ) {
		if (strlen(nameformat) == 1) {
			if (*nameformat == 'd') {
				ftype=1;
				nameformat="%010lu";
			} else if (*nameformat == 'x') {
				ftype=1;
				nameformat="%08lx";
#ifdef HAVE_STRFTIME
			} else if ((*nameformat >= '1') &&
			    (*nameformat <= MAXSHORTCUT)) {
				ftype=2;
				nameformat=shortcut[(*nameformat)-'1'];
#endif
			} else {
				fprintf(stderr,
					"invalid shortcut `-f %c', using default\n",
					*nameformat);
				nameformat=NULL;
			}
		} else {
#ifdef HAVE_STRFTIME
			ftype=2;
#else
			fprintf(stderr,
				"format `-f %s' unsupported, using default\n",
				nameformat);
			nameformat=NULL;
#endif
		}
	}

	if (nameformat && (ftype == 2)) {
		/* see if there is a field for count */
		enum {deflt,escaped,pc1,pc2,digit} state=deflt;

		for (p=nameformat;*p;p++) switch (state) {
		case deflt:
			switch (*p) {
			case '\\': state=escaped; break;
			case '%': state=pc1; break;
			default: state=deflt; break;
			}
			break;
		case escaped:
			state=deflt;
			break;
		case pc1:
			switch (*p) {
			case '%': state=pc2; break;
			default: state=deflt; break;
			}
			break;
		case pc2:
			if ((*p >= '0') && (*p <= '9')) {
				state=digit;
				width*=10;
				width+=(*p-'0');
			} else
				state=deflt;
			break;
		case digit:
			if ((*p >= '0') && (*p <= '9')) {
				state=digit;
				width*=10;
				width+=(*p-'0');
			} else
				state=deflt;
			if (*p == 'd') hascount++;
			break;
		}
	}

	if (hascount > 1) {
		fprintf(stderr,"bad `-f %s' option: more than one count fields\n",
				nameformat);
		hascount=0;
		width=0;
	}

	if (hascount && ((width < 1) || (width > 8))) {
		fprintf(stderr,"bad count field width in `-f %s' option\n",
				nameformat);
		hascount=0;
		width=0;
	}

	switch (width) {
	case 1: maxcount=10; break;
	case 2: maxcount=100; break;
	default: maxcount=1000; break;
	}

	for (count=1;count<maxcount;count++) {
		if (ftype == 0) {
			sprintf(fname,"%s%s%02d%02d_%03d.jp%s",
					filenm,
					ifslash,
					pictm->tm_mon+1,
					pictm->tm_mday,
					count,
					thumbnail?"t":"g");
			if ((stat(fname,&st) < 0) && (ERRNO == ENOENT)) {
				if (!quiet) printf("file \"%s\"\n",fname);
				break;
			}
		} else if (ftype == 1) {
			char timestr[MAXFORMAT+1];
			char ext[5];

			sprintf(timestr,nameformat,pictime);
			if (count == 1) ext[0]='\0';
			else sprintf(ext,".%03d",count-1);
			sprintf(fname,"%s%s%s.jp%s%s",
					filenm,
					ifslash,
					timestr,
					thumbnail?"t":"g",
					ext);
			if ((stat(fname,&st) < 0) && (ERRNO == ENOENT)) {
				if (!quiet) printf("file \"%s\"\n",fname);
				break;
			}
#ifdef HAVE_STRFTIME
		} else if (ftype == 2) {
			char timestr[MAXFORMAT+1];
			char timestr2[MAXFORMAT+1];
			char ext[5];

			if (hascount) {
				(void)strftime(timestr2,MAXFORMAT-width,
						nameformat,pictm);
				sprintf(timestr,timestr2,count);
			} else {
				(void)strftime(timestr,MAXFORMAT,
						nameformat,pictm);
			}
			if ((hascount) || (count == 1)) ext[0]='\0';
			else sprintf(ext,".%03d",count-1);
			sprintf(fname,"%s%s%s%s",
					filenm,
					ifslash,
					timestr,
					ext);
			if ((stat(fname,&st) < 0) && (ERRNO == ENOENT)) {
				if (!quiet) printf("file \"%s\"\n",fname);
				break;
			}
#endif /* HAVE_STRFTIME */
		}
	}

	/* Just for case (we should not have overwritten the buffer) */
	fname[flen-1]='\0';

	/* OK, now time to create intermediate directories */
	for (p=fname+strlen(filenm);*p;p++) if (*p == '/') {
		*p='\0';
		(void)mkdir(fname,0777);
		*p='/';
	}
}

static void reporterror(int errcode,char *errstr) {
	if (errcode != ERR_TIMEOUT)
		fprintf(stderr,"Error %d: %s\n",errcode,errstr);
}

void running(off_t count) {
	if (!quiet && filesize)
	{
		printf("%lu: %lu of %lu\r",(unsigned long)frame,
				(unsigned long)count,(unsigned long)filesize);
		fflush(stdout);
	}
}

int storing(char *data,size_t len) {
	if (fwrite(data,len,1,fp) != 1) {
		perror(fname);
		return -1;
	}
	return 0;
}

int retrfile(eph_iob *iob,int thumbnail,int frameno,char *filenm) {
	char *buffer;
	off_t bufsize;
	long length,ilength,tlength;
	off_t got;
	time_t pictime;
	unsigned char *res;
	off_t ressize;
	char *p;
	struct stat st;

	frame=frameno;
	if (eph_setint(iob,4,frame)) return -1;
	if (eph_getint(iob,0x0d,&tlength)) return -1;
	filesize=length=tlength;
	if (!thumbnail) {
		if (eph_getint(iob,0x0c,&ilength)) return -1;
		length+=ilength;
		filesize=ilength;
	}

#ifdef LOWMEMORY
	bufsize=((tlength-1)/2048+2)*2048; /* only download thumbnail */
#else
	bufsize=((length-1)/2048+2)*2048;
#endif
	/*                       ^ we should have 1 here if we knew
		strict file size.  Although, we do not.  All we know is
		the size of image data chunk, and the size of thumbnail
		data chunk.  For now, we add extra 2048 in hope that
		extra JFIF data would not be more than that.  If it is,
		the buffer will be automatically realloc()'ed anyway.
	*/
	buffer=malloc(bufsize);
	if (buffer == NULL) {
		fprintf(stderr,"could not alloc %lu bytes\n",
				(unsigned long)bufsize);
		return -1;
	}
	got=bufsize;
#ifdef LOWMEMORY
	if (eph_getvar(iob,0x0f,&buffer,&got)) {
#else
	if (eph_getvar(iob,thumbnail?0x0f:0x0e,&buffer,&got)) {
#endif
		free(buffer);
		return -1;
	}
#ifndef LOWMEMORY
	if (!quiet) printf("\n");
#endif
	res=buffer;
	ressize=bufsize;
	jscan(&res,&ressize);
	p=jsearch("TimeDate",res,ressize);
	if (p && (*p != '-')) {
		sscanf(p,"%lu",&pictime);
	} else {
		pictime=(time_t)-1;
	}
	if (!quiet) {
		printf("taken %s\n",ctimetz(&pictime));
	}
	if (strcmp(filenm,"-") == 0) {
		fp=stdout;
	} else {
		if ((stat(filenm,&st) == 0) && (S_ISDIR(st.st_mode))) {
			int flen;

			flen=strlen(filenm)+MAXFORMAT+5;
			fname=malloc(flen);
			if (fname == NULL) {
				free(buffer);
				return -1;
			}
			makename(fname,flen,filenm,thumbnail,pictime,nameformat);
		} else {
			fname=malloc(strlen(filenm)+1);
			if (fname == NULL) {
				free(buffer);
				return -1;
			}
			strcpy(fname,filenm);
		}
		if ((fp=fopen(fname,WRITEMODE)) == NULL) {
			perror(fname);
			free(fname); fname=NULL;
			free(buffer);
			return -1;
		}
	}
#ifdef LOWMEMORY
	if (!thumbnail) {
		/* we still need to read the image itself */
               if (eph_setint(iob,4,frame)) return -1;
		filesize=ilength;
		if (eph_getvar(iob,0x0e,NULL,NULL)) {
			file_abort_cleanup();
			free(buffer);
			return -1;
		}
	} else
#endif /* LOWMEMORY */
		/* the image is already in the buffer, just write it out */
		if (fwrite(buffer,(size_t)got,1,fp) != 1) {
			perror("fname");
			file_abort_cleanup();
			free(buffer);
			return -1;
		}

	free(fname); fname=NULL;
	fclose(fp); fp=NULL;
	free(buffer);
	return 0;
}

int retrfiles(eph_iob *iob,int thumbnail,int argc,char *argv[]) {
	int i,rc;
	long bot,top;
	struct stat st;

	if (strcasecmp(argv[1],"all") == 0) {
		bot=1;
		if (eph_getint(iob,0x0a,&top)) {
			fprintf(stderr,"could not get number of frames\n");
			return -1;
		}
		if (!quiet) printf("Retreiving %ld frames\n",top);
	} else if (sscanf(argv[1],"%ld-%ld",&bot,&top) == 2) {
		/* do nothing */ ;
	} else if (sscanf(argv[1],"%ld",&bot) == 1) {
		top=bot;
	} else {
		fprintf(stderr,"bad frames interval \"%s\"\n",argv[1]);
		return -1;
	}
	if ((top != bot) && (stat(argv[2],&st) == 0) && !S_ISDIR(st.st_mode)) {
		fprintf(stderr,"target must be directory for interval\n");
		return -1;
	}
	if (bot < 1) {
		fprintf(stderr,"Bottom frame must be 1 or greater\n");
		return -1;
	}
	for (i=bot;i<=top;i++) {
		if ((rc=retrfile(iob,thumbnail,i,argv[2]))) return rc;
	}
	return 0;
}

int thumbnail(eph_iob *iob,int argc,char *argv[]) {
	return retrfiles(iob,1,argc,argv);
}

int image(eph_iob *iob,int argc,char *argv[]) {
	return retrfiles(iob,0,argc,argv);
}

#ifndef LOWMEMORY
int upload(eph_iob *iob,int argc,char *argv[]) {
	FILE *fp=NULL;
	char *buffer=NULL;
	struct stat stbuf;
	unsigned long res;
	char zero=0;
	int rc=-1;

	if (stat(argv[1],&stbuf)) {
		fprintf(stderr,"upload cannot stat file \"%s\": %s\n",
				argv[1],strerror(errno));
		goto failure;
	}
	if ((fp=fopen(argv[1],READMODE)) == NULL) {
		fprintf(stderr,"upload cannot open file \"%s\": %s\n",
				argv[1],strerror(errno));
		goto failure;
	}
	if (stbuf.st_size == (size_t)0) {
		fprintf(stderr,"upload file \"%s\" has zero length\n",
				argv[1]);
		goto failure;
	}
	buffer=(char*)malloc(stbuf.st_size);
	if (buffer == NULL) {
		fprintf(stderr,"upload cannot allocate %lu bytes: %s\n",
				(unsigned long)stbuf.st_size,strerror(errno));
		goto failure;
	}
	if (fread(buffer,stbuf.st_size,1,fp) != 1) {
		fprintf(stderr,"upload cannot read from %s: %s\n",
				argv[1],strerror(errno));
		goto failure;
	}

	if (eph_getint(iob,28,&res)) {
		fprintf(stderr,"upload cannot get free memory\n");
		goto failure;
	}

	if (res < stbuf.st_size) {
		fprintf(stderr,"upload free memory %lu less than file size%lu\n",
				res,(unsigned long)stbuf.st_size);
		goto failure;
	}

	if (eph_setint(iob,32,0x0FEC000E)) {
		fprintf(stderr,"upload cannot cast magic spell\n");
		goto failure;
	}

	frame=0;
	filesize=stbuf.st_size;

	if (eph_setvar(iob,29,buffer,stbuf.st_size)) {
		if (!quiet) printf("\n");
		fprintf(stderr,"upload cannot write image\n");
		goto failure;
	}
	if (!quiet) printf("\n");

	if (eph_action(iob,11,&zero,1)) {
		fprintf(stderr,"upload cannot store image\n");
		goto failure;
	}

	rc=0;

failure:
	if (buffer) free(buffer);
	if (fp) fclose(fp);
	return rc;
}
#endif /* LOWMEMORY */

int off(eph_iob *iob,int argc,char *argv[]) {
	switchoff=1;
	return 0;
}

/* -------------------------------------------------------------------- */

struct _cmdlist {
	char *cmd;
	int argc;
	int (*executor)(eph_iob *iob,int argc, char *argv[]);
	char *help;
} cmdlist[] = {
	{"",		0,	NULL,		"== Query parameters =="	},
	{"query",	0,	query,		""			},
	{"count",	0,	count,		""			},
	{"list",	0,	list,		""			},
	{"geti",	1,	geti,		"-<reg-no>"		},
	{"getv",	1,	getv,		"-<reg-no>"		},
	{"allregs",	1,	allregs,	"-<max-reg-no>"			},
	{"",		0,	NULL,		"== Do some actions =="	},
	{"erase",	1,	erase,		"<frame-no>"		},
	{"protect",	2,	protect,	"`On' | `Off' <frame-no>"	},
	{"snapshot",	0,	snapshot,	""			},
	{"eraseall",	0,	eraseall,	""			},
	{"",		0,	NULL,		"== Retreive files =="	},
	{"image",	2,	image,		"`All' | <frame-lo>[-<frame-hi>] <filename>"	},
	{"thumbnail",	2,	thumbnail,	"`All' | <frame-lo>[-<frame-hi>] <filename>"	},
#ifndef LOWMEMORY
	{"",		0,	NULL,		"== Upload files =="	},
	{"upload",	1,	upload,		"<filename>"		},
#endif
	{"",		0,	NULL,		"== Set parameters =="			},
	{"resolution",	1,	resolution,	"`Hi' | `Lo' | `Ext'"		},
	{"clock",	0,	setclock,	""			},
	{"shutter",	1,	shutter,	"`Auto' | <microseconds> | 1/<fraction-of-second>"			},
	{"flash",	1,	flash,		"`Auto' | `Force' | `Off' | `AntiRedeye'"},
	{"id",		1,	setid,		"<string>"		},
	{"autoshut-host",1,	autoshut_host,	"<seconds>"		},
	{"autoshut-field",1,	autoshut_field,	"<seconds>"		},
	{"lcd-autoshut",1,	lcd_autoshut,	"<seconds>"		},
	{"lcd-brightness",1,	lcd_brightness,	"1 to 7"		},
	{"macro",	1,	macro,		"`On' | `Off'"		},
	{"color",	1,	color,		"`On' | `Off'"		},
	{"seti",	2,	seti,		"-<reg-no> <value-to-set>"	},
	{"setv",	2,	setv,		"-<reg-no> <value-to-set>"	},
	{"cmd",		2,	cmd,		"-<cmd-code> <arg>"	},
	{"off",		0,	off,		""			},
	{"",		0,	NULL,		""			},
	{NULL,		0,	NULL,		NULL			}
};

/* -------------------------------------------------------------------- */

void showhelp(char *name) {
	int i;
	printf("usage: %s [-h] [-V] [-v[v]] [-q] [-z] [-s speed] [-l device] [command [params]] ...\n\n",name);
	printf("Options:\n\n");
	printf("\t-h\t- show this help screen\n");
	printf("\t-V\t- show software version number\n");
	printf("\t-v\t- increase debugging verbosity\n");
	printf("\t-q\t- do not show running download indicator\n");
	printf("\t-z\t- suppress timezone conversions (camera's clock is local time)\n");
	printf("\t-f fmt\t- create file names using strftime(3) with the time of snapshot\n");
	printf("\t\t\t fmt = 'd' - name is seconds since the epoch in decimal\n");
	printf("\t\t\t fmt = 'x' - name is seconds since the epoch in hex\n");
#ifdef HAVE_STRFTIME
	printf("\t\t\t fmt = '1' - name is MMDD_CCC (default)\n");
	printf("\t\t\t fmt = '2' - name is YYMMDDCC\n");
	printf("\t\t\t fmt = '3' - name is YYYY_MM_DD-HH:MM:SS\n");
	printf("\t\t\t otherwise make file names using strftime(3),\n");
	printf("\t\t\t if fmt contains `%%%%NNNd' then place count there\n");
#endif
	printf("\t-s baud\t- set port speed to 9600,19200,38400,57600 or 115200\n");
	printf("\t\t\tdefault is %d\n",MAX_SPEED);
	printf("\t-l dev\t- use device name instead of default %s\n\n",device);
	printf("Commands:\n");
	for (i=0;cmdlist[i].cmd;i++) {
		if (cmdlist[i].help[0] == '-') continue;
		printf("%-20.20s%s\n",cmdlist[i].cmd,cmdlist[i].help);
	}
	printf("EXAMPLE:\n%s id \"Eugene Crosser www.average.org\" query\n",name);
}

void showversion(char *name) {
#ifdef UNIX
#define OS uts.sysname
	struct utsname uts;

	if (uname(&uts)) {
		strncpy(uts.sysname,"Unix",sizeof(uts.sysname));
		uts.sysname[sizeof(uts.sysname)-1]='\0';
	}
#endif

#ifdef DOS
#define OS "MS-DOS"
#endif

#ifdef MSWINDOWS
#define OS "Windows 95/NT"
#endif

#ifndef OS
#define OS "Unknown OS"
#endif

	printf("%s version %s (%s) build %s\n",name,VERSION,OS,__DATE__);
	printf("     Copyright (c) 1998 Eugene Crosser and Bruce Lightner\n");
	printf("     URL: http://www.average.org/digicam/\n");
}

int main(int argc,char *argv[]) {
	int c,i,rc=0;
	char *cmd;
	int debug=0;
	long speed=0;
	eph_iob *iob;
	char *program = argv[0];
#ifdef MS_PROGRAM_NAMES
	char *q;

	/* remove program extension */
	if ((q = strrchr(program, '.')) != NULL)
		*q = '\0';
	/* remove directory */
	if ((q = strrchr(program, '/')) != NULL)
		program = q + 1;
	if ((q = strrchr(program, '\\')) != NULL)
		program = q + 1;
#endif

#ifdef HAVE_PRIOCTL
	if (geteuid() == 0) {
		/* Try to set realtime priority */
		struct sched_param sp;
		int rc,minp,maxp;

		minp=sched_get_priority_min(SCHED_FIFO);
		maxp=sched_get_priority_max(SCHED_FIFO);
		sp.sched_priority=minp+(maxp-minp)/2;
		if ((rc=sched_setscheduler(0,SCHED_FIFO,&sp)) == -1)
			fprintf(stderr,"failed to set realtime priority: %s\n",
				strerror(errno));
#if 0
		if ((rc=sched_getscheduler(0)) == -1)
			fprintf(stderr,"getscheduler: %s\n",strerror(errno));
		else
			if (sched_getparam(0,&sp) == -1)
				fprintf(stderr,"sched_getparm: %s\n",
					strerror(errno));
			else
				printf("New scheduling policy: %d, prio %d\n",
					rc,sp.sched_priority);
#endif
		/* Drop supervisor privelege */
		(void)seteuid(getuid());
	}
#endif /* HAVE_PRIOCTL */

	while ((c=getopt(argc,argv,"l:s:f:vqhVz")) != EOF)
	switch (c) {
	case 'l':
		device=optarg;
		break;
	case 's':
		speed=atol(optarg);
		break;
	case 'f':
		nameformat=optarg;
		break;
	case 'v':
		debug++;
		break;
	case 'q':
		quiet=1;
		break;
	case 'h':
		showhelp(program);
		return 0;
	case 'V':
		showversion(program);
		return 0;
	case 'z':
		notimezone=1;
		break;
	default:
		showhelp(program);
		return 1;
	}

#ifdef DOS
	/* MS/DOS has silly assumptions about the timezone if TZ variable
	   is not set.  Hope the following will help in most cases, the
	   results should be at least predictable... */
	if (getenv("TZ") == NULL) notimezone = 1;
#endif
	if (notimezone) {
		if (putenv("TZ=GMT0")) {
			perror("error setting TZ environment variable");
		}
	}
	tzset();

#ifdef LOWMEMORY
	if (atexit(file_abort_cleanup) == -1)
		perror("error setting atexit function");
#endif

#ifdef UNIX
	if (*device != '/') {
		char *p;
		p=device;
		device=malloc(strlen("/dev/")+strlen(p)+1);
		strcpy(device,"/dev/");
		strcat(device,p);
	}
#endif

	iob=eph_new(reporterror,NULL,running,storing,debug);
	if (!iob) {
		fprintf(stderr,"eph_new failed\n");
		return 1;
	}
	if (eph_open(iob,device,speed)) {
		fprintf(stderr,"eph_open failed\n");
		return 1;
	}

	if ((rc=probe(iob))) {
		fprintf(stderr,"probe failed\n");
		goto exit;
	}

	while ((cmd=argv[optind])) {
		if (switchoff) {
			fprintf(stderr,"commands after \"off\" ignored\n");
			goto exit;
		}
		for (i=0;
		     cmdlist[i].cmd&&strcmp(cmd,cmdlist[i].cmd);
		     i++) /* nothing */ ;
		if (cmdlist[i].cmd == NULL) {
			fprintf(stderr,"bad command \"%s\" ignored,",cmd);
			fprintf(stderr," run \"%s -h\" for help\n",program);
			optind++;
			continue;
		}
		if ((optind + cmdlist[i].argc) >= argc) {
			fprintf(stderr,"too few arguments for \"%s\" command\n",cmd);
			rc=1;
			goto exit;
		}
		if ((rc=(cmdlist[i].executor)(iob,cmdlist[i].argc+1,
							argv+optind))) {
			fprintf(stderr,"command \"%s\" failed, abort\n",cmd);
			goto exit;
		}
		optind+=cmdlist[i].argc;
		optind++;
	}

exit:
/*
	On older models, action 04 with zero paramater terminates
	session but leaves the camera ON.  On Olympus 600 (at least)
	this command not only terminates session but also turns the
	camera off.  So, the "off" command will turn off newer models
	and *just* terminate session more quickly on older.  You may
	want to terminate the command array by "off" on models like
	PhotoPC 600 to decrease power consumption when the session is
	over.
*/
	eph_close(iob,switchoff);
	eph_free(iob);
	return rc;
}

Back