/* (C) Tim Graves 20th April 1994
   This code is supplied AS IS. no warrantee either expressed or implied 
   is provided. This code may be freeley modified and modified as long as my
   origional authorship is acknowledged. 
   
   Tim Graves
    Sun Microsystems
     
     */
/* a set of routines for transfering data between the Psion and the Sun machines
   where possible I have provided a sensible wrapper for the low level function
   This program should be started BEFORE the suncom program on the psion */
#include <stdio.h>
#include "psion.h"
#include "psmain.h"
#include "psfilt.h"
#define MAXSEND 200
/*#define DEBUG 1*/

static char * vsn = "@(#) psmain.c 2.27@(#)" ;
long pscs(), sunsum() ;

int cmlist(), cmspeed(), cmmkdir(), cmtmode(), cmget(), cmput(), cmsetp(), cmdiscon() ;
int cmhelp(), cmecho(), cmignore(), cmsystem(), cmrun() ;
int cmgetenv(), cmdumpv(), cmsetv(), cmunsetv(), cmifusetv();
int cmrm(), cmcs() ;
int cmforeach(), cmfilter(),cmfilterprint() ;
int cmexclude(), cmexclprint(), cmrlist(), cmbackup(), cminfo(), cmsetwriteflag() ;
char * findvar() ;
int running ;
int level ;
int linecmd ;
int dovsnchk ;
int debugargs = FALSE ;
int debugrun = FALSE ;
int runtype, finish ;
int debugcall = FALSE ;
extern int debuglink ;
extern int debugpath ;
extern int debugbackup ;
extern char psfname[] ;

/* command parsing structures, the command name (primary key), the number
   of arguments (secondary key) and the function to call which will be
   passed two parameters, an int (argc) and the value array (char * argv[]), then the usage text followed by the help text */
struct cmdstruct cmds[] = {
	{"get", 2, 1, cmget, "path [sun filename]",
	   "gets a file from the psion to the sun, optionaly using a different filename\n filename only can contain wildcards in which case multiple files are got,\nno sun filename is applicable in this case"},
	{"put", 2, 1, cmput, "path [sun filename]",
	   "Puts a file to the psion from the sun, optionaly using a different filename"},
	{"sum", 2, 1, cmcs, "path [sun|psion]",
	   "Computes a checksum on the path given either on the sun or the psion (default)\n"} ,
	{"foreach", MAXARGS-3, 2, cmforeach, "list_filename cmd [args]", 
	   "Opens the list_filename which contains a list of psion file names IN PSION\nPATHS FORMAT and executes cmd with its args followed by the filenames"},
	{"list", 2, 1, cmlist, "path [sun filename]",
	   "Lists a path on the psion optionaly storing the information in a file"},
	{"rlist", 2, 1, cmrlist, "path [sun filename]",
	   "Produces a listing of path and all files below it, optionaly storing the information in sun filename"},
	{"info", 1 ,1, cminfo, "path",
	   "Provides info on the file size, its access modes and its type"},
	{"setro", 1, 1, cmsetwriteflag, "path",
	   "Sets the path to be read only"},
	{"setrw", 1, 1, cmsetwriteflag, "path",
	   "Sets the path to be read write"},
	{"mkdir", 1, 1, cmmkdir, "path",
	    "Creates a directory on the psion"} ,
	{"rm", 1, 1, cmrm, "path",
	    "Removes the path from the psion"},
	{"fast", 0, 0, cmspeed, "",
	    "Turns the data screen of on the psion, this will slightly speed things up"},
	{"display", 0, 0, cmspeed, "",
	    "Turns the data screen on on the psion, this may slow things down"},
	{"run", MAXARGS, 1, cmrun, "pscmd_filename [args]",
	    "Reads the command file pscmd and executes the instructions in it, a new level of\nvariables is created and variables manipulated in side this new level will not affect\nvariables from lower levels (Like to the shells). Any arguments to run will be saved in a\nsequence of variables arg0 (the command name) arg1 (first arg) arg2 (second arg) etc. Any \nexisting variables will be copied to the new level"},
	{"source", 1, 1, cmrun, "pscmd_filename",
	    "Reads the command file and executes the instructions in it. No new variable level\nis created and any variable manipulation affects the existing level. No arg variables\n are created. It is expected that source will be used to load in configurations"},
	{"which", 1, 1, cmrun, "pscmd_filename",
	    "Similar to the csh which command, locates the first occurence of\n pscmd_filename in the current path"},
	{"!", MAXARGS, 0, cmsystem, "unix command string",
	    "Executes the unix command string via the system call, only variable interpretation is\nperformed before the command is executed"},
	{"#", MAXARGS, 0, cmignore, "text",
	    "Ignores the remainder of the line, a comment indicator, intended to be used in \npscmd files"},
	{"REM", MAXARGS, 0, cmignore, "text",
	    "Ignores the remainder of the line, a comment indicator, intended to be used in \npscmd files"},
	{"Rem", MAXARGS, 0, cmignore, "text",
	    "Ignores the remainder of the line, a comment indicator, intended to be used in \npscmd files"} ,
	{"echo", MAXARGS, 0, cmecho, "text",
	    "echoes the text back, intended to be used in pscmd files"},
	{"setv", 2, 1, cmsetv, "varname [varvalue]",
	    "Associates varvalue with varname in the variables list, replacing any previous value\nif required. If varvalue is not present the null string is used"},
	{"unsetv", 1, 1, cmunsetv, "varname",
	    "Removes the varname from the variables list"}, 
	{"getenv", 3, 1, cmgetenv, "unixname psionvarname defaultvalue", 
	    "Gets the unix variable unixname from the unix environment and\noptionaly calls it a different name psionname if the variable does not\nexist use defaultvalue"},
	{"printv", 0, 0, cmdumpv, "",
	    "Prints out the current variables list"},
	{"ifusetv", 2, 2, cmifusetv, "varname value",
	    "If varname is already set do nothing, otherwise set varname to value"} ,
	{"vars", 0, 1, cmdumpv, "Not a command, see help",
	   "Variables are set using setv and printv, to use a variable it must be a word\non its own enclosed in square brackets e.g. [varname], the word will be\nsubstituted for the text of the variable, e.g. echo [arg0] will print out\nthe commands name if envoked by run."},
	{"specialvars",0,0,cmdumpv,"Not a command, se help",
	    "Special Variables\npath\tA : seperated list of locations run will use to find its pscmd files\nrmcheck\tChecks with you that you realy want to delete the file (like rm -i)\nbackupnocheck\tStops the system asking you to exit all psion applications before\trunning the backup (usefull for scripts)\nautofilefilter\tCauses all file transfers to be checked to see if there is a conversion\n\tfilter to be run during the transfer\ndebugargs\tIf set the system will print debugging info for argument parsing\ndebuglink\tIf set the system will print debugging info for the data link\ndebugpath\tIf set the system will print out path handling debuging info\ndebugrun\tIf set the system will print debugging info about run\ndebugcall (level) If set the system will printout (very verbose) subroutine call debuging info\ndebugbackup\tIf set the system will print backup debugging calls\nverbose\tIf set the system will display transfer statistics\ndolower\tIf set maps all psion paths for get and put to lower case"},
	{"help", 1, 0, cmhelp, "[cmdname]",
	    "Prints out the help message for cmdname or the list of available commands if there is\nno cmdname"},
	{"usage", 1, 0, cmhelp, "[cmdname]",
	    "Prints out the usage text for cmdname or the list of available commands if there is\nno cmdname"}, 
	{"addfilter",3, 3, cmfilter,"direction (in/out/restore) filesuffix, pscmd",
	    "Sets up a filter in the given directionto run pscmd on all files with\nsuffix suffix the pscmd file should exist before the filter is called\nrestore filters are called to restore the filt to its state before a put"},
	{"delfilter",2, 2, cmfilter,"direction (in/out/restore) filesuffix, pscmd",
	    "Removes a filter with suffix and pscmd in direction"},
	{"printfilters",0,0,cmfilterprint, "",
	    "Prints the list of current filters"},
	{"addexcl", 1,1,cmexclude,"path",
	    "Adds path to the exclude list for backups\n"} ,
	{"delexcl", 1,1,cmexclude,"path",
	    "Removes path from the backup exclude list\n"},
	{"printexcl",0,0,cmexclprint, "",
	    "Prints the current list of files excluded from the backup\n"},
	{"backup",2,2,cmbackup,"path backuppath",
	    "Recursivley copies from path on the psion to backup path\nomiting excluded files (see addexcl, delexcl, printexcl"},
	{"ibackup",2,2,cmbackup, "path backuppath",
	    "Same as backup but this function only extracts modified files"},
	{"pause", 0, 0, cmdiscon, "",
	    "Exit the sun end of the comms link without effecting the psion\nNOTE you should only use this in shell scripts as the psion will detect\na period of inactivity and powerdown, currently the software cannot cope with this !"},
	{"quit", 0, 0, cmdiscon, "",
	    "Instruct the psion end of the link to exit then exit the sun end"}
	    } ;

