/* DISKTAPE.C
 *
 * "tape on a disk" emulator for MS-DOS and OS/2.
 *
 * Autor:    Kai Uwe Rommel
 * Datum:    Thu 28-Dec-1989
 * Stand:    Sun 14-Jan-1990
 *
 * Compiler: MS C ab 5.00
 * System:   OS/2 ab 1.1
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>

#include "diskacc.h"


#define min(a, b) ((a) < (b) ? (a) : (b))


#define SLOTS 4


struct slot
{
  int handle;
  unsigned sides, tracks, sectors;
  unsigned long current, size;
};

static struct slot tbl[SLOTS];


int dsk_isdev(char *path)
{
  if ( isalpha(path[0]) && path[1] == ':' && path[2] == 0 )
    return 1;

  if ( path[0] == '$' && isdigit(path[1]) && path[2] == ':' && path[3] == 0 )
    return 1;

  return 0;
}


int dsk_open(char *path, int oflag, int logical)
{
  int cnt, fd;

  for ( cnt = 0; cnt < SLOTS; cnt++ )
    if ( tbl[cnt].handle == 0 )
      break;

  if ( cnt == SLOTS )
    return -1;

  fd = DskOpen(path, logical, 
	       &tbl[cnt].sides, &tbl[cnt].tracks, &tbl[cnt].sectors);

  if ( fd < 0 )
    return -1;

  tbl[cnt].handle = fd;
  tbl[cnt].current = 0L;
  tbl[cnt].size = 512L * tbl[cnt].sides * tbl[cnt].tracks * tbl[cnt].sectors;

  return cnt;
}


int dsk_close(int handle)
{
  if ( (handle < 0) || (SLOTS <= handle) )
    return -1;

  if ( tbl[handle].handle == 0 )
    return -1;

  DskClose(tbl[handle].handle);
  tbl[handle].handle = 0;

  return 0;
}


int dsk_read(int handle, char *buf, unsigned nbyte)
{
  unsigned side, track, sector, nsects, ok, chunk;

  if ( (handle < 0) || (SLOTS <= handle) )
    return -1;

  if ( tbl[handle].handle == 0 )
    return -1;

  if ( nbyte % 512 != 0 )
    return -1;

  sector = (unsigned) (tbl[handle].current / 512L);

  track  = sector / tbl[handle].sectors;
  sector -= track * tbl[handle].sectors;

  side   = track % tbl[handle].sides;
  track  /= tbl[handle].sides;

  nsects = nbyte / 512;
  ok = 0;

  while ( nsects != 0 )
  {
    if ( track >= tbl[handle].tracks )
      break;

    chunk = min(tbl[handle].sectors - sector, nsects);

    if ( DskRead(tbl[handle].handle, side, track, sector + 1, chunk, buf) != 0 )
      break;

    ok += chunk;
    nsects -= chunk;
    sector = 0;

    if ( ++side >= tbl[handle].sides )
    {
      side = 0;
      track++;
    }

    tbl[handle].current += 512L * chunk;
    buf += 512 * chunk;
  }

  return ok * 512;
}


int dsk_write(int handle, char *buf, unsigned nbyte)
{
  unsigned side, track, sector, nsects, ok, chunk;

  if ( (handle < 0) || (SLOTS <= handle) )
    return -1;

  if ( tbl[handle].handle == 0 )
    return -1;

  if ( nbyte % 512 != 0 )
    return -1;

  sector = (unsigned) (tbl[handle].current / 512L);

  track  = sector / tbl[handle].sectors;
  sector -= track * tbl[handle].sectors;

  side   = track % tbl[handle].sides;
  track  /= tbl[handle].sides;

  nsects = nbyte / 512;
  ok = 0;

  while ( nsects != 0 )
  {
    if ( track >= tbl[handle].tracks )
      break;

    chunk = min(tbl[handle].sectors - sector, nsects);

    if ( DskWrite(tbl[handle].handle, side, track, sector + 1, chunk, buf) != 0 )
      break;

    ok += chunk;
    nsects -= chunk;
    sector = 0;

    if ( ++side >= tbl[handle].sides )
    {
      side = 0;
      track++;
    }

    tbl[handle].current += 512L * chunk;
    buf += 512 * chunk;
  }

  if ( nsects != 0 )
    errno = ENOSPC;

  return ok * 512;
}


long dsk_lseek(int handle, long offset, int where)
{
  if ( (handle < 0) || (SLOTS <= handle) )
    return -1;

  if ( tbl[handle].handle == 0 )
    return -1;

  if ( offset % 512L != 0L )
    return -1;

  switch ( where )
  {
  case SEEK_SET:
    tbl[handle].current = offset;
    break;
  case SEEK_CUR:
    tbl[handle].current += offset;
    break;
  case SEEK_END:
    tbl[handle].current = tbl[handle].size + offset;
    break;
  }

  if ( tbl[handle].current > tbl[handle].size )
  {
    tbl[handle].current = 0L;
    errno = EINVAL;
    return -1L;
  }

  return tbl[handle].current;
}


int dsk_size(int handle, long *total, long *cylinder)
{
  if ( (handle < 0) || (SLOTS <= handle) )
    return -1;

  if ( tbl[handle].handle == 0 )
    return -1;

  *total = tbl[handle].size;
  *cylinder = (long) tbl[handle].sides * tbl[handle].sectors;

  return 0;
}


/* Ende DISKTAPE.C */
