/* main.c:
 *
 * Interactive test program for the ISO- and Rock-Ridge-support
 * routines.
 *
 * ----------------------------------------------------------------------
 * This code is (C) Copyright 1993 by Frank Munkert.
 * All rights reserved.
 * This software may be freely distributed and redistributed for
 * non-commercial purposes, provided this notice is included.
 */

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

#include <dos/var.h>
#include <clib/dos_protos.h>

#include "cdrom.h"
#include "iso9660.h"
#include "rock.h"

CDROM *cd = NULL;
char g_the_device[80];
int g_the_unit;
int g_trackdisk = 0;
int g_fastmem = 0;

void Cleanup (void)
{
  if (cd)
    Cleanup_CDROM (cd);
}

void Usage (void)
{
  fprintf (stderr,
    "Usage: cdrom command [parameters]\n"
    "Commands:\n"
    "  c name        Show contents of file 'name'\n"
    "  d dir         Show contents of directory 'dir' (use ISO names)\n"
    "  e dir         Show contents of directory 'dir' (use Rock Ridge names)\n"
    "  f dir name    Change to directory 'dir' and try to find object 'name'\n"
    "  i             Check which protocol is used\n"
    "  o name        Try to open object 'name'\n"
    "  p             Read top-level path table\n"
    "  r             Read contents of root directory\n"
    "  s num         Read sector number 'num'\n"
    "  t name        Try to open parent of object 'name'\n"
    "  v             Read primary volume descriptor\n"
    "  z             Send test unit ready command\n"
    "Use \":\" as the name of the root directory\n"
    );
  exit (1);
}

char *MKSTR (char *p_in, int p_length, char *p_out)
{
  char *res = p_out;
  int len = p_length;
  int i;
  
  while (len && p_in[len-1] == ' ')
    len--;

  for (i=0; i<len; i++)
    *p_out++ = *p_in++;
    
  *p_out = 0;
  
  return res;
}

void Show_Flags (unsigned char p_flags)
{
  if (p_flags & 1)
    printf ("existence ");
  if (p_flags & 2)
    printf ("directory ");
  if (p_flags & 4)
    printf ("associated ");
  if (p_flags & 8)
    printf ("record ");
  if (p_flags & 16)
    printf ("protection ");
  if (p_flags & 128)
    printf ("multi-extent ");
}

void Show_Directory_Record (directory_record *p_dir)
{
  char buf[256];

  printf ("Extended Attr Record Length: %d\n", (int) p_dir->ext_attr_length);
  printf ("Location of Extent:          %lu\n", p_dir->extent_loc_m);
  printf ("Data Length:                 %lu\n", p_dir->data_length_m);
  printf ("Recording Date and Time:     %02d.%02d.19%02d %02d:%02d:%02d %+d\n",
  	  (int) p_dir->day, (int) p_dir->month, (int) p_dir->year,
	  (int) p_dir->hour, (int) p_dir->minute, (int) p_dir->second,
	  (int) p_dir->tz);
  printf ("Flags:                       %d (", (int) p_dir->flags);
  Show_Flags (p_dir->flags);
  printf (")\n");
  printf ("File Unit Size:              %d\n", (int) p_dir->file_unit_size);
  printf ("Gap Size:                    %d\n", (int) p_dir->gap_size);
  printf ("Volume Sequence Number:      %hu\n", p_dir->sequence_m);
  printf ("File Identifier:             ");
  if (p_dir->file_id[0] == 0)
    printf ("(00)\n");
  else if (p_dir->file_id[0] == 1)
    printf ("(01)\n");
  else
    printf ("%s\n", MKSTR (p_dir->file_id, p_dir->file_id_length, buf));
}

