/* Mac-specific code for Xconq kernel.
   Copyright (C) 1992-1997, 1999 Stanley T. Shebs.

Xconq 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, or (at your option)
any later version.  See the file COPYING.  */

/* The code in this file is Mac-specific, but not necessarily specific
   to any particular app; it should be usable with utilities and with
   apps that don't have fancy interface. */

#include "config.h"
#include "misc.h"
#include "dir.h"
#include "lisp.h"
#include "module.h"
#include "system.h"

#if 0  /* Unused. */
void mac_abort(void);
#endif

/* Following are from tp.h, but tp.h is too high-level to include. */

#if 0  /* shouldn't be here */
extern int numremotes;
extern int my_rid;
extern int master_rid;
#endif

extern void init_file_port(int willhost);
extern void close_file_port(void);
extern void low_file_send(int id, char *buf);
extern int low_file_receive(int *id, char *buf, int maxchars, int timeout);

/* Private functions. */

static void init_serial_port(int port);
static void close_serial_port(void);
static void open_appletalk(void);
static int low_appletalk_receive(int *id, char *buf, int maxchars, int timout);
static void low_appletalk_send(int id, char *buf);
static void close_appletalk(void);
static void low_serial_send(int id, char *buf);
static int low_serial_readchars(char *buf, int maxchars, int timeout);

#ifdef THINK_C
#include <MacHeaders>
#endif /* THINK_C */

#ifdef MPW
#ifdef NEW_HEADERS
#include <MacTypes.h>
#else
#include <Types.h>
#endif /* NEW_HEADERS */
#include <Resources.h>
#include <Events.h>  /* for TickCount */
#include <TextUtils.h>  /* for GetIndString */
#endif /* MPW */

#ifdef __MWERKS__
#ifdef NEW_HEADERS
#include <MacTypes.h>
#else
#include <Types.h>
#endif /* NEW_HEADERS */
#include <Resources.h>
#include <Events.h>  /* for TickCount */
#include <TextUtils.h>  /* for GetIndString */
#include <Quickdraw.h>
#include <StandardFile.h>
#include <Devices.h>
#endif /* __MWERKS__ */

#include <signal.h>

/* We need this in order to find string resources that have filenames
   in them. */
#include "macdefs.h"

#include <Serial.h>

#include <AppleTalk.h>

#ifdef MPW_C
#define QD(whatever) (qd.##whatever)
#define QDPat(whatever) (&(qd.##whatever))
#endif
#ifdef THINK_C
#define QD(whatever) (whatever)
#define QDPat(whatever) (whatever)
#endif
#ifdef __MWERKS__
#define QD(whatever) (qd.##whatever)
#define QDPat(whatever) (&(qd.##whatever))
#endif

#ifndef c2p
#define c2p(STR,PBUF) \
  strcpy(((char *) PBUF) + 1, STR);  \
  PBUF[0] = strlen(STR);
#endif

#ifndef p2c
#define p2c(PSTR,BUF)  \
  strncpy(BUF, ((char *) (PSTR) + 1), PSTR[0]);  \
  BUF[PSTR[0]] = '\0';
#endif

extern CursHandle sendcursor;
extern CursHandle receivecursor;
extern CursHandle current_cursor;

int connection_method;

/* The HFS volume that the program started with. */

short initialvrefnum;

static char *news_fname;
static char *save_fname;
static char *checkpoint_fname;
static char *error_save_fname;
static char *statistics_fname;

/* We normally keep the library and image folders in the same folder
   as the app. */

#ifndef XCONQLIB
#define XCONQLIB ":lib"
#endif

#ifndef XCONQIMAGES
#define XCONQIMAGES ":images"
#endif

char *
default_library_pathname()
{
    return XCONQLIB;
}

char *
default_images_pathname()
{
    return XCONQIMAGES;
}

char *
news_filename()
{
    Str255 tmpstr;
    char tmpbuf[255];
    
    if (news_fname == NULL) {
	GetIndString(tmpstr, sFilenames, siNews);
	p2c(tmpstr, tmpbuf);
	if (!empty_string(tmpbuf))
	  news_fname = copy_string(tmpbuf);
	else
	  news_fname = NEWSFILE;
    }
    return news_fname;
}

