/****************************************************************************
   Program Name: FileIO.c
  
   Author      : Phil Shotton. 
  
   Description : This program is an example on how to develop external
                 functions in WingZ. It allows text input-output and
		 arbitrary file positioning with read and write.
		 
		 Your attention is also directed at the file 'FILEIO.README'
		 for documention on these functions.
  
   Version     : @(#)FileIO.c	2.1.2.1 95/05/11 12:38:21

     copyright 1994, Investment Intelligence Systems Corporation
  
****************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

#define numfuncs 12
#define MAXFILES 128

#include "WZTools.h"

#ifndef min
# define min(a,b) ((a)<(b)?(a):(b))
#endif

/* prototypes */

typedef struct
{
    FILE * fp;
    char * name;
    char * mode;
} Fs;

Fs filep[MAXFILES];
    
void InitFunc();
void NumLines(PVAL,PVAL), OpenFile(PVAL,PVAL), CloseFile(PVAL,PVAL);
void ReadFile(PVAL,PVAL), WriteFile(PVAL,PVAL);
void GetBytes(PVAL,PVAL), PutBytes(PVAL,PVAL), FilePos(PVAL,PVAL);
void Shell(PVAL,PVAL), POpen(PVAL,PVAL), PClose(PVAL,PVAL);
void Showf(PVAL,PVAL);
FILE * find_fp(int);
int add_fp(FILE *,char *,char *), del_fp(int);
/* set up structure */
void (*initfunc) () = InitFunc;

ROUT rout = {
  numfuncs, NULL,
  {
    {NumLines, "\010NumLines", 1},
    {OpenFile, "\010OpenFile", 2},
    {CloseFile, "\011CloseFile", 1},
    {ReadFile, "\010ReadFile", 1},
    {WriteFile, "\011WriteFile", 2},
    {GetBytes, "\010GetBytes", 3},
    {PutBytes, "\010PutBytes", 3},
    {FilePos, "\007FilePos", 1},
    {Shell,"\005Shell", 1},
    {POpen,"\005POpen", 2},
    {PClose,"\006PClose", 1},
    {Showf,"\005Showf", 0}
  }
};

void Showf(PVAL pr,PVAL ps)
{
    int i;
    for(i=0;i<MAXFILES;i++)
    {
	if(filep[i].fp)
	    fprintf(stderr,"Fd:%d, fp:%X, Name:%s, Mode:%s\n",
		    i,filep[i].fp,filep[i].name,filep[i].mode);
    }
}

/* -------------------------------------------------------------- */
/*  initialize function */
void InitFunc(PVAL pret, PVAL parg)
{
    int i;
    for(i=0;i<MAXFILES;i++)
	memset(&filep,0,sizeof(Fs));
}

/* -------------------------------------------------------------- */
/* Function counts the number of lines in a text file.
   in:  char string (PASCAL) filename.
   out: int number of lines in file
*/
void NumLines(PVAL pret, PVAL parg)
{

    int flen, linecount = 0;
    char filename[255],c;
    int fd;

    if (parg[0].flag == STRING)
    {

	pret->flag = NUMERIC;

	/* remember first byte is length of string */
	flen = parg[0].val.string[0];
	strncpy(filename, &parg[0].val.string[1], flen);
	filename[flen] = '\0';

	/* open the file */
	if ((fd = open(filename, O_RDONLY)) != -1)
	{
	    while ( read(fd,&c,1))
		if (c == '\n')
		    linecount++;

	    close(fd);

	    /* set up the return values */
	    pret->val.numeric = linecount;

	    /* printf("wk_NumLines: found %d lines.\n",linecount); */

	}
	else
	{
	    /* could not open file */
	    perror("open");
	    pret->val.numeric = 0;
	}

    }
    else
    {
	/* variable in was not a string */
	pret->flag = ERR;
	pret->val.err = 12;
    }

}

