static char *rcsid = "$Header: /plaid/homes/zeus/Blind-2.46/cmds/RCS/Cmds-lib.c,v 2.46.0.1 1997/05/27 21:06:09 zeus Exp zeus $";

/*

 * $Log: Cmds-lib.c,v $
 * Revision 2.46.0.1  1997/05/27 21:06:09  zeus
 * Revision 2.46  1997/04/18 12:13:56  zeus
 * Revision 2.45  1996/12/31  23:53:07  zeus
 * Revision 2.44  1995/12/31  19:45:18  zeus
 * Revision 2.43  1995/03/20  01:43:09  zeus
 *
 */


#define NOTIMEB

#include "common.h"
#include "prototypes.h"
#include "enterturn-g.h"
#include "cmds.h"
#include "cmds-g.h"
#include "misc-g.h"
#include "options.h"

extern shiptype *shiptypes;
extern int groupno;
extern fleetname *fleets;
extern player *players;
extern planet *planets;

char tbuf[MAXSTRLEN];
int pos = 0;
int comment = 0;
int null_planet_ok;             /* Used to differentiate between missing 

                                   planet name, and optional one. */
char err_tmp[MAXSTRLEN * 2 + 1];
static char *productionname[] =
{
  "cap",
  "mat",
  "ship",
  "drive",
  "weapons",
  "shields",
  "cargo",
};


void
cmds_rcsid (void)
{
  printf ("%s\n", rcsid);
}

char y[80];

/* This is a "safer" strcpy- null termination guarenteed */
/* BUG: overrun of bounds is possible */
void
hstrcpy (char *a, const char *b)
{
  int x;

  a[0] = '\0';
  if (b == NULL)
    return;
  for (x = 0; b[x]; x++)
  {
    a[x] = b[x];
  }
  a[x] = b[x];
}

/* strcpy, that sets a to NULL if it is a comment, and removes any
   trailing comment characters/contents. */
void
hcstrcpy (char *a, const char *b)
{
  int x;

  a[0] = '\0';
  if (b == NULL)
    return;
  if ((b[0] == ';') || (b[0] == '#'))
  {
    /* b is a comment in its entirety, so toss back NULL for a */
    return;
  }
  a[0] = b[0];
  if (b[1])                     /* Copy 2nd and later non comment characters */
  {
    for (x = 1; b[x]; x++)
    {
      if (x == MAXSTRLEN)
      {                         /* Prevent string length overrun */
        a[MAXSTRLEN - 1] = '\0';
        return;
      }
      /* Comment char preceeded by whitespace, so puke and return */
      if (((b[x] == ';') || (b[x] == '#')) && ((b[x - 1] == ' ') || (b[x - 1] == '\t')))
      {
        a[x] = '\0';
        return;
      }
      a[x] = b[x];
    }
    a[x] = b[x];
  }
  else
    a[1] = '\0';
}

/* This is a "safe" strncpy- null termination guarenteed, and no overrun 
   (given correct sizes) */
void
hstrncpy (char *a, const char *b, const int sz)
{
  int x;

  a[0] = '\0';
  if (b == NULL)
    return;
  for (x = 0; b[x] && (x < sz); x++)
  {
    a[x] = b[x];
  }
  if (sz == MAXTEXT)
    a[sz - 1] = '\0';
  if (x < sz)
    a[x] = '\0';
}

/* This is a "safe" strncpy- null termination guarenteed, and no overrun 
   (given correct sizes).
   This is a strncpy that sets a to NULL if it is a comment. */
void
hcstrncpy (char *a, const char *b, const int sz)
{
  int x = 0;

  assert (sz);                  /* Just in case.... */
  a[0] = '\0';                  /* Ensure NULL string returned */
  if (b == NULL)
    return;
  if ((b[0] == ';') || (b[0] == '#'))
    return;
  a[0] = b[0];
  if ((b[1]) && (sz > 1))       /* Continue copying */
  {
    for (x = 1; b[x] && (x < sz); x++)
    {
      if (((b[x] == ';') || (b[x] == '#')) && ((b[x - 1] == ' ') || (b[x - 1] == '\t')))
      {                         /* Comment char preceeded by whitespace, so puke and return */
        a[x] = '\0';
        return;
      }
      a[x] = b[x];
    }
    if (sz == MAXTEXT)
      a[sz - 1] = '\0';
  }
  if (x < sz)
    a[x] = '\0';
}

