/*  comms.c  */

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

#include "kernel.h"
#include "newfs.h"
#include "swis.h"

/***************************************************************************/

static _kernel_oserror *raw_read_bytes(int fswh, char *buff, int n, int pos)

  /* read n bytes from pos in file fswh to buff */

{
  return _swix(OS_GBPB, I0|I1|I2|I3|I4,
               3,
               fswh,
               buff,
               n,
               pos
              );
}

/***************************************************************************/

static void check_open_image(int fswh)

  /* checks a NewFS image file for consistency */

{
  int file_size = 0;
  int image_size;
  int block_size;
  int pos = 0;

  image_hdr im_hdr;
  object_hdr obj_hdr;
  object_name obj_name;
  int obj_body_size;

 /* determine file size */
  _swix(OS_Args, I0|I1|O2,
        2,         /* read open file's extent */
        fswh,      /* file handle */
        &file_size
       );

 /* make sure file is large enough to contain image header */
  if (file_size < sizeof(image_hdr))
  {
    printf("File too small to contain NewFS image\n");
    return;
  }


 /* read image header and check it */
  raw_read_bytes(fswh, (char*)&im_hdr, sizeof(image_hdr), 0);

  if (im_hdr.size != sizeof(image_hdr))
  {
    printf("Image header size field invalid\n");
    return;
  }

  if (im_hdr.id != NEWFS_ID)
  {
    printf("Image header id field invalid\n");
    return;
  }

  image_size = im_hdr.image_size;
  block_size = im_hdr.block_size;

  if (file_size < image_size)
  {
    printf("Image size (%d) is larger than file size (%d)\n",
                image_size, file_size);
    return;
  }

  if (block_size != 64   &&
      block_size != 128  &&
      block_size != 256  &&
      block_size != 512  &&
      block_size != 1024)
  {
    printf("Block size (%d) is invalid\n", block_size);
    return;
  }


 /* scan and check each object in the image */
  pos = sizeof(image_hdr);

  while (pos < image_size)
  {
   /* read object header, object name, and object body size */
    if (pos + sizeof(object_hdr) > file_size)
    {
      printf("Object header extends beyond end of file\n");
      return;
    }

    raw_read_bytes(fswh, (char*)&obj_hdr, sizeof(object_hdr), pos);
    pos += sizeof(object_hdr);

    raw_read_bytes(fswh, (char*)&obj_name.size, 4, pos);
    if (pos + obj_name.size + 4 > file_size)
    {
      printf("Object name or body size field extends beyond end of file\n");
      return;
    }

    raw_read_bytes(fswh, (char*)&obj_name, obj_name.size, pos);
    pos += obj_name.size;
    raw_read_bytes(fswh, (char*)&obj_body_size, 4, pos);
    pos += obj_body_size;
    if (pos > file_size)
    {
      printf("Object body extends beyond end of file\n");
      return;
    }

   /* check object fields */
    if (obj_hdr.size != sizeof(object_hdr))
    {
      printf("Object header size field is invalid\n");
      return;
    }

    if (obj_hdr.type != TYPE_FILE &&
        obj_hdr.type != TYPE_DIR)
    {
      printf("Object has invalid type (%d)\n", obj_hdr.type);
      return;
    }

    {
      int len = strlen(obj_name.name);

      if (round_up_to_word(len+1) + 4 != obj_name.size)
      {
        printf("Object name size field is incorrect\n");
        return;
      }

      if (obj_hdr.extent > obj_body_size-4)
      {
        printf("Object extent (%d) exceeds allocation (%d)\n",
                   obj_hdr.extent, obj_body_size-4);
        return;
      }

      if (((obj_body_size-4) & (block_size-1)) != 0)
      {
        printf("Object allocation (%d) is not a multiple of block_size\n",
                  obj_body_size-4);
        return;
      }

      len = (obj_hdr.extent + (block_size-1)) & ~(block_size-1);
      if (obj_body_size != len+4)
        printf("Object body size (%d) not minimal for extent (%d)\n",
                obj_body_size, obj_hdr.extent);
    }
  }

 /* and now we must be exactly at the end of the image */
  if (pos != image_size)
  {
    printf("Object extends beyond end of image\n");
    return;
  }

  printf("Image file structures are consistent\n");
  return;
}

/***************************************************************************/

void check_image(char *file_name)

  /* checks a NewFS image file for consistency */

{
  int fswh;
  _kernel_oserror *err;

  debug("Checkimage '%s'\n", file_name);

 /* open image file */
  err = _swix(OS_Find, I0|I1|O0,
              0x4c,    /* open existing file, error if absent or directory */
              file_name,
              &fswh
             );

  if (err != NULL || fswh == 0)
  {
    printf("Cannot open image file: %s\n", err->errmess);
    return;
  }

  check_open_image(fswh);

 /* close image file */
  _swix(OS_Find, I0|I1,
        0,      /* close file */
        fswh
       );

  return;
}

/***************************************************************************/

static void list_open_image(int fswh)

  /* list summary of contents of open image file */

