/* -> c.plib
 *
 *      (c) Cosmos Nicolaou 25/6/87
 */


#include <stdio.h>
#include <stdlib.h>
#include "arthur.h"
#include <string.h>
#include <ctype.h>
#include "ckafio.h"
#include "plib.h"
#include "tty.h"
#include "ckamis.h"
#include "dir.h"


/*
 * Temporary kludge to allow use of fatal.
 * It is not good practice to use an application function
 * in a library!
 */
#include "ckcdeb.h"
#include "ckuusr.h"

/*
 * Source Code file for Panos compatibility library
 */

#define UNUSED 0

#define FTYPE   1
#define VDU     2
#define RAWVDU  3
#define KB      4
#define RAWKB   5
#define BBC     6
#define TTY     7
#define RS423   8
#define PRINTER 9
#define NULL_DEV 10



#define VDU_STR     "VDU:"
#define RAWVDU_STR  "RAWVDU:"
#define KB_STR      "KB:"
#define RAWKB_STR   "RAWKB:"
#define BBC_STR     "BBC:"
#define TTY_STR     "TTY:"
#define RS423_STR   "RS423:"
#define PRINTER_STR "PRINTER:"
#define INPUT_STR   "INPUT:"
#define OUTPUT_STR  "OUTPUT"
#define CTL_STR     "CONTROL:"
#define ERR_STR     "ERROR:"

#define keybf      0
#define rs423inbf  1
#define rs423outbf 2

#define IP 0
#define OP 1

static int getnextstr( void );

typedef struct {
    int type;
    int inout;
    FILE *fp;
} stream_desc;

#define MAXSTREAMS 20

static stream_desc streams[MAXSTREAMS];

static stream_desc *cur_desc;

int rs423_num_users = 0;
int kb_num_users = 0;

static reg_set rs;
static error *ret_val;



int
GetFileInformation( info, tstamp, name, len )
struct FileData *info;
struct BTim *tstamp;
char * name;
int len;
{
static osfile_block o_b;
static reg_set rs;
error *ret;

    /* Open file for reading */
    rs.r[0] = 64;
    rs.r[1] = (int)name;
    if( (ret=osfind( &rs )) != NULL )
    {
        fprintf(stderr,"%s (%d)\n",ret->errmess,ret->errnum);
        return -1;
    }

    if( rs.r[0] == 0 )
        /* File does not exist */
        return -1;
     
    /* close the file just opened */
    rs.r[1] = rs.r[0];
    rs.r[0] = 0;

    if( (ret=osfind( &rs )) != NULL )
    {
        fprintf(stderr,"%s (%d)\n",ret->errmess,ret->errnum);
        return -1;
    }

    o_b.action = 5;
    o_b.name = name;

    if( (ret=osfile( &o_b )) != NULL )
    {
        fprintf(stderr,"%s (%d)\n",ret->errmess,ret->errnum);
        return -1;
    }

    info->loadaddr = o_b.loadaddr;
    info->execaddr = o_b.execaddr;
    info->length = o_b.start;
    info->attrib = o_b.end;

    /* Return file type */
    return o_b.action;

}


int
EndOfFile( stream )
int stream;
{
   
    cur_desc = &streams[stream];

    if( cur_desc->type == UNUSED )
    {
       fprintf(stderr,"ENDOFFILE, op on unused stream (%d)\n",stream);
       fatal("ENDOFFILE, op on unused stream");
    }

    if( cur_desc->type != FTYPE )
        /* Not a file */
        fatal("End of file unimplemented function");

    if( feof( cur_desc->fp ) != 0 )
        /* At end of file */
        return 1;
    else
        return 0;

}


