#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS))
/*
 * /src/NTP/REPOSITORY/v3/parse/parse.c,v 3.27 1994/06/01 08:18:33 kardel Exp
 *
 * parse.c,v 3.27 1994/06/01 08:18:33 kardel Exp
 *
 * Parser module for reference clock
 *
 * PARSEKERNEL define switches between two personalities of the module
 * if PARSEKERNEL is defined this module can be used with dcf77sync.c as
 * a PARSEKERNEL kernel module. In this case the time stamps will be
 * a struct timeval.
 * when PARSEKERNEL is not defined NTP time stamps will be used.
 *
 * Copyright (c) 1992,1993,1994
 * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 */

#if	!(defined(lint) || defined(__GNUC__))
static char rcsid[] = "parse.c,v 3.19 1994/01/25 19:05:20 kardel Exp";
#endif

#include "sys/types.h"
#include "sys/time.h"
#include "sys/errno.h"

#include "ntp_fp.h"
#include "ntp_unixtime.h"
#include "ntp_calendar.h"

#include "ntp_machine.h"

#if defined(PARSESTREAM) && (defined(SYS_SUNOS4) || defined(SYS_SOLARIS)) && defined(STREAM)
/*
 * Sorry, but in SunOS 4.x AND Solaris 2.x kernels there are no
 * mem* operations. I don't want them - bcopy, bzero
 * are fine in the kernel
 */
#ifndef NTP_NEED_BOPS
#define NTP_NEED_BOPS
#endif
#else
#ifndef NTP_NEED_BOPS
#ifndef bzero
#define bzero(_X_, _Y_) memset(_X_, 0, _Y_)
#define bcopy(_X_, _Y_, _Z_) memmove(_Y_, _X_, _Z_)
#endif
#endif
#endif

#include "parse.h"

#include "ntp_stdlib.h"

#ifdef PARSESTREAM
#include "sys/parsestreams.h"
#endif

extern clockformat_t *clockformats[];
extern unsigned short nformats;

static u_long timepacket();

/*
 * strings support usually not in kernel - duplicated, but what the heck
 */
static int
Strlen(s)
  register char *s;
{
  register int c;

  c = 0;
  if (s)
    {
      while (*s++)
	{
	  c++;
	}
    }
  return c;
}

static int
Strcmp(s, t)
  register char *s;
  register char *t;
{
  register int c = 0;

  if (!s || !t || (s == t))
    {
      return 0;
    }

  while (!(c = *s++ - *t++) && *s && *t)
    /* empty loop */;

  return c;
}

static int
timedout(parseio, ctime)
  register parse_t *parseio;
  register timestamp_t *ctime;
{
  struct timeval delta;

#ifdef PARSEKERNEL
  delta.tv_sec = ctime->tv.tv_sec - parseio->parse_lastchar.tv.tv_sec;
  delta.tv_usec = ctime->tv.tv_usec - parseio->parse_lastchar.tv.tv_usec;
  if (delta.tv_usec < 0)
    {
      delta.tv_sec  -= 1;
      delta.tv_usec += 1000000;
    }
#else
  extern long tstouslo[];
  extern long tstousmid[];
  extern long tstoushi[];

  l_fp delt;

  delt = ctime->fp;
  L_SUB(&delt, &parseio->parse_lastchar.fp);
  TSTOTV(&delt, &delta);
#endif

  if (timercmp(&delta, &parseio->parse_timeout, >))
    {
      parseprintf(DD_PARSE, ("parse: timedout: TRUE\n"));
      return 1;
    }
  else
    {
      parseprintf(DD_PARSE, ("parse: timedout: FALSE\n"));
      return 0;
    }
}

/*
 * setup_bitmaps
 * WARNING: NOT TO BE CALLED CONCURRENTLY WITH
 *          parse_ioread, parse_ioend, parse_ioinit
 */