char *
next_token (void)
{
  int i;
  int pos2;

  if (!tbuf[pos])
    return (NULL);
  for (;;)
  {
    if ((tbuf[pos] == '\0') || ((tbuf[pos] != ' ') && tbuf[pos] != '\t'))
      break;
    else
      pos++;
  }
  if (tbuf[pos] != '\0')
  {
    if (tbuf[pos] == '"')
    {
      for (tbuf[pos] = ' ', pos2 = pos + 1;; pos2++)
      {
        /* Bugish Should distinguish btw bad quote and empty */
        if (tbuf[pos] == '\0')
          return (NULL);
        if (tbuf[pos2] == '"')
        {
          tbuf[pos2] = ' ';
          break;
        }
        if (tbuf[pos2] == ' ')
        {
          tbuf[pos2] = '_';
        }
      }
      pos++;
      if (tbuf[pos] == '\0')    /* String was " */
        return (NULL);
    }
    i = 0;
    for (;;)
    {
      if ((!tbuf[pos + i]) || (tbuf[pos + i] == ' ') || (tbuf[pos + i] == '\t'))
      {
        hstrncpy (y, &tbuf[pos], i);
        pos += i;
        y[i] = '\0';
        return (y);
      }
      i++;
    }
  }
  else
    return (NULL);
}

void
extra_token (void)
{
  char t[MAXTEXT];
  char *x;

  if (comment)
  {
    comment = 0;
    return;
  }
  if (tbuf[pos] == '\0')
    return;
  x = next_token ();
  if (x == NULL)
    return;
  strncpy (t, x, MAXTEXT);
  if ((t[0] == ';') || (t[0] == '#'))
    return;
  sprintf (err_tmp, "Warning!!! Extra token '%s' ignored\n", t);
  error (err_tmp);
}

int
isnum (char *numstr, int type)
{
  /* Type = 1 -> int
     Type = 2 -> float */

  int x;

  assert (type > 0);
  assert (type < 3);
  if (!numstr[0])
    return (0);
  for (x = 0; numstr[x]; x++)
    if (!isdigit (numstr[x]) && ((type == INT) ||
             ((type == FLOAT) && (numstr[x] != '.') && (numstr[x] != '-'))))
      return 0;
  return 1;
}

int
ngeti (void)
{
  char cmd[MAXTEXT];

  hstrncpy (cmd, next_token (), MAXTEXT);
  if (!cmd[0])
  {
    sprintf (err_tmp, "Missing integer?\n");
    return (MISSINGI);
  }
  if (cmd[0] == ';' || cmd[0] == '#')
  {
    sprintf (err_tmp, "Comment?\n");
    return (NAI);
  }
  if (!isdigit (cmd[0]))
  {
    sprintf (err_tmp, "Integer expected.\n");

#ifndef PREPROC
    fprintf (stderr, "%s", err_tmp);
#endif

    return (BADI);
  }
  if (isnum (cmd, INT))
    return atoi (cmd);
  else
  {
    sprintf (err_tmp, "Number expected.\n");
    return (BADI);
  }
}


double
ngetf (void)
{
  char cmd[MAXTEXT];

  hstrncpy (cmd, next_token (), MAXTEXT);
  if (!cmd[0])
  {
    sprintf (err_tmp, "Missing number?\n");
    return (MISSINGF);
  }
  if (!isdigit (cmd[0]) && cmd[0] != '.')
  {
    sprintf (err_tmp, "Number expected, got \"%s\".\n", cmd);
    return (NAF);
  }
  if (isnum (cmd, FLOAT))
    return atof (cmd);
  else
  {
    sprintf (err_tmp, "Number expected, got \"%s\".\n", cmd);
    return (NAF);
  }
}

