/*trace.c - simple trace*/

/*OSLib---efficient, type-safe, transparent, extensible,\n"
   register-safe A P I coverage of RISC O S*/
/*Copyright  1994 Jonathan Coxhead*/

/*
      OSLib is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 1, or (at your option)
   any later version.

      OSLib is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
   along with this programme; if not, write to the Free Software
   Foundation, Inc, 675 Mass Ave, Cambridge, MA 02139, U S A.
*/

/* This source file has no specification, which is a bit of a shame.
 * What we do depends on the stream, but the general idea is to send
 * everything along in a form which can be read at the far end. This
 * means, for files, just send it; for VDU, ungstrans it; for tube, render
 * it in a C-like way; but in every case leave \n and \r alone, but
 * append a \r if there was a final \n.
 */

#undef  TRACE
#define TRACE 1

/*From CLib*/
#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/*From OSLib*/
#include "oslib/types.h"
//#include "oslib/econet.h"
#include "oslib/macros.h"
#include "oslib/os.h"
#include "oslib/osargs.h"
#include "oslib/osfile.h"
#include "oslib/osfind.h"
#include "oslib/osgbpb.h"

/*From Support*/
#include "hostfs.h"
//#include "socket.h"
#include "trace.h"

#define TRACE_BUFFER_SIZE 1024

static enum {STDOUT, DISC, TUBE, NOWHERE, ECONET, VDU, INTERNET} Trace =
      VDU;

static os_error *Error = NULL;
//static int       Station, Net; /*for ECONET*/
//static socket_s  S; /*for INTERNET*/
static os_f      F; /*for DISC*/

char *trace_file;
int  trace_line;
/*------------------------------------------------------------------------*/

#if 0
static os_error *Send
(
   char *s,
   int n
)
{
   int       status;
   os_error *error = 0;
   osbool nonlocal;
   do
      if ((error = xeconet_do_transmit (0x81, 0xF0, Station, Net, (byte *) s,
            n, 1, 0, &status, NULL)) != NULL)
         goto finish;
   while (status != econet_STATUS_TRANSMITTED);

finish:
   return error;
}
#endif

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

os_error *trace_initialise
(
   char *var
)
{
   os_error *error = NULL;
   char      to [80 + 1], *val;

   if ((val = getenv (var)) == NULL)
      strcpy (to, "v");
   else
      strcpy (to, val);

   switch (to [0])
   {
#if 0
      case 'e': case 'E':
         if ((error = xeconet_read_station_number (to + 1, NULL, &Station,
               &Net)) != NULL)
            goto finish;

         Trace = ECONET;
      break;
#endif

      case 'f': case 'F':
         if ((error = xosfind_openout (osfind_NO_PATH |
               osfind_ERROR_IF_ABSENT | osfind_ERROR_IF_DIR, to + 1, NULL,
               &F)) != NULL)
            goto finish;

         if ((error = xosfile_set_type (to + 1, osfile_TYPE_TEXT)) != NULL)
            goto finish;

         Trace = DISC;
      break;

#if 0
    case 'i': case 'I':
      {
         socket_sockaddr addr;
         bits i0, i1, i2, i3;
         int n;

         if (sscanf (to + 1, "%u.%u.%u.%u%n", &i0, &i1, &i2, &i3, &n) < 4 ||
               n < strlen (to + 1) || i0 > 255 || i1 > 255 || i2 > 255 ||
               i3 > 255)
         {
            error = (os_error *) "\0\0\0\1Trace variable should be <byte>."
                  "<byte>.<byte>.<byte>";
            goto finish;
         }

         if ((error = xsocket_creat (socket_AF_INET, socket_SOCK_STREAM,
               0, &S)) != NULL)
            goto finish;

         addr AS sockaddr_in.af = socket_AF_INET;
         addr AS sockaddr_in.port = *(short *) "JC";
         ((byte *) (&addr AS sockaddr_in.addr)) [0] = i0;
         ((byte *) (&addr AS sockaddr_in.addr)) [1] = i1;
         ((byte *) (&addr AS sockaddr_in.addr)) [2] = i2;
         ((byte *) (&addr AS sockaddr_in.addr)) [3] = i3;

         if ((error = xsocket_connect (S, &addr, sizeof addr AS sockaddr_in))
               != NULL)
            goto finish;

         Trace = INTERNET;
      }
      break;
#endif

      case 'n': case 'N':
         Trace = NOWHERE;
      break;

      case 's': case 'S':
         Trace = STDOUT;
      break;

      case 't': case 'T':
         Trace = TUBE;
      break;

      case 'v': case 'V':
         Trace = VDU;
      break;
   }

finish:
   return Error = error;
}
/*------------------------------------------------------------------------*/