static int
setup_bitmaps(parseio, low, high)
  register parse_t *parseio;
  register unsigned short low;
  register unsigned short high;
{
  register unsigned short i;
  register int f = 0;
  register clockformat_t *fmt;
  register unsigned short index, mask, plen;

  if ((low >= high) ||
      (high > nformats))
    {
      parseprintf(DD_PARSE, ("setup_bitmaps: failed: bounds error (low=%d, high=%d, nformats=%d)\n", low, high, nformats));
      return 0;
    }

  bzero(parseio->parse_startsym, sizeof (parseio->parse_startsym));
  bzero(parseio->parse_endsym, sizeof (parseio->parse_endsym));
  bzero(parseio->parse_syncsym, sizeof (parseio->parse_syncsym));

  plen = 0;

  parseio->parse_syncflags = 0;
  parseio->parse_timeout.tv_sec = 0;
  parseio->parse_timeout.tv_usec = 0;

  /*
   * gather bitmaps of possible start and end values
   */
  for (i=low; i < high; i++)
    {
      fmt = clockformats[i];

      if (!(parseio->parse_flags & PARSE_FIXED_FMT) &&
	   (fmt->flags & CVT_FIXEDONLY)) {
        if (parseio->parse_dsize < fmt->length)
	  parseio->parse_dsize = fmt->length;
	continue;
      }

      if (fmt->flags & F_START)
	{
	  index = fmt->startsym >> 3;
	  mask  = 1 << (fmt->startsym & 0x7);

	  if (parseio->parse_endsym[index] & mask)
	    {
#ifdef PARSEKERNEL
	      printf("parse: setup_bitmaps: failed: START symbol collides with END symbol (format %d)\n", i);
#else
	      syslog(LOG_ERR, "parse: setup_bitmaps: failed: START symbol collides with END symbol (format %d)\n", i);
#endif
	      return 0;
	    }
	  else
	    {
	      parseio->parse_startsym[index] |= mask;
	      f = 1;
	    }
	}

      if (fmt->flags & F_END)
	{
	  index = fmt->endsym >> 3;
	  mask  = 1 << (fmt->endsym & 0x7);

	  if (parseio->parse_startsym[index] & mask)
	    {
#ifdef PARSEKERNEL
	      printf("parse: setup_bitmaps: failed: END symbol collides with START symbol (format %d)\n", i);
#else
	      syslog(LOG_ERR, "parse: setup_bitmaps: failed: END symbol collides with START symbol (format %d)\n", i);
#endif
	      return 0;
	    }
	  else
	    {
	      parseio->parse_endsym[index] |= mask;
	      f = 1;
	    }
	}

      if (fmt->flags & SYNC_CHAR)
	{
	  parseio->parse_syncsym[fmt->syncsym >> 3] |= (1 << (fmt->syncsym & 0x7));
	}

      parseio->parse_syncflags |= fmt->flags & (SYNC_START|SYNC_END|SYNC_CHAR|SYNC_ONE|SYNC_ZERO|SYNC_TIMEOUT|SYNC_SYNTHESIZE);

      if (((fmt->flags & (SYNC_TIMEOUT|CVT_FIXEDONLY)) == (SYNC_TIMEOUT|CVT_FIXEDONLY)) &&
	  ((parseio->parse_timeout.tv_sec || parseio->parse_timeout.tv_usec) ? timercmp(&parseio->parse_timeout, &fmt->timeout, >) : 1))
	{
	  parseio->parse_timeout = fmt->timeout;
	}

      if (parseio->parse_dsize < fmt->length)
	parseio->parse_dsize = fmt->length;
    }

  if (parseio->parse_pdata)
    {
      FREE(parseio->parse_pdata, parseio->parse_plen);
      parseio->parse_plen = 0;
      parseio->parse_pdata = (void *)0;
    }

  if (!f && ((int)(high - low) > 1))
    {
      /*
       * need at least one start or end symbol
       */
#ifdef PARSEKERNEL
      printf("parse: setup_bitmaps: failed: neither START nor END symbol defined\n");
#else
      syslog(LOG_ERR, "parse: setup_bitmaps: failed: neither START nor END symbol defined\n");
#endif
      return 0;
    }

  if ((high - low == 1) && (clockformats[low]->flags & CVT_FIXEDONLY) &&
      (clockformats[low]->plen))
    {
      parseio->parse_plen  = clockformats[low]->plen;
      parseio->parse_pdata = (void *)MALLOC(parseio->parse_plen);

      if (!parseio->parse_pdata)
	{
	  /*
	   * no memory
	   */
#ifdef PARSEKERNEL
	  printf("parse: setup_bitmaps: failed: no memory for private data\n");
#else
	  syslog(LOG_ERR, "parse: setup_bitmaps: failed: no memory for private data\n");
#endif
	  return 0;
	}
      bzero((char *)parseio->parse_pdata, parseio->parse_plen);
    }

  return 1;
}

/*ARGSUSED*/
int
parse_ioinit(parseio)
  register parse_t *parseio;
{
  parseprintf(DD_PARSE, ("parse_iostart\n"));

  parseio->parse_plen = 0;
  parseio->parse_pdata = (void *)0;

  if (!setup_bitmaps(parseio, 0, nformats))
    return 0;

  parseio->parse_data = MALLOC(parseio->parse_dsize * 2 + 2);
  if (!parseio->parse_data)
    {
      parseprintf(DD_PARSE, ("init failed: malloc for data area failed\n"));
      return 0;
    }

  /*
   * leave room for '\0'
   */
  parseio->parse_ldata     = parseio->parse_data + parseio->parse_dsize + 1;
  parseio->parse_lformat   = 0;
  parseio->parse_badformat = 0;
  parseio->parse_ioflags   = PARSE_IO_CS7;	/* usual unix default */
  parseio->parse_flags     = 0;		/* true samples */
  parseio->parse_index     = 0;
  parseio->parse_ldsize    = 0;

  return 1;
}