char *
saved_game_filename()
{
    Str255 tmpstr;
    char tmpbuf[255];
    
    if (save_fname == NULL) {
	GetIndString(tmpstr, sFilenames, siSavedGame);
	p2c(tmpstr, tmpbuf);
	if (!empty_string(tmpbuf))
	  save_fname = copy_string(tmpbuf);
	else
	  save_fname = "Saved Game";
    }
    return save_fname;
}

/* (should make use of turn number in checkpoint filename) */

char *
checkpoint_filename(int n)
{
    Str255 tmpstr;
    char tmpbuf[255];

    if (checkpoint_fname == NULL) {
	GetIndString(tmpstr, sFilenames, siCheckpoint);
	p2c(tmpstr, tmpbuf);
	if (!empty_string(tmpbuf))
	  checkpoint_fname = copy_string(tmpbuf);
	else
	  checkpoint_fname = "Checkpoint";
    }
    return checkpoint_fname;
}

char *
error_save_filename()
{
    Str255 tmpstr;
    char tmpbuf[255];

    if (error_save_fname == NULL) {
	GetIndString(tmpstr, sFilenames, siErrorSave);
	p2c(tmpstr, tmpbuf);
	if (!empty_string(tmpbuf))
	  error_save_fname = copy_string(tmpbuf);
	else
	  error_save_fname = "Error Save";
    }
    return error_save_fname;
}

char *
statistics_filename()
{
    Str255 tmpstr;
    char namebuf[256];
    Point pnt;
    SFReply reply;

    if (statistics_fname == NULL) {
	GetIndString(tmpstr, sFilenames, siStatistics);
	p2c(tmpstr, namebuf);
	if (!empty_string(namebuf))
	  statistics_fname = copy_string(namebuf);
	else
	  statistics_fname = "Statistics";
	/* Collect the file and path to save to. */
	SetPt(&pnt, 100, 100);
	c2p(statistics_fname, tmpstr);
	SFPutFile(pnt, "\pSave game statistics as:", tmpstr, /*(DlgHookProcPtr)*/ nil, &reply);
	if (!reply.good)
	  return NULL;
	/* Make the location of the file be the current volume. */
	SetVol(reply.fName, reply.vRefNum);
	p2c(((char *) reply.fName), namebuf);
	statistics_fname = copy_string(namebuf);
    }
    return statistics_fname;
}

/* The purpose of this wrapper for fopen is to handle cases where the 
mac is opening a unix file and automatic linefeed conversion therefore
should be disabled. */

FILE *
open_file(char *filename, char *mode)
{
	FILE 	*fp;
	short	curvrefnum;
	int 	mac_linefeeds = 0;
	int 	unix_linefeeds = 0;
	int	ch;

	/* Hack. Force debug output to top directory. */ 
	if (strcmp(filename, "Xconq.DebugOut") == 0)
	    SetVol(NULL, initialvrefnum);

	/* Hack. Force warning output to top directory. */ 
	if (strcmp(filename, "Xconq.Warnings") == 0)
	    SetVol(NULL, initialvrefnum);

	/* Writing or Appending: always mac format. */	
	if (strcmp(mode, "w") == 0) {
		fp = fopen(filename, "w");
		return fp;
	}		
	if (strcmp(mode, "a") == 0) {
		fp = fopen(filename, "a");
		return fp;
	}		
	/* Reading: first open in binary mode (no conversion). */
	fp = fopen(filename, "rb");
	/* Look in xconq start directory if we did not find a file. */
	if (fp == NULL) {
		GetVol(NULL, &curvrefnum);
		SetVol(NULL, initialvrefnum);
		fp = fopen(filename, "rb");
		SetVol(NULL, curvrefnum);
	}
	/* Give up if we found nothing there either. */
	if (fp == NULL)
	    return fp;
	/* Binary (image) files don't need conversion. */
	if (strcmp(mode, "rb") == 0)
	    return fp;
	/* Text file. Count the number of linefeeds of each kind. */
	while ((ch = getc(fp)) != EOF) {
		if (ch == '\r')
			++mac_linefeeds;
		if (ch == '\n')
			++unix_linefeeds;
	}
	/* Close the file (also needed in unix case to reset file pointer). */
	fclose(fp);
	/* Assume that a mac file has more mac than unix linefeeds. */
	if (mac_linefeeds > unix_linefeeds) {
		/* Reopen mac file in text mode (with conversion). */
		fp = fopen(filename, "r");
	} else	{
		/* Reopen unix file in binary mode (no conversion). */
		fp = fopen(filename, "rb");
	}
	return fp;		
}

