/*
 * args.c
 *
 * Author: Tomi Ollila <too@ajk.tele.fi>
 *
 * Copyright (c) 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>
 *                    All rights reserved.
 *
 * Created: Mon Sep 27 08:33:42 1993 too
 * Last modified: Sun Nov 14 19:21:10 1993 too
 *
 * $Id: args.c,v 1.2 1993/11/17 12:06:50 too Exp $
 *
 * HISTORY
 * $Log: args.c,v $
 * Revision 1.2  1993/11/17  12:06:50  too
 * small fix + updates
 *
 * Revision 1.1  1993/10/24  12:50:39  too
 * Initial revision
 *
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>

#define BSDSOCKET_H /* defining BSDSOCKET_H inhibitis automatic inline incl. */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#ifndef inline
#define inline __inline
#endif

#include "system_includes.h" /* overkill but who cares.. */
#include "c.h"
#include "args.h"
#include "readargs.h"

int getPort(struct Library * SocketBase, char * service)
{
  struct servent * srv;
  int port;
  
  if (!isdigit(service[0])) {
    if ((srv = getservbyname(service, "TCP")) == NULL)
      port = -1;
    else
      port = srv->s_port;
  }
  else
    port = atoi(service);

  return port;
}

long getAddr(struct Library * SocketBase, char * host)
{
  struct hostent * h;
  long retval;
  
  if ((h = gethostbyname(host)) == NULL)
    return -1;

  CopyMem(h->h_addr_list[0], &retval, 4);
  return retval;
}

/****************************************************************************/

#define TEMPLATE "H=HOST,P=PORT,S=SERVICE/K,O=OBTAIN/K"
                    
enum { CI_HOST, CI_PORT, CI_SERVICE, CI_OBTAIN, CI_SIZE };

#define HOSTKW	"HOST"
#define hostkw	"host"
#define HKW	"H"
#define isHostKw(z) (z == sizeof HOSTKW - 1 || z == sizeof HKW - 1)

#define Return(x) do { retval = x; goto End; } while (0)

inline UBYTE handleArgs(struct Library * SocketBase, ULONG * flags, LONG * sdp,
			char * name, int namelen)
{
  struct readargs * rargs;
  int shp = 0;
  UBYTE retval = 0;

  {
    int len;
    char * zapbuf, * p, * z;

    /*
     * skip handler name
     */
    for (p = name, len = namelen; len >0; p++, len--)
      if (*p == ':') {
	namelen = len;
	p++;
	name = p;
	len--;	  /* namelen is len + 1 since dos ReadArgs needs '\n' at end */
	break;
      }
    if (len == 0) 
      return ERROR_OBJECT_NOT_FOUND;
    
    if ((zapbuf = AllocMem(namelen * sizeof (char), MEMF_PUBLIC)) == NULL)
      return ERROR_NO_FREE_STORE;
    z = zapbuf;
	
    /*
     * scan 'args', change '/'s to ' 's. Check if H or HOST is given as
     * a keyword.
     */ 
    for (; len > 0; p++, z++, len--) {
      switch (*p) {
      case '/':
	*z = ' ';
	if (isHostKw(shp))
	  goto skip;
	else
	  shp = 0; /* start from beginning */
	continue;
      case '=':
	if (isHostKw(shp))
	  goto skip;
	else
	  shp = -1; /* dont start at all */
	break;
      default:
	if (shp != -1)
	  if (*p == HOSTKW[shp] || *p == hostkw[shp])
	    shp++;
	  else
	    shp = -1;
      }
      *z = *p;
    }
  skip:
    /*
     * scan rest chars if H or HOST found as a keyword. (if previously
     * skipped and there was hostkw and /, one write is done twice).
     */
    for (; len > 0; p++, z++, len--)
      if (*p == '/')
	*z = ' ';
      else
	*z = *p;

    *z = '\n';	/* sentinel */
  
    rargs = readargs(TEMPLATE, CI_SIZE, zapbuf, namelen);
    FreeMem(zapbuf, namelen * sizeof (char));

    if (rargs == NULL)
      return IoErr();
  }

  {
    char * port;
    struct sockaddr_in addr = { 0 };
    /*
     */
    if (rargs->ra_Args[CI_SERVICE]) {
      if (rargs->ra_Args[CI_PORT])
	Return(ERROR_TOO_MANY_ARGS); /* both port and service */
      else
	port = (char *)rargs->ra_Args[CI_SERVICE];
    }
    else {
      port = (char *)rargs->ra_Args[CI_PORT];
      if (rargs->ra_Args[CI_HOST] != NULL && port == NULL)
	if (isHostKw(shp))
	  Return(ERROR_REQUIRED_ARG_MISSING); /* host and no port */
	else {
	  port = (char *)rargs->ra_Args[CI_HOST];
	  rargs->ra_Args[CI_HOST] = NULL;
	}
    }
#if 0    
    if (rargs->ra_Args[CI_RLOGIND])
      if (rargs->ar_Args[CI_HOST])
	Return(ERROR_TOO_MANY_ARGS); /* host cannot be used w/ rlogind */
      else
	*flags |= APF_RLOGIND;
#endif

    if (rargs->ra_Args[CI_OBTAIN]) {
      if (rargs->ra_Args[CI_PORT])
	Return(ERROR_TOO_MANY_ARGS); /* port and obtain at the same time */
      else {
	LONG id;
	/*
	 * One can use service name as an id for ObtainSocket().
	 */
	if ((id = getPort(SocketBase,
			  (char *)rargs->ra_Args[CI_OBTAIN])) == -1 ||
	    (*sdp = ObtainSocket(id, AF_INET, SOCK_STREAM, 0)) == -1)
	  Return(ERROR_OBJECT_NOT_FOUND);
      }
    }
    /*
     * drop here if only host and port or only port (rlogind possible if
     * only port but it is not an issue here)
     */

    addr.sin_family = AF_INET;
    if ((addr.sin_port = getPort(SocketBase, port)) == (u_short)-1 ||
	(*sdp = socket(AF_INET, SOCK_STREAM, 0)) == -1)
      Return(ERROR_OBJECT_NOT_FOUND);
    
    if (rargs->ra_Args[CI_HOST]) {
      /*
       * connect
       */
      if ((addr.sin_addr.s_addr = getAddr(SocketBase,
			    (char *)rargs->ra_Args[CI_HOST])) == -1 ||
	  connect(*sdp, (struct sockaddr *)&addr, sizeof addr) == -1) {
	CloseSocket(*sdp);
 	Return(ERROR_OBJECT_NOT_FOUND);
      }
    }
    else {
      /*
       * bind & listen
       */
      setsockopt(*sdp, SOL_SOCKET, SO_REUSEADDR, &addr.sin_family,
		 sizeof (int));
      if (bind(*sdp, (struct sockaddr *)&addr, sizeof addr) == -1 ||
	  listen(*sdp, 1) == -1) {
	CloseSocket(*sdp);
	Return(ERROR_OBJECT_NOT_FOUND);
      }
      else
	*flags |= APF_LISTENING; /* not connected yet */
    }
  }
 End:
  freeargs(rargs);
  return retval;
}