/*ARGSUSED*/
void
parse_ioend(parseio)
  register parse_t *parseio;
{
  parseprintf(DD_PARSE, ("parse_ioend\n"));

  if (parseio->parse_pdata)
    FREE(parseio->parse_pdata, parseio->parse_plen);

  if (parseio->parse_data)
    FREE(parseio->parse_data, parseio->parse_dsize * 2 + 2);
}

/*ARGSUSED*/
int
parse_ioread(parseio, ch, ctime)
  register parse_t *parseio;
  register unsigned char ch;
  register timestamp_t *ctime;
{
  register unsigned updated = CVT_NONE;
  register unsigned short low, high;
  register unsigned index, mask;

  /*
   * within STREAMS CSx (x < 8) chars still have the upper bits set
   * so we normalize the characters by masking unecessary bits off.
   */
  switch (parseio->parse_ioflags & PARSE_IO_CSIZE)
    {
    case PARSE_IO_CS5:
      ch &= 0x1F;
      break;

    case PARSE_IO_CS6:
      ch &= 0x3F;
      break;

    case PARSE_IO_CS7:
      ch &= 0x7F;
      break;

    case PARSE_IO_CS8:
      break;
    }

  parseprintf(DD_PARSE, ("parse_ioread(0x%x, char=0x%x, ..., ...)\n", (unsigned int)parseio, ch & 0xFF));

  if (parseio->parse_flags & PARSE_FIXED_FMT)
    {
      if (!clockformats[parseio->parse_lformat]->convert)
	{
	  parseprintf(DD_PARSE, ("parse_ioread: input dropped.\n"));
	  return CVT_NONE;
	}

      if (clockformats[parseio->parse_lformat]->input)
	{
	  if (clockformats[parseio->parse_lformat]->input(parseio, ch, ctime))
	    updated = timepacket(parseio); /* got a datagram - process */

	  low = high = 0;	/* all done - just post processing */
	}
      else
	{
	  low = parseio->parse_lformat;
	  high = low + 1;	/* scan just one format */
	}
    }
  else
    {
      low = 0;
      high = nformats;		/* scan all non fixed formats */
    }

  if (low != high)
    {
      index = ch >> 3;
      mask  =  1 << (ch & 0x7);

      if ((parseio->parse_syncflags & SYNC_CHAR) &&
	  (parseio->parse_syncsym[index] & mask))
	{
	  register clockformat_t *fmt;
	  register unsigned short i;
	  /*
	   * got a sync event - call sync routine
	   */

	  for (i = low; i < high; i++)
	    {
	      fmt = clockformats[i];

	      if ((fmt->flags & SYNC_CHAR) &&
		  (fmt->syncsym == ch))
		{
		  parseprintf(DD_PARSE, ("parse_ioread: SYNC_CHAR event\n"));
		  if (fmt->syncevt)
		    fmt->syncevt(parseio, ctime, fmt->data, SYNC_CHAR);
		}
	    }
	}

      if ((((parseio->parse_syncflags & SYNC_START) &&
	    (parseio->parse_startsym[index] & mask)) ||
	   (parseio->parse_index == 0)) ||
	  ((parseio->parse_syncflags & SYNC_TIMEOUT) &&
	   timedout(parseio, ctime)))
	{
	  register unsigned short i;
	  /*
	   * packet start - re-fill buffer
	   */
	  if (parseio->parse_index)
	    {
	      /*
	       * filled buffer - thus not end character found
	       * do processing now
	       */
	      parseio->parse_data[parseio->parse_index] = '\0';

	      updated = timepacket(parseio);
	      bcopy(parseio->parse_data, parseio->parse_ldata, parseio->parse_index+1);
	      parseio->parse_ldsize = parseio->parse_index+1;
	      if (parseio->parse_syncflags & SYNC_TIMEOUT)
		parseio->parse_dtime.parse_stime = *ctime;
	    }

	  /*
	   * could be a sync event - call sync routine if needed
	   */
	  if (parseio->parse_syncflags & SYNC_START)
	    for (i = low; i < high; i++)
	      {
		register clockformat_t *fmt = clockformats[i];

		if ((parseio->parse_index == 0) ||
		    ((fmt->flags & SYNC_START) && (fmt->startsym == ch)))
		  {
		    parseprintf(DD_PARSE, ("parse_ioread: SYNC_START event\n"));
		    if (fmt->syncevt)
		      fmt->syncevt(parseio, ctime, fmt->data, SYNC_START);
		  }
	      }
	  parseio->parse_index = 1;
	  parseio->parse_data[0] = ch;
	  parseprintf(DD_PARSE, ("parse: parse_ioread: buffer start\n"));
	}
      else
	{
	  register unsigned short i;

	  if (parseio->parse_index < parseio->parse_dsize)
	    {
	      /*
	       * collect into buffer
	       */
	      parseprintf(DD_PARSE, ("parse: parse_ioread: buffer[%d] = 0x%x\n", parseio->parse_index, ch));
	      parseio->parse_data[parseio->parse_index++] = ch;
	    }

	  if ((parseio->parse_endsym[index] & mask) ||
	      (parseio->parse_index >= parseio->parse_dsize))
	    {
	      /*
	       * packet end - process buffer
	       */
	      if (parseio->parse_syncflags & SYNC_END)
		for (i = low; i < high; i++)
		  {
		    register clockformat_t *fmt = clockformats[i];

		    if ((fmt->flags & SYNC_END) && (fmt->endsym == ch))
		      {
			parseprintf(DD_PARSE, ("parse_ioread: SYNC_END event\n"));
			if (fmt->syncevt)
			  fmt->syncevt(parseio, ctime, fmt->data, SYNC_END);
		      }
		  }
	      parseio->parse_data[parseio->parse_index] = '\0';
	      updated = timepacket(parseio);
	      bcopy(parseio->parse_data, parseio->parse_ldata, parseio->parse_index+1);
	      parseio->parse_ldsize = parseio->parse_index+1;
	      parseio->parse_index = 0;
	      parseprintf(DD_PARSE, ("parse: parse_ioread: buffer end\n"));
	    }
	}
    }

  if ((updated == CVT_NONE) &&
      (parseio->parse_flags & PARSE_FIXED_FMT) &&
      (parseio->parse_syncflags & SYNC_SYNTHESIZE) &&
      ((parseio->parse_dtime.parse_status & CVT_MASK) == CVT_OK) &&
      clockformats[parseio->parse_lformat]->synth)
    {
      updated = clockformats[parseio->parse_lformat]->synth(parseio, ctime);
    }

  /*
   * remember last character time
   */
  parseio->parse_lastchar = *ctime;

#ifdef DEBUG
  if ((updated & CVT_MASK) != CVT_NONE)
    {
      parseprintf(DD_PARSE, ("parse_ioread: time sample accumulated (status=0x%x)\n", updated));
    }
#endif

  parseio->parse_dtime.parse_status = updated;

  return (updated & CVT_MASK) != CVT_NONE;
}

