/**************************************************************************
*
*  Alien File Server
*
*  12th March 1987.
*
*  Copyright INMOS Limited, 1987.
*
*  Upgrade to 3L-Server by Gerhard Bartz
*
*  18th April 1989
*
***************************************************************************/

#define LINT_ARGS 1      /* Enable Compiler Parameter Checking */
#include <stdio.h>
#include <stat.h>
#include <time.h>
#include <string.h>

#include "srvconst.h"

extern char *mktemp();   

extern unsigned int link_base;
/* Server static variables */
/* Transputer command line buffer. Contains parameters from the server */
/* command line which the server does not recognise. */
static char tcom[COMMAND_LINE_LENGTH];
static int tcominptr = 0, tcomoutptr = 0;
static int terminate_code;                    /* Server result */
static int transputer_result = 0;             /* Result from transputer */
static int command;
static int boot_file_exists = FALSE;          /* If true, use boot file */
static int alien_option_flag = 0x0;
static char version[] =
   "Alien file server V1.3 (14th October 1987) + 3L 15th January 1988\n";
static char copyright[] =
   "Copyright INMOS Ltd, 1985\n";

static struct STREAM_DESC filers[N_STREAMS];

static char *open_types [MAX_OPEN_MODE+1]
  [MAX_EXIST_MODE+1][MAX_ACCESS_METHOD+1] =
    { { { "r",  "r"  }, { "r",  "r" } },
      { { "r+", "r+" }, { "w",  "w" } },
      { { "r+", "r+" }, { "w+", "w+"} } };
/* Server global variables */
int running;                   /* Server is running flag */
int test_error_flag = FALSE;   /* If true, test error flag */

void terminate_server (t_code)
int t_code;
/* Set flags to terminate the server. */
{
    terminate_code = t_code;
    running = FALSE;
}

int fexists (file_str)
char *file_str;
/* Return TRUE if the file whose name is in file_str exists,
   FALSE otherwise. */
{
    FILE *stream;
    if ( (stream = fopen (file_str, "r")) != NULL )
    {
        fclose (stream);
        return (TRUE);
    } else
        return (FALSE);
}

int filer_close_file (streamp, close_mode)
struct STREAM_DESC *streamp;
int close_mode;
{
    int res = F_OK;
    if ( (close_mode >= 0) && (close_mode <= MAX_CLOSE_OPTION) )
    {
        if (streamp->lifetime_m != SPECIAL)
        {
            if (fclose (streamp->fileptr) == 0)
            {
if ((close_mode == CLOSEDEL_OPTION) ||
    (streamp->lifetime_m == TEMPORARY))
{
    if (unlink (streamp->name) != 0)
    {
        res = OPERATIONFAILED_ERR;
    };
};
                streamp->inuse = FALSE;
            } else
            {
                res = OPERATIONFAILED_ERR;
                /* Remove our reference anyway */
                streamp->inuse = FALSE;
            };
        } else
            streamp->inuse = FALSE; /* Just remove our reference */
    } else
        res = INVALIDCLOSEOPTION_ERR;
    return (res);
}

void filer_close ()
{
    int i;
    for (i = 0; i < N_STREAMS; i++)
        if ( filers[i].inuse )
        {
            filer_close_file ( &(filers[i]), CLOSE_OPTION );
            filers[i].inuse = FALSE;
        };
}

void record_to_string (rec, len, str)
char *rec;
int len;
char *str;
{
  int i;
  for (i = 0; i < len; i++)
    *str++ = rec[i];
  *str = '\0';
}

void string_to_record (str, rec, len)
char *str;
char *rec;
int  *len;
{
    int i = 0;
    while (*str != '\0')
        rec[i++] = *str++;
    *len = i;
}

int find_stream ()
{
    int i = 0;
    int found = FALSE;
    while( (i < N_STREAMS) && (found == FALSE) )
    {
        if ((filers[i].inuse) == FALSE)
            found = TRUE;
        else
            i++;
    };
    if (found)
        return (i);
    else
        return (INVALID_STREAM);
}


int valid_stream (n)
int n;
{
    if ( (n >= 0) && (n < N_STREAMS) )
        if (filers[n].inuse)
            return (TRUE);
    return (FALSE);
}