/* -------------------------------------------------------------- */
/* Function opens a file and passes back a file descriptor to 
   wingz so futher processing can be applied.
  
   in:  char string (PASCAL) filename.
   in:  char string (PASCAL) access mode. 
              "r" "w" see UNIX man page fopen()
   out: int file handle.   
*/
void OpenFile(PVAL pret, PVAL parg)
{
    char filename[255];
    int flen, fd;
    FILE * fp;
    char mode[5];
 
    pret->flag = NUMERIC;

    if ((parg[0].flag == STRING) && (parg[1].flag == STRING))
    {

	flen = parg[0].val.string[0];
	strncpy(filename, &parg[0].val.string[1], flen);
	filename[flen] = '\0';
	flen=(int)parg[1].val.string[0];
	flen=min(4,flen);
	strncpy(mode,parg[1].val.string+1,flen);
	mode[flen]=0;
	/* open the file */
	if (NULL != (fp = fopen(filename,mode)))
	{
	    if(0 > (fd=add_fp(fp,filename,mode)))
		fclose(fp);
	    pret->val.numeric = fd;
	}
	else
	{
	    perror("file open failed\n");
	    pret->val.numeric = -1;
	}

    }
    else
    {
	/* variable in was not a string */
	pret->flag = ERR;
	pret->val.err = 12;
    }

}

/* -------------------------------------------------------------- */
/* Function will close an existing file
   which was opened with OpenFile().
  
   in: int filehandle fd from an OpenFile
*/
void CloseFile(PVAL pret, PVAL parg)
{
    int fd;
    FILE * fp;
    if (parg[0].flag == NUMERIC)
    {

	pret->flag = NUMERIC;
	fd = parg[0].val.numeric;
	if(NULL!=(fp=find_fp(fd)))
	{
	    fclose(fp);
	    del_fp(fd);
	    pret->val.numeric = 1;
	}
	else
	{
		/* problem in closing file */
	    perror("fclose");
	    pret->val.numeric = 0;
	}
    }
    else
    {
	/* variable in was not numeric */
	pret->flag = ERR;
	pret->val.err = 12;
    }

}

/* -------------------------------------------------------------- */
/* Function reads up to 255 characters or until a '\n' from a file, 
   opened with OpenFile.
  
   in:  int file handle.      
   out: char string (PASCAL) line of file.
*/
void ReadFile(PVAL pret, PVAL parg)
{

    int fd,len,c;
    char buff[255];
    FILE * fp;

    if (parg[0].flag == NUMERIC)
    {

	fd = parg[0].val.numeric;
	len=0;
	if(NULL!=(fp=find_fp(fd)))
	{
	    if (fgets(buff+1, 254, fp))
	    {
		/* okay lets return a PASCAL string */
		buff[254]=0;
		len=strlen(buff+1);
		if('\n'==buff[len])
		    buff[len--]='\0';
	    }
	}
	buff[0] = (char)len;
	pret->flag = STRING;
	pret->val.string = buff;
    }
    else
    {
	/* variable in was not a number */
	pret->flag = ERR;
	pret->val.err = 12;
    }
}

/* -------------------------------------------------------------- */
/* Functions writes up to 255 characters at current position
   to a file opened with OpenFile() and appends a \n
  
   in:  int file handle.
   in:  char string (PASCAL) line to inserted.
  
*/
void WriteFile(PVAL pret, PVAL parg)
{

    int n, fd;
    FILE * fp;

    if ((parg[0].flag == NUMERIC) && (parg[1].flag == STRING))
    {

	pret->flag = NUMERIC;
	fd = parg[0].val.numeric;
	
	    /* pascal string, first byte is the length */
	n = parg[1].val.string[0];
	if(NULL!=(fp=find_fp(fd)))
	{
	    if(0>fprintf(fp,"%.*s\n",n,parg[1].val.string+1))
	    {
		perror("fprintf");
		pret->val.numeric = 0;
	    }
	    else
		pret->val.numeric = 1;
	}
    }
    else
    {
	/* variable in was not a number */
	pret->flag = ERR;
	pret->val.err = 12;
    }
}