/*
 * parse_iopps
 *
 * take status line indication and derive synchronisation information
 * from it.
 * It can also be used to decode a serial serial data format (such as the
 * ONE, ZERO, MINUTE sync data stream from DCF77)
 */
/*ARGSUSED*/
int
parse_iopps(parseio, status, ptime)
  register parse_t *parseio;
  register int status;
  register timestamp_t *ptime;
{
  register unsigned updated = CVT_NONE;

  /*
   * PPS pulse information will only be delivered to ONE clock format
   * this is either the last successful conversion module with a ppssync
   * routine, or a fixed format with a ppssync routine
   */
  parseprintf(DD_PARSE, ("parse_iopps: STATUS %s\n", (status == SYNC_ONE) ? "ONE" : "ZERO"));

  if (((parseio->parse_flags & PARSE_FIXED_FMT) ||
       ((parseio->parse_dtime.parse_status & CVT_MASK) == CVT_OK)) &&
      clockformats[parseio->parse_lformat]->syncpps &&
      (status & clockformats[parseio->parse_lformat]->flags))
    {
      updated = clockformats[parseio->parse_lformat]->syncpps(parseio, status == SYNC_ONE, ptime);
      parseprintf(DD_PARSE, ("parse_iopps: updated = 0x%x\n", updated));
    }
  else
    {
      parseprintf(DD_PARSE, ("parse_iopps: STATUS dropped\n"));
    }

  return (updated & CVT_MASK) != CVT_NONE;
}

/*
 * parse_iodone
 *
 * clean up internal status for new round
 */
/*ARGSUSED*/
void
parse_iodone(parseio)
  register parse_t *parseio;
{
  /*
   * we need to clean up certain flags for the next round
   */
  parseprintf(DD_PARSE, ("parse_iodone: DONE\n"));
  parseio->parse_dtime.parse_state = 0; /* no problems with ISRs */
}

/*---------- conversion implementation --------------------*/

/*
 * convert a struct clock to UTC since Jan, 1st 1970 0:00 (the UNIX EPOCH)
 */
#define dysize(x)	(((x) % 4) ? 365 : \
                                    (((x) % 100) ? 366 : \
                                                    (((x) % 400) ? 365 : 366)))