/* Attempt to open a library file. */

FILE *
open_module_library_file(Module *module)
{
    short curvrefnum;
    char fullnamebuf[255];
    LibraryPath *p;
    FILE *fp;
    
    /* Can't open anonymous library modules. */
    if (module->name == NULL)
      return NULL;
    /* Generate library pathname. */
    for_all_library_paths(p) {
	make_pathname(p->path, module->name, "g", fullnamebuf);
	/* Now try to open the file. */
	fp = open_file(fullnamebuf, "r");
	if (fp != NULL) {
	    /* Remember the filename where we found it. */
	    module->filename = copy_string(fullnamebuf);
	    return fp;
	}
    }
    return NULL;
}

FILE *
open_module_explicit_file(Module *module)
{
    short curvrefnum;
    char fullnamebuf[255];
    LibraryPath *p;
    FILE *fp = NULL;
    
    if (module->filename == NULL) {
	/* Try guessing a filename, since none supplied. */
	if (module->name != NULL) {
	    for_all_library_paths(p) {
		make_pathname(p->path, module->name, "g", fullnamebuf);
		/* Now try to open the file. */
		fp = open_file(fullnamebuf, "r");
		if (fp != NULL)
		  return fp;
	    }
	}
    } else {
	/* Try some other random ideas. */
	sprintf(fullnamebuf, "%s", module->filename);
	fp = open_file(module->filename, "r");
	if (fp != NULL) {
	    add_library_path("");
	    return fp;
	}
	sprintf(fullnamebuf, ":%s", module->filename);
	fp = open_file(fullnamebuf, "r");
	if (fp != NULL)
	  return fp;
	sprintf(fullnamebuf, "%s%s", ":lib:", module->filename);
	fp = open_file(fullnamebuf, "r");
	if (fp != NULL)
	  return fp;
    }
    return NULL;
}

FILE *
open_library_file(char *filename)
{
    short curvrefnum;
    char fullnamebuf[255];
    LibraryPath *p;
    FILE *fp;
	
    /* Now try to open the file. */
    fp = open_file(filename, "r");
    if (fp != NULL)
      return fp;
    /* Generate library pathname. */
    for_all_library_paths(p) {
	make_pathname(p->path, filename, NULL, fullnamebuf);
	/* Now try to open the file. */
 	fp = open_file(fullnamebuf, "r");
	if (fp != NULL)
	  return fp;
    }
    return NULL;
}

FILE *
open_scorefile_for_reading(char *name)
{
    char buf[255];
    short curvrefnum;
    FILE *fp;
	
    GetVol(NULL, &curvrefnum);
    SetVol(NULL, initialvrefnum);
    fp = open_file(name, "r");
    SetVol(NULL, curvrefnum);
    return fp;
}

FILE *
open_scorefile_for_writing(char *name)
{
    char buf[255];
    short curvrefnum;
    FILE *fp;
	
    GetVol(NULL, &curvrefnum);
    SetVol(NULL, initialvrefnum);
    /* Now try to open the file. */
    sprintf(buf, "%s", name);
    fp = open_file(buf, "a");
    SetVol(NULL, curvrefnum);
    return fp;
}

/* Close scorefile after having written to it; nothing special to do on Macs. */

void
close_scorefile_for_writing(fp)
FILE *fp;
{
    fclose(fp);
}

void
make_pathname(char *path, char *name, char *extn, char *pathbuf)
{
    strcpy(pathbuf, "");
    if (!empty_string(path)) {
	strcat(pathbuf, path);
	strcat(pathbuf, ":");
    }
    strcat(pathbuf, name);
    /* Don't add a second identical extension, but do add if extension
       is different (in case we want "foo.12" -> "foo.12.g" for instance) */
    if (strrchr(name, '.')
	&& extn
	&& strcmp(strrchr(name, '.') + 1, extn) == 0)
      return;
    if (!empty_string(extn)) {
	strcat(pathbuf, ".");
	strcat(pathbuf, extn);
    }
}