void Show_Primary_Volume_Descriptor (CDROM *p_cd)
{
  prim_vol_desc *pvd;
  char buf[129];
  
  if (!Read_Sector (p_cd, 16)) {
    fprintf (stderr, "cannot read sector 16\n");
    exit (1);
  }

  pvd = (prim_vol_desc *) p_cd->buffer;
  printf ("Volume Descriptor Type:          %d\n", (int) pvd->type);
  printf ("Standard Identifier:             %s\n", pvd->id);
  printf ("Volume Descriptor Version:       %d\n", (int) pvd->version);
  printf ("System Identifier:               %s\n", MKSTR (pvd->system_id,32,buf));
  printf ("Volume Identifier:               %s\n", MKSTR (pvd->volume_id,32,buf));
  printf ("Volume Space Size:               %lu\n", pvd->space_size_m);
  printf ("Volume Set Size:                 %hu\n", pvd->set_size_m);
  printf ("Volume Sequence Number:          %hu\n", pvd->sequence_m);
  printf ("Logical Block Size:              %hu\n", pvd->block_size_m);
  printf ("Path Table Size:                 %lu\n", pvd->path_size_m);
  printf ("Location of Occ of M Path Table: %lu\n", pvd->m_table); 
  printf ("Location of Occ of Opt M Path T: %lu\n", pvd->opt_m_table);
  printf ("Volume Set Identifier:           %s\n",
  					MKSTR (pvd->volume_set_id,128,buf));  
  printf ("Publisher Identifier:            %s\n",
  					MKSTR (pvd->publisher_id,128,buf)); 
  printf ("Data Preparer Identifier:        %s\n",
  					MKSTR (pvd->data_preparer,128,buf));
  printf ("Application Identifier:          %s\n",
  					MKSTR (pvd->application_id,128,buf));
  printf ("Copyright File Identifier:       %s\n",
  					MKSTR (pvd->copyright,37,buf));
  printf ("Abstract File Identifier:        %s\n",
  					MKSTR (pvd->abstract_file_id,37,buf));
  printf ("Bibliographic File Identifier:   %s\n",
  					MKSTR (pvd->bibliographic_id,37,buf));
  printf ("File Structure Version:          %d\n",
  					(int) pvd->file_structure_version);
  printf ("ROOT DIRECTORY RECORD:\n");
  Show_Directory_Record (&pvd->root);
}

void Show_Path_Table (CDROM *p_cd, unsigned long p_size, unsigned long p_location)
{
  unsigned long cnt = 0;
  int len;
  int scsi_pos = 0;
  int todo;
  char buf[256];
  path_table_record *ptr = (path_table_record *) buf;

  printf ("Ext Attr RecLen  Location   Parent Dir Num    Identifier\n"
          "---------------  --------   --------------    ----------\n");

  if (!Read_Sector (p_cd, p_location)) {
    fprintf (stderr, "cannot read sector %lu\n", p_location);
    exit (1);
  }

  while (cnt < p_size) {
    len = p_cd->buffer[scsi_pos] + 8;
    if (len & 1)
      len++;

    if (len + scsi_pos <= 2048) {
      memcpy (buf, p_cd->buffer + scsi_pos, len);
      todo = 0;
      scsi_pos += len;
    } else {
      memcpy (buf, p_cd->buffer + scsi_pos, 2048 - scsi_pos);
      todo = len - (2048 - scsi_pos);
      scsi_pos = 2048;
    }

    if (scsi_pos == 2048) {
      p_location++;
      if (!Read_Sector (p_cd, p_location)) {
        fprintf (stderr, "cannot read sector %lu\n", p_location);
        exit (1);
      }
      scsi_pos = 0;
      if (todo) {
        memcpy (buf + (len - todo), p_cd->buffer, todo);
        scsi_pos = todo;
      }
    }

    cnt += len;
    
    printf ("%-17d%-11lu%-18hu",
            (int) ptr->ext_attr_length,
	    ptr->location,
	    ptr->parent);
    if (ptr->id[0] == 0)
      printf ("(00)\n");
    else if (ptr->id[0] == 1)
      printf ("(01)\n");
    else {
      fwrite (ptr->id, ptr->id_length, 1, stdout);
      printf ("\n");
    }
  }
}

