/*
 * Copyright 1988, 1989, 1990, 1991 Massachusetts Institute of Technology
 */
/****************************************************************
  This is the subroutine library for Galatea, the video control
  system.
****************************************************************/

/*********************** Version 2.5 **************************/

#include"Glib.h"
#ifdef MAC
#include <Storage.h>
#endif /* MAC */

G_INT32 g_errno = NO_ERROR;

static short DEBUG = 0;
static G_INT32 sync = 0;
static struct timeval timeout, stattime, *timer = NULL, statreadto = {0,0};
static G_INT32 (*proc)() = NULL;
static char *opened_host = (char *)NULL;

/* returns Server or NULL if failure */
Server *GNewOpenServer(hoststring)
     char *hoststring;
{
  char *another_string, *out_only, *getenv(), *strchr();
  Server *tserv;
  if (hoststring) another_string = hoststring;
  else another_string = getenv("GALATEA");
  /* This next should use a resolve file to match DISPLAY's to GALATEA's */
  if (!another_string) another_string = "unix:0";
  out_only = strchr(another_string, ':');
  if (!out_only) return(NULL);
  *(out_only) = 0;
  out_only++;
  return(GOpenServer(another_string, (G_INT32)atol(out_only)));
}

/* Returns name of previously opened or failed to opened host or ""
 * name is saved in GOpenServer() */
char *GOpenedHost()
{
  if (opened_host != (char *)NULL && opened_host == "\0") {
    return("unix");
  }
  else
    return(opened_host);
}

char *GErrorString(errcode)
     int errcode;
{
    if (errcode < NO_ERROR || errcode > HIGHEST_ERROR)
	return NULL;
    return (G_error_strings[errcode-999999]);
}
    
/* returns Server or NULL if failure */
Server *GOpenServer(host, output)
     char *host;
     G_INT32 output;
{
  Server *tempserv;
  char *finalhost;

  tempserv = (Server *)calloc(1, sizeof(Server));
  if (tempserv == NULL) return(NULL);

  tempserv->output = output;
  tempserv->num_db = 0;
  tempserv->databases = (DatabaseList *)NULL;
  tempserv->fd = _gconnserv(host, &finalhost);
  if (finalhost) {
    if (opened_host != (char *)(NULL)) free(opened_host);
    opened_host = (char *)malloc((strlen(finalhost) + 1) * sizeof(char));
    if (opened_host)
      strcpy(opened_host, finalhost);
  }
  else {
    opened_host = NULL;
  }
  if (tempserv->fd == -1) {
    _freeserv(tempserv);
    return(NULL);
  }
  if (opened_host != (char *)NULL) {
    tempserv->server = 
      (char *)malloc((strlen(opened_host) + 1) * sizeof(char));
    if (tempserv->server != (char *)NULL)
      strcpy(tempserv->server, opened_host);
  }
  else tempserv->server = NULL;
  if (_gopenserv(tempserv) == -1) {
    GCloseServer(tempserv);
    return(NULL);
  }
  return(tempserv);
}

/* returns Server or NULL if failure */
Server *GReopenServer(msgsock)
     Server *msgsock;
{
  G_INT32 i;
  if (msgsock->volumes) {
    for (i = 0; i < msgsock->num_volumes; i++) {
      free(msgsock->volumes[i].volume);
    }
    free(msgsock->volumes);
  }
  msgsock->volumes = NULL;
  msgsock->num_volumes = 0;
  if (msgsock->extensions) {
    for (i = 0; i < msgsock->num_extensions; i++) {
      free(msgsock->extensions[i]);
    }
    free(msgsock->extensions);
  }
  msgsock->num_extensions = 0;
  _gopenserv(msgsock);
  return(msgsock);
}

G_INT32 _freeserv(msgsock)
     Server *msgsock;
{
  G_INT32 i;

  if (msgsock->server) free(msgsock->server);
  if (msgsock->volumes) {
    for (i = 0; i < msgsock->num_volumes; i++) {
      if (msgsock->volumes[i].volume) 
	free(msgsock->volumes[i].volume);
    }
    free(msgsock->volumes);
  }
  if (msgsock->extensions) {
    for (i = 0; i < msgsock->num_extensions; i++) {
      free(msgsock->extensions[i]);
    }
    free(msgsock->extensions);
  }
  free(msgsock);
  return 0;
}