/* BUG : What what to return if not an float */
double
ngetf2 (void)
{
  char cmd[MAXTEXT];
  float z;

  hstrncpy (cmd, next_token (), MAXTEXT);
  if (!cmd[0])
  {
    sprintf (err_tmp, "Mising number?\n");
    return (MISSINGF);
  }
  if (cmd[0] && !isdigit (cmd[0]) && cmd[0] != '.' && cmd[0] != '-')
  {
    sprintf (err_tmp, "Number expected, got \"%s\".\n", cmd);
    return (NAF);
  }
  sscanf (cmd, "%f", &z);
  return ((double) z);
}


double
ninputcomponent ()
{
  double x;

  x = ngetf ();
  if (!x)
    return (0.0);
  if (x < 0.0)
    return (x);
  if (x < 1.0)
    return (BADF);
  return (x);
}

int
ninputcargotype (void)
{
  char cmd[MAXTEXT];
  int i;
  static char *cargonames[] =
  {
    "col",
    "cap",
    "mat",
    "emp",
  };

  hstrncpy (cmd, next_token (), MAXTEXT);
  if (!cmd[0])
  {
    sprintf (err_tmp, "No cargo type specified!\n");
    return NOCARGO;
  }
  for (i = 0; i < sizeof (cargonames) / sizeof (char *); i++)

    if (!hstricmp (cargonames[i], cmd))
      return i;
  sprintf (err_tmp, "\"%s\" is not a cargo type.\n", cmd);
  return NOCARGO;
}


int
ninputcargotype2 (void)
{
  char cmd[MAXTEXT];
  int i;
  static char *cargonames[] =
  {
    "col",
    "cap",
    "mat",
    "emp",
    "all",
  };

  hstrncpy (cmd, next_token (), MAXTEXT);
  if (!cmd[0])
  {
    sprintf (err_tmp, "No cargo type specified!\n");
    return NOCARGO;
  }
  for (i = 0; i < sizeof (cargonames) / sizeof (char *); i++)

    if (!hstricmp (cargonames[i], cmd))
      return i;
  sprintf (err_tmp, "\"%s\" is not a cargo type.\n", cmd);
  return NOCARGO;
}

int
ninputproduction (player * P, planet * p)
{
  int i;
  char t[MAXTEXT];
  shiptype *T;

  hstrncpy (t, next_token (), MAXTEXT);
  if (t[0])
  {
    for (i = 0; i < sizeof (productionname) / sizeof (char *); i++)

    {
      if (!hstricmp (productionname[i], t))
      {
        if (i == PR_SHIP)
        {
          hstrncpy (t, next_token (), MAXTEXT);
          break;
        }
        else
          return (i);
      }
    }

    if (t[0])
    {
      for (T = shiptypes; T; T = T->next)
        if (T->owner == P && !hstricmp (T->name, t))
        {
          if ((p->producing == PR_SHIP) && (p->producingshiptype == T))
            /* Short circut */
            return (-PR_SHIP);  /* So you don't scrap the partial ship */
          p->producingshiptype = T;

#ifdef PARTTECH
          /* Nuke partial techs, becuase building new ship class */
          p->drive = p->guns = p->shields = p->cargo = 0.0;
#endif

          return (PR_SHIP);
        }

      sprintf (err_tmp, "\"%s\" is not a production type.\n", t);
      return (PR_NOTHING);
    }
  }
  sprintf (err_tmp, "No production type given!\n");
  return (PR_NOTHING);
}

shiptype *
ninputshiptype (player * P)
{
  shiptype *T;
  char cmd[MAXTEXT];

  hstrncpy (cmd, next_token (), MAXTEXT);

  if (!cmd[0])
  {
    sprintf (err_tmp, "No ship type name given!\n");
    return 0;
  }
  for (T = shiptypes; T; T = T->next)
    if (T->owner == P && !hstricmp (T->name, cmd))
      return T;
  sprintf (err_tmp, "\"%s\" is not a ship type.\n", cmd);

  return NULL;
}