void Show_Top_Level_Path_Table (CDROM *p_cd)
{
  prim_vol_desc *pvd;
  
  if (!Read_Sector (p_cd, 16)) {
    fprintf (stderr, "cannot read sector 16\n");
    exit (1);
  }

  pvd = (prim_vol_desc *) p_cd->buffer;
  
  Show_Path_Table (p_cd, pvd->path_size_m, pvd->m_table);

}

void Show_Directory (CDROM *p_cd, unsigned long p_location, unsigned long p_length)
{
  int cnt = 0;
  int pos = 0;
  
  if (!Read_Sector (p_cd, p_location)) {
    fprintf (stderr, "cannot read sector %lu\n", p_location);
    exit (1);
  }

  while (cnt < p_length) {
    directory_record *dir = (directory_record *) (p_cd->buffer + pos);
    
    if (dir->length == 0)
      break;
    Show_Directory_Record (dir);
    cnt += dir->length;
    pos += dir->length;
    if (cnt < p_length) {
      printf ("------------------------------------------------------------\n");
      if (pos >= 2048) {
        if (!Read_Sector (p_cd, ++p_location)) {
          fprintf (stderr, "cannot read sector %lu\n", p_location);
          exit (1);
        }
        pos = 0;
      }
    }
  }
}

void Show_Root_Directory (CDROM *p_cd)
{
  prim_vol_desc *pvd;
  
  if (!Read_Sector (p_cd, 16)) {
    fprintf (stderr, "cannot read sector 16\n");
    exit (1);
  }

  pvd = (prim_vol_desc *) p_cd->buffer;

  Show_Directory (p_cd, pvd->root.extent_loc_m, pvd->root.data_length_m);
}

void Check_Protocol (CDROM *p_cd)
{
  VOLUME *vol;

  if (!(vol = Open_Volume (p_cd, 0))) {
    fprintf (stderr, "cannot open volume; iso_errno = %d\n", iso_errno);
    exit (1);
  }
  
  if (Uses_Rock_Ridge_Protocol (vol)) {
    printf ("Rock Ridge Protocol, skip length = %d\n", vol->skip);
  } else
    printf ("ISO-9660 protocol\n");

  Close_Volume (vol);
}

void Try_To_Open (CDROM *p_cd, char *p_directory, char *p_name)
{
  VOLUME *vol;
  CDROM_OBJ *top = NULL;
  CDROM_OBJ *home;
  CDROM_OBJ *obj;
  CDROM_OBJ *parent;

  if (!(vol = Open_Volume (p_cd, 1))) {
    fprintf (stderr, "cannot open volume; iso_errno = %d\n", iso_errno);
    exit (1);
  }

  if (p_directory && p_directory != (char *) -1) {
    if (!(top = Open_Top_Level_Directory (vol))) {
      fprintf (stderr, "cannot open top level directory\n");
      Close_Volume (vol);
      exit (1);
    }
    
    if (!(home = Open_Object (top, p_directory))) {
      fprintf (stderr, "cannot open top level directory\n");
      Close_Object (top);
      Close_Volume (vol);
      exit (1);
    }
  } else {
    if (!(home = Open_Top_Level_Directory (vol))) {
      fprintf (stderr, "cannot open home directory;"
                       " iso_errno = %d\n", iso_errno);
      Close_Volume (vol);
      exit (1);
    }
  }

  if (obj = Open_Object (home, p_name)) {
    CDROM_INFO info;
    printf ("%s '%s' found, location = %lu\n",
            obj->directory_f ? "Directory" : "File",
    	    p_name, obj->dir_record->extent_loc_m);
    if (CDROM_Info (obj, &info)) {
      printf ("INFO Name = ");
      fwrite (info.name, info.name_length, 1, stdout);
      printf ("\n");
    } else
      printf ("CANNOT FIND INFO FOR OBJECT!\n");
    if (p_directory == (char *) -1) {
      parent = Find_Parent (obj);
      if (parent) {
        printf ("parent found, location = %lu\n",
	        parent->dir_record->extent_loc_m);
        Close_Object (parent);
      } else
        printf ("parent not found, iso_errno = %d\n", iso_errno);
    }
    Close_Object (obj);
  } else {
    if (iso_errno == ISOERR_NOT_FOUND)
      printf ("Object '%s' not found\n", p_name);
    else
      printf ("Object '%s': iso_errno = %d\n", iso_errno);
  }

  if (top)
    Close_Object (top);
  Close_Object (home);
  Close_Volume (vol);
}

