
/*
 *  MBUTIL.C - 2/17/88 - Utility functions.
 */

#include "mb.h"

static char *onoff[2] = { "off", "on" };

/*
 *  ! Command: Do DOS command ...
 */
dosys()
{
  strcpy(tmp->scr, port->line + 2);
  remnl(tmp->scr);
#ifndef MCH_AMIGA
  if (system(tmp->scr)) perror("Error");
#else
   /* Workaround MANX problem */
  if (system(&tmp->scr[0])) perror("Error");
#endif
}

/*
 *  Sort utility.
 *  base is the start of the array of items.
 *  n is the number of items.
 *  w is the item size.
 *  t is scratch space the size of one item.
 */

sort(base, n, w, t)
char *base;
int   n;
int   w;
char *t;
{
  register int i;
  register int j;
  register int g;
  char *p1, *p2;

  for (g = n/2; g; g /= 2)
  for (i = g; i < n; i++)
  for (j = i - g; j >= 0; j -= g)
  {
    p1 = base + (w * j);
    p2 = base + (w * (j + g));

    if (strncmp(p1, p2, w) <= 0) break;
    memcpy(t, p1, w);
    memcpy(p1, p2, w);
    memcpy(p2, t, w);
  }
}

/*
 *  Return number of days between two dates.
 */

ddiff(fr, to)
char fr[], to[];
{
  register int d;
  static short dpm[13] =
  { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };

  d = 365 * (10 * (to[0] - fr[0]) + to[1] - fr[1]);

  d += (dpm[10 * (to[2] - '0') + to[3] - '0'] -
        dpm[10 * (fr[2] - '0') + fr[3] - '0']);

  d += 10 * (to[4] - fr[4]) + to[5] - fr[5];

  return d;
}


/*
 *  Wild card match of callsigns with ? and * in first arg.
 */

wcm(s1, s2)
char *s1, *s2;
{
  register short l;

  if (*s1 is '*') if (matchn(s2, cport->user->call, ln_call)) return false;

  for (l = 0; l < ln_call; l++, s1++, s2++)
  {
    if (*s1 is '*') return true;
    if (*s1 isnt '?') if (*s1 isnt *s2) return false;
  }
  return true;
}

/*
 *  Save remote sysop command, for later execution.
 */

savecmd()
{
  strcpy(scmd, port->line);
  s_param setbit s_cmd;
  port->msg = mdone;
}

/*
 *  EP command: change port parameters.
 */

edport()
{
  register PORTS *p;
  register int i;

  if ((p = findport(*port->fld[1])) is NULL) { port->msg = mnport; return; }

  sprintf(tmp->scr, "Change parameters for port %c, %s\n\n", p->id, p->name);
  outstr(tmp->scr);

  while (true)
  {
    outstr("Bbs Down Up Gate Illegal Monitor Remote Xparent 1 2 3 Time Cdigi Fwd Error\n");

    sprintf(tmp->scr,"%c   %c    %c  %c    %c       %c       %c      %c       %c %c %c %-3d   %d    %-2d  %d\n",
     (p->priv & p_bbs)/p_bbs +'0',
     (p->priv & p_dnload)/p_dnload +'0', (p->priv & p_upload)/p_upload +'0',
     (p->priv & p_gate)/p_gate +'0', (p->priv & p_ilcal)/p_ilcal +'0',
     (p->priv & p_mon)/p_mon +'0', (p->priv & p_sysop)/p_sysop +'0',
     p->tmode +'0',p->ecmon +'0', p->ecuser +'0', p->eccmds +'0',
     p->ctime, p->ndigi, p->fwdmin, p->errmax);
    outstr(tmp->scr);

    getcmd();
    if (port->mode & gone) return;
    i = atoi(port->fld[1]);
    switch(port->opt1)
    {
      case 'B' : p->priv flipbit p_bbs;     break;
      case 'C' : p->ndigi = i;              break;
      case 'D' : p->priv flipbit p_dnload;  break;
      case 'E' : p->errmax = i;             break;
      case 'F' : p->fwdmin = i;             break;
      case 'G' : p->priv flipbit p_gate;    break;
      case 'I' : p->priv flipbit p_ilcal;   break;
      case 'M' : p->priv flipbit p_mon;     break;
      case 'R' : p->priv flipbit p_sysop;   break;
      case 'T' : p->ctime = i;              break;
      case 'U' : p->priv flipbit p_upload;  break;
      case 'X' : p->tmode  = 1 - p->tmode;  break;
      case '1' : p->ecmon  = 1 - p->ecmon;  break;
      case '2' : p->ecuser = 1 - p->ecuser; break;
      case '3' : p->eccmds = 1 - p->eccmds; break;
      default  : if (!port->opt1) return;
    }
  }
}