int make_file_name (n)
char *n;
{
    char *template = "afXXXXXX";
    char *result;
    result = mktemp(template);
    strcpy (n, result);
    if (*n=='\0') return(FALSE);
    return (TRUE);
}

int make_af_err (c_err)
int c_err;
{
    return (HOST_ERROR_BASE - c_err);
}

int seek_file_length (fptr, length)
FILE *fptr;
long *length;
{
    long curpos;
    int result = F_OK;
    curpos = ftell(fptr);                    /* Remember where we are    */
    if (fseek(fptr, 0L, 2) == 0)             /* Go to the end to find    */
        *length = ftell(fptr);               /* where it is              */
    else
        result = OPERATIONFAILED_ERR;
    if (fseek(fptr, curpos, 0) != 0)            /* Go back to where */
        result = OPERATIONFAILED_ERR;          /* We were          */
    return (result);
}

int tag_from_link ()
{
    return (word_from_link());
}

int read_integer (v)
int *v;
{
    if (tag_from_link() == INT32_VALUE)
    {
        *v = word_from_link ();
        return (TRUE);
    } else
    {
        fprintf (stderr,"Last command = %n\n", command);
        terminate_server (T_BAD_INT32);
        return (FALSE);
    };
}

extern long int long_word_from_link();
int read_long_integer (v)
long int *v;
{
    if (tag_from_link() == INT32_VALUE)
    {
        *v = long_word_from_link ();
        return (TRUE);
    } else
    {
        fprintf (stderr,"Last command = %n\n", command);
        terminate_server (T_BAD_INT32);
        return(FALSE);
    };
}

int read_record (len, buffer)
int *len;
char *buffer;
{
    int t = tag_from_link ();
    if (t == NILRECORD_VALUE)
    {
        *len = 0;
        return (TRUE);
    } else
        if (t == RECORD32_VALUE)
        {
            *len = trunc_slice_from_link (RECORD_LENGTH, buffer);
            return (TRUE);
        } else
        {
            fprintf (stderr, "Last command = %n\n", command);
            terminate_server (T_BAD_RECORD);
            return (FALSE);
        };
}

int read_long_record (len, buffer)
long int *len;
char *buffer;
{
    int t = tag_from_link ();
    if (t == NILRECORD_VALUE)
    {
        *len = 0;
        return (TRUE);
    } else
        if (t == RECORD32_VALUE)
        {
            long_slice_from_link (len, buffer);
            return (TRUE);
        } else
        {
            fprintf (stderr, "Last command = %n\n", command);
            terminate_server (T_BAD_RECORD);
            return (FALSE);
        };
}

void tag_to_link (tag)
int tag;
{
    word_to_link (tag);
}

void write_integer (n)
int n;
{
    tag_to_link (INT32_VALUE);
    word_to_link (n);
}

void write_long_integer (n)
long int n;
{
    tag_to_link (INT32_VALUE);
    long_word_to_link (n);
}

void write_record (len, buffer)
int len;
char *buffer;
{
    if (len == 0)
        tag_to_link (NILRECORD_VALUE);
    else
    {
        tag_to_link (RECORD32_VALUE);
        slice_to_link (len, buffer);
    };
}

void write_long_record (len, buffer)
long int len;
char *buffer;
{
    if (len == 0)
        tag_to_link (NILRECORD_VALUE);
    else
    {
        tag_to_link (RECORD32_VALUE);
        long_slice_to_link (len, buffer);
    };
}

void open_temp ()
{
    int access_method, record_length;
    int stream_id = INVALID_STREAM;
    int res = F_OK;
    if (read_integer (&access_method))
    if (read_integer (&record_length))
{
if ((access_method >= 0) && (access_method <= MAX_ACCESS_METHOD))
{
stream_id = find_stream ();
if (stream_id >= 0)
{
    struct STREAM_DESC *streamp = &filers[stream_id];
    if (make_file_name (streamp->name))
    {
char *type = open_types[UPDATE_MODE][NEW_FILE][access_method];
FILE *fptr;
fptr = fopen (streamp->name, type);
if (fptr != NULL)
{
    streamp->inuse = TRUE;
    streamp->fileptr = fptr;
    streamp->open_m = UPDATE_MODE;
    streamp->exist_m = NEW_FILE;
    streamp->access_m = access_method;
    streamp->lifetime_m = TEMPORARY;
    streamp->result = F_OK;
} else
    res = OPERATIONFAILED_ERR;
    } else
        res = OPERATIONFAILED_ERR;
} else
    res = NOFREECHANNEL_ERR;
} else
    res = INVALIDACCESSMETHOD_ERR;
write_integer (stream_id);
write_integer (res);
};
}