void Show_File_Contents (CDROM *p_cd, char *p_name)
{
  VOLUME *vol;
  CDROM_OBJ *home;
  CDROM_OBJ *obj;
#define THEBUFSIZE 5000
  char buffer[THEBUFSIZE];
  int cnt;

  if (!(vol = Open_Volume (p_cd, 1))) {
    fprintf (stderr, "cannot open volume; iso_errno = %d\n", iso_errno);
    exit (1);
  }

  if (!(home = Open_Top_Level_Directory (vol))) {
    fprintf (stderr, "cannot open top level directory;"
                     " iso_errno = %d\n", iso_errno);
    Close_Volume (vol);
    exit (1);
  }

  if (obj = Open_Object (home, p_name)) {
    for (;;) {
      cnt = Read_From_File (obj, buffer, THEBUFSIZE);
      if (cnt == -1) {
        fprintf (stderr, "cannot read from file!\n");
	break;
      }
      if (cnt == 0)
        break;
      fwrite (buffer, cnt, 1, stdout);
    }

    Close_Object (obj);
  } else {
    fprintf (stderr, "Object '%s': iso_errno = %d\n", p_name, iso_errno);
  }

  Close_Object (home);
  Close_Volume (vol);
  
}

void Show_Dir_Contents (CDROM *p_cd, char *p_name, int p_rock_ridge)
{
  VOLUME *vol;
  CDROM_OBJ *home;
  CDROM_OBJ *obj;
  CDROM_INFO info;
  int cnt;
  char buf[200];
  int len;

  if (!(vol = Open_Volume (p_cd, p_rock_ridge))) {
    fprintf (stderr, "cannot open volume; iso_errno = %d\n", iso_errno);
    exit (1);
  }

  if (!(home = Open_Top_Level_Directory (vol))) {
    fprintf (stderr, "cannot open top level directory;"
                     " iso_errno = %d\n", iso_errno);
    Close_Volume (vol);
    exit (1);
  }

  if (obj = Open_Object (home, p_name)) {
    unsigned long offset = 0;
    
    while (Examine_Next (obj, &info, &offset)) {
      fwrite (info.name, info.name_length, 1, stdout);
      printf ("\n");
    }

    Close_Object (obj);
  } else {
    fprintf (stderr, "Object '%s': iso_errno = %d\n", p_name, iso_errno);
  }

  Close_Object (home);
  Close_Volume (vol);
  
}

void Send_Test_Unit_Ready (CDROM *p_cd)
{
  printf ("result = %d\n", Test_Unit_Ready (p_cd));
}

void Show_Sector (CDROM *p_cd, int p_sector)
{
  int i, j;
  int off = 0;
  
  if (p_sector < 0)
    return;
  
  if (!Read_Sector (p_cd, p_sector)) {
    fprintf (stderr, "cannot read sector %d\n", p_sector);
    exit (1);
  }
  
  for (i=0; i<128; i++) {
    printf ("%03x0: ", i);
    for (j=0; j<16; j++)
      printf ("%02x ", (int) p_cd->buffer[off++]);
    off -= 16;
    putchar (' ');
    for (j=0; j<16; j++) {
      char c = p_cd->buffer[off++];
      if (32<=c && c<=127)
        putchar (c);
      else
        putchar ('.');
    }
    putchar ('\n');
  }
}