/*
 *  ES command: change system parameters.
 */

chgparam()
{
  register int i;

  while (true)
  {
    outstr("Bell Kill F&B-kill Svc-msg\n");
    sprintf(tmp->scr, "%c    %c    %c        %c\n",
      (s_param & s_page)/s_page+'0', (s_param & s_kill)/s_kill+'0',
      (s_param & s_fkill)/s_fkill+'0', (s_param & s_svc)/s_svc+'0');
    outstr(tmp->scr);

    getcmd();
    if (port->mode & gone) return;
    switch(port->opt1)
    {
      case 'B' : s_param flipbit s_page;  break;
      case 'F' : s_param flipbit s_fkill; break;
      case 'K' : s_param flipbit s_kill;  break;
      case 'S' : s_param flipbit s_svc;   break;
      default  : if(!port->opt1) return;
    }
  }
}

/*
 *  Screen paging.
 */

char *pausemsg;
static char *pghdr = NULL;
static short lnmax;
static short lncnt;

/*
 *  If local console, pause until user types a character.
 *  Return the character.
 *  If not local console, just return a blank.
 */

char pause()
{
  register char t;

  if (port->mode is local)
  if (!pt_flag)
  {
    prtx(pausemsg);
    t = inchar();
    outchar('\n');
    return toupper(t);
  }
  return ' ';
}

/*
 *  Start screen paging.
 */

pgst(cp)
char *cp;
{
  if ((pghdr = cp) is NULL) lnmax = 23; else lnmax = 22;
  lncnt = lnmax;
}

/*
 *  Pause if full screen.
 */

char pgck()
{
  if (--lncnt) return ' ';
  lncnt = lnmax;
  return pause();
}

/*
 *  Display header if at top of screen.
 */

pghd()
{
  if (lncnt is lnmax) if (pghdr isnt NULL) outstr(pghdr);
}

/*
 *  Pause if required at last line.
 */

pgdn()
{
  if (lncnt isnt lnmax) pause();
}

/*
 *  Display "no such file" message, and file name.
 */

nofile(c)
char *c;
{
  prtx(mnfile); outstr(c); outchar('\n');
}

/*
 *  Memory allocation problem, just plain quit.
 */

errall()
{
  outstr("Not enough memory\n");
  exit (0);
}

/*
 *  Is this a callsign?
 */

iscall(c)
char *c;
{
  register short i, ld, nd;

  nd = 0;
  for (i = 0; (i < ln_call) and (*c isnt ' '); i++, c++)
  {
    if (ld = isdigit(*c)) nd++;
  }
  return ((i > 3) and ((nd is 1) or (nd is 2)) and !ld);
}

/*
 *  Print list of ports and port names.
 */

shports()
{
  register PORTS *p;

  sprintf(port->line, "Use %c and port ID:\n", port->opt1);
  outstr(port->line);
  for (p = porthd; p isnt NULL; p = p->next)
  {
    sprintf(port->line, "%c%c  %s\n", port->opt1, p->id, p->name);
    outstr(port->line);
  }
}

/*
 *  Return pointer to port with id pid, or NULL.
 */

PORTS *findport(pid)
char pid;
{
  register PORTS *p;

  for (p = porthd; p isnt NULL; p = p->next) if (p->id is pid) return p;
  return NULL;
}

/*
 *  Is it?
 */

iseof(cp)
char *cp;
{
  return match(cp, "*** EOF\n");
}

/*
 *  Remove new line  and control characters from end of string.
 */

remnl(p)
char *p;
{
  for (; *p; p++)
  {
    if (*p is '\n') { *p = '\0'; return; }
    if (*p < ' ') *p = ' ';
  }
}

/*
 *  Parse a callsign.
 *  Blank pad the output field, remove trailing ssid from call.
 *  Return the SSID.
*/

pcall(c, p)
char *c;
char *p;
{
  register short i;

/*
 *  Blank fill the target buffer.
 */

  fill (c, ' ', ln_call);

/*
 *  Ignore leading spaces.
 */

  while (*p and (*p is ' ')) p++;

/*
 *  Copy the call from the string into the call buffer.
 */

  for (i = ln_call; i and *p; i--)
  {
    if (*p <= ' ') return 0;
#ifdef MCH_AMIGA
/* handle strange things from the KAM */
    if(*p == '/')return 0;
#endif
    if (*p is '-') return atoi(++p);
    if (*p is '.') return 0;
    *c++ = *p++;
  }
  if (*p++ is '-') return atoi(p); else return 0;
}

/*
 *  Is the string a number?
 */

num(p)
char *p;
{
  for (; *p; p++) if (!isdigit(*p)) return false;
  return true;
}