void open_file (lifetime)
int lifetime;
{
    char filename [FILE_NAME_LENGTH];
    int len = 0;
    int access_method, open_mode, exist_mode, record_length;
    int res = F_OK;
    if (read_record (&len, filename))
    if (read_integer (&access_method))
    if (read_integer (&open_mode))
    if (read_integer (&exist_mode))
    if (read_integer (&record_length))
{
    int stream = INVALID_STREAM;
    if ((access_method >= 0) && (access_method <= MAX_ACCESS_METHOD))
    {
        if ((open_mode >= 0) && (open_mode <= MAX_OPEN_MODE))
        {
            if ((exist_mode >= 0) && (exist_mode <= MAX_EXIST_MODE))
            {
stream = find_stream ();
if (stream >= 0)
{
    struct STREAM_DESC *streamp = &filers[stream];
    record_to_string (filename, len, streamp->name);
    {
char *type = open_types[open_mode][exist_mode][access_method];
FILE *fptr;
fptr = fopen (streamp->name, type);
if (fptr != NULL)
{
    streamp->inuse = TRUE;
    streamp->fileptr = fptr;
    streamp->open_m = open_mode;
    streamp->exist_m = exist_mode;
    streamp->access_m = access_method;
    streamp->lifetime_m = lifetime;
    streamp->result = F_OK;
} else
    res = OPERATIONFAILED_ERR;
    };
} else
    res = NOFREECHANNEL_ERR;
            } else
                res = INVALIDEXISTMODE_ERR;
        } else
            res = INVALIDOPENMODE_ERR;
    } else
        res = INVALIDACCESSMETHOD_ERR;
    write_integer (stream);
    write_integer (res);
};
}

void open_stream (open_mode)
int open_mode;
{
    int stream_id = INVALID_STREAM;
    int std_stream_no;
    int res = F_OK;
    if (read_integer(&std_stream_no))
    {
        if (open_mode == S_INPUT)
        { 
switch (std_stream_no)
{
case 0:
    stream_id = find_stream ();
    if (stream_id >= 0)
    {
struct STREAM_DESC *streamp = &filers[stream_id];
streamp->inuse = TRUE;
strcpy (streamp->name, "stdin");
streamp->fileptr = stdin;
streamp->open_m = READ_MODE;
streamp->exist_m = OLD_FILE;
streamp->access_m = TEXTBYTESTREAM_ACCESS;
streamp->lifetime_m = SPECIAL;
streamp->result = F_OK;
    } else
        res = NOFREECHANNEL_ERR;
    break;
case 1:
    stream_id = PARAM_STREAM;
    break;
default:
    res = INVALIDSTDSTREAM_ERR;
};
        } else
            if (open_mode == S_OUTPUT)
            {
switch (std_stream_no)
{
case 0:
    stream_id = find_stream ();
    if (stream_id >= 0)
    {
struct STREAM_DESC *streamp = &filers[stream_id];
streamp->inuse = TRUE;
strcpy (streamp->name, "stdout");
streamp->fileptr = stdout;
streamp->open_m = WRITE_MODE;
streamp->exist_m = OLD_FILE;
streamp->access_m = TEXTBYTESTREAM_ACCESS;
streamp->lifetime_m = SPECIAL;
streamp->result = F_OK;
    } else
        res = NOFREECHANNEL_ERR;
    break;
case 1:
    stream_id = find_stream ();
    if (stream_id >= 0)
    {
struct STREAM_DESC *streamp = &filers[stream_id];
streamp->inuse = TRUE;
strcpy (streamp->name, "stderr");
streamp->fileptr = stderr;
streamp->open_m = WRITE_MODE;
streamp->exist_m = OLD_FILE;
streamp->access_m = TEXTBYTESTREAM_ACCESS;
streamp->lifetime_m = SPECIAL;
streamp->result = F_OK;
    } else
        res = NOFREECHANNEL_ERR;
    break;
default:
    res = INVALIDSTDSTREAM_ERR;
};
            };
        write_integer (stream_id);
        write_integer (res);
    };
}