/* Remove a given file. */

int
remove_file(char *fname)
{
    /* (should implement) */
    return FALSE;
}

void
init_signal_handlers()
{
}

int last_ticks = 0;

int
n_seconds_elapsed(n)
int n;
{
    int ticks = TickCount();

    if (((ticks - last_ticks) / 60) > n) {
	last_ticks = ticks;
    	return TRUE;
    } else {
	return FALSE;
    }
}

int last_ticks_for_ms = 0;

int
n_ms_elapsed(n)
int n;
{
    return (((TickCount() - last_ticks_for_ms) * 16) > n);
}

void
record_ms()
{
    last_ticks_for_ms = TickCount();
}

#if 0		/* Not called from anywhere. */

/* Instead of coredumping, which is not a normal Mac facility, we
   drop into Macsbug.  If we then "g" from Macsbug, the program will
   exit cleanly. */

void
mac_abort ()
{
    /* Make sure no output still buffered up, then zap into MacsBug. */
#if 0 /* how to know if stdio in use? */
    fflush(stdout);
    fflush(stderr);
    printf("## Abort! ##\n");
#endif
#ifdef MPW_SADE
    SysError(8005);
#else 
    Debugger();
#endif
    /* "g" in MacsBug will then cause a regular error exit. */
    exit(1);
}

#endif

int
open_remote_connection(char *methodname, int willhost)
{
    int serial_port, rslt;

    if (methodname == NULL) {
	connection_method = 0;
    } else if (strcmp(methodname, "serial") == 0) {
	connection_method = 1;
    } else if (strcmp(methodname, "appletalk") == 0) {
	connection_method = 2;
    } else if (strcmp(methodname, "tcp") == 0) {
	connection_method = 3;
    } else if (strcmp(methodname, "file") == 0) {
	connection_method = 4;
    } else {
	connection_method = 0;
    }
    switch (connection_method) {
      case 0:
	break;
      case 1:
	/* Get a decision about which serial port to use. */
	serial_port = serial_port_dialog();
	/* If cancelled here, get out and don't open anything. */
	if (serial_port < 0)
	  break;
	init_serial_port(serial_port);
	/* Serial supports exactly two programs, so we can easily
	   set up all the rids right now. */
#if 0 /* but that would be wrong */
	master_rid = 1;
	if (willhost) {
	    my_rid = 1;
	} else {
	    my_rid = 2;
	}
	numremotes = 2;
#endif
	break;
      case 2:
	open_appletalk();
	break;
      case 3:
	init_warning("No TCP/IP support yet, ignoring");
	break;
      case 4:
	init_file_port(willhost);
	break;
      default:
	break;
    }
    return connection_method;
}

extern short ser_input_refnum;
extern short ser_output_refnum;

/* Low-level transmission to another program. */

void
low_send(int id, char *buf)
{
    if (sendcursor != nil)
      SetCursor(*sendcursor);
    Dprintf("Sending: %d \"%s\"...", id, (buf ? buf : "<null>"));
    switch (connection_method) {
      case 0:
	/* Not connected anywhere, no need to complain. */
	break;
      case 1:
	low_serial_send(id, buf);
	break;
      case 2:
	low_appletalk_send(id, buf);
	break;
      case 3:
	break;
      case 4:
	low_file_send(id, buf);
	break;
      default:
	case_panic("connection method", connection_method);
	break;
    }
    Dprintf(" sent.\n");
    if (sendcursor != nil) {
	if (current_cursor != nil)
	  SetCursor(*current_cursor);
	else
	  SetCursor(&QD(arrow));
    }
}