group *
ninputgroup (player * P)
{
  group *g, *g2;
  char cmd[MAXTEXT];

  hstrncpy (cmd, next_token (), MAXTEXT);

  if (!cmd[0])
  {
    sprintf (err_tmp, "No group # specified!\n");
    return (NULL);
  }

  if (!hstricmp (cmd, "max"))
  {                             /* We want higest numbered group ... */
    g = P->groups;
    g2 = NULL;
    if (!g)
      return (NULL);
    if (P->opts2 & UNSAFEMAX)
    {                           /* ... not in hyperspace */
      while (g->next)
      {
        if (g->dist == 0.0)
          g2 = g;
        g = g->next;
      }
      g = g2;
    }
    else
    {                           /* ... in or out of hyperspace */
      while (g->next)
        g = g->next;
    }
    groupno = ptonum (P->groups, g);    /* Set this so that errors return
                                           a sane group number */
    return g;
  }

  /* should be an explicit number */
  if (cmd[0] && !isdigit (cmd[0]))
  {
    sprintf (err_tmp, "Integer expected.\n");
    return (NULL);
  }
  if (isnum (cmd, INT))
    groupno = atoi (cmd);
  else
  {
    sprintf (err_tmp, "Integer expected.\n");
    return (NULL);
  }
  if (!groupno)                 /* This is probably redundant... */
    return (NULL);
  g = numtop (P->groups, groupno);
  if (!g)
  {
    sprintf (err_tmp, "There is no group %d.\n", groupno);
    return (NULL);
  }
  return g;
}

group *
matchgroup (player * P, char *cmd)
{
  group *g, *g2;

  groupno = 0;
  if (!cmd[0])
  {
    sprintf (err_tmp, "No group # specified!\n");
    return (NULL);
  }

  if (!hstricmp (cmd, "max"))
  {
    g = P->groups;
    if (!g)
      return 0;
    if (P->opts2 & UNSAFEMAX)
    {
      g2 = g;
      while (g->next)
      {
        g = g->next;
        if (g->dist == 0.0)
          g2 = g;
      }
      g = g2;
    }
    else
    {
      while (g->next)
        g = g->next;
    }
    groupno = ptonum (P->groups, g);    /* Set this so that errors return */
    return (g);                 /* sane values */
  }
  if (cmd[0] && !isdigit (cmd[0]))
  {
    sprintf (err_tmp, "Source group or fleet not recognized\n");
  }
  if (isnum (cmd, INT))
    groupno = atoi (cmd);
  else
  {
    sprintf (err_tmp, "Source group or fleet not recognized\n");
  }
  if (!groupno)
    return (NULL);
  g = numtop (P->groups, groupno);
  if (!g)
  {
    sprintf (err_tmp, "There is no group %d.\n", groupno);
  }
  return g;
}

fleetname *
matchfleet (player * P, char *cmd)
{
  fleetname *fl;

  if (!cmd[0])
  {
    sprintf (err_tmp, "No group/fleet specified!\n");
    return (NULL);
  }

  for (fl = fleets; fl; fl = fl->next)
  {
    if (!(hstricmp (cmd, fl->name)) && (fl->owner == P))
      return (fl);
  }
  sprintf (err_tmp, "You do not have a fleet %s.\n", cmd);
  return (NULL);
}

shiptype *
matchtype (player * P, char *cmd)
{
  shiptype *T;

  if (!cmd[0])
  {
    sprintf (err_tmp, "No shiptype!\n");
    return (NULL);
  }

  for (T = shiptypes; T; T = T->next)
  {
    if (!(hstricmp (cmd, T->name)) && (T->owner == P))
      return (T);
  }
  sprintf (err_tmp, "There is no shiptype \"%s\".\n", cmd);
  return (NULL);
}

planet *
nametoknownplan (player * P, planet * head, const char *s)
{
  planet *p;

  /* Check by number (alaways works) */
  for (p = head; p; p = p->next)
    if (!hstricmp (p->num, s))
      return p;
  for (p = head; p; p = p->next)
    if (!hstricmp (p->name, s))
    {
      if (canseeplanet (P, p, 0))
        return p;
      else
        return (NULL);
    }
  return (NULL);
}