void close_stream ()
{
    int stream_id = INVALID_STREAM;
    int close_mode;
    int res = F_OK;
    if (read_integer(&stream_id))
    if (read_integer(&close_mode))
    {
        if (valid_stream (stream_id))
        {
            res = filer_close_file (&(filers[stream_id]), close_mode);
        } else
        {
            if (stream_id == PARAM_STREAM)
                tcomoutptr = 0;
            else
                res = INVALIDSTREAMID_ERR;
        };
        write_integer (res);
    };
}

void read_block ()
{
    int stream_id = INVALID_STREAM;
    int record_len = 0;
    int res = F_OK;
    int bytes_read = 0;
    char buffer[RECORD_LENGTH];
    if (read_integer (&stream_id))
    if (read_integer (&record_len))
    { 
        if (valid_stream (stream_id))
{
    struct STREAM_DESC *streamp = &filers[stream_id];
    if ((record_len >= 0) && (record_len <= RECORD_LENGTH))
    {
        if (streamp->fileptr == stdin)
        {
            int chr = '\0';
            while ((chr != EOF) && (bytes_read < record_len))
            {
                chr = getchar();
                buffer[bytes_read++] = chr;
                if (chr == EOF)
                    bytes_read--;            /* Remove EOF from block */
                else if (chr == '\n')
                    break;
            }
        } else
            bytes_read = fread(buffer, 1, record_len, streamp->fileptr);
        if (feof(streamp->fileptr))
        {
            streamp->result = F_EOF; res = F_EOF;
        } else
        {
            int r = ferror(streamp->fileptr);
            streamp->result = make_af_err(r);
            if (r != 0)
                res = OPERATIONFAILED_ERR;
        };
    } else
        res = INVALIDRECORDLENGTH_ERR;
        } else
        {
            if (stream_id == PARAM_STREAM)
{
    if ((record_len >=0) && (record_len <= RECORD_LENGTH))
    {
        bytes_read = tcomread(buffer, record_len);
        if (bytes_read < record_len)
            res = F_EOF;
    } else
        res = INVALIDRECORDLENGTH_ERR;
        } else
            {
                res = INVALIDSTREAMID_ERR;
            };
        };
        write_record (bytes_read, buffer);
        write_integer (res);
    };
}

void write_block ()
{
    int stream_id = INVALID_STREAM;
    int record_len = 0;
    int bytes_written = 0;
    char buffer [RECORD_LENGTH];
    int res = F_OK;
    if (read_integer (&stream_id))
    if (read_record (&record_len, buffer))
    {
        if (valid_stream (stream_id))
{
    struct STREAM_DESC *streamp = &filers[stream_id];
    bytes_written = fwrite (buffer, 1, record_len, streamp->fileptr);
    if (bytes_written != record_len)
    {
        streamp->result = make_af_err(ferror(streamp->fileptr));
        res = OPERATIONFAILED_ERR;
    };
        } else
            res = INVALIDSTREAMID_ERR;
        write_integer (bytes_written);
        write_integer (res);
    };
}

void stream_seek ()
{
    int stream_id = INVALID_STREAM;
    long int offset = 0;
    int res = F_OK;
    if (read_integer (&stream_id))
    if (read_long_integer (&offset))
    {
        if (valid_stream (stream_id))
        {
            struct STREAM_DESC *streamp = &filers[stream_id];
            int r;
            if ((r = fseek(streamp->fileptr, offset, 0)) != 0)
            {
                streamp->result = make_af_err(r);
                res = OPERATIONFAILED_ERR;
            } else
                streamp->result = F_OK;
        } else
        {
            if (stream_id == PARAM_STREAM)
                res = NOSEEKPOSSIBLE_ERR;
            else
                res = INVALIDSTREAMID_ERR;
        };
        write_integer (res);
    };
}