{
  int file_size;
  int image_size;
  int pos = 0;

  image_hdr im_hdr;
  object_hdr obj_hdr;
  object_name obj_name;
  int obj_body_size;

 /* determine file size */
  _swix(OS_Args, I0|I1|O2,
        2,         /* read open file's extent */
        fswh,      /* file handle */
        &file_size
       );

  printf("\nFile size = %d\n", file_size);

 /* read image header */
  raw_read_bytes(fswh, (char*)&im_hdr, sizeof(image_hdr), 0);

  printf("\nImage header fields:\n");
  printf("  image_name = '%s'\n", im_hdr.image_name);
  printf("  image_size = %d\n", im_hdr.image_size);
  printf("  header size = %d\n", im_hdr.size);
  printf("  version = %d\n", im_hdr.version);
  printf("  block_size = %d\n", im_hdr.block_size);
  printf("  boot_option = %d\n", im_hdr.boot_option);
  printf("  stamp_value = %d\n", im_hdr.stamp_value);
  printf("  id = %08x\n", im_hdr.id);

  image_size = im_hdr.image_size;

 /* process each object in the image */
  pos = sizeof(image_hdr);
  while (pos < image_size)
  {
    int obj_pos = pos;

   /* read object header, object name, and object body size */
    raw_read_bytes(fswh, (char*)&obj_hdr, sizeof(object_hdr), pos);
    pos += sizeof(object_hdr);
    raw_read_bytes(fswh, (char*)&obj_name.size, 4, pos);
    raw_read_bytes(fswh, (char*)&obj_name, obj_name.size, pos);
    pos += obj_name.size;
    raw_read_bytes(fswh, (char*)&obj_body_size, 4, pos);
    pos += obj_body_size;

   /* list details of object */
    printf("\n%s at offset %d '%s':\n",
            (obj_hdr.type == TYPE_FILE) ? "File" : "Directory",
            obj_pos, obj_name.name);
    printf("  extent = %d\n", obj_hdr.extent);
    printf("  allocation = %d\n", obj_body_size-4);
    printf("  load = %08x\n", obj_hdr.load);
    printf("  exec = %08x\n", obj_hdr.exec);
    printf("  attributes = %08x\n", obj_hdr.attributes);

    if ((obj_hdr.load & 0xfff00000) == 0xfff00000)      /* if timestamp */
    {
      char timestamp[5];
      char datetime[24];
      int load = obj_hdr.load;
      int exec = obj_hdr.exec;

      timestamp[0] = exec;
      timestamp[1] = exec >> 8;
      timestamp[2] = exec >> 16;
      timestamp[3] = exec >> 24;
      timestamp[4] = load;

      _swix(OS_ConvertStandardDateAndTime, I0|I1|I2,
            timestamp,
            datetime,
            24
           );

      printf("  filetype = %03x\n", (load & 0x000fff00) >> 8);
      printf("  timestamp = %s\n", datetime);
    }
  }

  return;
}

/***************************************************************************/

void list_image(char *file_name)

  /* lists the contents of a NewFS image file */

{
  int fswh;
  _kernel_oserror *err;

  debug("Listimage '%s'\n", file_name);

 /* open image file */
  err = _swix(OS_Find, I0|I1|O0,
              0x4c,    /* open existing file, error if absent or directory */
              file_name,
              &fswh
             );

  if (err != NULL || fswh == 0)
  {
    printf("Cannot open image file: %s\n", err->errmess);
    return;
  }

  list_open_image(fswh);

 /* close image file */
  _swix(OS_Find, I0|I1,
        0,      /* close file */
        fswh
       );

  return;
}

/***************************************************************************/

void list_control_blocks(void)

  /* lists internal control blocks etc. for NewFS */

{
  image_handle ih = image_list;
  file_handle fh = file_list;

  debug("Listcbs\n");

  if (ih == NULL)
    printf("\nNo ICBs are present");
  else
  {
    printf("\nICB    image   image    file image blck opt  disc name\n");
    printf("fswh  handle    size    size stamp size\n\n");

    do
    {
      printf("%3d %08x %7d %7d %4d%c %4d %2d %s\n",
             ih->fswh,
             (int)ih,
             ih->hdr.image_size,
             ih->file_size,
             ih->hdr.stamp_value,
             ih->stamp_pending?'*':' ',
             ih->hdr.block_size,
             ih->hdr.boot_option,
             ih->hdr.image_name
            );
      ih = ih->next;
    } while (ih != NULL);
  }

  if (fh == NULL)
    printf("\nNo FCBs are present\n");
  else
  {
    printf("\nFCB      ICB     hdr     pos   file name\n");
    printf("handle  fswh     pos\n\n");

    do
    {
      read_obj_name(fh->ih, fh->hdr_pos);

      printf("%08x %3d %7d %7d %c %s\n",
             (int)fh,
             fh->ih->fswh,
             fh->hdr_pos,
             fh->pos,
             (fh->reason == OPEN_FOR_READ)?'R':'W',
             curr_obj_name.name
            );
      fh = fh->next;
    } while (fh != NULL);
  }

  return;
}

/***************************************************************************/

void create_image(char *file_name, char *image_name, int size, int blk_size)

  /* Create a new image file */

{
  int fswh;
  _kernel_oserror *err;

  debug("Crimage: '%s', name='%s', size=%d, block-size = %d\n",
        file_name, image_name, size, blk_size);

 /* create image file */
  err = _swix(OS_File, I0|I1|I2|I4|I5,
              11,        /* create file with timestamp and filetype */
              file_name,
              IMAGE_TYPE,  /* filetype */
              0,
              size
             );

  if (err != NULL)
  {
    printf("Problem creating image file: %s\n", err->errmess);
    return;
  }

 /* open image file */
  _swix(OS_Find, I0|I1|O0,
        0xcc,    /* open existing file, error if absent or directory */
        file_name,
        &fswh
       );

 /* write out header structure to image file */
  layout_image(fswh, image_name, blk_size);

 /* close image file */
  _swix(OS_Find, I0|I1,
        0,      /* close file */
        fswh
       );

  return;
}

/***************************************************************************/

void specify_logfile_name(char *file_name)

  /* called to change the name of the log file */

{
  debug("Newfslog '%s'\n", file_name);

  strcpy(logfile, file_name);

  return;
}

/***************************************************************************/