main (argc, argv, environ)
int argc ;
char * argv[] ;
char * environ[] ;
{

   int restart ;
   int talk ;
   char devname[100] ;

   if (debugcall >= 1)
	printf("CALL: Main\n") ;
   /* init any argv dependent variables to their default state */
   restart = FALSE ;
   talk = FALSE ;
   devname[0] ='\0' ;
   dovsnchk = TRUE ;
   finish = QUIT ;
   runtype = FALSE ;
   /* init the variable handler code */
   initvars() ;


   /* handle any command line switches */
   /* we must have at least one value in argv and it must start with
      a - to have any command line switches NOTE due to this restriction
      no psion command interpreter command may start with - though they
      may also have flags as the routine below will break on the first
      arg it finds that does not start with a - */

   /* remove the command name */
   argv ++ ;
   argc -- ;
   while (argc > 0)
   {
   /* break out of the loop if required */
	if(argv[0][0] != '-')
		break ;
	/* -var variable, value set up the variables */
	if (strcmp (argv[0], "-var") == 0 )
	{
		/* use the next argument as the variable and the following
		   one as the value unless the next argument starts with a
		    - in which case just set the variable*/
		if (argc < 2)
		{
			/* no variable name */
			useage() ;
		}
		if (argc == 2)
		{
			/* variable name but no value and at end of the args */
			cmsetv(2,argv) ;
			argc -= 2;
			argv += 2;
			continue ;
		}
		if (argv[2][0] == '-')
		{
			cmsetv(2,argv) ;
			argc -= 2 ;
			argv += 2 ;
			continue ;
		}
		else
		{
			cmsetv (3,argv) ;
			argc -= 3 ;
			argv -= 3;
			continue ;
		}		
	}
   	/* -r initiate the coms link with a running suncom program 
      	on the psion, we do this by calling psinit with a true falue,
      	if we want a cold start call psinit with a FALSE parameter */
	if (strcmp(argv[0], "-r") == 0)
	{
		restart = TRUE ;
		argc -- ;
		argv ++ ;
		continue ;
	}
	/* -t - give cmdline parameters dump */
	if (strcmp(argv[0], "-t") == 0)
	{
		talk = TRUE ;
		argc -- ;
		argv ++ ;
		continue ;
	}
	/* -q on exiting (has effect in batch only) quit (default) */
	if (strcmp(argv[0], "-q") == 0)
	{
		finish = QUIT ;
		argc -- ;
		argv ++ ;
		continue ;
	}
	/* -p on exiting (has effect in batch only) pause instead of quit */
	if (strcmp(argv[0], "-p") == 0)
	{
		finish = PAUSE ;
		argc -- ;
		argv ++ ;
		continue ;
	}
	/* -b batch mode, dont be verbose etc */
	if (strcmp(argv[0], "-b") == 0)
	{
		runtype = BATCH ;
		argc -- ;
		argv ++ ;
		continue ;
	}
	/* -i interactive mode, oposite of batch mode assumed if no command line commands given */
	if (strcmp(argv[0], "-i") == 0)
	{
		runtype = INTERACTIVE ;
		argc -- ;
		argv ++ ;
		continue ;
	}
	/* -d next arg contains the name of the serial device, default to /dev/ttya*/
	if (strcmp(argv[0], "-d") == 0)
	{
		if (argc < 2)
			useage() ;
		strcpy(devname, argv[1]) ;
		/* shift along the arguments */
		argc -= 2 ;
		argv += 2 ;
		continue ;
	}
	if (strcmp(argv[0], "-nv") == 0)
	{
		dovsnchk = FALSE ;
		argc -- ;
		argv ++ ;
		continue ;
	}
	if (strcmp(argv[0], "-v") == 0)
	{
		dovsnchk = TRUE ;
		argc -- ;
		argv ++ ;
		continue ;
	}
   }

   /* if there are any args left (argc > 0) we are running batch else interactive, only work this out is we have not had a runtype previously set */
   if (runtype == FALSE) ;
	if (argc > 0)
		runtype = BATCH ;
	else
		runtype = INTERACTIVE ;

   psinit(restart, devname) ;
   initfilters() ;
   initexclude() ;
   
   /* we have to load this variable first otherwise the system wont find the
      ~/.psrc.pscmd file */
   loadvar("HOME", "HOME", environ) ;

   if (dovsnchk == FALSE)
   {
	pssetdefvsn() ;
   }
   if (runtype = INTERACTIVE)
   	printf("Suncom program version %s, psion program version, 2.27,\n (C) Tim Graves 1994\n", psversion()) ;
   /* check that the psion code is atleast version 2 otherwise the system
      will fail as the path specifications will differ and the tranfer of
      any path information (required for virtualy anything) will fail */
   if (psvsnlt(SUNCOM_VERSION_REQUIRED))
   {
   	versiontolow("Psion program", SUNCOM_VERSION_REQUIRED, TRUE) ;
   }

   runpscom(argc, argv) ;
}