/*
 *  "Output, no blanks".
 */

outnb(p, n)
char *p;
short n;
{
  while (n--) switch (*p)
  {
    case ' ' :
    case '\n' :
    case '\0' : return;
    default: outchar(*p++);
  }
}

/*
 *  Print a number right justified in a fixed width field.
 */

outnr(i, n)
int   i;  /* The number to print */
short n;  /* Field width */
{
  char num[7];

  sprintf (num, "%-6u", i);
  outnb (num, n);
}

/*
 *  Display software version.
 */

prtver()
{
  outstr(ver); outchar('\n');
}

/*
 *  Print multi-line message.
 */

prtm(m)
MLM *m;
{
  register MLM *p;

  for (p = m; p isnt NULL; p = p->next) prtx(p->text);
}

/*
 *  Print a string, expanding the "$" fields.
 */

prtx(p)
char *p;
{

  for (; *p; p++)
  {
    if (*p isnt '$') outchar(*p);
    else switch (*(++p))
    {
      case 'A' : outnb(port->mmhs->bbs, ln_call); break;
      case 'B' : outchar(port->mmhs->type); break;
      case 'C' : outnr(mfhs->next_msg, 5); break;
      case 'D' : outstr(l_date); break;
      case 'E' : outstr(port->mmhs->title); break;
      case 'F' : outstr(port->lport->name); break;
      case 'G' : outnb(port->mmhs->to, ln_call); break;
      case 'H' : p++; break;
      case 'I' : outnb(port->user->handle, ln_handle); break;
      case 'J' : outnb(port->mmhs->date, ln_date); break;
      case 'K' : outnb(port->mmhs->time, ln_time); break;
      case 'L' : outnr(mfhs->next_msg - 1, 5); break;
      case 'M' : outnr(port->mmhs->number, 5); break;
      case 'N' : outnr(mfhs->count, 4); break;
      case 'O' : outnb(cport->user->call, ln_call); break;
      case 'P' : outnb(port->mmhs->from, ln_call); break;
      case 'Q' : outstr(qth); break;
      case 'R' : outnb(port->rcall, ln_call); break;
      case 'S' : outnb(port->lport->user->call, ln_call); break;
      case 'T' : outnb(l_time, ln_time); break;
      case 'U' : outnb(port->user->call, ln_call); break;
      case 'V' : outstr(ver); break;
      case 'W' : outnb(port->lport->user->handle, ln_handle); break;
      case 'X' : outnb(port->user->date, ln_date); break;
      case 'Y' : outnb(port->user->time, ln_time); break;
      case 'Z' : outnr(port->user->msg_number, 5); break;
      case 'a' : outstr(orgbbs); break;
      case 'h' : outstr(port->mmhs->call); break;
      case 'j' : outstr(orgdate); break;
      case 'k' : outstr(orgtime); break;
      case 'm' : outstr(orgmsg); break;
      default  : outchar(*p);
    }
  }
}

/*
 *  Write random record.
 */

write_rec(fid, rec, buffer)
int fid;
int rec;
char buffer[];
{
#ifndef MCH_AMIGA
  long lseek();
#endif
  long offs;

  offs = (long)rec * (long)RECSIZE;
  lseek(fid, offs, 0);
  return (write(fid, buffer, RECSIZE) is RECSIZE);
}

/*
 *  Read random record.
 */

read_rec(fid, rec, buffer)
int fid;
int rec;
char buffer[];
{
#ifndef MCH_AMIGA
  long lseek();
#endif
  long offs;

  offs = (long)rec * (long)RECSIZE;
  lseek(fid, offs, 0);
  return (read(fid, buffer, RECSIZE) is RECSIZE);
}

/*
 *  Fill buffer with characters from the current input,
 *  until CR or buffer overflow. Terminate buffer with
 *  '\0', thus making it a string.
 *  Translate \r to \n.
 *  Ignore \n.
 *  Deal with backspace and things like that.
 *  Set flags for timeout and disconnect.
 */