int
SWriteByte( 
#ifdef ANSI
int stream, char ch)
#else
stream, ch )
int stream;
char ch;
#endif
{
    cur_desc = &streams[stream];


    switch( cur_desc->type )
    {
        case UNUSED:
            fprintf(stderr,"SWRITEBYTE, op on unused stream (%d)\n",stream);
            break;
        case FTYPE:
            if( fputc( ch, cur_desc->fp ) == EOF )
                return -9;
            break;
        case VDU:
            /* printable ascii, clear-screen, newline, and carry return */
            if( ch == '\n' )
            {
                vdu(10);vdu(13);break;
            }
            if( !iscntrl(ch) || isspace(ch) )
                vdu( ch );
            break;
        case RAWVDU:
            vdu( ch );
            break;
        case BBC:
            /* RAWVDU for output */
            cur_desc->type = RAWVDU;
            if( SWriteByte( stream, ch ) < 0 )
            {
                cur_desc->type = BBC;   
                return -1;
            }
            cur_desc->type = BBC;
            break;
        case TTY:
            /* DU for output */
            cur_desc->type = VDU;
            if( SWriteByte( stream, ch ) < 0 )
            {
                cur_desc->type = BBC;   
                return -1;
            }
            cur_desc->type = BBC;
            break;
        case RS423:
            rs.r[0] = 153;
            rs.r[1] = rs423outbf;
            rs.r[2] = ch;
            ret_val = osbyte( &rs );
            inter();
            break;
        case PRINTER:
        case NULL_DEV:
            fatal("SWB unimplemented function");
            break;
        default:
            fprintf(stderr,"SWRITEBYTE WOW should never get here!!\n");
            return -1;
            break;
    }

    return 0;

}

int
XSWriteByte(
#ifdef ANSI
int stream, char ch)
#else
stream, ch )
int stream;
char ch;
#endif
{
    cur_desc = &streams[stream];

    switch( cur_desc->type )
    {
        case UNUSED:
            fprintf(stderr,"XSWRITEBYTE, op on unused stream (%d)\n",stream);
            break;
        case FTYPE:
            if( fputc( ch, cur_desc->fp ) == EOF )
                return -9;
            break;
        case VDU:
            /* printable ascii, clear-screen, newline, and carry return */
            if( ch == '\n' )
            {
                vdu(10);vdu(13);break;
            }
            if( !iscntrl(ch) || isspace(ch) )
                vdu( ch );
            break;
        case RAWVDU:
            vdu( ch );
            break;
        case KB:
        case RAWKB:
            fatal("XSWB unimplemented function");
            break;
        case BBC:
            /* RAWVDU for output */
            cur_desc->type = RAWVDU;
            if( XSWriteByte( stream, ch ) < 0 )
            {
                cur_desc->type = BBC;
                return -1;
            }
            cur_desc->type = BBC;
            break;
        case TTY:
            /* VDU for output */
            cur_desc->type = VDU;
            if( XSWriteByte( stream, ch ) < 0 )
            {
                cur_desc->type = BBC;
                return -1;
            }
            cur_desc->type = BBC;
            break;
        case RS423:
            rs.r[0] = 153;
            rs.r[1] = rs423outbf;
            rs.r[2] = ch;
            ret_val = osbyte( &rs );
            inter();
            break;
        case PRINTER:
        case NULL_DEV:
            fatal("XSW unimplemented function");
            break;
        default:
            fprintf(stderr,"XSWRITEBYTE WOW should never get here!!\n");
            abort();
            return -1;
            break;
    }

    return 0;

}

int
XSBlockWrite( stream, bufsiz, buf )
int stream, bufsiz;
char * buf;
{
int i;
int ch;

    cur_desc = &streams[stream];

    switch( cur_desc->type )
    {
        case UNUSED:
            fprintf(stderr,"XSBLOCKWRITE, op on unused stream (%d)\n",stream);
            break;
        case FTYPE:
            if( fwrite( buf, sizeof(char), bufsiz, cur_desc->fp ) != bufsiz )
                return -1;
            break;
        case VDU:
            for( i=0; i < bufsiz; i++ )
            {
                ch = *(buf+i);
                if( ch == '\n' )
                {
                    vdu(10);vdu(13);break;
                }
                /* printable ascii, clear-screen, newline, and carry return */
                if( !iscntrl(ch) || isspace(ch) )
                    vdu( ch );
            }
            break;
        case RAWVDU:
            for( i=0; i < bufsiz; i++ )
                vdu( *(buf+i) );
            break;
        case BBC:
            /* RAWVDU for output */
            cur_desc->type = RAWVDU;
            if( XSBlockWrite( stream, bufsiz, buf ) < 0 )
            {
                cur_desc->type = BBC;
                return -1;
            }
            cur_desc->type = BBC;
            break;
        case TTY:
            /* VDU for output */
            cur_desc->type = VDU;
            if( XSBlockWrite( stream, bufsiz, buf ) < 0 )
            {
                cur_desc->type = BBC;
                return -1;
            }
            cur_desc->type = BBC;
            break;
        case RS423:
            for( i=0; i < bufsiz; i++ )
            {

                rs.r[0] = 153;
                rs.r[1] = rs423outbf;
                rs.r[2] = *(buf+i);
                ret_val = osbyte( &rs );

            }
            inter();
            break;
        case PRINTER:
        case NULL_DEV:
            fatal("XSBLKW unimplemented function");
            break;
        default:
            fprintf(stderr,"XSBLOCK WRITE WOW should never get here!!\n");
            return -1;
            break;
    }


    return 0;

}