useage()
{
	if (debugcall >= 1)
		printf("CALL: usage\n") ;
	fprintf(stderr, "Useage: psion [-v|-nv] [-i|-b] [-p|-q] [-r] [-d devname] [cmds] [-var varable_name variable_value]\n") ;
	exit() ;
}

dodebugargs(argc, argv)
int argc ;
char * argv[] ;
{
	int i ;
	if (debugcall < 2)
		return ;
	printf("argc = (%d) ", argc) ;
	for (i = 0 ; i < argc ; i ++)
		printf("argv[%d] = (%s) ", i, argv[i]) ;
	printf("\n") ;
}

runpscom(argc, argv)
int argc ;
char * argv[] ;
{
   if (debugcall >= 1)
   {
	printf("CALL: Runpscom\n") ;
	dodebugargs(argc, argv) ;
   }

   /* run a rc file */
   runrc() ;
   /* if there are args, use them otherwise call the command interpreter */
   if (argc == 0)
   {
	/* at least initialy we are using the cmd input */
	linecmd = TRUE ;
	cmdintrprt() ;
   }
   else
   {
	/* we are definatley not using the command line input */
	linecmd = FALSE ;
   	/* handle the command line we do this by passing the argc and argv
	   values direct to the command interpreter which execuret them once */
	cmdrun(argc, argv) ;
	/* we are running in a batch type mode here, determine whether to quit of pause */
	if (finish == QUIT)
		cmdintr("quit") ;
	else
		cmdintr("pause") ;
   }
   psshutdown(0) ;
}
int cmdintrprt()
{
	char cmdstring [300] ;

	if (debugcall >= 1)
		printf("CALL: cmdintrprt\n") ;
	/* read the cmdstring and untill it returns false execute it */
	running = TRUE ;
	do
	{
		if (linecmd)
			printf("%d, CMD> ", level) ;
#ifdef DEBUG
		printf("getting input\n") ;
#endif
		fgets(cmdstring,sizeof(cmdstring),stdin) ;
#ifdef DEBUG
		printf("Got input (%s)\n",cmdstring) ;
#endif
		cmdintr(cmdstring) ;
	}
	while (running) ;
	return (TRUE) ;
}

int cmdintr(str)
char * str ;
{
	char args[MAXARGS] [255] ;
	char *argv[MAXARGS] ;
	int res ;
	int ctr ;
	int lenstr ;
	int argln ;
	int argc ;
	int white ;

	if (debugcall >= 1)
	{
		printf("CALL: cmdintr(%s)\n", str) ;
	}
	/* remove any whitespace at the end of the line */
	ctr = strlen(str) -1 ;
	while((ctr > 0) && isspace(str[ctr]))
	{
		str[ctr] = '\0' ;
		ctr -- ;
	}

	/* debug if required */
	if (debugargs)
		printf("Command line = :%s:\n", str) ;
	/* up the level if required */
	level ++ ;
	/* parse the string into its arguments */
	white = FALSE ;
	argc = 0 ;
	argln = 0 ;
	lenstr = strlen(str) ;
	for (ctr = 0 ; ctr <= lenstr ; ctr ++ )
	{
		/* if whitespace next argument */
		if (isspace(str[ctr]))
		{
			if (white == FALSE)
			{
				/* terminate the line */
				args[argc][argln] = '\0' ;
				/* copy the pointer across to argv */ ;
				argv[argc] = args[argc] ;
				argc ++ ;
				argln = 0 ;
				white = TRUE ;
				if (argc == MAXARGS)
					return(BAD) ;
			}
			continue ;
		}
		args[argc][argln] = str[ctr] ;
		argln ++ ;
		white = FALSE ;
	}
	/* terminate the last command */
	args[argc][argln] = '\0' ;
	argv[argc] = args[argc] ;
	argc ++ ;
	/* scan the args list, if the first character of any arg is [
	   treat the arg as a variable, lop of the end of it (assumed to be ])
	   and find its value, if there is no equivalent, complain and return*/
	for(ctr = 0 ; ctr < argc ; ctr ++)
	{
		if (args[ctr][0] == '[' )
		{
			args[ctr][strlen(args[ctr])-1] = '\0' ;
			argv[ctr] = findvar(args[ctr] +1) ;
			if (argv[ctr] == NULL)
			{
				printf("No Such Variable %s\n", args[ctr]+1);
				level -- ;
				return(OK) ;
			}
		}
	}

	/* unless the line was zero length OR a comment 
	   execute the command */
	/* store the resule */
	res = cmdrun(argc, argv) ;
	/* now reduce the level */
	level -- ;

	return(res);
}