int Get_Device_And_Unit (void)
{
  int len;
  char buf[10];
  
  len = GetVar ((UBYTE *) "CDROM_DEVICE", (UBYTE *) g_the_device,
  		sizeof (g_the_device), 0);
  if (len < 0)
    return 0;
  if (len >= sizeof (g_the_device)) {
    fprintf (stderr, "CDROM_DEVICE too long\n");
    exit (1);
  }
  g_the_device[len] = 0;
  
  len = GetVar ((UBYTE *) "CDROM_UNIT", (UBYTE *) buf,
  		sizeof (buf), 0);
  if (len < 0)
    return 0;
  if (len >= sizeof (buf)) {
    fprintf (stderr, "CDROM_UNIT too long\n");
    exit (1);
  }
  buf[len] = 0;
  g_the_unit = atoi (buf);
  
  if (GetVar ((UBYTE *) "CDROM_TRACKDISK", (UBYTE *) buf,
      sizeof (buf), 0) > 0) {
    fprintf (stderr, "using trackdisk\n");
    g_trackdisk = 1;
  }

  if (GetVar ((UBYTE *) "CDROM_FASTMEM", (UBYTE *) buf,
      sizeof (buf), 0) > 0) {
    fprintf (stderr, "using fastmem\n");
    g_fastmem = 1;
  }

  return 1;
}

void main (int argc, char *argv[])
{
  atexit (Cleanup);

  if (argc < 2)
    Usage ();

  if (!Get_Device_And_Unit ()) {
    fprintf (stderr,
      "Please the following environment variables:\n"
      "  CDROM_DEVICE    name of SCSI device\n"
      "  CDROM_UNIT      unit number of CDROM drive\n"
      "e.g.\n"
      "  setenv CDROM_DEVICE scsi.device\n"
      "  setenv CDROM_UNIT 2\n"
      "Set the variable CDROM_TRACKDISK to any value if you\n"
      "want to use trackdisk calls instead of SCSI-direct calls\n"
      "Set the variable CDROM_FASTMEM to any value if you\n"
      "want to use fast memory for SCSI buffers (does not work\n"
      "with all SCSI devices!)\n"
      );
    exit (1);
  }

  cd = Open_CDROM (g_the_device, g_the_unit, g_trackdisk, g_fastmem);
  if (!cd) {
    fprintf (stderr, "cannot open CDROM\n");
    exit (1);
  }

  if (argv[1][0] == 'c' && argc == 3)
    Show_File_Contents (cd, argv[2]);
  else if (argv[1][0] == 'd' && argc == 3)
    Show_Dir_Contents (cd, argv[2], 0);
  else if (argv[1][0] == 'e' && argc == 3)
    Show_Dir_Contents (cd, argv[2], 1);
  else if (argv[1][0] == 'f' && argc == 4)
    Try_To_Open (cd, argv[2], argv[3]);
  else if (argv[1][0] == 'i')
    Check_Protocol (cd);
  else if (argv[1][0] == 'o' && argc == 3)
    Try_To_Open (cd, NULL, argv[2]);
  else if (argv[1][0] == 'p')
    Show_Top_Level_Path_Table (cd);
  else if (argv[1][0] == 'r')
    Show_Root_Directory (cd);
  else if (argv[1][0] == 's' && argc == 3)
    Show_Sector (cd, atoi (argv[2]));
  else if (argv[1][0] == 't' && argc == 3)
    Try_To_Open (cd, (char *) -1, argv[2]);
  else if (argv[1][0] == 'v')
    Show_Primary_Volume_Descriptor (cd);
  else if (argv[1][0] == 'z')
    Send_Test_Unit_Ready (cd);
  else
    Usage ();

  exit (0);
}