int
SReadByte( stream )
int stream;
{
int ch;

    cur_desc = &streams[stream];

    switch( cur_desc->type )
    {
        case UNUSED:
            fprintf(stderr,"SREADBYTE, op on unused stream (%d)\n",stream);
            break;
        case FTYPE:
            if( (ch=fgetc( cur_desc->fp )) == EOF )
                return -9;
            else
                return ch;
            break;
        case KB:
            fatal("SRB unimplemented function");
            break;
        case RAWKB:
            while( testbf( keybf, &ch ) == 0 )
                ;
            return ch;
            break;
        case BBC:
            /* RAWKB for input */
            cur_desc->type = RAWKB;
            ch = SReadByte( stream );
            cur_desc->type = BBC;
            return ch;
            break;
        case TTY:
            /* KB for input */
            cur_desc->type = KB;
            ch = SReadByte( stream );
            cur_desc->type = BBC;
            return ch;
            break;
        case RS423:
            if( (ch = rsremove(rs423inbf)) == 0 )
                return 0;
            return ch & 0xff;
            break;
        case PRINTER:
        case NULL_DEV:
            fatal("SRB unimplemented function");
            break;
        default:
            fprintf(stderr,"SREADBYTE WOW should never get here!!\n");
            return -1;
            break;
    }


    return 0;

}

int
XSReadByte( stream )
int stream;
{
int ch;

    cur_desc = &streams[stream];

    switch( cur_desc->type )
    {
        case UNUSED:
            fprintf(stderr,"XSREADBYTE, op on unused stream (%d)\n",stream);
            break;
        case FTYPE:
            if( (ch=fgetc( cur_desc->fp )) == EOF )
                return -9;
            else
                return ch;
            break;
        case KB:
            fatal("XSRB unimplemented function");
            break;
        case RAWKB:
            while( testbf( keybf, &ch ) == 0 )
                ;
            return ch;
        case BBC:
            /* RAWKB for input */
            cur_desc->type = RAWKB;
            ch = XSReadByte( stream );
            cur_desc->type = BBC;
            return ch;
            break;
        case TTY:
            /* KB for input */
            cur_desc->type = KB;
            ch = XSReadByte( stream );
            cur_desc->type = BBC;
            return ch;
            break;
        case RS423:
            if( (ch = rsremove(rs423inbf)) == 0 )
                return 0;
            return ch & 0xff;
            break;
        case PRINTER:
        case NULL_DEV:
            fatal("XSRB unimplemented function");
            break;
        default:
            fprintf(stderr,"XSREADBYTE WOW should never get here!!\n");
            fprintf(stderr,"Type is %lx\n",cur_desc->type);
            fprintf(stderr,"Type is %lx\n",-0xe);
            return -1;
            break;
    }


    return 0;

}