os_error *trace_terminate (void)
{
   os_error *error = NULL;

   switch (Trace)
   {
      case DISC:
         if ((error = xosfind_close (F)) != NULL)
            goto finish;
      break;

#if 0
    case INTERNET:
         if ((error = xsocket_close (S)) != NULL)
            goto finish;
      break;
#endif
   }

finish:
   return Error;
}
/*------------------------------------------------------------------------*/

void trace_f
(
   char *file,
   int line,
   char *format,
   ...
)
{
   va_list   list;
   char      s [TRACE_BUFFER_SIZE + 1], *cc;
   osbool    has_format = format != NULL && strlen (format) > 0;
   os_error *error = NULL;
//   int       len;

   /*Do not do anything once Error is set.*/
   if (Error == NULL)
   {
      s [TRACE_BUFFER_SIZE] = '\0';
      if (file != NULL)
      {
         sprintf (s, has_format? "%s,%d: ": "%s,%d\n\r", file, line);
         assert (s [TRACE_BUFFER_SIZE] == '\0');

         switch (Trace)
         {
#if 0
            case ECONET:
               if ((error = Send (s, strlen (s))) != NULL)
                  goto finish;
            break;
#endif
            case DISC:
               if ((error = xosgbpb_write (F, (byte *) s, strlen (s), NULL))
                     != NULL)
                  goto finish;

               if ((error = xosargs_ensure (F)) != NULL)
                  goto finish;
            break;

#if 0
          case INTERNET:
               if ((error = xsocket_write (S, (byte *) s, strlen (s), NULL))
                     != NULL)
                  goto finish;
            break;
#endif

            case STDOUT:
               printf ("%s", s);
            break;

#if 0
          case TUBE:
               for (cc = s; *cc != '\0'; cc++)
                  if ((error = xhostfs_writec (*cc)) != NULL)
                     goto finish;
            break;
#endif

            case VDU:
               if ((error = xos_write0 (s)) != NULL)
                  goto finish;
            break;
         }
      }

      if (has_format)
      {
         s [TRACE_BUFFER_SIZE] = '\0';
         va_start (list, format);
         vsprintf (s, format, list);
         va_end (list);
         assert (s [TRACE_BUFFER_SIZE] == '\0');

         switch (Trace)
         {
#if 0
            case ECONET:
               len = strlen (s);
               if ((error = Send (s, len)) != NULL)
                  goto finish;

               if (s [len - 1] == '\n')
                  if ((error = Send ("\r", 1)) != NULL)
                     goto finish;
            break;
#endif

            case DISC:
               if ((error = xosgbpb_write (F, (byte *) s, strlen (s), NULL))
                     != NULL)
                  goto finish;

               if ((error = xosargs_ensure (F)) != NULL)
                  goto finish;
            break;

#if 0
          case INTERNET:
               len = strlen (s);
               if ((error = xsocket_write (S, (byte *) s, len, NULL))
                     != NULL)
                  goto finish;

               if (s [len - 1] == '\n')
                  if ((error = xsocket_write (S, (byte *) "\r", 1,
                        NULL)) != NULL)
                     goto finish;
            break;
#endif

            case STDOUT:
               printf ("%s", s);
            break;

#if 0
          case TUBE:
               for (cc = s; *cc != '\0'; cc++)
                  if (*cc >= 32)
                  {
                     if ((error = xhostfs_writec (*cc)) != NULL)
                        goto finish;
                     if (*cc == '\\')
                        if ((error = xhostfs_writec (*cc)) != NULL)
                           goto finish;
                  }
                  else if (*cc == '\n' || *cc == '\r')
                  {
                     if ((error = xhostfs_writec (*cc)) != NULL)
                        goto finish;
                  }
                  else
                  {
                     if ((error = xhostfs_writec ('\\')) != NULL)
                        goto finish;
                     if ((error = xhostfs_writec ('x')) != NULL)
                        goto finish;
                     if ((error = xhostfs_writec (UCHAR (*cc/16))) != NULL)
                        goto finish;
                     if ((error = xhostfs_writec (UCHAR (*cc%16))) != NULL)
                        goto finish;
                  }

               if (cc [-1] == '\n')
                  if ((error = xhostfs_writec ('\r')) != NULL)
                     goto finish;
            break;
#endif

            case VDU:
               for (cc = s; *cc != '\0'; cc++)
               {
                  char c = *cc;

                  if (c >= 128)
                  {
                     if ((error = xos_writen ("|!", 2)) != NULL)
                        goto finish;
                     c -= 128;
                  }

                  if (c < 32)
                  {
                     if (c == '\n')
                     {
                        if ((error = xos_new_line ()) != NULL)
                           goto finish;
                        continue;
                     }
                     else
                     {
                        if ((error = xos_writec ('|')) != NULL)
                           goto finish;
                        c += 64;
                     }
                  }

                  if ((error = xos_writec (c)) != NULL)
                     goto finish;
                  if (c == '|')
                     if ((error = xos_writec (c)) != NULL)
                        goto finish;
               }
            break;
         }
      }
   }

finish:
   if (error != NULL)
      Error = error;
}
/*------------------------------------------------------------------------*/