planet *
ninputplanet (player * P)
{
  planet *p;
  char cmd[MAXTEXT];

  hstrncpy (cmd, next_token (), MAXTEXT);
  if (cmd[0])
  {
    /*if ((p = nametoplan (planets, cmd)) != 0) */
    if ((p = nametoknownplan (P, planets, cmd)) != 0)
      return p;
    sprintf (err_tmp, "System \"%s\" not found.\n", cmd);
    if ((strlen (cmd) == 1) && ((cmd[0] == ';') || (cmd[0] == '#')) &&
        (null_planet_ok == DASH))       /* Must be a -/_ not comment */
      null_planet_ok = 0;
    if (strlen (cmd) != 1)
      null_planet_ok = 0;
  }
  else
    sprintf (err_tmp, "No planet given\n");

  return NULL;
}

player *
ninputplayer (void)
{
  player *p;
  char cmd[MAXTEXT];

  hstrncpy (cmd, next_token (), MAXTEXT);
  if (cmd[0])
  {
    if ((p = nametop (players, cmd)) != 0)
      return p;
    sprintf (err_tmp, "Player \"%s\" not found.\n", cmd);
    return 0;
  }
  sprintf (err_tmp, "No Empire name specified!\n");
  return 0;
}

#ifdef PARTTECH
/* Get tech string. Return BADF if fail, or 0.0+ otherwise */
double
get_tech (char str[], double tech)
{
  if (!hstricmp (str, "max"))   /* Upgrade to current? */
  {
    return (tech);
  }
  if (!isdigit (str[0]))        /* Not a tech value */
  {
    sprintf (err_tmp, "'%s' is not a valid tech level\n", str);
    return (BADF);
  }
  if (isnum (str, FLOAT))
    return (atof (str));
  else
  {
    sprintf (err_tmp, "Number expected.\n");
    return (BADF);
  }
}
#endif

int
group_check (player * P, group * g, fleetname * fl, int groupno, int flag)
{
  if (fl)
  {
    return (0);
  }
  else
  {
    if (((g->dist > 0.0) && (flag & G_DIST)) ||
        ((g->dist == INTERCEPT) && (flag & G_DIST) && (flag & G_INTERCEPT)))
    {
      sprintf (err_tmp, "Group %d is still in hyperspace.\n", groupno);
      return (G_DIST);
    }
    if ((g->type->drive == 0.0) && (flag & G_DRIVES))
    {
      sprintf (err_tmp, "Ships in group %d can't move.\n", groupno);
      return (G_DRIVES);
    }
    if ((g->where->owner != P) && (flag & G_OWNPLANET))
    {
      sprintf (err_tmp, "You don't own system %s.\n", g->where->num);
      return (G_OWNPLANET);
    }
    if ((g->dist == UPG) && (flag & G_NOUPG))
    {
      sprintf (err_tmp, "Group %d has been upgraded.\n", groupno);
      return (G_NOUPG);
    }
    return (0);
  }
}

group *
new_group (player * P, group * g, const int num)
{
  group *g2;

  g2 = alloc (sizeof (group));
  *g2 = *g;
  g->ships -= num;
  g2->ships = num;
  addlist (&P->groups, g2);
  return (g2);
}


/* Do all the initialziation that has to be done with each new order-
   i.e. copy entire command string to tbuf, and toss first token. */
void
cmd_init (char *buf)
{
  hcstrcpy (tbuf, buf);
  pos = 0;
  (void) next_token ();         /* We've already know what this token is */
}

/* For those people that insist on doing things outside the game,
   check to see if they've actually seen an empire. We intentionally give
   a different error, since we want them to know that Big Brother is
   watching */

int
known_empire (const player * P, const player * P2)
{
  techinfo *tx;

  for (tx = P->othertechs; tx; tx = tx->next)
  {
    if (tx->whoose == P2)
      return (1);
  }

  return (0);
}