time_t
parse_to_unixtime(clock, cvtrtc)
  register clocktime_t   *clock;
  register u_long *cvtrtc;
{
#define SETRTC(_X_)	{ if (cvtrtc) *cvtrtc = (_X_); }
  static int days_of_month[] =
    {
      0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    };
  register int i;
  time_t t;

  if (clock->utctime)
    return clock->utctime;	/* if the conversion routine gets it right away - why not */

  if (clock->year < 100)
    clock->year += 1900;

  if (clock->year < 1970)
    clock->year += 100;		/* XXX this will do it till <2070 */

  if (clock->year < 0)
    {
      SETRTC(CVT_FAIL|CVT_BADDATE);
      return -1;
    }

  /*
   * sorry, slow section here - but it's not time critical anyway
   */
  t =  (clock->year - 1970) * 365;
  t += (clock->year >> 2) - (1970 >> 2);
  t -= clock->year / 100 - 1970 / 100;
  t += clock->year / 400 - 1970 / 400;

  				/* month */
  if (clock->month <= 0 || clock->month > 12)
    {
      SETRTC(CVT_FAIL|CVT_BADDATE);
      return -1;		/* bad month */
    }
				/* adjust leap year */
  if (clock->month < 3 && dysize(clock->year) == 366)
      t--;

  for (i = 1; i < clock->month; i++)
    {
      t += days_of_month[i];
    }
				/* day */
  if (clock->day < 1 || ((clock->month == 2 && dysize(clock->year) == 366) ?
		  clock->day > 29 : clock->day > days_of_month[clock->month]))
    {
      SETRTC(CVT_FAIL|CVT_BADDATE);
      return -1;		/* bad day */
    }

  t += clock->day - 1;
				/* hour */
  if (clock->hour < 0 || clock->hour >= 24)
    {
      SETRTC(CVT_FAIL|CVT_BADTIME);
      return -1;		/* bad hour */
    }

  t = TIMES24(t) + clock->hour;

  				/* min */
  if (clock->minute < 0 || clock->minute > 59)
    {
      SETRTC(CVT_FAIL|CVT_BADTIME);
      return -1;		/* bad min */
    }

  t = TIMES60(t) + clock->minute;
				/* sec */

  if (clock->second < 0 || clock->second > 60)	/* allow for LEAPs */
    {
      SETRTC(CVT_FAIL|CVT_BADTIME);
      return -1;		/* bad sec */
    }

  t  = TIMES60(t) + clock->second;

  t += clock->utcoffset;	/* warp to UTC */

				/* done */

  clock->utctime = t;		/* documentray only */

  return t;
}

/*--------------- format conversion -----------------------------------*/

int
Stoi(s, zp, cnt)
  char *s;
  long *zp;
  int cnt;
{
  char *b = s;
  int f,z,v;
  char c;

  f=z=v=0;

  while(*s == ' ')
    s++;

  if (*s == '-')
    {
      s++;
      v = 1;
    }
  else
    if (*s == '+')
      s++;

  for(;;)
    {
      c = *s++;
      if (c == '\0' || c < '0' || c > '9' || (cnt && ((s-b) > cnt)))
	{
	  if (f == 0)
	    {
	      return(-1);
	    }
	  if (v)
	    z = -z;
	  *zp = z;
	  return(0);
	}
      z = (z << 3) + (z << 1) + ( c - '0' );
      f=1;
    }
}


int
Strok(s, m)
  char *s;
  char *m;
{
  if (!s || !m)
    return 0;

  while(*s && *m)
    {
      if ((*m == ' ') ? 1 : (*s == *m))
	{
	  s++;
	  m++;
	}
      else
	{
	  return 0;
	}
    }
  return !*m;
}