int
low_receive(int *idp, char *buf, int maxchars, int timeout)
{
    int rslt = FALSE;

    if (receivecursor != nil)
      SetCursor(*receivecursor);
    switch (connection_method) {
      case 0:
	/* Not connected anywhere, no need to complain. */
	break;
      case 1:
	/* Only one remote if using serial. */
	*idp = (0 /*my_rid == 1 */? 2 : 1);
	rslt = low_serial_readchars(buf, maxchars, timeout);
	break;
      case 2:
	/* Only one remote for now. */
	*idp = (0 /*my_rid == 1*/ ? 2 : 1);
	rslt = low_appletalk_receive(idp, buf, maxchars, timeout);
	break;
      case 3:
	break;
      case 4:
	rslt = low_file_receive(idp, buf, maxchars, timeout);
	break;
      default:
	case_panic("connection method", connection_method);
	break;
    }
    if (receivecursor != nil) {
	if (current_cursor != nil)
	  SetCursor(*current_cursor);
	else
	  SetCursor(&QD(arrow));
    }
    return rslt;
}

void
close_remote_connection(int rid)
{
    switch (connection_method) {
      case 0:
	break;
      case 1:
	close_serial_port();
	break;
      case 2:
	close_appletalk();
	break;
      case 3:
	break;
      case 4:
	close_file_port();
	break;
      default:
	break;
    }
}

short ser_input_refnum;
short ser_output_refnum;

static void
init_serial_port(port)
int port;
{
    OSErr err;

    if (port == 0) {
	/* Open the modem port. */
	err = OpenDriver("\p.AIn", &ser_input_refnum);
	if (err != 0) {
	    return;
	}
	err = OpenDriver("\p.AOut", &ser_output_refnum);
	if (err != 0) {
	    /* This isn't working, so close the input as well. */
	    CloseDriver(ser_input_refnum);
	    return;
	}
    } else {
	/* Open the printer port. */
	err = OpenDriver("\p.BIn", &ser_input_refnum);
	if (err != 0) {
	    return;
	}
	err = OpenDriver("\p.BOut", &ser_output_refnum);
	if (err != 0) {
	    /* This isn't working, so close the input as well. */
	    CloseDriver(ser_input_refnum);
	    return;
	}
    }
#if 0 /* might need this eventually? */
    if (0 /* using custom buffer */)
      SerSetBuf (ser_input_refnum, mac_input_buffer, 256);
#endif
    SerReset (ser_input_refnum,  stop10|noParity|data8|baud9600);
    SerReset (ser_output_refnum, stop10|noParity|data8|baud9600);
    /* (what does this do?) */
    {
	CntrlParam cb;
	struct SerShk *handshake;
	
	cb.ioCRefNum = ser_output_refnum;
	cb.csCode = 14;
	handshake = (struct SerShk *) &cb.csParam[0];
	handshake->fXOn = 0;
	handshake->fCTS = 0;
	handshake->xOn = 0;
	handshake->xOff = 0;
	handshake->errs = 0;
	handshake->evts = 0;
	handshake->fInX = 0;
	handshake->fDTR = 0;
	err = PBControl ((ParmBlkPtr) &cb, 0);
    }
}

static void
low_serial_send(int id, char *buf)
{
    OSErr err;
    IOParam pb;

    pb.ioRefNum = ser_output_refnum;
    pb.ioBuffer = (Ptr) buf;
    pb.ioReqCount = strlen(buf);
    err = PBWrite((ParmBlkPtr) &pb, 0);
}

static int
low_serial_readchars(char *buf, int maxchars, int timeout)
{
    int status, n, n2;
    /* time_t */ unsigned long start_time, now;
    OSErr err;
    CntrlParam cb;
    IOParam pb;

    time (&start_time);

    while (1) {
	cb.ioCRefNum = ser_input_refnum;
	cb.csCode = 2;
	err = PBStatus((ParmBlkPtr) &cb, 0);
	if (err < 0)
	  return FALSE;
	n = *((long *) &cb.csParam[0]);
	if (n > 0) {
	    pb.ioRefNum = ser_input_refnum;
	    pb.ioBuffer = (Ptr) buf;
	    pb.ioReqCount = (n > 64 ? 64 : n);
	    err = PBRead((ParmBlkPtr) &pb, 0);
	    if (err < 0)
	      return FALSE;
	    n2 = pb.ioReqCount;
	    buf[n2] = '\0';
	    return TRUE;
	} else if (timeout == 0) {
	    return FALSE;
	} else if (timeout == -1) {
	    /* Go around again. */
	} else {
	    time (&now);
	    if (now > start_time + timeout) {
		Dprintf("%ul > %ul + %d\n", now, start_time, timeout);
		return FALSE /* timed out */;
	    }
	}
    }
}