int cmdrun (argc, argv)
int argc ;
char  *argv[] ;
{
	int i ;
	int cmdmax ;

	if (debugcall >= 1)
	{
		printf("CALL: cmdrun\n") ;
		dodebugargs(argc, argv) ;
	}

	cmdmax = sizeof(cmds) / sizeof(cmds[1]) ;

	if (debugargs)
	{
	/* print the arguments */
		printf("argc %d\n", argc) ;
		for (i = 0 ; i < argc ; i ++ )
	   		printf("argv[%d] = %s\n", i, argv[i]) ;
	}

	/* run down the parameters list to find a command that matches the 
	   string and arguments, then call the appropriate function */
	for (i = 0 ; i < cmdmax ; i ++ )
	{
		/* if the command is the same */
		if (strcmp(argv[0], cmds[i].cmdname) == 0) 
		{
			/* if the args are within the permited range */
			if (((argc -1) >= cmds[i].minargs) && ((argc -1) <= cmds[i].maxargs))
			{
				/* execute the command pass the argc and argv */
				return(cmds[i].cmdfn(argc, argv));
			}
		}
	}
	if (i == cmdmax)
		printf("Error Cmd not known\n") ;
}

cmtmode(argc, argv)
int argc ;
char *argv [];
{
	if (debugcall >= 1)
	{
		printf("CALL: cmtmode\n") ;
		dodebugargs(argc, argv) ;
	}
	/* set the app-ropriate transfer mode */
	if (strcmp(argv[0], "ascii") == 0)
		printf("ASCII mode is nolonger supported\n") ;
	else
		psbinary() ;
	return(OK) ;
}

cmlist(argc, argv)
int argc ;
char * argv[] ;
{
	FILE *fd ;

	if (debugcall >= 1)
	{
		printf("CALL: cmlist\n") ;
		dodebugargs(argc, argv) ;
	}
	fd = NULL ;
	if (argc == 3) /* send output to a file */
		if ((fd = fopen(argv[2], "w")) == NULL)
		{
			printf("%s, Bad file %s\n", argv[0], argv[2]) ;
			return(BAD) ;
		}
	pssetpath(argv[1]) ;
	pslp(fd) ;
	if (argc == 3)
		fclose(fd) ;
	return(OK) ;
}
cmrm(argc, argv)
int argc ;
char * argv[] ;
{
	char ch[10];
	if (debugcall >= 1)
	{
		printf("CALL: cmrm\n") ;
		dodebugargs(argc, argv) ;
	}
	/* if the VAR_RMCHECK variable is set ensure that this is what the user 
	   realy wants to do */
	if (findvar(VAR_RMCHECK) != NULL)
	{
		printf("rm %s (y/n)\n", argv[1]) ;
		fgets(ch, sizeof(ch),stdin);
		if ((ch[0] == 'y') || (ch[0] == 'Y'))
		{
			pssetpath(argv[1]) ;
			psrp() ;
		}
	}
	else
	{
		pssetpath(argv[1]) ;
		psrp() ;
	}
}

cmmkdir(argc, argv)
int argc ;
char * argv[] ;
{
	if (debugcall >= 1)
	{
		printf("CALL: cmmkdir\n") ;
		dodebugargs(argc, argv) ;
	}
	if (argc-1 == 1)
	{
		printf("Creating directory %s\n", argv[1]) ;
		pssetpath(argv[1]) ;
		psmd() ;
	}
	return(OK) ;
}

cmecho(argc, argv)
int argc ;
char * argv[] ;
{
	if (debugcall >= 1)
	{
		printf("CALL: cmecho\n") ;
		dodebugargs(argc, argv) ;
	}
	while(--argc > 0)
		printf((argc > 1) ? "%s " : "%s\n", *++argv) ;
	return(OK) ;
}

cmignore(argc, argv)
int argc ;
char * argv[] ;
{
	if (debugcall >= 1)
	{
		printf("CALL: cmignore\n") ;
		dodebugargs(argc, argv) ;
	}
	return (OK) ;
}

cmgetenv(argc, argv)
int argc ;
char * argv[] ;
{
	int diffnames ;
	int defval ;
	int varexists ;

	if (debugcall >= 1)
	{
		printf("CALL: cmtgetenv\n") ;
		dodebugargs(argc, argv) ;
	}
	varexists = diffnames = defval = FALSE ;
	/* figure out how the variables are supposed to work */
	if (argc == 3) /* different names but no default */
		diffnames = TRUE ;

	if (argc== 4) /* different names and default */
		diffnames = defval = TRUE ;

	/* get the darn things */
	varexists = diffnames ? loadvar(argv[1], argv[2], environ) : loadvar(argv[1], argv[1], environ) ;

	if (varexists)
		return(OK) ;

	/* the var does not exist in the unix environment if there is a default use that */
	if (defval)
		/* we dont need to check if there are different names, there
		   must be to allow us to have got to this stage */
		   addvar(argv[2], argv[3]) ;
	return (OK) ;
}
cmdumpv (argc, argv)
int argc ;
char * argv[] ;
{
	if (debugcall >= 1)
	{
		printf("CALL: cmdumpv\n") ;
		dodebugargs(argc, argv) ;
	}
	/* just dump all the variables */
	dumpvars() ;
	return(OK) ;
}

