/* vi:sw=2
 * Copyright © 1994-1996 Marko Mäkelä and Olaf Seibert
 * Original Linux and Commodore 64/128/Vic-20 version by Marko Mäkelä
 * Ported to the PET and the Amiga series by Olaf Seibert
 * 
 *     This program 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 2 of the License, or
 *     (at your option) any later version.
 * 
 *     This program 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 program; if not, write to the Free Software
 *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

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

#include "prtrans.h"

#if !defined(__MAIN_C__)
#define main	main_prrfile
#endif

#if defined(__MAIN_C__)
unsigned char buffer[256];
#else
extern unsigned char buffer[];
#endif

int main(int argc, char **argv);

void
printdir(unsigned char *p, int length, FILE *file)
{
  /*
   * State: 0,1: ignore load address
   *	    2,3: line link
   *	    4,5: line number
   *	    6  : line contents
   *	    7  : end
   */
  static int state = 0;
  static int lineno;

  while (length > 0) {
    switch(state) {
    case 0:
    case 1:
      state++;
      break;
    case 2:	/* ignore low byte of link */
      state++;
      break;
    case 3:
      state++;
      if (*p == 0) {
	state = 7;
      }
      break;
    case 4:
      state++;
      lineno = *p;
      break;
    case 5:
      state++;
      lineno += 256 * *p;
      fprintf(file, " %d ", lineno);
      break;
    case 6:
      if (*p == 0) {
	state = 2;
	putc('\n', file);
      } else {
	static char tmp[2];
	tmp[0] = *p;
	petscii2ascii(tmp);
	putc(tmp[0], file);
      }
      break;
    }
    length--;
    p++;
  }
}