/* -------------------------------------------------------------- */
/* Function reads count bytes (up to 255 characters) from a file
   opened with OpenFile.
  
   in:  int file handle, int position, int count.      
   out: char string (PASCAL) count bytes from file.
*/
void GetBytes(PVAL pret, PVAL parg)
{

    int fd,len,pos;
    char buffer[255];
    FILE * fp;

    if ( (parg[0].flag == NUMERIC) &&
	(parg[1].flag == NUMERIC) &&
	(parg[2].flag == NUMERIC) )
    {

	fd = parg[0].val.numeric;
	pos = parg[1].val.numeric;
	len = parg[2].val.numeric;
	if( (NULL!=(fp=find_fp(fd))) &&
	   (fseek(fp,pos,SEEK_SET) != -1) )
	{	
	    if (len=fread(buffer + 1,1, min(254,len),fp))
	    {
		buffer[0] = (char)len;
		pret->flag = STRING;
		pret->val.string = buffer;
		return;
	    }
	    else
	    {
		perror("fread");
		pret->flag = ERR;
		pret->val.err = 6;
	    }
	}
	else
	{
	    perror("fseek");
	    pret->flag = ERR;
	    pret->val.err = 6;
	}
    }
    else
    {
	/* variable in was not a number */
	pret->flag = ERR;
	pret->val.err = 12;
    }
}

/* -------------------------------------------------------------- */
/* Functions writes count bytes (up to 254 characters) at specified
   position to a file opened with OpenFile().
  
   in:  int file handle, int pos, char string (PASCAL) line to inserted.
  
*/
void PutBytes(PVAL pret, PVAL parg)
{

    int pos, fd;
    FILE * fp;

    if ((parg[0].flag == NUMERIC) &&
	(parg[1].flag == NUMERIC) &&
	(parg[2].flag == STRING))
    {

	pret->flag = NUMERIC;
	fd = parg[0].val.numeric;
	pos = parg[1].val.numeric;
	if( (NULL!=(fp=find_fp(fd))) &&
	   (fseek(fp,pos,SEEK_SET)!= -1) )
	{
	    if(pos=fwrite(parg[2].val.string+1,1,parg[2].val.string[0],fp)!= 0)
		pret->val.numeric = pos;
	    else
	    {
		perror("fwrite");
		pret->val.numeric = 0;
	    }
	    return;
	}
	perror("fseek");
	pret->flag = ERR;
	pret->val.err = 6;
    }
    else
    {
	/* variable in was not a number */
	pret->flag = ERR;
	pret->val.err = 12;
    }
}

/* -------------------------------------------------------------- */
/* Functions returns current file position of a file 
   opened with OpenFile().
  
   in:  int file handle
   out: current position (byte offset from 0)
  
*/
void FilePos(PVAL pret, PVAL parg)
{

    int pos, fd;
    FILE * fp;

    if (parg[0].flag == NUMERIC)
    {

	pret->flag = NUMERIC;
	fd = parg[0].val.numeric;
	/* seek 0 bytes from current pos, returns pos */
	if( (NULL!=(fp=find_fp(fd))) &&
	   ((pos=ftell(fp))!= -1) )
	    pret->val.numeric = pos;
	else
	{
	    perror("fseek");
	    pret->flag = ERR;
	    pret->val.err = 6;
	}
    }
    else
    {
	/* variable in was not a number */
	pret->flag = ERR;
	pret->val.err = 12;
    }
}