G_INT32 _gopenserv(sock)
     Server *sock;
{
  G_INT32 cur_type, cur_index, i, auth_type = 0;
  char tempstring[30];
#ifdef KERBEROS
  auth_type = 1;
#endif /* KERBEROS */
    if (SendCommand(sock->fd,G_OpenServer,sock->output,getuid(),auth_type,
		    1,0,0,0,0,0) == -1)
    return(GIO_ERROR);
  sock->num_volumes = acceptint(sock->fd);
  if (sock->num_volumes == GIO_ERROR) return(-1);
  sock->volumes=(VolumeList *)malloc(sizeof(VolumeList)*(sock->num_volumes));
  if (sock->volumes == NULL) return(-1);
  for (i = 0; i < sock->num_volumes; i++) {
    sock->volumes[i].index = acceptint(sock->fd);
    if (sock->volumes[i].index == GIO_ERROR) return(-1);
    sock->volumes[i].type = acceptint(sock->fd);
    if (sock->volumes[i].type == GIO_ERROR) return(-1);
    sock->volumes[i].volume = acceptstring(sock->fd);
    if (sock->volumes[i].volume == NULL) {
      if (DEBUG) printf("Error reading Volume_List (volume)\n");
      return(-1);
    }
    if (DEBUG) printf ("volume %d: %s\n",i,sock->volumes[i].volume);
  }
  sock->switching_mask = acceptint(sock->fd);
  if (sock->switching_mask == GIO_ERROR) return(-1);
  sock->num_extensions = acceptint(sock->fd);
  if (sock->num_extensions == GIO_ERROR) return(-1);
  sock->extensions = (char **)malloc(sock->num_extensions*sizeof(char *));
  if (sock->extensions == NULL) return(-1);
  for (i = 0; i < sock->num_extensions; i++) {
    sock->extensions[i] = acceptstring(sock->fd);
    if (sock->extensions[i] == NULL) return(-1);
    if (DEBUG) printf("Extension: %s\n", sock->extensions[i]);
  }
  sock->max_lock_time = acceptint(sock->fd);
  if (sock->max_lock_time == GIO_ERROR) return(-1);

  /* these are left here for extensions to the structure */

  sock->ex1 = acceptint(sock->fd);
  if (sock->ex1 == GIO_ERROR) return(GIO_ERROR);
  if (sock->ex1 != 0) {
    /* Why didn't the server allow */
    /* the connection */
    g_errno = sock->ex1;
    return(sock->ex1);
  }  

  sock->ex2 = acceptint(sock->fd);        /* What kind of authentication */
  if (sock->ex2 == GIO_ERROR) return(-1); /* to use */

  sock->ex3 = acceptint(sock->fd);        /* Are databases available? */
  if (sock->ex3 == GIO_ERROR) return(-1);

  sock->ex4 = acceptint(sock->fd);
  if (sock->ex4 == GIO_ERROR) return(-1);  /* Revision number */

  /* Base extension number if non-zero */
  sock->ex5 = acceptint(sock->fd);
  if (sock->ex5 == GIO_ERROR) return(-1);

  sock->ex6 = acceptint(sock->fd);
  if (sock->ex6 == GIO_ERROR) return(-1);
  sock->ex7 = acceptint(sock->fd);
  if (sock->ex7 == GIO_ERROR) return(-1);
  sock->ex8 = acceptint(sock->fd);
  if (sock->ex8 == GIO_ERROR) return(-1);
  return(0);
}

/* returns {NO_ERROR} */
G_INT32 GCloseServer(msgsock)
Server *msgsock;
{
  close(msgsock->fd);
  _freeserv(msgsock);
  return(NO_ERROR);
}