int
main(int argc, char **argv)
{
  FILE *file = stdout;
  unsigned char device = 8;
  unsigned char secaddr = 0;
  unsigned char byte;
  char **parameters = argv;
  char *remotefile;
  char *localfile;
  char *portspecified = NULL;
  static char prgname[128];
  char *prg = prgname; /* loader program file name */
  int specified_localfile = 0;
  baseaddr = portaddr[DEFAULT_PORT];

  while (*++parameters && **parameters == '-') { /* check for options */
    if (parameters[0][1] == '-') { /* "--" ends options */
      parameters++;
      break;
    }

    switch (parameters[0][1]) {
    case 'd':
      device = strtoul (*++parameters, NULL, 0);

      if (device > 15) {
	fprintf (stderr, "%s: Illegal device number specified.\n", *argv);
	return 1;
      }

      break;

    case 'l':
      prg = *++parameters;
      break;

    case 'p':
      baseaddr = strtoul (*++parameters, NULL, 16);

      if (baseaddr > 3) {
	fprintf (stderr, "%s: The printer port number must be between 0 and 3.\n",
			 *argv);
	return 1;
      }

      portspecified = *parameters;
      baseaddr = portaddr[baseaddr];
      break;

    case 's':
      secaddr = strtoul (*++parameters, NULL, 0);

      if (secaddr > 15) {
	fprintf (stderr, "%s: Illegal secondary address specified.\n", *argv);
	return 1;
      }

      break;

    case '?':
    case 'h':
    Usage:
      fprintf (stderr, "%s: Dumps a file accessible from a remote "
		       "computer to a file.\n\n", *argv);
      fprintf (stderr, "Usage: %s [options] filename [local filename]\n",
		       *argv);
      fprintf (stderr, "Options:\n\t"
			 "-d device\t"
			     "Specify the device number (0-15)\n\t"
			 "-l filename\t"
			     "Specify the filename of the client program\n\t"
			 "-p port\t"
			     "Specify the printer port (0 to 3)\n\t"
			 "-s sec. addr.\t"
			     "Specify the secondary addrsse (0-15)\n");
      fprintf (stderr, "The filename can start with a $, in which case it is\n"
		       "printed as a directory, unless a local name is given.\n"
		       "The remote filename is converted to PETSCII.\n"
		       "The default local file name has / characters converted to -.\n");

      return 1;

    default:
      fprintf (stderr, "%s: Illegal option `%s'.\n", *argv, *parameters);
      goto Usage;
    }
  }

  /* Special case to read the error channel */
  if (device >= 8 && secaddr == 15) {
    if (*parameters)
      remotefile = strdup(*parameters++);
    else
      remotefile = "";
    ascii2petscii(remotefile);
    localfile = "-";
  } else {
    if (!*parameters) goto Usage;

    localfile = *parameters++;
    remotefile = strdup(localfile);
    ascii2petscii(remotefile);
  }
  {
    char *bad;
    while ((bad = strpbrk(localfile, "/")) != NULL)
      *bad = '-';
  }

  if (*parameters) {
    localfile = *parameters++;
    specified_localfile = 1;
  }

  if (!specified_localfile && localfile[1] == ':') {
    localfile += 2;
  }
  if (remotefile[0] == '$' && !specified_localfile) {
    file = stdout;
    localfile = "$";
  } else if (strcmp(localfile, "-") == 0) {
    file = stdout;
  } else if (!(file = fopen(localfile, "wb"))) {
    fprintf (stderr, "%s: Could not open the file `%s'.\n", *argv,
		       localfile);
    return 1;
  }

  if (prinit()) {
    fprintf (stderr, "%s: Could not get the I/O permissions.\n", *argv);
    return 1;
  }

  /* determine the program name to download */
  wait_output (REQ_INFO);
  sprintf (prgname, "PRLINK_PRRFILE%u", wait_input());
  getname (prgname, prgname, "prrfile.prg");
  input(); input(); input(); input(); /* skip the address information */

  {
    int addr;

    FILE *f = fopen(prg, "r");
    if (f == NULL) {
      fprintf(stderr, "%s: Cannot find %s to download\n", *argv, prg);
      prclose();
      return 1;
    }

    addr = fgetc(f);
    addr |= fgetc(f) << 8;
    fclose(f);

#if defined(__MAIN_C__)
    {
      char cmd[80];
      sprintf(cmd, "prload -j %x ", addr);

      if (portspecified) {
	strcat (cmd, "-p ");
	strcat (cmd, portspecified);
	strcat (cmd, " ");
      }

      strcat (cmd, prg);

      fprintf(stderr, "%s\n", cmd);
      system(cmd);
    }
#else
    {
      char saddr[10];

      sprintf(saddr, "%x", addr);

#define STR2(s)     # s
#define STR(s)	    STR2(s)

      vmain (main_prload, 6,
	     "prload",
	     "-j", saddr,
	     "-p", (portspecified ? portspecified : STR(DEFAULT_PORT)),
	     prg,
	     NULL );
    }
#endif
  }

  prinit();
  if (wait_input ()) {
    fprintf (stderr, "%s: not ready to transfer\n", *argv);
    prclose();
    return 6;
  }
  else
    fprintf (stderr, "%s: ok to transfer\n", *argv);

  byte = strlen(remotefile);
  output(byte);
  if (byte) {
    send(remotefile, byte);
  }
  output(device);
  output(secaddr);

  if ((byte = wait_input ()) != 0) {
    fprintf (stderr, "%s: file error %d\n", *argv, byte);
    output(255); /* terminate remote side */
    prclose();
    return 7;
  }
  fprintf(stderr, "reading \"%s\",%d,%d to \"%s\"...\n", remotefile, device, secaddr, localfile);

  /* loop to get all file blocks */
  for (;;) {
    int length;

    for (;;) {
      int chksum;
      int i;

      length = input();
      if (length == 0)
	break;	      /* end of file */

      receive(buffer, length + 1);

      chksum = 0;
      for (i = 0; i < length; i++) {
	chksum += buffer[1 + i];
      }

      if ((chksum & 0xFF) == buffer[0]) {
	output(0x80);
	break;
      }
      fprintf(stderr, "checksum error "
		      "(received %02x, calculated %02x), retrying\n",
		      buffer[0], (chksum & 0xFF));
      output(0x81);
    }
    if (length == 0)
      break;
    if (localfile[0] == '$') {
      printdir(buffer + 1, length, file);
    } else {
      if (secaddr == 15) {
	buffer[1 + length] = 0;
	petscii2ascii(buffer + 1);
      }
      fwrite (buffer + 1, 1, length, file);
    }
  }

  if (file != stdout) fclose(file);

  output(255);	/* terminate remote side */

  prclose ();

  return 0;
}