#ifndef MCH_AMIGA
getdat()
{
  register char *cp;
  register PORTS *p;
  register char ch;

  p = port;

  if (p->flags & p_dotmr)
  {
    if ((p->mode & idle) and (p->dev is p_tnc)) settmr(&p->itime, 2);
    else settmr(&p->itime, p->ctime);
  }

  p->flags setbit p_dotmr;
  cp = p->line;

  while (true)
  {
    while(!instat())
    {
      if (p isnt cport)
      {
        ioport(cport);
        if (instat())
        {
          ch = inchar();

          if (ch is achar)
          {
            p->mode = forced;
            ioport(p);
            return true;
          }

          if (ch is tchar)
          {
            p->flags setbit p_opreq;
            ioport(p);
            return true;
          }

        }
        ioport(p);
      }
      if (p->flags & p_trans)
      if (!isdcd())
          {
          if (!(p->mode & idle)) p->mode = discon;
          return true;
          }

      if (!chktmr(p->itime))
      {
        *cp = '\0';
        if (!(p->mode & idle)) p->mode = timeout;
        return true;
      }
    }

    *cp = inchar();

/*
 *  Got a character, deal with it.
 */

    if ((cp is (p->line + linelen - 2)) or (*cp is '\r'))
    {
/*
 *  Got a whole line. Either buffer full, or character is CR.
 */
      if (*cp is '\r') *cp = '\n';
      cp++; *cp = '\0';

      if (p->flags & p_echo) outchar('\n');
      if (!(p->flags & p_trans))
      {
         if (isdis(p->line))
         {
           if (!(p->mode & idle)) p->mode = discon;
           return true;
         }
         if (isretry(p->line)) return false;

         if (isreq(p->line))
         {
           p->flags setbit p_req;
           pcall(p->rcall, p->line + 20);
           p->flags clrbit p_dotmr;
           return false;
         }
      }
      return true;
    }

/*
 *  Not end of line.
 *  Stuff the character in the buffer.
 *  Handle some control characters. Ignore some.
 */

    switch(*cp)
    {
      default   : if (p->flags & p_echo) outchar(*cp); cp++; break;
      case '\b' :
        if (cp > p->line) { cp--; if (p->flags & p_echo) outstr("\b \b"); }
        break;

      case '\n' : if (cp is p->line) { *cp = '\0'; return false; }
      case '\0' : ;   /* Ignore most ctl char */
      case ctl_c: ;   /* Ignore most ctl char */
      case ctl_v: ;   /* Ignore most ctl char */
    }
  }
}
#endif
/*
 *  Is the string a header line?
 */

ishead(p)
char *p;
{
  if (!*p or (*p is '\n')) return false;      /* end of headers */
  if (strnicmp(p, "R:" ,2) and strnicmp(p, "I:", 2)) return false;
  return true;
}

/*
 *  Parse a command line.
 *
 *  Input:   Line of text in port->line.
 *  Returns: Fields are placed in port->cmd.
 *           Fields are pointed to by port->fld[].
 *           port->flds is set to the number of fields found.
 *           Each field is null-terminated.
 *           Fields beyond maxflds are ignored.
 */

parse()
{
  register short bl;
  register char *in, *out;

  for (port->flds = 0; port->flds < maxflds;)
    port->fld[port->flds++] = nullstr;

  in  = port->line;
  out = port->cmd;
  bl  = false;
  port->flds = 0;

  while (*in and (port->flds < maxflds) and (out < (port->cmd + cmdlen - 1)))
  {
    *out = toupper(*in);
    if (bl)
    {
      if ((*in <= ' ') or ((*in is '@') and (port->flds is 2)))
      {
        bl = false;
        *out = '\0';
        if (*in is '@')
        {
          out++;
          *out = '@';
          port->fld[port->flds++] = out++;
          *out = '\0';
        }
      }
      out++;
    }
    else
    {
      if (*in > ' ')
      {
        bl = true;
        port->fld[port->flds++] = out++;
      }
    }
    in++;
  }
  *out = '\0';

  port->opt1 = *port->fld[0];
  port->opt2 = *(port->fld[0] + 1);
  if (!port->opt2) port->opt2 = ' ';
}

/*
 *  Bundle getdat and parse.
 */

getcmd()
{
  while(!getdat());
  parse();
}

/*
 *  Fill some memory with a character.
 */

fill(adr, ch, len)
char *adr;
char ch;
int len;
{
  while (len--) *adr++ = ch;
}

/*
 *  Copy C string to LJSF string.
 */

ljsf(to, from, size)
char *to;
char *from;
int  size;
{
  fill(to, ' ', size);
  while (size--)
  {
    if (*from < ' ') return;
    *to++ = *from++;
  }
}

/*
 *  Copy LJSF string to C string.
 */

unbl(to, from, size)
char *to, *from;
int size;
{
  while (size--)
  {
    if (*from <= ' ') { *to = '\0'; return; }
    *to++ = *from++;
  }
  *to = '\0';
}

/*
 *  Are you sure? .....
 */
sure()
{
  outstr("Are you sure (Y/N)?\n");
  while (!getdat());
  if (toupper(*port->line) is 'Y') return false;
  return true;
}

/* Search string for match */

search(a,b,c)
char *a, *b;
int c;
{
  for( ; *a; a++)
    if (matchn(a, b, c)) return true;
  return false;
}