static void
close_serial_port()
{
    if (ser_input_refnum)
      CloseDriver(ser_input_refnum);
    if (ser_output_refnum)
      CloseDriver(ser_output_refnum);
}

NamesTableEntry names_table_entry;

int nbp_registered;

unsigned char atp_socket_number;

AddrBlock remote_addr;

static void
open_appletalk()
{
#ifndef __MWERKS__
    int i, numgotten;
    OSErr err;
    ATPParamBlock tmpATPPB;
    MPPParamBlock tmpMPPPB, tmpCancelMPPPB;
    NamesTableEntry lookup;
    char *namesbuffer;
    char *bigbuffer;
    EntityName entityname;
    AddrBlock entityaddr;

    if (IsMPPOpen == false) {
	err = MPPOpen();
	/* (should check result) */
    }

    tmpMPPPB.SETSELF.newSelfFlag = true;
    err = PSetSelfSend(&tmpMPPPB, false);
    /* (should check result) */

    /* (should collect our own network address?) */

    tmpATPPB.ATP.atpSocket = 0;
    tmpATPPB.ATP.addrBlock.aNet = 0;
    tmpATPPB.ATP.addrBlock.aNode = 0;
    tmpATPPB.ATP.addrBlock.aSocket = 0;
    err = POpenATPSkt(&tmpATPPB, false);
    /* (should check result) */

    NBPSetNTE(&names_table_entry, "\pStan", "\pXconq", "\p*", 64);

    tmpMPPPB.NBP.ioCompletion = nil;
    tmpMPPPB.NBP.interval = 0x0F;
    tmpMPPPB.NBP.count = 0x03;
#if 0
    tmpMPPPB.NBP.nbpPtrs.entityPtr = &names_table_entry;
#else
    tmpMPPPB.NBP.NBPPtrs.entityPtr = &names_table_entry;
#endif
    tmpMPPPB.NBP.parm.verifyFlag = true;
	
    err = PRegisterName(&tmpMPPPB, true);
    /* (should check result) */

    {
  	short itemHit;
  	EventRecord theEvent;
  	Point mouse;
	extern void get_global_mouse(Point *mouse);

	do 
	  {
	      itemHit = 0;
		
	      if (GetNextEvent(everyEvent,&theEvent) == true && IsDialogEvent(&theEvent) == true)
		{
			
		    get_global_mouse(&mouse);
		    if (mouse.v == 0 && mouse.h == 0)
		      itemHit = 1;	
			
		    else
		      {
			  itemHit = 0;
		      }
		}
		
	  } while (itemHit != 1 && tmpMPPPB.NBP.ioResult == 1);

	if (itemHit == 1)
	  {
	      tmpCancelMPPPB.NBPKILL.nKillQEl = (Ptr) &tmpMPPPB;
		
	      PKillNBP(&tmpCancelMPPPB, false);
	  } else
	    nbp_registered = TRUE;
    }

    bigbuffer = xmalloc(10000);
    namesbuffer = xmalloc(250 * 33);

    NBPSetEntity((Ptr)&lookup.nt.entityData, "\p=", "\pXconq", "\p*");

    tmpMPPPB.NBP.ioCompletion = nil;
    tmpMPPPB.NBP.interval = 3;
    tmpMPPPB.NBP.count = 3;
    tmpMPPPB.NBPentityPtr = &lookup.nt.entityData;
    tmpMPPPB.NBPretBuffSize = 10000;
    tmpMPPPB.NBPretBuffPtr = bigbuffer;
    tmpMPPPB.NBPmaxToGet = 1000 / sizeof(NTElement);

    err = PLookupName(&tmpMPPPB, false);

    numgotten = tmpMPPPB.NBP.parm.Lookup.numGotten;
    if (numgotten > 0) {
#if 0
	master_rid = 1;
	if (willhost) {
	    my_rid = 1;
	} else {
	    my_rid = 2;
	}
	numremotes = 1;
#endif
	/* (should use this loop to get actual number of remotes) */
	for (i = 0; i < numgotten; ++i) {
	    err = NBPExtract(namesbuffer, numgotten, i + 1, &entityname, &remote_addr); 
	}
    }

    /* (should release big buffers) */

    /* Open an ATP socket. */

    tmpATPPB.ATP.atpSocket = 0;
    tmpATPPB.ATP.addrBlock.aNet = 0;
    tmpATPPB.ATP.addrBlock.aNode = 0;
    tmpATPPB.ATP.addrBlock.aSocket = 0;

    err = POpenATPSkt(&tmpATPPB, false);

    atp_socket_number = tmpATPPB.ATP.atpSocket;
#endif /* __MWERKS__ */
}

