/*
 * dd.c - copies raw data from/to any DOS-mounted drive to/from a file
 *
 * Bruno Costa - 27 Nov 91 - 7 May 92
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/errors.h>
#include <devices/trackdisk.h>
#include <dos/dos.h>
#include <dos/filehandler.h>
#include <dos/dosextens.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>

#include <string.h>
#include <stdarg.h>

#include "getoption.h"
#include "dossupport.h"
#include "dev.h"

extern struct Library *DOSBase;
extern struct Library *SysBase;

char version[] = "$VER: dd 1.1b (" __DATE__ ") by Bruno Costa [bruno@impa.br]\n";

typedef enum {
  UNIT_BLOCKS,
  UNIT_SECTORS,
  UNIT_KBYTES,
  UNIT_BYTES,
  UNIT_MBYTES,	/* !!! */
  UNIT_SIZEOF
} unit;

char unitid[UNIT_SIZEOF] = {
  'B', 's', 'k', 'b', 'M'
};

int quiet = FALSE;


/* This prevents Lattice ctrl-C processing... */
int CXBRK (void)
{
  return (0);
}


void doserror (long err)
{
 PrintFault (err, "dd: ");
}


void usage (void)
{
 Printf ("\x1b[1mdd 1.1b\x1b[0m - \2511992 by Bruno Costa [bruno@brlncc.bitnet]\n");
 Printf ("usage: dd [-q] [-c<count>] [-s<skip>] -i<dev>|-r<dev>|-w<dev> <file>\n"
         "       -q = quiet operation\n"
         "       -c = amount to be read\n"
         "       -s = amount to be skipped before reading (relative to start of disk)\n"
         "       -i = just print OS information on given device and exit\n"
         "       -r = device to read data from\n"
         "       -w = device to write data to\n"
         "       -h = help (this message)\n"
         "\n");
 Printf ("where: <dev> is any AmigaDOS device (DF0:, DH0:, etc.)\n"
         "       <file> is the name of the file the data will be written to/read from\n"
         "       <count>,<skip> are integers followed by a unit specifier:\n"
         "        B = blocks [default]; s = sectors; b = bytes; k = kbytes; M = megabytes\n"
        );
 exit (10);
}


void convert (long int *count, unit countunit)
{
 switch (countunit)
 {
   case UNIT_BLOCKS:
     *count *= devblocksize ();
     break;

   case UNIT_SECTORS:
     *count *= devsectorsize ();
     break;

   case UNIT_KBYTES:
     *count = (*count) << 10;
     break;

   case UNIT_MBYTES:
     *count = (*count) << 20;
     break;

   case UNIT_BYTES:
   default:
     break;
 }
}


int getcount (char *data, long int *count, unit *countunit)
{
 unit i;
 int n = StrToLong (data, count);

 if (n > 0)
 {
   for (i = 0; i < UNIT_SIZEOF; i++)
     if (data[n] == unitid[i])
     {
       *countunit = i;
       return TRUE;
     }
   *countunit = UNIT_BLOCKS;
   return TRUE;
 }

 return FALSE;
}


#define REQUIRES_2_0 "Sorry, you have to upgrade to Workbench 2.0 to run this program...\n"

void main (int argc, char *argv[])
{
 char data[100];
 int c;
 char *file = NULL;
 int write = FALSE;
 int info = TRUE;
 long int count = -1;
 unit countunit = UNIT_BYTES;
 long int skip = 0;
 unit skipunit = UNIT_BYTES;
 char *dev = NULL;
 struct IOExtTD *devreq;

 if (DOSBase->lib_Version < 36)
 {
   Write (Output (), REQUIRES_2_0, sizeof (REQUIRES_2_0));
   exit (100);
 }

 while (c = getoption (argc, argv, "csrwi", "qh", data))
   if (c < 0)
   {
     Printf ("dd: unknown option %c\n", -c);
     usage ();
   }
   else if (c == 1)
   {
     if (file)
       usage ();
     else
       file = strdup (data);
   }
   else
     switch (c)
     {
       case 'i':
         if (dev)
           usage ();
         else
           dev = strdup (data);
         write = FALSE;
         info = TRUE;
         break;

       case 'r':
         if (dev)
           usage ();
         else
           dev = strdup (data);
         write = FALSE;
         info = FALSE;
         break;

       case 'w':
         if (dev)
           usage ();
         else
           dev = strdup (data);
         write = TRUE;
         info = FALSE;
         break;

       case 'c':
         if (!getcount (data, &count, &countunit))
           usage ();
         break;

       case 's':
         if (!getcount (data, &skip, &skipunit))
           usage ();
         break;

       case 'q':
         quiet = TRUE;
         break;

       case 'h':
         usage ();
         break;
     }

 if (!dev  ||  (!info && !file))
   usage ();

 devreq = opendev (dev, info);
 if (devreq)
 {
   int err = -1;

   if (!info)
   {
     convert (&count, countunit);
     convert (&skip, skipunit);

     if (write)
     {
       BPTR f = Open (file, MODE_OLDFILE);
       if (f)
       {
         err = devwrite (f, devreq, skip, count);
         Close (f);
       }
     }
     else
     {
       BPTR f = Open (file, MODE_NEWFILE);
       if (f)
       {
         err = devread (devreq, f, skip, count);
         Close (f);
       }
     }
   }
   closedev (dev, devreq);

   if (err)
   {
     if (!deverror (err))
       doserror (err);
   }
 }
}