u_long
updatetimeinfo(parseio, t, usec, flags)
  register parse_t         *parseio;
  register time_t         t;
  register u_long  usec;
  register u_long  flags;
{
  register long usecoff;
  register long mean;
  long delta[PARSE_DELTA];

#ifdef PARSEKERNEL
    usecoff = (t - parseio->parse_dtime.parse_stime.tv.tv_sec) * 1000000
      - parseio->parse_dtime.parse_stime.tv.tv_usec + usec;
#else
    extern long tstouslo[];
    extern long tstousmid[];
    extern long tstoushi[];

    TSFTOTVU(parseio->parse_dtime.parse_stime.fp.l_uf, usecoff);
    usecoff  = -usecoff;
    usecoff += (t - parseio->parse_dtime.parse_stime.fp.l_ui + JAN_1970) * 1000000
              + usec;
#endif

  /*
   * filtering (median) if requested
   */
  if (parseio->parse_flags & PARSE_STAT_FILTER)
    {
      register int n, i, s, k;

      parseio->parse_delta[parseio->parse_dindex] = usecoff;

      parseio->parse_dindex = (parseio->parse_dindex + 1) % PARSE_DELTA;

      /*
       * sort always - thus every sample gets its data
       */
      bcopy((caddr_t)parseio->parse_delta, (caddr_t)delta, sizeof(delta));

      for (s = 0; s < PARSE_DELTA; s++)
	for (k = s+1; k < PARSE_DELTA; k++)
	  {			/* Yes - it's slow sort */
	    if (delta[s] > delta[k])
	      {
		register long tmp;

		tmp      = delta[k];
		delta[k] = delta[s];
		delta[s] = tmp;
	      }
	  }

      i = 0;
      n = PARSE_DELTA;

      /*
       * you know this median loop if you have read the other code
       */
      while ((n - i) > 8)
	{
	  register long top = delta[n-1];
	  register long mid = delta[(n+i)>>1];
	  register long low = delta[i];

	  if ((top - mid) > (mid - low))
	    {
	      /*
	       * cut off high end
	       */
	      n--;
	    }
	  else
	    {
	      /*
	       * cut off low end
	       */
	      i++;
	    }
	}

      parseio->parse_dtime.parse_usecdisp  = delta[n-1] - delta[i];

      if (parseio->parse_flags & PARSE_STAT_AVG)
	{
	  /*
	   * take the average of the median samples as this clock
	   * is a little bumpy
	   */
	  mean = 0;

	  while (i < n)
	    {
	      mean += delta[i++];
	    }

	  mean >>= 3;
	}
      else
	{
	  mean = delta[(n+i)>>1];
	}

      parseio->parse_dtime.parse_usecerror = mean;
    }
  else
    {
      parseio->parse_dtime.parse_usecerror = usecoff;
      parseio->parse_dtime.parse_usecdisp  = 0;
    }

  parseprintf(DD_PARSE,("parse: updatetimeinfo: T=%x+%d usec, useccoff=%d, usecerror=%d, usecdisp=%d\n",
			(int)t, (int)usec, (int)usecoff, (int)parseio->parse_dtime.parse_usecerror,
			(int)parseio->parse_dtime.parse_usecdisp));


#ifdef PARSEKERNEL
  {
    int s = splhigh();
#endif

    parseio->parse_lstate          = parseio->parse_dtime.parse_state | flags | PARSEB_TIMECODE;

    parseio->parse_dtime.parse_state = parseio->parse_lstate;

#ifdef PARSEKERNEL
    (void)splx(s);
  }
#endif

  return CVT_OK;		/* everything fine and dandy... */
}


/*
 * syn_simple
 *
 * handle a sync time stamp
 */
/*ARGSUSED*/
void
syn_simple(parseio, ts, format, why)
  register parse_t *parseio;
  register timestamp_t *ts;
  register struct format *format;
  register u_long why;
{
  parseio->parse_dtime.parse_stime = *ts;
}

/*
 * pps_simple
 *
 * handle a pps time stamp
 */
/*ARGSUSED*/
u_long
pps_simple(parseio, status, ptime)
  register parse_t *parseio;
  register int status;
  register timestamp_t *ptime;
{
  parseio->parse_dtime.parse_ptime  = *ptime;
  parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;

  return CVT_NONE;
}

/*
 * timepacket
 *
 * process a data packet
 */