cmsetv(argc, argv) 
int argc ;
char * argv[] ;
{
	if (debugcall >= 1)
	{
		printf("CALL: cmsetv\n") ;
		dodebugargs(argc, argv) ;
	}
	if (argc == 2)
		addvar(argv[1], "") ;
	else
		addvar(argv[1], argv[2]) ;
	/* there are some special variables, which also need to set C variables*/
	if (strncmp(argv[1], "debug", 5) != 0)
		return(OK) ;
	/* handle the special cases */
	if (strcmp(argv[1], "debugargs") == 0)
		debugargs = TRUE ;
	else if (strcmp(argv[1], "debugcall") == 0)
	{
		if (argc == 2)
			debugcall = TRUE ;
		else
		{
			sscanf(argv[2], "%d", &debugcall) ;
		}
		printf("Call debuging now at level %d\n", debugcall) ;
	}
	else if (strcmp(argv[1], "debuglink") == 0)
		debuglink = TRUE ;
	else if (strcmp(argv[1], "debugpath") == 0)
		debugpath= TRUE ;
	else if (strcmp(argv[1], "debugrun") == 0)
		debugrun = TRUE ;
	else if (strcmp(argv[1], "debugbackup") == 0)
		debugbackup = TRUE ;
	return(OK) ;
}

cmunsetv(argc, argv)
int argc ;
char * argv[] ;
{
	if (debugcall >= 1)
	{
		printf("CALL: cmunsetv\n") ;
		dodebugargs(argc, argv) ;
	}
	delvar(argv[1]) ;
	/* there are some special variables, which also need to set C variables*/
	if (strncmp(argv[1], "debug", 5) != 0)
		return(OK) ;
	/* handle the special cases */
	if (strcmp(argv[1], "debugargs") == 0)
		debugargs = FALSE ;
	else if (strcmp(argv[1], "debuglink") == 0)
		debuglink = FALSE ;
	else if (strcmp(argv[1], "debugpath") == 0)
		debugpath= FALSE ;
	else if (strcmp(argv[1], "debugcall") == 0)
		debugcall = FALSE ;
	else if (strcmp(argv[1], "debugrun") == 0)
		debugrun = FALSE ;
	else if (strcmp(argv[1], "debugbackup") == 0)
		debugbackup = FALSE ;
	return(OK) ;
}

cmifusetv(argc, argv) 
int argc ;
char * argv[] ;
{
	/* if the variable is not set setit else leave allone */
	char * ptr ;
	if (debugcall >= 1)
	{
		printf("CALL: cmifusetv\n") ;
		dodebugargs(argc, argv) ;
	}
	ptr = findvar(argv[1]) ;

	if (ptr == NULL)
	{
		addvar(argv[1], argv[2]) ;
	}
	return(OK) ;
}

cmsystem(argc, argv)
int argc ;
char * argv[] ;
{
	char command [10000] ;

	if (debugcall >= 1)
	{
		printf("CALL: cmsystem\n") ;
		dodebugargs(argc, argv) ;
	}
	command[0] = '\0' ;

	while (-- argc > 0)
	{
		strcat (command, *++argv) ;
		strcat (command, " ") ;
	}
	if (system (command) >= 0) 
		return(OK) ;
	else
		return(BAD) ;
}

cmhelp(argc, argv)
int argc ;
char * argv[];
{
	int cmdmax, ctr ;

	if (debugcall >= 1)
	{
		printf("CALL: cmhelp\n") ;
		dodebugargs(argc, argv) ;
	}
	cmdmax = sizeof(cmds) / sizeof(cmds[1]) ;

	/* if there is no arg just print out the list of commands */
	if (argc == 1)
	{
	   printf("%s, list of available commands is\n", argv[0]) ;
	   for (ctr = 0 ; ctr < cmdmax ; ctr ++)
	      printf("%s\n", cmds[ctr].cmdname) ;
	   return(OK) ;
	}
	/* else find the command and pring out the message */
	for (ctr = 0 ; ctr < cmdmax ; ctr++)
	{
		if (strcmp(cmds[ctr].cmdname, argv[1]) == 0)
		{
			/* are we help or usage ? */
			if (strcmp(argv[0], "help") == 0)
			   printf("help for %s\n%s\n", argv[1], cmds[ctr].helptext) ;
			else
			{
			   printf("Usage: %s %s\n", argv[1], cmds[ctr].usage) ;
			   printf("Argument count, Min %d, Max %d\n", cmds[ctr].minargs, cmds[ctr].maxargs) ;
			}
			
			return(OK) ;
		}
	}
	printf("%s: %s not found\n", argv[0], argv[1]) ;
	return (OK) ;
}

cmrun(argc, argv)
int argc ;
char * argv [] ;
{
	/* execute a script, open the file (suffix .pscmd)
	   and execute the commands in it to EOF. I guess this MAY expand
	   to be a command language given time !
	   NOTE as this is used in a subroutine recursive run commands
	   are feasible  and variables are correctly handled */
	   FILE *fd ;
	   char cmdline[1000] ;
	   char cmdfile [1000] ;
	   int ctr ;
	   char varname[50] ;
	   char * path ;
	   int source, which ;

	if (debugcall >= 1)
	{
		printf("CALL: cmrun\n") ;
		dodebugargs(argc, argv) ;
	}
	   source = FALSE ;
	   which = FALSE ;
	   if (strcmp(argv[0], "which") == 0)
	   {
	   	which = TRUE ;
	   }
	   
	   if (strcmp(argv[0], "source") == 0)
	   {
		source = TRUE ;
	   }

   	/* if path is set use it, otherwise us "." as the path */
   	if ((path = findvar("path")) == NULL)
   	{
		path = "." ;
	}
	   	
   	/* locate the command */
   	if (pscmdpath(argv[1], path, cmdfile) == NULL)
   	{
		printf("Error, %s not found\n", argv[1]) ;
		return(OK) ;
   	}

	   if ((fd = fopen(cmdfile, "r")) == NULL)
	   {
		fprintf(stderr, "Error in opening %s\n", argv[1]) ;
		return(BAD) ;
	   }
	   
	   /* if we have got here we know that the file exists and is readable,
	      report back on the file details if which == TRUE and tidy up */
	   if (which == TRUE)
	   {
	   	fclose(fd) ;
	   	printf("run %s would use: %s\n", argv[1], cmdfile) ;
	   	return (OK) ;
	   }

	   /* go up one variable stack level if we are running 
	      otherwise we are sourcing so stay put 
	      We do not do anything with args if we are sourcing as the 
	      sourced files are used to setup variables etc, not
	      run programs */
	   if (source == FALSE)
	   {
	   	upvarlevel() ;

	        /* firet remove "run" from the argument list */
	        argc -- ;
	        argv ++ ;
	        /* then setup all the parameters */
	        for(ctr = 0 ; ctr < argc ; ctr ++)
	        {
		     sprintf(varname, "arg%d", ctr ) ;
		     addvar(varname, argv[ctr]) ;
	        }
	   }

	   while (fgets(cmdline, 1000, fd) != NULL)
	   {
		/* remove the newline ae the end of the string */
		cmdline[strlen(cmdline) -1] = '\0' ;
		if (debugrun)
			printf("Cmd :%s:\n", cmdline) ;
		if (cmdintr(cmdline) != OK)
			break ;
	   }
	   /* go down one level if we are running */
	   if (source == FALSE)
	   {
	   	downvarlevel() ;
	   }
	
	   fclose (fd) ;
	   return (OK) ;
}