int
XBytesOutstanding( stream )
int stream;
{
int cur_pos, end_pos;

    cur_desc = &streams[stream];

    switch( cur_desc->type )
    {
        case UNUSED:
            fprintf(stderr,"XBYTESOUTSTANDING, op on unused stream (%d)\n",
                                                                      stream);
            break;
        case FTYPE:
            cur_pos = ftell( cur_desc->fp );
            fseek( cur_desc->fp, 0, SEEK_END );
            end_pos = ftell( cur_desc->fp );
            fseek( cur_desc->fp, cur_pos, SEEK_SET );
            return end_pos - cur_pos;
            break;
        case KB:
        case RAWKB:
               return kbcount( 0, keybf );
            break;
        case BBC:
        case TTY:
            fatal("BO unimplemented function");
            break;
        case RS423:
            if( cur_desc->inout == IP )
                return rscount( 0, rs423inbf );
            else
                /* Output is unbufferred */
                return 0;
            break;
        case PRINTER:
        case NULL_DEV:
            fatal("BO unimplemented function");
            break;
        default:
            fprintf(stderr,"BYTES OUTSTANDING WOW should never get here!!\n");
            return -1;
            break;
    }


    return -1;
}

void
dummy()
{
    return;
}

int
XFindInput( name, namlen )
char *name;
int namlen;
{
FILE *fp;
int i;
/*static int last;*/

    if( (i = getnextstr()) == -1 )
        return -1;

    streams[i].inout = IP;

    if ( strcmp( name, VDU_STR ) == 0 )
         fatal("XFI VDU unimplemented function");
    else if ( strcmp( name, RAWVDU_STR ) == 0 )
         fatal("XFI RVDU unimplemented function");
    else if( strcmp( name, KB_STR ) == 0 )
    {
        streams[i].type = KB;
        streams[i].fp = stdin;
        if( kb_num_users == 0 )
            kbintercept();
        kb_num_users++;

    }
    else if( strcmp( name, RAWKB_STR ) == 0 )
    {
        streams[i].type = RAWKB;
        streams[i].fp = stdin;
        if( kb_num_users == 0 )
            kbintercept();
        kb_num_users++;
    }
    else if( strcmp( name, BBC_STR ) == 0 )
    {
        streams[i].type = BBC;
        streams[i].fp = stdin;
    }
    else if( strcmp( name, TTY_STR ) == 0 )
    {
        streams[i].type = TTY;
        streams[i].fp = stdin;
    }
    else if( strcmp( name, RS423_STR ) == 0 )
    {
        streams[i].type = RS423;
        if( rs423_num_users == 0 )
        {
  
            /* Disable rs423 as output stream */
            rs.r[0] = 3;
            rs.r[1] = 0;
            ret_val = osbyte( &rs );

            /* Enable kb and rs423 as input */
            rs.r[0] = 2;
            rs.r[1] = 2;
            ret_val = osbyte( &rs );

            /* disable cursor editing and fn key nums to cursor and copy keys */            rs.r[0] = 4;
            rs.r[1] = 2;
            ret_val = osbyte( &rs );

            /* Disable escape */
            rs.r[0] = 229;
            rs.r[1] = 1;
            ret_val = osbyte( &rs );

            rsintercept();
        }
        rs423_num_users++;
    }
    else if( strcmp( name, PRINTER_STR ) == 0 )
        fatal("XFI PR unimplemented function");
    else if( strcmp( name, INPUT_STR ) == 0 )
    {

        fatal("XFI IP unimplemented function");

        /* Return currently selected input stream, a bug in
           PANOS, reintroduced here for a laugh!! */
/*fprintf(stderr,"findinput for INPUT: returning %d\n",last);
        return last;*/

    }
    else if( strcmp( name, OUTPUT_STR ) == 0 )
        fatal("XFI OP unimplemented function");
    else if( strcmp( name, CTL_STR ) == 0 )
        fatal("XFI CTL unimplemented function");
    else if( strcmp( name, ERR_STR ) == 0 )
        fatal("XFI ERR unimplemented function");
    else
    {
        /* An ordinary file */
        if( (fp=fopen( name, "r")) == NULL )
            return -1;

        streams[i].type = FTYPE;
        streams[i].fp = fp;

    }   

/*    last = i;*/

    return i;

}