static int
low_appletalk_receive(int *idp, char *buf, int maxchars, int timeout)
{
#ifndef __MWERKS__
    int status, n;
    /* time_t */ unsigned long start_time, now;
    OSErr err;
    int numbds;
    char atprequestbuf[256];
    BDSType bds;
    ATPParamBlock requestATPPB, replyATPPB;

    time (&start_time);

    while (1) {
	requestATPPB.ATP.ioCompletion = nil;
	requestATPPB.ATP.atpSocket = atp_socket_number;
	requestATPPB.ATP.reqLength = maxchars;
	requestATPPB.ATP.reqPointer = buf;

	err = PGetRequest(&requestATPPB, false);

	numbds = BuildBDS("OK", &bds, 256);

	replyATPPB.ATP.ioCompletion = nil;
	replyATPPB.ATP.atpFlags = atpEOMvalue;
	replyATPPB.ATP.atpSocket = atp_socket_number;
	replyATPPB.ATP.addrBlock = requestATPPB.ATP.addrBlock;
	replyATPPB.ATP.reqLength = strlen(buf) + 1;
	replyATPPB.ATP.bdsPointer = &bds;
	replyATPPB.OTH1.u0.numOfBuffs = 1;
	replyATPPB.OTH2.bdsSize = 256;
	replyATPPB.OTH2.transID = requestATPPB.ATP.reqTID;

	err = PSendResponse(&replyATPPB, false);

	n = requestATPPB.ATP.reqLength;
	if (n > 0) {
	    buf[n] = '\0';
	    return TRUE;
	} else if (timeout == 0) {
	    return FALSE;
	} else if (timeout == -1) {
	    /* Go around again. */
	} else {
	    time (&now);
	    if (now > start_time + timeout) {
		Dprintf("%ul > %ul + %d\n", now, start_time, timeout);
		return FALSE /* timed out */;
	    }
	}
    }
#else
    return 0;
#endif /* __MWERKS__ */
}

static void
low_appletalk_send(int id, char *buf)
{
#ifndef __MWERKS__
    int numbds;
    char atpresponsebuf[256];
    OSErr err;
    BDSType bds;
    ATPParamBlock tmpATPPB;

    numbds = BuildBDS(atpresponsebuf, &bds, 256);

    tmpATPPB.ATP.ioCompletion = nil;
    tmpATPPB.ATP.atpFlags = 0;

    tmpATPPB.ATP.atpSocket = atp_socket_number;
    tmpATPPB.ATP.addrBlock.aNet = remote_addr.aNet;
    tmpATPPB.ATP.addrBlock.aNode = remote_addr.aNode;
    tmpATPPB.ATP.addrBlock.aSocket = remote_addr.aSocket;
    tmpATPPB.ATP.reqLength = strlen(buf) + 1;
    tmpATPPB.ATP.reqPointer = buf;
    tmpATPPB.ATP.bdsPointer = &bds;
    tmpATPPB.SREQ.timeOutVal = 10;
    tmpATPPB.SREQ.retryCount = 3;

    err = PSendRequest(&tmpATPPB, false);
#endif /* __MWERKS __ */
}

static void
close_appletalk()
{
#ifndef __MWERKS__
    OSErr err;
    ATPParamBlock tmpATPPB;
    MPPParamBlock tmpMPPPB;

    tmpATPPB.ATP.atpSocket = atp_socket_number;

    err = PCloseATPSkt(&tmpATPPB, false);

    if (nbp_registered) {
	tmpMPPPB.NBP.NBPPtrs.entityPtr = (Ptr) &names_table_entry.nt.entityData;
		
	err = PRemoveName(&tmpMPPPB, false);
	nbp_registered = FALSE;
    }
#endif /* __MWERKS __ */
}