/* returns {BAD_VOLUME | NO_ERROR} */
/* channel corresponds to volume */
G_INT32 GSwitch(msgsock,channel,mask)
     Server *msgsock;
     G_INT32 channel, mask;
{
  if (SendCommand(msgsock->fd,G_Switch,channel,mask,sync,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  if (!sync) return(_handleerror(msgsock,acceptint(msgsock->fd)));
  return (NO_ERROR);
}
 
/* returns {actual current speed | BAD_VOLUME | FUNC_NOT_SUPPORTED} */
G_INT32 GRun(msgsock,volume, speed, mask)
     Server *msgsock;
     G_INT32 speed, volume, mask;
{
  if (SendCommand(msgsock->fd,G_Run,volume,speed,mask,sync,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  if (!sync) return(_handleerror(msgsock,acceptint(msgsock->fd)));
  return(speed);
}

/* direction is one of FORWARD or REVERSE */
/* returns {actual current frame | BAD_VOLUME |
   FUNC_NOT_SUPPORTED | NO_ERROR} */
G_INT32 GJog(msgsock, volume, direction, mask)
     Server *msgsock;
     G_INT32 direction, volume, mask;
{
  if (SendCommand(msgsock->fd,G_Jog,volume,direction,mask,sync,0,0,0,0,0)== -1)
    return(_handleerror(msgsock,GIO_ERROR));
  if (!sync) return(_handleerror(msgsock,acceptint(msgsock->fd)));
  return(NO_ERROR);
}

/* returns {actual current frame | BAD_VOLUME | 
   FUNC_NOT_SUPPORTED | NO_ERROR} */
G_INT32 GStill(msgsock,volume, mask)
     Server *msgsock;
     G_INT32 volume, mask;
{
  if (SendCommand(msgsock->fd,G_Still,volume,mask,sync,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  if (!sync) return(_handleerror(msgsock,acceptint(msgsock->fd)));
  return(NO_ERROR);
}
#ifdef OBSOLETE
/* returns {NO_ERROR | BAD_VOLUME | BAD_ARGUMENT | FUNC_NOT_SUPPORTED} */
/* on_off is INDEX_ON or INDEX_OFF or INDEX_TOGGLE */
GIndex(msgsock, volume, on_off)
     Server *msgsock;
     G_INT32 volume, on_off;
{
  if (SendCommand(msgsock->fd,G_Index,volume,on_off,sync,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  if (!sync) return(_handleerror(msgsock,acceptint(msgsock->fd)));
  return(NO_ERROR);
}
#endif /* OBSOLETE */
/* returns {NO_ERROR | BAD_VOLUME | BAD_ARGUMENT | FUNC_NOT_SUPPORTED} */
/* chan is {LEFT_CHAN | RIGHT_CHAN | INDEX_CTRL} */
/* on_off is TURN_ON or TURN_OFF */
G_INT32 GConfigure(msgsock, volume, chan, on_off)
     Server *msgsock;
     G_INT32 volume, chan, on_off;
{
  if(SendCommand(msgsock->fd,G_Configure,volume,chan,on_off,
		 sync,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  if (!sync) return(_handleerror(msgsock,acceptint(msgsock->fd)));
  return(NO_ERROR);
}

/* returns {NO_ERROR | BAD_VOLUME | BAD_ARGUMENT | FUNC_NOT_SUPPORTED} */
/* on_off is LOAD or UNLOAD */
G_INT32 GLoad(msgsock, volume, on_off)
     Server *msgsock;
     G_INT32 volume, on_off;
{
  if (SendCommand(msgsock->fd,G_Load,volume,on_off,sync,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  if (!sync) return(_handleerror(msgsock,acceptint(msgsock->fd)));
  return(NO_ERROR);
}

/* returns {actual current frame | BAD_VOLUME | FUNC_NOT_SUPPORTED | 
 COULDNT_SEARCH}*/
G_INT32 GSearch(msgsock,volume,frame, mask)
     Server *msgsock;
     G_INT32 volume, frame, mask;
{
  if (SendCommand(msgsock->fd,G_Search,volume,frame,mask,sync,0,0,0,0,0) == -1)
     return(_handleerror(msgsock,GIO_ERROR));
  if (!sync) return(_handleerror(msgsock,acceptint(msgsock->fd)));
  return(frame);
}

/* returns {actual current frame | BAD_VOLUME | FUNC_NOT_SUPPORTED} */
G_INT32 GGetFrame(msgsock,volume)
     Server *msgsock;
     G_INT32 volume;
{
  if (SendCommand(msgsock->fd,G_GetFrame,volume,sync,0,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  return(_handleerror(msgsock,acceptint(msgsock->fd)));
}

/* returns {actual current speed | BAD_VOLUME | FUNC_NOT_SUPPORTED} */
G_INT32 GGetSpeed(msgsock,volume)
     Server *msgsock;
     G_INT32 volume;
{
  if (SendCommand(msgsock->fd,G_GetSpeed,volume,sync,0,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  return(_handleerror(msgsock,acceptint(msgsock->fd)));
}

/* returns {actual current frame | actual current speed |
   BAD_VOLUME | FUNC_NOT_SUPPORTED} */
G_INT32 GPlaySeg(msgsock,volume,start,end,speed,mask,return_flag)
     Server *msgsock;
     G_INT32 start, end, speed, mask, return_flag;
{
  if (SendCommand(msgsock->fd,G_PlaySeg,volume,start,end,
		  speed,mask,return_flag,sync,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  if (!sync) return(_handleerror(msgsock,acceptint(msgsock->fd)));
  return(NO_ERROR);
}
/* return {NO_ERROR | BAD_VOLUME | FUNC_NOT_SUPPORTED | SEGMENT_TOO_LONG */
G_INT32 GPlaySequence(serv, numsegs, segstoplay, return_flag)
     Server *serv;
     int numsegs, return_flag;
     GSegment *segstoplay;
{
  int i;

  if (SendCommand(serv->fd, G_PlaySequence, numsegs, return_flag, sync,
		  0, 0, 0, 0, 0, 0) == -1) 
    return(_handleerror(serv, GIO_ERROR));
  
  for (i = 0; i < numsegs; i++) {
    if (sendint(serv->fd, segstoplay[i].volumenum) == -1)
      return(_handleerror(serv, GIO_ERROR));
    if (sendint(serv->fd, segstoplay[i].start) == -1)
      return(_handleerror(serv, GIO_ERROR));
    if (sendint(serv->fd, segstoplay[i].end) == -1)
      return(_handleerror(serv, GIO_ERROR));
    if (sendint(serv->fd, segstoplay[i].speed) == -1)
      return(_handleerror(serv, GIO_ERROR));
    if (sendint(serv->fd, segstoplay[i].mask) == -1)
      return(_handleerror(serv, GIO_ERROR));
  }
  if (!sync) return(_handleerror(serv,acceptint(serv->fd)));
  return(NO_ERROR);
}

#ifdef OBSOLETE
/* returns {NO_ERROR | BAD_VOLUME | FUNC_NOT_SUPPORTED} */
GReset(msgsock,volume)
     Server *msgsock;
     G_INT32 volume;
{
  if (SendCommand(msgsock->fd,G_Reset,volume,sync,0,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  if (!sync) return(_handleerror(msgsock,acceptint(msgsock->fd)));
  return(NO_ERROR);
}
#endif /* OBSOLETE */

/* returns {NO_ERROR | BAD_ARGUMENT} */
G_INT32 GShutOff(msgsock,shutoffmode)
     Server *msgsock;
     G_INT32 shutoffmode;
{
  if (SendCommand(msgsock->fd,G_ShutOff,shutoffmode,0,0,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  return(_handleerror(msgsock,acceptint(msgsock->fd)));
}

/* Record functions */
/* Returns {start_frame | COULDNT_ALLOC} */
G_INT32 GAlloc(msgsock,volume,nframes)
     Server *msgsock;
     G_INT32 volume, nframes;
{
  if (SendCommand(msgsock->fd,G_Alloc,volume,nframes,0,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  return(_handleerror(msgsock,acceptint(msgsock->fd)));
}

/* Returns {start_frame | COULDNT_ALLOC} */
G_INT32 GAllocAtFrame(msgsock,volume,start,nframes)
     Server *msgsock;
     G_INT32 volume, start, nframes;
{
  if (SendCommand(msgsock->fd,G_AllocAtFrame,volume,start,nframes,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  return(_handleerror(msgsock,acceptint(msgsock->fd)));
}

/* returns {NO_ERROR | COULDNT_FREE} */
G_INT32 GFree(msgsock, volume, start, nframes)
     Server *msgsock;
     G_INT32 volume, start, nframes;
{
  if (SendCommand(msgsock->fd,G_Free,volume,start,nframes,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  return(_handleerror(msgsock,acceptint(msgsock->fd)));
}

/* returns {VIOLATE_ALLOC | SEGMENT_TOO_LONG | COULDNT_ROUTE | NO_ERROR} */
G_INT32 GRecord(msgsock, destvolume, start, end, speed, mask, sourcevolume, return_flag)
     Server *msgsock;
     G_INT32 destvolume, start, end, speed, mask, sourcevolume, return_flag;
{
  if (SendCommand(msgsock->fd, G_Record, destvolume, start, end, speed,
		  mask, sourcevolume, return_flag, sync, 0) == -1)
    return(_handleerror(msgsock, GIO_ERROR));
  if (!sync) return(_handleerror(msgsock, acceptint(msgsock->fd)));
  return(NO_ERROR);
}

G_INT32 GDub(msgsock, destvolume, start, end, speed, sourcevolume, srcstart, srcspeed, mask, return_flag)
     Server *msgsock;
     G_INT32 destvolume, start, end, speed, mask, sourcevolume, return_flag;
     G_INT32 srcstart, srcspeed;
{
  if (SendCommand(msgsock->fd, G_Dub, destvolume, start, end, speed,
		  sourcevolume, srcstart, srcspeed, mask, return_flag) == -1)
    return(_handleerror(msgsock, GIO_ERROR));
  if (sendint(msgsock->fd, sync) == -1)
    return(_handleerror(msgsock, GIO_ERROR));
  if (!sync) return(_handleerror(msgsock, acceptint(msgsock->fd)));
  return(NO_ERROR);
}

#ifdef OBSOLETE
/* This can handle preemptions, but not volume-table rebuilds.
   The client might want to be prepared to call this routine again,
   if a new volume table is formed.  The numcompleted return-value
   should allow a client to call this again in order to continue after
   an error. */
/* This routine takes thirteen arguments.  This should be a structure,
   perhaps. */
G_INT32 GCopy(serv, destvol, deststart, destinc, srcvol, srcstart, srcinc, numcycles, mask, numcompleted, onchans, offchans, lockstrat)
     Server *serv;
     G_INT32 destvol, deststart, destinc, srcvol, srcstart, srcinc, 
       numcycles, mask, *numcompleted, onchans, offchans, lockstrat;
{
  G_INT32 curcycle, curdest, cursrc, ret;
  
  curdest = deststart;
  cursrc = srcstart;
  
  if (lockstrat == TURN_ON) {
    ret = GLock(serv);
    if (ret >= LOWEST_ERROR) {
      if (numcompleted != (G_INT32 *)NULL) *numcompleted = 0;
      return(ret);
    }
  }
  for (curcycle = 0; curcycle < numcycles; 
       curcycle++, curdest+=destinc, cursrc+=srcinc) {
    if (lockstrat != TURN_ON) {
      ret = GLock(serv);
      if (ret >= LOWEST_ERROR) break;
    }
    ret = GSearch(serv, srcvol, cursrc, 0);
    if (ret >= LOWEST_ERROR) break;
    if (onchans != (G_INT32)0) {
      ret = GConfigure(serv, srcvol, onchans, TURN_ON);
      if (ret >= LOWEST_ERROR) break;
    }
    if (offchans != (G_INT32)0) {
      ret = GConfigure(serv, srcvol, offchans, TURN_OFF);
      if (ret >= LOWEST_ERROR) break;
    }
    ret = GRecord(serv, destvol, curdest, curdest + destinc - 1, F_PLAY, 
		  mask, srcvol, RETURN_SYNC);
    if (ret >= LOWEST_ERROR) break;
    if (lockstrat != TURN_ON) {
      ret = GUnLock(serv);
      if (ret >= LOWEST_ERROR) break;
    }
  }
  if (lockstrat == TURN_ON) {
    ret = GUnLock(serv);
    if (ret >= LOWEST_ERROR) {
      if (numcompleted != (G_INT32 *)NULL) *numcompleted = 0;
      return(ret);
    }
  }
  if (numcompleted != (G_INT32 *)NULL) *numcompleted = curcycle;
  /*  _handleerror(serv, ret);*/ /* Already handled by subroutines */
  return(ret);
}
#endif /* OBSOLETE */

G_INT32 GRecordPreview(msgsock, destvolume, sourcevolume, mask)
     Server *msgsock;
     G_INT32 destvolume, sourcevolume, mask;
{
  if (SendCommand(msgsock->fd, G_RecordPreview, destvolume, sourcevolume,
		  mask, sync, 0, 0, 0, 0, 0) == -1)
    return(_handleerror(msgsock, GIO_ERROR));
  if (!sync) return(_handleerror(msgsock, acceptint(msgsock->fd)));
  return(NO_ERROR);
}
/* End of record functions */

/* returns {NO_ERROR | VOLUME_LIST_OLD} */
G_INT32 GCheckRevision(msgsock)
     Server *msgsock;
{
  if (SendCommand(msgsock->fd,G_CheckRevision,0,0,0,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  return(_handleerror(msgsock,acceptint(msgsock->fd)));
}

/* returns {NO_ERROR} */
G_INT32 GLock(msgsock)
     Server *msgsock;
{
  if (SendCommand(msgsock->fd,G_Lock,0,0,0,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  return(_handleerror(msgsock,acceptint(msgsock->fd)));
}

/* returns {NO_ERROR} */
G_INT32 GUnLock(msgsock)
     Server *msgsock;
{
  if (SendCommand(msgsock->fd,G_UnLock,0,0,0,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  return(_handleerror(msgsock,acceptint(msgsock->fd)));
}

/* return current server time in seconds since midnight, Jan 1, 1970 */
G_INT32 GGetServerTime(msgsock)
     Server *msgsock;
{
  long nettime;

  if (SendCommand(msgsock->fd, G_GetServerTime, 0,0,0,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  return(acceptint(msgsock->fd));
}

/* returns {NO_ERROR | BAD_ARGUMENT} */
G_INT32 GSetState(setDEBUG, settime, setsync)
     G_INT32 setDEBUG, setsync;
     struct timeval *settime;
{
  DEBUG = (short)setDEBUG;
  sync = setsync;
  if (settime == NULL) timer = NULL;
  else {
    if (settime->tv_sec < 0 || settime->tv_usec < 0) return(BAD_ARGUMENT);
    else {
      stattime = *settime;
      timer = &timeout;
    }
  }
  return(NO_ERROR);
}


/* handler take the arguments (Server, ErrorCode) */
/* returns {NO_ERROR} */
G_INT32 GSetErrorHandler( handler )
     G_INT32 (*handler)();  
{
  proc = handler;
  return(NO_ERROR);
}

/* returns success */
G_INT32 GRelease(msgsock)
     Server *msgsock;
{
  if (SendCommand(msgsock->fd,G_Release,0,0,0,0,0,0,0,0,0) == -1)
    return(_handleerror(msgsock,GIO_ERROR));
  return(NO_ERROR);
}

/* yesno is {Notify | NoNotify} */
/* returns {NO_ERROR} */
G_INT32 GRequestNotification(msgsock, yesno)
     Server *msgsock;
     G_INT32 yesno;
{
  if (SendCommand(msgsock->fd,G_RequestNotification,yesno,0,0,0,0,0,0,0,0)==-1)
    return(_handleerror(msgsock,GIO_ERROR));
#ifdef SOL_SOCKET
#ifdef SO_KEEPALIVE
  setsockopt( msgsock->fd, SOL_SOCKET, SO_KEEPALIVE, &yesno, sizeof(int));
#endif /* SO_KEEPALIVE */
#endif /* SOL_SOCKET */
  return(NO_ERROR);
}

/* returns {index number of volume | BAD_VOLUME} */
G_INT32 GWhichVolumeIndex(serv, volume)
     Server *serv;
     char *volume;
{
  G_INT32 i;
  for ( i = 0 ; i < serv->num_volumes; i++)
    if (!strcmp(serv->volumes[i].volume, volume))
      return(serv->volumes[i].index);
  return(_handleerror(serv,BAD_VOLUME));
}

/* returns {name for requested index # | NULL} */
char *GWhichVolumeName(serv, volume)
     Server *serv;
     G_INT32 volume;
{
  G_INT32 i;
  for ( i = 0 ; i < serv->num_volumes; i++)
    if (serv->volumes[i].index == volume)
      return (serv->volumes[i].volume);
  return(NULL);
}

G_INT32 GVolumeIndex(serv, which)
     Server *serv;
     G_INT32 which;
{
  if (which >= serv->num_volumes) return(_handleerror(serv,BAD_VOLUME));
  return(serv->volumes[which].index);
}

G_SOCKTYPE GConnection(serv)
     Server *serv;
{
  return(serv->fd);
}

G_INT32 GMaxLockTime(serv)
     Server *serv;
{
  return(serv->max_lock_time);
}

G_INT32 GRevisionNumber(serv)
     Server *serv;
{
  return(serv->ex4);
}

#ifdef OBSOLETE
G_INT32 GSwitchingMask(serv)
     Server *serv;
{
  return(serv->switching_mask);
}
#endif /* OBSOLETE */
G_INT32 GVolumeType(serv, which)
     Server *serv;
     G_INT32 which;
{
  if (which >= serv->num_volumes) return(_handleerror(serv,BAD_VOLUME));
  return(serv->volumes[which].type & 0xff);
}

G_INT32 GVolumeCapabilities(serv, which)
     Server *serv;
     G_INT32 which;
{
  if (which >= serv->num_volumes) return(_handleerror(serv, BAD_VOLUME));
  return(serv->volumes[which].type);
}

char *GVolumeName(serv, which)
     Server *serv;
     G_INT32 which;
{
  if (which >= serv->num_volumes) return(NULL);
  return(serv->volumes[which].volume);
}

G_INT32 GNumberVolumes(serv)
     Server *serv;
{
  return(serv->num_volumes);
}

G_INT32 GNumberExtensions(serv)
     Server *serv;
{
  return(serv->num_extensions);
}

char *GExtensionName(serv, which)
     Server *serv;
     G_INT32 which;
{
  if (which >= serv->num_extensions) return(NULL);
  return(serv->extensions[which]);
}

G_INT32 GExtensionMajor(serv, extname)
     Server *serv;     
     char *extname;
{
  int i;
  if (serv->ex5 == 0 || serv->ex5 == -1) 
    return(_handleerror(serv, NO_EXTENSION));
  else
    {
      for (i = 0; i < serv->num_extensions; i++)
	if (strcmp(extname, serv->extensions[i]) == 0)
	  return( serv->ex5 + i);
    }
  return(_handleerror(serv, NO_EXTENSION));
}

G_INT32 GGetStatistics(serv, stat_struct)
     Server *serv;
     GStats *stat_struct;
{
  if (SendCommand(serv->fd,G_GetStatistics,0,0,0,0,0,0,0,0,0) == -1)
    return(_handleerror(serv, GIO_ERROR));
  if ((stat_struct->revision = acceptint(serv->fd)) == GIO_ERROR)
    return(_handleerror(serv, GIO_ERROR));
  if ((stat_struct->num_requests = acceptint(serv->fd)) == GIO_ERROR)
    return(_handleerror(serv, GIO_ERROR));
  if ((stat_struct->run_time = acceptint(serv->fd)) == GIO_ERROR)
    return(_handleerror(serv, GIO_ERROR));
  if ((stat_struct->num_collisions = acceptint(serv->fd)) == GIO_ERROR)
    return(_handleerror(serv, GIO_ERROR));
  if ((stat_struct->num_connections = acceptint(serv->fd)) == GIO_ERROR)
    return(_handleerror(serv, GIO_ERROR));
  if ((stat_struct->max_connections = acceptint(serv->fd)) == GIO_ERROR)
    return(_handleerror(serv, GIO_ERROR));
  if ((stat_struct->delayed_requests = acceptint(serv->fd)) == GIO_ERROR)
    return(_handleerror(serv, GIO_ERROR));
  return(NO_ERROR);
}

G_INT32 GRebuild(serv)
     Server *serv;
{
  if (SendCommand(serv->fd,G_Rebuild,0,0,0,0,0,0,0,0,0) == -1)
    return(_handleerror(serv->fd,GIO_ERROR));
  return(_handleerror(serv,acceptint(serv->fd)));
}
/* GMount and GUmount are convenient for (u)mounting single discs */
G_INT32 GMount(serv, disc_name, dev_name, options)
     Server *serv;
     char *dev_name, *disc_name;
     G_INT32 options;
{
  if (SendCommand(serv->fd,G_Mount,1,options,0,0,0,0,0,0,0) == -1)
    return(_handleerror(serv,GIO_ERROR));
  if (sendstring(serv->fd,dev_name) == -1)
    return(_handleerror(serv,GIO_ERROR));
  if (sendstring(serv->fd,disc_name) == -1)
    return(_handleerror(serv,GIO_ERROR));
  return(_handleerror(serv,acceptint(serv->fd)));
}

G_INT32 GUmount(serv, dev_name, options)
     Server *serv;
     char *dev_name;
     G_INT32 options;
{
  if (SendCommand(serv->fd,G_Umount,1,options,0,0,0,0,0,0,0) == -1)
    return(_handleerror(serv,GIO_ERROR));
  if (sendstring(serv->fd,dev_name) == -1)
    return(_handleerror(serv,GIO_ERROR));
  return(_handleerror(serv,acceptint(serv->fd)));
}

G_INT32 GMounts(serv, num_mounts, disc_names, dev_names, rets, options)
     Server *serv;
     G_INT32 num_mounts, *rets, options;
     char **disc_names, **dev_names;
{
  G_INT32 i;
  if (SendCommand(serv->fd,G_Mount,num_mounts,options,0,0,0,0,0,0,0) == -1)
    return(_handleerror(serv,GIO_ERROR));
  for (i = 0; i < num_mounts; i++) {
    if (sendstring(serv->fd,dev_names[i]) == -1)
      return(_handleerror(serv,GIO_ERROR));
    if (sendstring(serv->fd,disc_names[i]) == -1)
      return(_handleerror(serv,GIO_ERROR));
  }
  for (i = 0; i < num_mounts; i++) {
    if ((rets[i] = acceptint(serv->fd)) == -1) 
      return(_handleerror(serv,GIO_ERROR));
  }
  return(NO_ERROR);
}

G_INT32 GUmounts(serv, num_mounts, dev_names, rets, options)
     Server *serv;
     G_INT32 num_mounts, *rets, options;
     char **dev_names;
{
  G_INT32 i;
  if (SendCommand(serv->fd,G_Umount,num_mounts,options,0,0,0,0,0,0,0) == -1)
    return(_handleerror(serv,GIO_ERROR));
  for (i = 0; i < num_mounts; i++) {
    if (sendstring(serv->fd,dev_names[i]) == -1)
      return(_handleerror(serv,GIO_ERROR));
  }
  for (i = 0; i < num_mounts; i++) {
    if ((rets[i] = acceptint(serv->fd)) == -1) 
      return(_handleerror(serv,GIO_ERROR));
  }
  return(NO_ERROR);
}

G_INT32 GGetMounts(serv, num_mounts, mount_points, mount_discs)
     Server *serv;
     G_INT32 *num_mounts;
     char ***mount_points, ***mount_discs;
{
  G_INT32 i;
  if (SendCommand(serv->fd,G_GetMounts,0,0,0,0,0,0,0,0,0) == -1)
    return(_handleerror(serv, GIO_ERROR));
  if ((*num_mounts = acceptint(serv->fd)) == -1)
    return(_handleerror(serv, GIO_ERROR));
  *mount_points = (char **)malloc(*num_mounts * sizeof(char *));
  *mount_discs = (char **)malloc(*num_mounts * sizeof(char *));
  for (i = 0 ; i < *num_mounts; i++) {
    (*mount_points)[i] = acceptstring(serv->fd);
    (*mount_discs)[i] = acceptstring(serv->fd);
  }
  return(NO_ERROR);
}

#ifdef DBM
/* Database commands */
G_INT32 GGetDatabases(serv)
     Server *serv;
{
  int i;
  if (serv->ex3 != 1) return(_handleerror(serv,FUNC_NOT_SUPPORTED));
  if (serv->num_db > 0 && serv->databases) {
    for (i = 0; i < serv->num_db; i++)
      free(serv->databases[i].dbmname);
    free(serv->databases);
  }
  serv->num_db = 0;
  serv->databases = (DatabaseList *)NULL;
  if (SendCommand(serv->fd,G_GetDatabases,0,0,0,0,0,0,0,0,0) == -1)
    return(_handleerror(serv, GIO_ERROR));
  serv->num_db = acceptint(serv->fd);
  if (serv->num_db == -1) return(_handleerror(serv, GIO_ERROR));
  serv->databases = 
    (DatabaseList *)malloc((serv->num_db) * sizeof(DatabaseList));
  if (serv->databases == (DatabaseList *)NULL) {
    return(_handleerror(serv, COULDNT_DO_FUNC));
  }
  for (i = 0; i < serv->num_db; i++) {
    serv->databases[i].index = acceptint(serv);
    serv->databases[i].uid = acceptint(serv);
    serv->databases[i].perm = acceptint(serv);
    serv->databases[i].dbmname = acceptstring(serv);
  }
  return (serv->num_db);
}
#endif /* DBM */

/* the following are support routines for the above calls and
   are NOT guaranteed to exist or remain the same between versions */

G_INT32 _handleerror( serv, ErrorCode )
     Server *serv;
     G_INT32 ErrorCode;
{
    g_errno = ErrorCode;
    if (ErrorCode < LOWEST_ERROR) return(ErrorCode);
    if (!proc) {
	if (ErrorCode == VOLUME_LIST_OLD || ErrorCode == EXTENSION_LIST_OLD) 
	    GReopenServer(serv);
    }
    else proc(serv, ErrorCode);
    return(ErrorCode);
}

fixtime()
{
  timeout = stattime;
}

G_INT32 acceptint(msgsock)
G_SOCKTYPE msgsock;
{
  G_INT32 i;
  if (full_read(msgsock,&i,sizeof(G_INT32)) < sizeof(G_INT32)) {
    if (DEBUG) printf("acceptint: Bad return size.\n");
    return(GIO_ERROR);
  }
  return(ntohl(i));
}

G_INT32 SendCommand(msgsock,parm0,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8,
	    parm9)
G_SOCKTYPE msgsock;
G_INT32 parm0,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8, parm9;
{
  Galatea_packet hostcommand, commandline;
  hostcommand.parm[0] = parm0;
  hostcommand.parm[1] = parm1;
  hostcommand.parm[2] = parm2;
  hostcommand.parm[3] = parm3;
  hostcommand.parm[4] = parm4;
  hostcommand.parm[5] = parm5;
  hostcommand.parm[6] = parm6;
  hostcommand.parm[7] = parm7;
  hostcommand.parm[8] = parm8;
  hostcommand.parm[9] = parm9;
  hosttonetcom(hostcommand.parm,commandline.parm,10);
  return(full_write(msgsock,commandline.parm,sizeof(commandline)));
}

hosttonetcom(hostints,networkints,number)
G_INT32 *networkints, *hostints, number;
{
  G_INT32 i;
  for ( i = 0; i < number; i++)
    networkints[i] = htonl(hostints[i]);
}

G_INT32 full_read( d, buf, nbytes)
     G_SOCKTYPE d;
     char *buf;
     G_INT32 nbytes;
{
  G_INT32 i = 0, actual = 0;

  while ( actual < nbytes) {
#ifdef MAC
    i = (G_INT32)recv(d, buf+actual, nbytes - actual,0);
#else /* MAC */
    i = (G_INT32)read(d, buf+actual, nbytes - actual);
#endif /* MAC */
    if ( i == -1) return(-1);
    if ( i == 0) return(actual);
    actual += i;
  }
  return(nbytes);
}

#ifndef MAC
G_INT32 serv_full_read( d, buf, nbytes, timeout)
     G_SOCKTYPE d;
     char *buf;
     G_INT32 nbytes;
     G_INT32 timeout;
{
  G_INT32 i = 0, actual = 0;
  struct timeval readto;
  fd_set ready;

  while ( actual < nbytes) {
    FD_ZERO(&ready);
    FD_SET(d,&ready);
    readto.tv_sec = (long)timeout;
    readto.tv_usec = 0;
    if (select(MAX_FDS, &ready, 0, 0, &readto) < 1) return(-1);
/* This #ifdef is just for consistency, as it should never be true */
#ifdef MAC
    i = (G_INT32)recv(d, buf+actual, nbytes - actual,0);
#else /* MAC */
    i = (G_INT32)read(d, buf+actual, nbytes - actual);
#endif /* MAC */
    if ( i == -1) return(-1);
    if ( i == 0) return(actual);
    actual += i;
  }
  return(nbytes);
}

G_INT32 serv_acceptint(msgsock, timeout)
     G_SOCKTYPE msgsock;
     G_INT32 timeout;

{
  G_INT32 i;
  if (serv_full_read(msgsock,&i,sizeof(G_INT32),timeout) < sizeof(G_INT32)) {
    if (DEBUG) printf("serv_acceptint: Bad return size.\n");
    return(GIO_ERROR);
  }
  return(ntohl(i));
}

char *serv_acceptstring(msgsock, timeout)
     G_SOCKTYPE msgsock;
     G_INT32 timeout;
{
  G_INT32 i;
  char *temp;
  i = serv_acceptint(msgsock, timeout);
  if (i == -1) return(NULL);
  temp = (char *)malloc(i * sizeof(char));
  if (temp == NULL) return(NULL);
  if (serv_full_read(msgsock, temp, i, timeout) != i) {
    free(temp);
    return(NULL);
  }
  return(temp);
}

#endif /* !MAC */

G_INT32 full_write( d, buf, nbytes)
     G_SOCKTYPE d;
     char *buf;
     G_INT32 nbytes;
{
  G_INT32 i = 0, actual = 0;
  
  while ( actual < nbytes) {
#ifdef MAC
    i = (G_INT32)send(d, buf+actual, nbytes - actual, 0);
#else /* MAC */
    i = (G_INT32)write(d, buf+actual, nbytes - actual);
#endif /* MAC */
    if ( i == -1) return(-1);
    actual += i;
  }
  return(nbytes);
}

G_SOCKTYPE _gconnserv(host, finalhost)
     char *host, **finalhost;
{
  G_SOCKTYPE sock;
  G_INT32 port, mi;
  struct sockaddr_in server;
#ifdef UNIXCONN
  struct sockaddr_un unix_server;
#endif /* UNIXCONN */
  struct sockaddr *addr;
  G_INT32 addrlen;
  struct sockaddr_un *addr_un;
  struct hostent *hp, *gethostbyname();
  struct servent *service;
  struct timeval to;
  fd_set ready;
  char realhost[60];
#ifdef UNIXCONN
  G_INT32 isunix = 0;
#endif /* UNIXCONN */

  *finalhost = NULL;

#ifdef UNIXCONN
  if (!host || host[0] == '\0' || !strcmp("unix",host)) {
    if (DEBUG) printf("attempting to connect using unix-domain socket\n");
    isunix = 1;
    strcpy(realhost,"unix");
  }
  else strcpy(realhost,host);
  if (isunix) sock = socket(AF_UNIX, SOCK_STREAM, 0);
  else {
#else
    if (DEBUG) printf("attempting to connect using internet-domain socket\n");
    if (!host || host[0] == '\0' || !strcmp("unix",host))
      gethostname(realhost,60);
    else strcpy(realhost,host);
#endif /* UNIXCONN */

    sock = socket(AF_INET,SOCK_STREAM,0);
#ifdef UNIXCONN
  }
#endif
  *finalhost = (char *)malloc((strlen(realhost)+1) * sizeof(char));
  if (*finalhost)
    strcpy(*finalhost, realhost);
  if (sock < 0) {
    if (DEBUG) printf("Error creating stream socket.\n");
    return(-1);
  }
#ifdef UNIXCONN
  if (isunix) {
    unix_server.sun_family = AF_UNIX;
    strcpy(unix_server.sun_path, G_UNIX_PATH);
    addr = (struct sockaddr *) &unix_server;
    addrlen = strlen(unix_server.sun_path) + 2;
    if (connect(sock, addr, addrlen) < 0) {
#ifdef G_OLD_UNIX_PATH
      if (DEBUG)
	printf("Workaround: attempting to open server on '%s'\n",
	       G_OLD_UNIX_PATH);
      strcpy(unix_server.sun_path, G_OLD_UNIX_PATH);
      addr = (struct sockaddr *) &unix_server;
      addrlen = strlen(unix_server.sun_path) + 2;
      if (connect(sock, addr, addrlen) < 0) {
	if (DEBUG) 
	  printf("Workaround: no server available on '%s'\n",
		 G_OLD_UNIX_PATH);
#endif /* G_OLD_UNIX_PATH */
	if (DEBUG) 
	  {
	    printf("No server available on UNIX connection.\n");
	    fflush(stdout);
	    perror("Glib:");
	  }
	return(-1);
#ifdef G_OLD_UNIX_PATH
      }
#endif /* G_OLD_UNIX_PATH */
    }
  } else {
#endif /* UNIXCONN */
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(host);
    if (server.sin_addr.s_addr == -1) {
      hp = gethostbyname(host);
      if (hp == 0) {
	  if (DEBUG) printf("Can't find host %s.\n",host);
	  return(-1);
      }
      bcopy(hp->h_addr,&server.sin_addr,hp->h_length);
    }
    service = getservbyname("galatea","tcp");
    if (service == NULL) {
      server.sin_port = htons(4001);
    }
    else {
      server.sin_port = service->s_port;
    }
    addr = (struct sockaddr *)&server;
    addrlen = sizeof(server);
    if (connect(sock,addr,addrlen) < 0) {
      if (DEBUG) printf("No server available on %s at port %d.\n",
			host, ntohs(server.sin_port));
      return(-1);
    }
#ifdef TCP_NODELAY
    mi = 1;
    setsockopt( sock, IPPROTO_TCP, TCP_NODELAY, &mi, sizeof(int));
#endif

#ifdef UNIXCONN
  }
#endif /* UNIXCONN */
  return(sock);
}

char *acceptstring(msgsock)
     G_SOCKTYPE msgsock;
{
  G_INT32 i;
  char *temp;
  i = acceptint(msgsock);
  if (i == -1) return(NULL);
  temp = (char *)malloc(i * sizeof(char));
  if (temp == NULL) return(NULL);
  if (full_read(msgsock, temp, i) != i) {
    free(temp);
    return(NULL);
  }
  return(temp);
}

G_INT32 sendstring(msgsock, mesg)
     G_SOCKTYPE msgsock;
     char *mesg;
{
  G_INT32 i;
  i = strlen(mesg) + 1;
  if (sendint(msgsock,i) == -1) return(-1);
  if (full_write(msgsock,mesg,i) != i) return(-1);
  return(0);
}


G_INT32 sendint(msgsock,msg)
     G_SOCKTYPE msgsock;
     G_INT32 msg;
{
  G_INT32 i;
  msg = htonl(msg);
  i = full_write(msgsock,&msg,sizeof(int));
  if ( i < sizeof(int)) i = -1;
  return(i);
}

char *strfile(s1, s2)
     char *s1, *s2;
{
  static char *res = NULL;
  static int oldspace = 0;
  int newlen = 0;
  newlen = strlen(s1) + strlen(s2) + 1;
  if (newlen > oldspace) {
    if (res) free(res);
    res = (char *)malloc(newlen * sizeof(char));
    oldspace = newlen;
  }
  strcpy(res, s1);
  strcat(res, s2);
  return(res);
}