int
XFindOutput( name, namlen )
char *name;
int namlen;
{
FILE *fp;
int i;


    if( (i = getnextstr()) == -1 )
        return -1;

    streams[i].inout = OP; 
  
    if( strcmp( name, VDU_STR ) == 0 )
    {
        streams[i].type = VDU;
        streams[i].fp = stdout;
    }
    else if( strcmp( name, RAWVDU_STR ) == 0 )
    {
        streams[i].type = RAWVDU;
        streams[i].fp = stdout;
    }
    else if( strcmp( name, KB_STR ) == 0 )
        fatal("XFO KB unimplemented function");
    else if( strcmp( name, RAWKB_STR ) == 0 )
        fatal("XFO RKB unimplemented function");
    else if( strcmp( name, BBC_STR ) == 0 )
    {
        streams[i].type = BBC;
        streams[i].fp = stdout;
    }
    else if( strcmp( name, TTY_STR ) == 0 )
    {
        streams[i].type = TTY;
        streams[i].fp = stdout;
    }
    else if( strcmp( name, RS423_STR ) == 0 )
    {
        streams[i].type = RS423;
        if( rs423_num_users == 1 )
        {
  
            /* Disable rs423 as output stream */
            rs.r[0] = 3;
            rs.r[1] = 0;
            ret_val = osbyte( &rs );
 
            /* Enable kb and rs423 as input */
            rs.r[0] = 2;
            rs.r[1] = 2;
            ret_val = osbyte( &rs );

            /* disable cursor editing and fn key nums to cursor and copy keys */            rs.r[0] = 4;
            rs.r[1] = 2;
            ret_val = osbyte( &rs );

            /* Disable escape */
            rs.r[0] = 229;
            rs.r[1] = 1;
            ret_val = osbyte( &rs );

            rsintercept();
        }
        rs423_num_users--;
    }
    else if( strcmp( name, PRINTER_STR ) == 0 )
        fatal("XFO PR unimplemented function");
    else if( strcmp( name, INPUT_STR ) == 0 )
        fatal("XFO IP unimplemented function");
    else if( strcmp( name, OUTPUT_STR ) == 0 )
        fatal("XFO OP unimplemented function");
    else if( strcmp( name, CTL_STR ) == 0 )
        fatal("XFO CTL unimplemented function");
    else if( strcmp( name, ERR_STR ) == 0 )
        fatal("XFO ERR unimplemented function");
    else
    {
        /* An ordinary file */
        if( (fp=fopen( name, "w")) == NULL )
            return -1;

        streams[i].type = FTYPE;
        streams[i].fp = fp;

    }   
 
    return i;

}

int
XCloseStream( stream )
int stream;
{

    cur_desc = &streams[stream];

    switch( cur_desc->type )
    {
        case UNUSED:
            fprintf(stderr,"XCLOSESTREAM, op on unused stream (%d)\n",stream);
            break;
        case FTYPE:
            if( fclose( cur_desc->fp ) != 0 )
                return -1;
            break;
        case VDU:
        case RAWVDU:
            break;
        case KB:
        case RAWKB:
            if( kb_num_users == 1 )
                kbrelease();
            kb_num_users--;
            break;
        case BBC:
        case TTY:
            fatal("XC TTY unimplemented function");
            break;
        case RS423:
            if( rs423_num_users == 1 )
            {

                rs.r[0] = 2;
                rs.r[1] = 0;
                ret_val = osbyte( &rs );

                rs.r[0] = 4;
                rs.r[1] = 0;
                ret_val = osbyte( &rs );


               /* Disable escape */
               rs.r[0] = 229;
               rs.r[1] = 0;
               ret_val = osbyte( &rs );

                rsrelease();
            }
            rs423_num_users--;
            break;
        case PRINTER:
        case NULL_DEV:
            fatal("XC NULLDEV unimplemented function");
            break;
        default:
            fprintf(stderr,"CLOSESTREAM OUTPUT WOW should never get here!!\n");
            fprintf(stderr,"Type is %lx\n",cur_desc->type);
            fprintf(stderr,"Type is %lx\n",-0xe);
            return -1;
            break;
    }
                                    
    cur_desc->type = UNUSED;
    return 0;

}