cmspeed(argc, argv)
int argc ;
char * argv[] ;
{
	if (debugcall >= 1)
	{
		printf("CALL: cmspeed\n") ;
		dodebugargs(argc, argv) ;
	}
	if (strcmp(argv[0], "fast")== 0)
	{
		psverboseoff() ;
		psscreenoff() ;
	}
	else
	{
		psverboseon() ;
		psscreenon() ;
	}
	return (OK) ;
}

cmforeach(argc, argv)
int argc ;
char * argv[] ;
{
	char psionname[300] ;
	char getcmd[300] ;
	char pspath[255] ;
	FILE *fd ;
	int ctr ;

	if (debugcall >= 1)
	{
		printf("CALL: cmforeach\n") ;
		dodebugargs(argc, argv) ;
	}
	/* for each of the files in the list in the file of the first arg
	   build a command line composed of the remaining arguments and append
	   the parameters, them call the command interpreter */
	/* now call a routine to read the filenames and 
	   parse them into part dir fame
	   and to get each one individualy */

	if ((fd = fopen(argv[1], "r")) == NULL)
	{
		printf("%s error on opening names file\n", argv[0]) ;
		return(BAD) ;
	}

	while (fgets(psionname, 300, fd) != NULL)
	{
		if (debugrun)
			printf("Foreach input :%s:\n", psionname) ;
		/* if required remove the trailing \n from the string */
		if (psionname[strlen(psionname)] == '\n')
			psionname[strlen(psionname) -1] == '\0' ;
		if (debugrun)
			printf("Foreach, output path%s\n",  pspath) ;
		/* create the command */
		/* init the string */
		getcmd[0] = '\0' ;

		/* first build the command from the strings handed to foreach */
		for (ctr = 2 ; ctr < argc ; ctr ++)
		{
			strcat(getcmd, argv[ctr]) ;
			strcat(getcmd, " ") ;
		}

		/* now append the partition, directory and file details */
		strcat (getcmd, psionname) ;
		cmdintr(getcmd) ;
	}
	fclose(fd) ;
	return(OK) ;
}
	
cmget(argc, argv)
int argc ; 
char * argv[] ;
{

	time_t timestart, timeend ;
	int timediff ;
	char listcmd[255] ;
	char getcmd[255] ;
	char psionname[300] ;
	char pspath[255] ;
	FILE *fd ;

	if (debugcall >= 1)
	{
		printf("CALL: cmget\n") ;
		dodebugargs(argc, argv) ;
	}
	/* if the filename contains a * then do a list to put the names
	   into a file and then get each file individualy, in this
	   case we can only operate with the local and remote filenames
	   being the same */
	if (strchr(argv[1], '*') != NULL)
	{
		if (argc == 3)
			printf("Sorry cannot use specific recieving names with wildcard get (yet)\n") ;
		/* set the list parameter up */
		strcpy (listcmd, "list ") ;
		/* create the other information */
		strcat (listcmd, argv[1]) ; /* the path name */
		strcat (listcmd, " ") ;
		strcat (listcmd, "/tmp/filenames") ; /* the temp filename */
		/* call cmdintr to do the work for us */
		if (cmdintr(listcmd) != OK)
			return(BAD) ;
		/* now build a foreach command to handle the filenames */
		strcpy(getcmd, "foreach /tmp/filenames get") ;
		/* call the command interpreter to get the files */
		cmdintr(getcmd) ;
		fclose(fd) ;
		return(OK) ;
	}
	if (findvar(VAR_DOLOWERPATH) != NULL)
		lowerpath(argv[1]) ;

	if (argc == 2)
	{
		if (findvar(VAR_VERBOSE) != NULL)
			printf("Getting one file %s\n", argv[1]) ;
		pssetpath(argv[1]) ;
		time(&timestart) ;
		psrecfile("") ;
	}
	else
	{
		if (findvar(VAR_VERBOSE) != NULL)
			printf("Getting %s to %s\n", argv[1], argv[2]) ;
		pssetpath(argv[1]) ;
		time(&timestart) ;
		psrecfile(argv[2]) ;
	}
	time(&timeend) ;

	timediff = (int) timeend - timestart ;
	if (timediff == 0)
		timediff = 1 ;

	if (findvar(VAR_VERBOSE) != NULL)
		printf("Transfer time %d, bytes %d, speed %d (bytes / sec)\n", timediff, pstrans(), (int) pstrans()/timediff) ;
	/* is file filtering enabled ? */
	if (findvar(VAR_FILEFILT) != NULL)
	{
		if (argc == 2)
		{
			/* we are dealing with a direct get from the same file, psfname is already setup*/
			filefilt(INFILTER,psfname) ;
		}
		else
		{
			/* the file was grabbed into argv[2] */
			filefilt(INFILTER,argv[2]) ;
		}
	}
	return(OK) ;
}