void stream_file ()
{
    int stream_id;
    if (read_integer (&stream_id))
    {
        char filename[RECORD_LENGTH];
        int len = 0;
        int res = F_OK;
        if (valid_stream(stream_id))
        {
            struct STREAM_DESC *streamp = &filers[stream_id];
            string_to_record (streamp->name, filename, &len);
        } else
        {
            if (stream_id == PARAM_STREAM)
                string_to_record ("Command line", filename, &len);
            else
                res = INVALIDSTREAMID_ERR;
        };
        write_record (len, filename);
        write_integer (res);
    };
}

void stream_connect ()
{
    int stream_id;
    if (read_integer (&stream_id))
    {
        int device_connected = -1;
        int res = F_OK;
        if (valid_stream(stream_id))
{
    struct STREAM_DESC *streamp = &filers[stream_id];
    struct stat buffer;
    if (stat(streamp->name, &buffer) == 0)
    {
if (isatty(fileno(streamp->fileptr)))
{   /* We are connected to a device */
    if (strcmp ("stdin", streamp->name) == 0)
        device_connected = KEYBOARD_USE;
    else
    {
        if ((strcmp ("stdout", streamp->name) == 0) ||
            (strcmp ("stderr", streamp->name) == 0))
            device_connected = SCREEN_USE;
        else
            res = OPERATIONFAILED_ERR;
    };
} else
{
    if (buffer.st_attr & ST_DELETE)
    { /* We are connected to a file */
        if (streamp->lifetime_m == TEMPORARY)
            device_connected = TEMP_USE;
        else
            device_connected = FILE_USE;
    } else
        res = OPERATIONFAILED_ERR;
};
    } else
        res = OPERATIONFAILED_ERR;
        } else
        {
            if (stream_id == PARAM_STREAM)
                device_connected = PARAMETER_USE;
            else
                res = INVALIDSTREAMID_ERR;
        };
        write_integer (device_connected);
        write_integer (res);
     };
}

void stream_access ()
{
    int stream_id;
    if (read_integer (&stream_id))
    {
        int access_method = 0;
        int res = F_OK;
        if (valid_stream(stream_id))
        {
            struct STREAM_DESC *streamp = &filers[stream_id];
            access_method = streamp->access_m;
        } else
        {
            if (stream_id == PARAM_STREAM)
                access_method = TEXTBYTESTREAM_ACCESS;
            else
            res = INVALIDSTREAMID_ERR;
        };
        write_integer (access_method);
        write_integer (res);
     };
}

void stream_length ()
{
    int stream_id;
    if (read_integer (&stream_id))
    {
        int res = F_OK;
        long int len = 0L;
        if (valid_stream(stream_id))
        {
            struct STREAM_DESC *streamp = &filers[stream_id];
            res = seek_file_length(streamp->fileptr, &len);
        } else
        {
            if (stream_id == PARAM_STREAM)
                len = (long) tcominptr;
            else
                res = INVALIDSTREAMID_ERR;
        };
        write_long_integer (len);
        write_integer (res);
     };
}

void stream_status ()
{
    int stream_id;
    if (read_integer (&stream_id))
    {
        int res = F_OK;
        if (valid_stream(stream_id))
        {
            struct STREAM_DESC *streamp = &filers[stream_id];
            res = streamp->result;
        } else
        {
            if (stream_id == PARAM_STREAM)
            {
                if (tcominptr == tcomoutptr)
                    res = F_EOF;
                else
                    res = F_OK;
            } else
                res = INVALIDSTREAMID_ERR;
        };
        write_integer (res);
     };
}

void set_result ()
{
    if (read_integer(&transputer_result))
    {
        write_integer (F_OK);
    }
}

void run_command ()
{
    int record_len = 0, res = F_OK;
    char buffer [RECORD_LENGTH + 1];
    if (read_record (&record_len, buffer))
    {
        buffer [record_len] = '\0';
        res = 1;                                  /* If ok, returns 0 */
        if (res != 0)                             /* Not ok, returns -1 */
            write_integer (OPERATIONFAILED_ERR);
        else
            write_integer (F_OK);
    };
}