/* -------------------------------------------------------------- */
/* Function pumps input into a system call 
     pret : return structure
     parg : argument structure 
*/
void Shell(PVAL pret, PVAL parg)
{
    char buffer[255];
    int n;
    int ret;
    char c;

    if (parg[0].flag == STRING)
    {
    /* pascal string, first byte is the length */
	n = parg[0].val.string[0];
    /* copy from the argument+1 to the buffer store */
	strncpy(buffer, &parg[0].val.string[1],n);
	buffer[n] = '\0';
    /* lets put the string thru. system() in the background */
	c=buffer[n-1];

	if(c=='&')
	{
	    if(fork() == 0) 
	    {
		ret=system(buffer);
		exit(ret);
	    }
	    else
		ret=0;
	}
	else 
	    ret=system(buffer);

	pret->flag = NUMERIC;
	pret->val.err=ret;

    }
    else
    {
	pret->flag = ERR;
	pret->val.err = 12;
    }
}
/* -------------------------------------------------------------- */
/* Function to implement a pipe to/from a 
   process, like UNIX's popen() 

   in:  char string (PASCAL) command to bex executed.
   in:  char string (PASCAL) access mode.
           "r" or "w" see UNIX man popen(). 
   out: int file handle.
*/
void POpen(PVAL pret, PVAL parg)
{ 
    int n, fd;
    char command[255];
    char type[2];
    FILE *fp;

    /* make sure that second argument is only 1 character long */
    if ( (parg[0].flag == STRING) && (parg[1].flag == STRING) 
	&& (parg[1].val.string[0]==1) )
    {
	pret->flag = NUMERIC;
	/* pascal string, first byte is the length */
	n = parg[0].val.string[0];
	/* copy from the 1st argument+1 to the buffer store */
	strncpy(command, &parg[0].val.string[1], n);
	command[n] = '\0';
	/* get the type from the 2nd argument */
	type[0] = parg[1].val.string[1];
	type[1] = '\0';
	/* printf("POpen received (%s %)\n",command,type);   */
	if ( (fp = popen(command, type)) != NULL)
	{
	    if ( (fd=add_fp(fp,command,type)) != -1)
	    {
		/* set variables to return File Handle */
		pret->val.numeric = fd;
	    }
	    else
	    {
		/* no more free slots return error */
		pret->val.numeric = -1;
	    }
	}
	else
	{
	    perror("popen");
	    pret->val.numeric = -1;
	}
    }
    else
    {
	/* variable in was not a number */
	pret->flag = ERR;
	pret->val.err = 12;
    }
}

/* -------------------------------------------------------------- */
/* Function to close an existing file handle returned from wk_POpen().
   
   in:  int file handle.
*/
void PClose(PVAL pret, PVAL parg)
{ 
    int fd;
    FILE *fp;

    if (parg[0].flag == NUMERIC)
    {
	pret->flag = NUMERIC;
	fd = parg[0].val.numeric;
	/* remove the file handle from the lookup, and get the FILE pointer */
	if (NULL!= (fp=find_fp(fd)))
	{
	    if (! pclose(fp))
	    {
		/* pfile closed ok */
		pret->val.numeric = 1;
	    }
	    else
	    {
		/* problem in closing pipe */
		pret->val.numeric = 0;
	    }
	    del_fp(fd);
	}
	else
	{
	    /* problem in deleting file */
	    pret->val.numeric = 0;
	}      
    }
    else
    {
	/* variable in was not a string */
	pret->flag = ERR;
	pret->val.err = 12;
    }
}   

int add_fp(FILE * fp,char * name,char * mode)
{
    int i;
    for(i=0;i<MAXFILES;i++)
	if(!filep[i].fp)
	{
	    filep[i].fp=fp;
	    filep[i].name=strdup(name);
	    filep[i].mode=strdup(mode);
	    return i;
	}
    return -1;
}

int del_fp(int i)
{
    if( (i>=0) && (i<MAXFILES))
    {
	filep[i].fp=NULL;
	if(filep[i].name)
	    free(filep[i].name);
	filep[i].name=NULL;
	if(filep[i].mode)
	    free(filep[i].mode);
	filep[i].mode=NULL;
	return 1;
    }
    return 0;
}

FILE * find_fp(int fd)
{
    if( (fd>=0) && (fd<MAXFILES))
	return filep[fd].fp;
    return NULL;
}

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