getfilefiles(fname)
char * fname ;
{
	/* scan each of the filenames and then get it */
	FILE *fd ;
	char psionname[300] ;
	char pspath[255] ;
	char cmdname[300] ;

	if (debugcall >= 1)
	{
		printf("CALL: getfilefiles from %s\n",fname) ;
	}
	if ((fd = fopen(fname, "r")) == NULL)
	{
		printf("Wildcard get error on opening names file\n") ;
		return(BAD) ;
	}

	while (fgets(psionname, 300, fd) != NULL)
	{
		/* if required remove the trailing \n from the string */
		if (psionname[strlen(psionname)] == '\n')
			psionname[strlen(psionname) -1] == '\0' ;
		/* create the get command */
		strcpy (cmdname, "get ") ;
		strcat (cmdname, pspath) ;
		cmdintr(cmdname) ;
	}
}

cmput(argc, argv)
int argc ; 
char * argv[] ;
{
	time_t timestart, timeend ;
	int timediff ;

	if (debugcall >= 1)
	{
		printf("CALL: cmput\n") ;
		dodebugargs(argc, argv) ;
	}
	/* map the path to lower case if desired */
	if (findvar(VAR_DOLOWERPATH) != NULL)
		lowerpath(argv[1]) ;

	/* send the path, do this here so that the next call to psgetfname will have the correct value installed and we can use it for filt output filtering */
	pssetpath(argv[1]) ;
	/* is file filtering enabled ? */
	if (findvar(VAR_FILEFILT) != NULL)
	{
		if (argc == 2)
		{
			/* we are dealing with a direct put from the same file, we need to setup psfname */
			psgetfname() ;
			filefilt(OUTFILTER,psfname) ;
		}
		else
		{
			/* the file is being sent from argv[2] */
			filefilt(OUTFILTER,argv[2]) ;
		}
	}

	if (argc-1 == 1)
	{
		if (findvar(VAR_VERBOSE) != NULL)
			printf("putting one file %s\n", argv[1]) ;
		time(&timestart) ;
		pssendfile("") ;
	}
	else
	{
		if (findvar(VAR_VERBOSE) != NULL)
			printf("putting %s from %s\n", argv[1], argv[2]) ;
		time(&timestart) ;
		pssendfile(argv[2]) ;
	}
	time(&timeend) ;

	timediff = (int) timeend - timestart ;
	if (timediff == 0)
		timediff = 1 ;

	if (findvar(VAR_VERBOSE) != NULL)
		printf("Transfer time %d, bytes %d, speed %d (bytes / sec)\n", timediff, pstrans(), (int) pstrans()/timediff) ;
	/* we may have we have mangled the file by using the output filter,
	   call the restore filter to reset it */
	if (findvar(VAR_FILEFILT) != NULL)
	{
		if (argc == 2)
		{
			/* psfname is already correctly set just call with 
			   the restore filter*/
			filefilt(RESTFILTER,psfname) ;
		}
		else
		{
			filefilt(RESTFILTER, argv[2]) ;
		}
	}

	return(OK) ;
}

cmdiscon(argc, argv)
int argc ; 
char * argv[] ;
{
	if (debugcall >= 1)
	{
		printf("CALL: cmdiscon\n") ;
		dodebugargs(argc, argv) ;
	}
	running = FALSE ; /* tell the command interpreter loop to quit */
	if (strcmp(argv[0], "quit") == 0)
	{
		/* if this is in batch mode, be quiet */
		if (runtype != BATCH)
			printf("Disconnecting and closing down psion\n") ;
		psdc() ;
		return(DISCONN) ;
	}
	else /* we are exiting the sun end of the prog but not the psion end */
	{
		/* if this is in batch mode, be quiet */
		if (runtype != BATCH)
			printf("Disconnecting and leaving psion active\n") ;
		return(DISCONN) ;
	}

}
cmfilter(argc, argv)
int argc ; 
char * argv[] ;
{
	int filtertype ;
	if (debugcall >= 1)
	{
		printf("CALL: cmfilter\n") ;
		dodebugargs(argc, argv) ;
	}
	/* are we adding or removing a filter ? */
	if (strcmp(argv[0], "addfilter") == 0)
	{
		/* is this an input or output or restore filter ? */
		if (strcmp(argv[1], "in") == 0)
			filtertype = INFILTER ;
		else if (strcmp(argv[1], "restore") == 0)
			filtertype = RESTFILTER ;
		else
			filtertype = OUTFILTER ;
		/* lets setup the filter */
		setupfilter(filtertype, argv[2], argv[3]) ;
	}
	else
	{
		/* is this an input or output or restore filter ? */
		if (strcmp(argv[1], "in") == 0)
			filtertype = INFILTER ;
		else if (strcmp(argv[1], "restore") == 0)
			filtertype = RESTFILTER ;
		else
			filtertype = OUTFILTER ;
		/* lets setup the filter */
		removefilter(filtertype, argv[2]) ;
	}

		return (OK) ;
}
cmfilterprint(argc, argv)
int argc ;
char * argv[] ;
{
	if (debugcall >= 1)
	{
		printf("CALL: cmfilterprint\n") ;
		dodebugargs(argc, argv) ;
	}
	printfilters() ;
}
cmexclprint(argc, argv)
int argc ;
char * argv[] ;
{
	if (debugcall >= 1)
	{
		printf("CALL: cmexclprint\n") ;
		dodebugargs(argc, argv) ;
	}
	printexclude() ;
}

cmexclude(argc, argv)
int argc ;
char * argv[] ;
{
	if (debugcall >= 1)
	{
		printf("CALL: cmexclude\n") ;
		dodebugargs(argc, argv) ;
	}
	/* all backup related paths must be in lower case ! */
	lowerpath(argv[1]) ;
	if (strcmp(argv[0], "addexcl") == 0)
		setupexclude(argv[1]) ;
	else
		removeexclude(argv[1]) ;
}