void rename_file ()
{
    int old_len = 0;
    int new_len = 0;
    char old_name [RECORD_LENGTH], new_name [RECORD_LENGTH];
    int res = F_OK;
    if (read_record (&old_len, old_name))
    if (read_record (&new_len, new_name))
    {
        res = rename (new_name, old_name);       /* If ok, returns 0 */
        if (res != 0)                          /* Not ok, returns non-zero */
            write_integer (OPERATIONFAILED_ERR);
        else
            write_integer (F_OK);
    };
}

void read_time ()
{
    /* beinhaltet Umrechnung eines bitkodierten Datums (Amiga)  */
    /* auf die Sekunden seit dem 1.1.1970, 0.00 h               */
    long int seconds,dummy;
    (void) time(&dummy);
    seconds =((dummy<<1 )&31L) + (60L*((dummy<<6 )&31L));
    seconds+=(3600L*((dummy<<11)&31L))+(24L*3600L*((dummy<<16)&31L));
    seconds+= (30L*24L*3600L*((dummy<<21)&15L));
    seconds+=(365L*24L*3600L*((dummy<<25)&31L));
    seconds+=(10L*365L*24L*3600L) - (30L*24L*3600L);
    write_long_integer (seconds);
    write_integer (F_OK);
}

void read_key ()
{
    int key = 0, key_hit=0;        /*1*/
                     /* character in keyboard buffer ? */
    if (key_hit != 0)                /* returns non-zero if key pressed */
        key = (int)getchar();        /* get key and echo it */
    write_integer (key);
    if (key_hit != 0) 
        write_integer (F_OK);
    else 
    write_integer (OPERATIONFAILED_ERR);
}


/**** ADD1 begin ****/

void run_time_data()
{
   int option;

   if (read_integer(&option))
   {
      switch (option)
      {
         case 0:
            write_integer(alien_option_flag);
            write_integer(F_OK);
            break;
         default:
            write_integer(0);
            write_integer(OPERATIONFAILED_ERR);
            break;
      }
   }
}


void read_environment()
{
   int block_size;
   char buffer[RECORD_LENGTH+1];

   if (read_record(&block_size,buffer))
   {
      char *ptr;
	  extern char *getenv();

	  buffer[block_size] = '\0';
	  ptr = getenv(buffer);
	  if (ptr==NULL)
	  {
         write_record(0,"");
         write_integer(OPERATIONFAILED_ERR);
	  }
	  else
	  {
	     int record_len=0;
		 char record[RECORD_LENGTH+1];

		 string_to_record(ptr,record,&record_len);
		 write_record (record_len,record);
		 write_integer(F_OK);
      }
   }
}

/****  ADD1 end ****/


void recieve_block ()
{
    long int source, block_size;
    if (read_long_integer (&source))
    if (read_long_integer (&block_size))
    {
        write_long_record(block_size, (char *) source);
        write_integer(F_OK);
    }
}

send_block ()
{
    long int destination, block_size;
    if (read_long_integer (&destination))
    if (read_long_record (&block_size, (char *) destination))
    {
        write_long_integer(block_size);
        write_integer(F_OK);
    }
}

/**** ADD2 begin ****/

call_interrupt ()
{
   /* NOT AVAILABLE ON AMIGA, JUST KEEP PROTOCOL */
   unsigned char buffer[RECORD_LENGTH+1];
   int interrupt,block_size, res=OPERATIONFAILED_ERR;

   if (read_integer(&interrupt))
   if (read_record(&block_size,buffer))
   {
      write_integer(0);
      write_record(0,"");
      write_integer(res);
   }
}

read_regs()
{
   /* NOT AVAILABLE ON AMIGA */
   write_record(0,"");
   write_integer(OPERATIONFAILED_ERR);
}

void port_read()
{
   /* NOT AVAILABLE ON AMIGA */
   int address;

   if (read_integer(&address))
   {
      write_integer(0);
      write_integer(OPERATIONFAILED_ERR);
   }
}


void port_write()
{
   /* NOT AVAILABLE ON AMIGA */
   int address, value;

   if(read_integer(&address))
   if(read_integer(&value))
   {
      write_integer(OPERATIONFAILED_ERR);
   }
}

/**** ADD2 end ****/