int
XSFlushOutput( stream )
int stream;
{


    cur_desc = &streams[stream];

    switch( cur_desc->type )
    {
        case UNUSED:
            fprintf(stderr,"XSFLUSHOUTPUT, op on unused stream (%d)\n",stream);
            break;   
        case FTYPE:

            if( fflush( cur_desc->fp ) != 0 )
                return -1;
            else    
                return 0;
            break;
        case VDU:
        case RAWVDU:
             break;
        case KB:
        case RAWKB:
            return -1;
            break;
        case BBC:
        case TTY:
            if( cur_desc->inout == IP )
                return -1;
            break;
        case RS423:
            if( cur_desc->inout == IP )
                rspurge( 1 );
            else if( cur_desc->inout == OP )
                rspurge( 2 );
            else
                fatal("XSF RS423 unimplemented function");
            break;
        case PRINTER:
        case NULL_DEV:
            fatal("XSV NULLDEV unimplemented function");
            break;
        default:
            fprintf(stderr,"XSFLUSH OUTPUT WOW should never get here!!\n");
            fprintf(stderr,"Type is %lx\n",cur_desc->type);
            fprintf(stderr,"Type is %lx\n",-0xe);
            return -1;
            break;
    }

    return 0;

}

int
Expand( wildname, wildnamelen, addfn, arg, dir )
char *wildname;
int wildnamelen;
int (*addfn)();
int arg, dir ;
{
fatal("Expand - not yet implemented!!\n");
    return -1;
}

static int
getnextstr()
{
int i;
  
    for(i=0; i < MAXSTREAMS; i++ )
        if( streams[i].type == UNUSED )
            return i;

    return -1;

}

int
isatty( stream )
int stream;
{
    
    /* Assume stdin and stdout, are never redirected!!! */
    return 1;

}

int
XStandardTime( time, timelen )
char *time;
int timelen;
{

    time[0] = 0;
    if( (ret_val = osword( 0x0e, (int*)time )) != NULL )
    {
        fprintf(stderr,"%s (%d)\n",ret_val->errmess,ret_val->errnum);
        return -1;
    }

    time[24] = '\0';
    return 0;


}


int
XBinaryTime( time )
timeval * time;
{
               
    time->low = 0x3;
    if( (ret_val = osword( 0x0e, (int*)time )) != NULL )
    {
        fprintf(stderr,"%s (%d)\n",ret_val->errmess,ret_val->errnum);
        return -1;
    }

    return 0;

}

/*
 *      (c) Cosmos Nicolaou 14/6/87
 *
 *      4.2 BSD style directory operations.
 */

DIR *
opendir( filename )
char *filename;
{
DIR * dirp;

        if( (dirp = (DIR*)malloc(sizeof(DIR))) == NULL )
                return NULL;

        if( (dirp->dd_dirent =
                (struct dirent*)malloc(sizeof(struct dirent))) == NULL )
                return NULL;

        dirp->dd_pos = 0;
        strcpy( dirp->dd_name, filename );

        return dirp;

}


struct dirent *
readdir( dirp )
DIR *dirp;
{
static osgbpb_block par;
struct dirent *dp;
char *name;

    par.action = 9;
    par.file_handle = (int)dirp->dd_name;
    par.data_addr = dirp->dd_buf;
    par.number = 1;
    par.seq_point = dirp->dd_pos;
    par.buf_len = sizeof( dirp->dd_buf );
    par.wild_fld = "*";

    if( osgbpb( &par ) != NULL )
        return NULL;

    if( (par.number != 1) || (par.seq_point == -1) )
        return NULL;

    name = dirp->dd_buf;

    dp = dirp->dd_dirent;

    dp->d_namlen = strlen( name );
    dp->d_reclen = sizeof(struct dirent) - (MAXNAMLEN+1) +
                                                  dp->d_namlen + 1;
    strcpy( dp->d_name, name );

    dirp->dd_pos++;

    return dp;

}

long
telldir( dirp )
DIR *dirp;
{
        return dirp->dd_pos;
}

void
seekdir( dirp, loc )
DIR *dirp;
long loc;
{
        dirp->dd_pos = loc;
        return;
}

void
closedir( dirp )
DIR *dirp;
{

        free( dirp->dd_dirent );
        free( dirp );
        return;
}