cmrlist(argc, argv)
int argc ;
char * argv[] ;
{
	FILE *fd ;
	if (debugcall >= 1)
	{
		printf("CALL: cmrlist\n") ;
		dodebugargs(argc, argv) ;
	}
	if (psvsnlt(MINIMUM_FILE_STAT_REQUIRED))
	{
		versiontolow(argv[0],MINIMUM_FILE_STAT_REQUIRED, FALSE) ;
		return ;
	}
	/* are we saving this to a file ? */
	if (argc == 2)
	{
		/* do the listing */
		psrlist (argv[1], NULL) ;
	}
	else
	{
		if ((fd = fopen(argv[2], "w")) == NULL)
		{
			printf("Error, cant open %s for output\n", argv[2]) ;
			return ;
		}
		psrlist(argv[1], fd) ;
		fclose(fd) ;
	}
}

cmbackup(argc, argv)
int argc ;
char * argv[] ;
{
	if (debugcall >= 1)
	{
		printf("CALL: cmbackup\n") ;
		dodebugargs(argc, argv) ;
	}
	if (psvsnlt(MINIMUM_FILE_STAT_REQUIRED))
	{
		versiontolow(argv[0],MINIMUM_FILE_STAT_REQUIRED, FALSE) ;
		return ;
	}
	/* is this an incremental backup ? */
	if (strcmp(argv[0] , "ibackup") == 0)
		psbackup(argv[1], argv[2], TRUE) ;
	else
		psbackup(argv[1], argv[2], FALSE) ;
}

cmcs(argc, argv)
int argc ;
char * argv[] ;
{
	int  ps ;
	long cs ;
	
	if (debugcall >= 1)
	{
		printf("CALL: cmcs\n") ;
		dodebugargs(argc, argv) ;
	}
	/* are we operating on the sun of the psion ? */
	ps = TRUE ;
	if ((argc == 3) && (strcmp(argv[2], "sun") == 0))
	   ps = FALSE ;
	if (ps)
	{
		if (psvsnlt(MINIMUM_FILE_CHECKSUM_REQUIRED))
		{
			versiontolow(argv[0], MINIMUM_FILE_CHECKSUM_REQUIRED, FALSE) ;
			return ;
		}
		pssetpath(argv[1]) ;
		if (psep())
		{
			cs = pscs() ;
			printf("Checksum is %ld\n", cs) ;
			return ;
		}
		else
		{
			printf("Error, %s does not exist\n", argv[1]) ;
			return ;
		}
	}
	else /* we are working on the sun */
	{
		cs = sunsum (argv[1]) ;
		if (cs == BADPARAM)
		{
			printf("Error, %s does not exist\n", argv[1]) ;
			return ;
		}
		else
		{
			printf("Checksum is %ld\n", cs) ;
			return ;
		}
	}
}

long psgetmodtime() ;
cminfo(argc, argv)
int argc ;
char * argv[] ;
{
	int size, rw,atts ;
	char tmp [100] ;
	long modtime ;
	char * timeptr ;
	if (debugcall >= 1)
	{
		printf("CALL: cminfo\n") ;
		dodebugargs(argc, argv) ;
	}
	if (psvsnlt(MINIMUM_FILE_STAT_REQUIRED))
	{
		versiontolow(argv[0],MINIMUM_FILE_STAT_REQUIRED, FALSE) ;
		return ;
	}

	/* set the path */
	pssetpath(argv[1]) ;
	rw = psgetrw() ;
	atts = psgetatts() ;
	size = psgetfsize() ;
	modtime = psgetmodtime() ;
	timeptr = ctime(&modtime) ;
	/* decode the attributes */
	tmp[0] = 0 ;
	if (atts & PSFILEDIR)
		strcat(tmp, "DIRECTORY ") ;
	if (atts & PSFILETEXT)
		strcat(tmp, "TEXT ") ;
	if (atts & PSFILEFILE)
		strcat(tmp, "FILE ") ;
	if (atts & PSFILEHIDDEN)
		strcat(tmp, "HIDDEN ") ;
	if (atts & PSFILESYSTEM)
		strcat(tmp, "SYSTEM ") ;
	if (atts & PSFILEVOLUME)
		strcat(tmp, "VOLUMEDIR ") ;
	
	printf("Information on %s\n", argv[1]) ;
	printf("Size %d\n", size) ;
	printf("Access is %s\n", rw == PSFILERW ? "Read / Write" : "Read only") ;
	printf("Attributes are %s\n", tmp) ;
	/* if this is not a file the mod date makes no sense */
	if (atts & PSFILEFILE)
		printf("Last modification time is %s\n", timeptr) ;
}
	
cmsetwriteflag(argc, argv)
int argc ;
char * argv[] ;
{
	int size, rw,atts ;
	char tmp [100] ;
	long modtime ;
	if (debugcall >= 1)
	{
		printf("CALL: cmsetwriteflag\n") ;
		dodebugargs(argc, argv) ;
	}
	if (psvsnlt(MINIMUM_FILE_STAT_REQUIRED))
	{
		versiontolow(argv[0],MINIMUM_FILE_STAT_REQUIRED, FALSE) ;
		return ;
	}
	pssetpath(argv[1]) ;
	if (strcmp(argv[0],"setro") == 0)
	{
		pssetro() ;
	}
	else
	{
		pssetrw() ;
	}
}

lowerpath(string)
char * string ;
{
	int i, len;
	len = strlen(string) ;
	for (i = 0 ; i < len; i ++)
		string[i] = tolower(string[i]) ;
}
versiontolow(fn,vsn,fatal)
char * fn, *vsn ;
int fatal;
{
	printf("Function %s requires version %s of suncom, you have version %s\n",fn, vsn, psversion()) ;
	printf("Please contact Tim Graves at Sun Microsystems (tim.graves@uk.sun.com)\n") ;
	printf("for a later release of this software\n");
	if (fatal)
	{
		printf("THIS IS A FATAL INCOMPATIBILITY, program exiting\n") ;
		psdc() ;
		return ;
	}
	else
	{
		printf("This is not a fatal problem, other functions may still work\n") ;
	}
}