void trace_vdu
(
   char *s,
   int   n
)
{
   os_error *error = NULL;
//   int       i;

   /*Do not do anything once Error is set.*/
   if (Error == NULL)
      switch (Trace)
      {
#if 0
         case ECONET:
            if ((error = Send (s, n)) != NULL)
               goto finish;
         break;
#endif
         case DISC:
            if ((error = xosgbpb_write (F, (byte *) s, n, NULL)) != NULL)
               goto finish;

            if ((error = xosargs_ensure (F)) != NULL)
               goto finish;
         break;

#if 0
         case INTERNET:
            if ((error = xsocket_write (S, (byte *) s, n, NULL)) != NULL)
               goto finish;
         break;
#endif
#if 0
         case TUBE:
            for (i = 0; i < n; i++)
               if ((error = xhostfs_writec (s [i])) != NULL)
                  goto finish;
         break;
#endif
         case VDU:
            if ((error = xos_writen (s, n)) != NULL)
               goto finish;
         break;
      }

finish:
   if (error != NULL)
      Error = error;
}
/*------------------------------------------------------------------------*/

void trace_wait
(
   char *file,
   int   line,
   int   t
)
{
   clock_t   end;
   int       left;
   os_error *error = 0;

   end = clock () + t*CLOCKS_PER_SEC;

#if 0
   while ((left = (int) end - (int) clock ()) > 0)
      switch (Trace)
      {
         case TUBE: case ECONET: case INTERNET:
            trace_f (file, line, "%-*d", DEC_WIDTH, left/CLOCKS_PER_SEC);
            if ((error = xhostfs_writec ('\r')) != NULL)
               goto finish;
         break;
      }
#endif

   while ( error == NULL && (left = (int) end - (int) clock ()) > 0)
      switch (Trace)
      {
#if 0
         case TUBE: case ECONET: case INTERNET:
            trace_f (file, line, "%-*d", DEC_WIDTH, left/CLOCKS_PER_SEC);
            error = xhostfs_writec ('\r')
         break;
#endif
      }

//finish:;
}