void add_to_tcom (str)
char *str;
{
    char c;
    while ((c = *str++) != '\0')
        tcom[tcominptr++] = c;
    tcom[tcominptr++] = ' ';
}

int tcomread (buffer, len)
char *buffer;
int len;
{
    int bytes_read = 0;
    while ( (tcomoutptr < tcominptr) && (bytes_read < len) )
    {
        *buffer++ = tcom[tcomoutptr++];
        bytes_read++;
    };
    return (bytes_read);
}

int read_number (str)
char *str;
{
    int result = 0;
    int base;
    int carry_on;
    int c;
if (*str == '#')
{
    base = 16; str++;
} else
    base = 10;
    carry_on = TRUE;
    while (carry_on)
    {
       c = *str++;
        if ((c >= '0') && (c <= '9'))
            result = (result * base) + c - '0';
        else
        {
            if (base == 16)
            {
                if ((c >= 'a') && (c <= 'f'))
                    result = (result * base) + 10 + c - 'a';
                else
                {
                    if ((c >= 'A') && (c <= 'F'))
                        result = (result * base) + 10 + c - 'A';
                    else
                        carry_on = FALSE;
                };
            } else
                carry_on = FALSE;
        };
    };
    return (result);
}

int parse_command (argc, argv, boot_file_name)
  int argc;
  char *argv[], *boot_file_name;
/* Parse the command line typed in by the user. */
{
    char *ch;
    int i = 1;
    strcpy (boot_file_name, DEFAULT_BOOT_FILE);
    while (i < argc)
    {
ch = argv[i++];
switch (*ch++)
{
case '-':
case '/':
switch (*ch++)
{
    case ':' :
switch (*ch++)
{
case 'b':
if (i < argc)
{
    boot_file_exists = TRUE;
    strcpy (boot_file_name, argv[i++]);
}
else
{
    printf ("Missing boot file name parameter\n");
    return (FALSE);
};
break;
case 'l':
if (i < argc)
{
    link_base = read_number (argv[i++]);
} else
{
    printf ("Missing link address parameter\n");
    return (FALSE);
};
break;
case 'i':
printf (version);
printf ("Copyright INMOS Limited, 1987\n");
printf ("Amiga-Version modified by SANG-Computersysteme GmbH, 1988\n");
break;
case 'e':
test_error_flag = TRUE;
break;
/**** ADD3 begin ****/
case 'o':
if (i<argc)
{
   alien_option_flag = read_number(argv[i++]);
}
else
{
   printf("Missing alien option parameter\n");
   return(FALSE);
}
break;
/**** ADD3 end ****/
default:
    add_to_tcom ( argv[i-1] );
    break;
};
break;
    default :
        add_to_tcom ( argv[i-1] );
        break;
};
break;
default:
    add_to_tcom ( argv[i-1] );
    break;
};
    };
    return (TRUE);
}

void read_link ()
/* Read a message coming down the link. */
{
    if (read_integer (&command))
    {
#ifdef DEBUG
        printf("Transputer sending command %d \n",command);
#endif
        switch (command)
{
case TERMINATE_CMD:
    filer_close ();
    write_integer (F_OK);
    terminate_server (T_TERMINATED);
    break;
case ALIENTERMINATE_CMD:
    write_integer (F_OK);
    break;
case OPENFILE_CMD:
    open_file (PERMANENT); break;
case OPENTEMP_CMD:
    open_temp (); break;
case OPENINPUTSTREAM_CMD:
    open_stream (S_INPUT); break;
case OPENOUTPUTSTREAM_CMD:
    open_stream (S_OUTPUT); break;
case STREAMACCESS_CMD:
    stream_access (); break;
case STREAMSTATUS_CMD:
    stream_status (); break;
case STREAMFILE_CMD:
    stream_file (); break;
case STREAMLENGTH_CMD:
    stream_length (); break;
case STREAMCONNECT_CMD:
    stream_connect (); break;
case CLOSESTREAM_CMD:
    close_stream (); break;
case READBLOCK_CMD:
    read_block (); break;
case WRITEBLOCK_CMD:
    write_block (); break;
case SEEK_CMD:
    stream_seek (); break;
case SETRESULT_CMD:
    set_result (); break;
case RUNCOMMAND_CMD:
    run_command (); break;
case RENAMEFILE_CMD:
    rename_file (); break;
case READTIME_CMD:
    read_time (); break;
case RUNTIMEDATA_CMD:
    run_time_data(); break;
case READENVIRONMENT_CMD:
    read_environment(); break;
case READKEY_CMD:
    read_key (); break;
case RECIEVEBLOCK_CMD :
    recieve_block (); break;
case SENDBLOCK_CMD:
    send_block (); break;
/**** ADD4 begin ****/
case CALLINTERRUPT_CMD:
    call_interrupt(); break;
case READREGS_CMD:
    read_regs(); break;
case PORTREAD_CMD:
    port_read(); break;
case PORTWRITE_CMD:
    port_write(); break;
/**** ADD4 end ****/
default:
    terminate_server (T_ILLEGAL_COMMAND);
}
    };
}