static u_long
timepacket(parseio)
  register parse_t *parseio;
{
  register int k;
  register unsigned short format;
  register time_t t;
  register u_long cvtsum = 0;/* accumulated CVT_FAIL errors */
  u_long cvtrtc;		/* current conversion result */
  clocktime_t clock;

  bzero(&clock, sizeof clock);
  format = parseio->parse_lformat;

  k = 0;

  if (parseio->parse_flags & PARSE_FIXED_FMT)
    {
      switch ((cvtrtc = clockformats[format]->convert ? clockformats[format]->convert(parseio->parse_data, parseio->parse_index, clockformats[format]->data, &clock, parseio->parse_pdata) : CVT_NONE) & CVT_MASK)
	{
	case CVT_FAIL:
	  parseio->parse_badformat++;
	  cvtsum = cvtrtc & ~CVT_MASK;

	  /*
	   * may be too often ... but is nice to know when it happens
	   */
#ifdef PARSEKERNEL
	  printf("parse: \"%s\" failed to convert\n", clockformats[format]->name);
#else
	  syslog(LOG_WARNING, "parse: \"%s\" failed to convert\n", clockformats[format]->name);
#endif
	  break;

	case CVT_NONE:
	  /*
	   * too bad - pretend bad format
	   */
	  parseio->parse_badformat++;
	  cvtsum = CVT_BADFMT;
	  break;

	case CVT_OK:
	  k = 1;
	  break;

	case CVT_SKIP:
	  k = 2;
	  break;

	default:
	  /* shouldn't happen */
#ifdef PARSEKERNEL
	  printf("parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
#else
	  syslog(LOG_WARNING, "parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
#endif
	  return CVT_FAIL|cvtrtc;
	}
    }
  else
    {
      /*
       * find correct conversion routine
       * and convert time packet
       * RR search starting at last successful conversion routine
       */

      if (nformats)		/* very careful ... */
	{
	  do
	    {
	      switch ((cvtrtc = (clockformats[format]->convert && !(clockformats[format]->flags & CVT_FIXEDONLY)) ?
		       clockformats[format]->convert(parseio->parse_data, parseio->parse_index, clockformats[format]->data, &clock, parseio->parse_pdata) :
		       CVT_NONE) & CVT_MASK)
		{
		case CVT_FAIL:
		  parseio->parse_badformat++;
		  cvtsum |= cvtrtc & ~CVT_MASK;

		  /*
		   * may be too often ... but is nice to know when it happens
		   */
#ifdef PARSEKERNEL
		  printf("parse: \"%s\" failed to convert\n", clockformats[format]->name);
#else
		  syslog(LOG_WARNING, "parse: \"%s\" failed to convert\n", clockformats[format]->name);
#endif
		  /*FALLTHROUGH*/
		case CVT_NONE:
		  format++;
		  break;

		case CVT_OK:
		  k = 1;
		  break;

		default:
		  /* shouldn't happen */
#ifdef PARSEKERNEL
		  printf("parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
#else
		  syslog(LOG_WARNING, "parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
#endif
		  return CVT_BADFMT;
		}
	      if (format >= nformats)
		format = 0;
	    }
	  while (!k && (format != parseio->parse_lformat));
	}
    }

  if (!k)
    {
#ifdef PARSEKERNEL
      printf("parse: time format \"%s\" not convertable\n", parseio->parse_data);
#else
      syslog(LOG_WARNING, "parse: time format \"%s\" not convertable\n", parseio->parse_data);
#endif
      return CVT_FAIL|cvtsum;
    }

  if (k == 2) return CVT_OK;

  if ((t = parse_to_unixtime(&clock, &cvtrtc)) == -1)
    {
#ifdef PARSEKERNEL
      printf("parse: bad time format \"%s\"\n", parseio->parse_data);
#else
      syslog(LOG_WARNING,"parse: bad time format \"%s\"\n", parseio->parse_data);
#endif
      return CVT_FAIL|cvtrtc;
    }

  parseio->parse_lformat = format;

  /*
   * time stamp
   */
#ifdef PARSEKERNEL
  parseio->parse_dtime.parse_time.tv.tv_sec  = t;
  parseio->parse_dtime.parse_time.tv.tv_usec = clock.usecond;
#else
  parseio->parse_dtime.parse_time.fp.l_ui = t + JAN_1970;
  TVUTOTSF(clock.usecond, parseio->parse_dtime.parse_time.fp.l_uf);
#endif

  parseio->parse_dtime.parse_format       = format;

  return updatetimeinfo(parseio, t, clock.usecond, clock.flags);
}


/*
 * control operations
 */
/*ARGSUSED*/
int
parse_getstat(dct, parse)
  parsectl_t *dct;
  parse_t    *parse;
{
  dct->parsestatus.flags = parse->parse_flags & PARSE_STAT_FLAGS;
  return 1;
}


/*ARGSUSED*/
int
parse_setstat(dct, parse)
  parsectl_t *dct;
  parse_t    *parse;
{
  parse->parse_flags = (parse->parse_flags & ~PARSE_STAT_FLAGS) | dct->parsestatus.flags;
  return 1;
}


/*ARGSUSED*/
int
parse_timecode(dct, parse)
  parsectl_t *dct;
  parse_t    *parse;
{
  dct->parsegettc.parse_state  = parse->parse_lstate;
  dct->parsegettc.parse_format = parse->parse_lformat;
  /*
   * move out current bad packet count
   * user program is expected to sum these up
   * this is not a problem, as "parse" module are
   * exclusive open only
   */
  dct->parsegettc.parse_badformat = parse->parse_badformat;
  parse->parse_badformat = 0;

  if (parse->parse_ldsize <= PARSE_TCMAX)
    {
      dct->parsegettc.parse_count = parse->parse_ldsize;
      bcopy(parse->parse_ldata, dct->parsegettc.parse_buffer, dct->parsegettc.parse_count);
      return 1;
    }
  else
    {
      return 0;
    }
}


/*ARGSUSED*/
int
parse_setfmt(dct, parse)
  parsectl_t *dct;
  parse_t    *parse;
{
  if (dct->parseformat.parse_count <= PARSE_TCMAX)
    {
      if (dct->parseformat.parse_count)
	{
	  register unsigned short i;

	  for (i = 0; i < nformats; i++)
	    {
	      if (!Strcmp(dct->parseformat.parse_buffer, clockformats[i]->name))
		{
		  parse->parse_lformat  = i;
		  parse->parse_flags   |= PARSE_FIXED_FMT; /* set fixed format indication */
		  return setup_bitmaps(parse, i, i+1);
		}
	    }

	  return 0;
	}
      else
	{
	  parse->parse_flags &= ~PARSE_FIXED_FMT; /* clear fixed format indication */
	  return setup_bitmaps(parse, 0, nformats);
	}
    }
  else
    {
      return 0;
    }
}

/*ARGSUSED*/
int
parse_getfmt(dct, parse)
  parsectl_t *dct;
  parse_t    *parse;
{
  if (dct->parseformat.parse_format < nformats &&
      Strlen(clockformats[dct->parseformat.parse_format]->name) <= PARSE_TCMAX)
    {
      dct->parseformat.parse_count = Strlen(clockformats[dct->parseformat.parse_format]->name)+1;
      bcopy(clockformats[dct->parseformat.parse_format]->name, dct->parseformat.parse_buffer, dct->parseformat.parse_count);
      return 1;
    }
  else
    {
      return 0;
    }
}

/*ARGSUSED*/
int
parse_setcs(dct, parse)
  parsectl_t *dct;
  parse_t    *parse;
{
  parse->parse_ioflags &= ~PARSE_IO_CSIZE;
  parse->parse_ioflags |= dct->parsesetcs.parse_cs & PARSE_IO_CSIZE;
  return 1;
}

#endif /* defined(REFCLOCK) && defined(PARSE) */

/*
 * History:
 *
 * parse.c,v
 * Revision 3.27  1994/06/01  08:18:33  kardel
 * more debug info
 *
 * Revision 3.26  1994/05/30  10:20:07  kardel
 * LONG cleanup
 *
 * Revision 3.25  1994/05/12  12:49:12  kardel
 * printf fmt/arg cleanup
 *
 * Revision 3.24  1994/03/27  15:01:36  kardel
 * reorder include file to cope with PTX
 *
 * Revision 3.23  1994/03/25  13:09:02  kardel
 * considering FIXEDONLY entries only in FIXEDONLY mode
 *
 * Revision 3.22  1994/02/25  12:34:49  kardel
 * allow for converter generated utc times
 *
 * Revision 3.21  1994/02/02  17:45:30  kardel
 * rcs ids fixed
 *
 * Revision 3.19  1994/01/25  19:05:20  kardel
 * 94/01/23 reconcilation
 *
 * Revision 3.18  1994/01/23  17:21:59  kardel
 * 1994 reconcilation
 *
 * Revision 3.17  1993/11/11  11:20:29  kardel
 * declaration fixes
 *
 * Revision 3.16  1993/11/06  22:26:07  duwe
 * Linux cleanup after config change
 *
 * Revision 3.15  1993/11/04  11:14:18  kardel
 * ansi/K&R traps
 *
 * Revision 3.14  1993/11/04  10:03:28  kardel
 * disarmed ansiism
 *
 * Revision 3.13  1993/11/01  20:14:13  kardel
 * useless comparision removed
 *
 * Revision 3.12  1993/11/01  20:00:22  kardel
 * parse Solaris support (initial version)
 *
 * Revision 3.11  1993/10/30  09:41:25  kardel
 * minor optimizations
 *
 * Revision 3.10  1993/10/22  14:27:51  kardel
 * Oct. 22nd 1993 reconcilation
 *
 * Revision 3.9  1993/10/05  23:15:09  kardel
 * more STREAM protection
 *
 * Revision 3.8  1993/09/27  21:08:00  kardel
 * utcoffset now in seconds
 *
 * Revision 3.7  1993/09/26  23:40:16  kardel
 * new parse driver logic
 *
 * Revision 3.6  1993/09/07  10:12:46  kardel
 * September 7th reconcilation - 3.2 (alpha)
 *
 * Revision 3.5  1993/09/01  21:44:48  kardel
 * conditional cleanup
 *
 * Revision 3.4  1993/08/27  00:29:39  kardel
 * compilation cleanup
 *
 * Revision 3.3  1993/08/24  22:27:13  kardel
 * cleaned up AUTOCONF DCF77 mess 8-) - wasn't too bad
 *
 * Revision 3.2  1993/07/09  11:37:11  kardel
 * Initial restructured version + GPS support
 *
 * Revision 3.1  1993/07/06  10:00:08  kardel
 * DCF77 driver goes generic...
 *
 */