void root_error ()
/* Process an error in the transputer system. */
{
    terminate_server (T_TRANSPUTER_ERR);
}

void terminate_message (t_code)
int t_code;
/* Report the server's reason for termination. */
{
    switch (t_code)
    {
    case T_TERMINATED:        break;
    case T_USR_STOP:
    case T_USR_BRK:
          fprintf (stderr, "Server aborted by user\n"); break;
    case T_TRANSPUTER_ERR:
          fprintf (stderr, 
                "Server terminated: error in transputer system\n");
          break;
    case T_BAD_BOOT:
          fprintf (stderr, 
                "Server terminated: cannot boot root transputer\n");
          break;
    case T_ILLEGAL_COMMAND:
        fprintf (stderr,
              "Server terminated: illegal filer command received\n");
          break;
    case T_BAD_COMMAND_LINE:
          fprintf (stderr, "Server terminated: bad command line\n");
          break;
    case T_BAD_RECORD:
          fprintf (stderr,
                  "Server terminated: bad protocol when expecting record\n");
          break;
    case T_BAD_INT32:
          fprintf (stderr,
                  "Server terminated: bad protocol when expecting INT32\n");
          break;
    }
}

int root_init( boot_file_name, analyse_flag )
char boot_file_name[];
int analyse_flag;
/* Initialise the root transputer board by resetting it and sending the boot
   file to it. */
{
    init_root();
    if (boot_file_exists)
    {
        printf ( "Booting root transputer ...\n" ); 
        if (analyse_flag)
            reset_analyse_root();
        else
            reset_root();
        return (boot_root ( boot_file_name ));
    }
    else
        return(F_OK);
}

void filer_init ()
{
    int i;
    for (i = 0; i < N_STREAMS; i++)
        filers[i].inuse = FALSE;
}

/**** REMARK: DIFFERENT ****/

int usr_brk_handler (sig)
int sig;
/* Process a user break ( control-break on the keyboard ). */
{
    terminate_server(T_USR_BRK);
}

int server_main()
{
   while (running)
   {
          if(link_in_test())  read_link();
     Chk_Abort();
   }
}

int main (argc, argv, envp)
int argc;
char *argv[], *envp[];
{
    int analyse_flag = FALSE,   /* Boot transputer in analyse mode flag */
        init_flag;              /* Boot transputer flag */
    char boot_file_name[FILE_NAME_LENGTH];
    if (!parse_command(argc, argv, boot_file_name))  /* Parse command line */
    {
        terminate_code = T_BAD_COMMAND_LINE;
        init_flag = FALSE;
    }
    else
        init_flag = TRUE;
    while (init_flag)
    {
running = TRUE;
init_flag = FALSE;
terminate_code = 0;
filer_init();
if (root_init (boot_file_name, analyse_flag) == F_OK)
{
/*    signal (SIGINT, usr_brk_handler); */
    server_main ();
switch (terminate_code)
{
    case T_TRANSPUTER_ERR:
        fprintf (stderr, "Error in transputer system\n");
    case T_USR_BRK:
    {
        int key;
        filer_close();
        analyse_flag = TRUE;
        break;
    }
    default: break;
};
}
else
    terminate_code = T_BAD_BOOT;
    };
    terminate_message (terminate_code);
    exit(transputer_result);
}
