parse.c revision 82499
1/*
2 * /src/NTP/ntp-4/libparse/parse.c,v 4.14 1999/11/28 09:13:52 kardel RELEASE_19991128_A
3 *
4 * parse.c,v 4.14 1999/11/28 09:13:52 kardel RELEASE_19991128_A
5 *
6 * Parser module for reference clock
7 *
8 * PARSEKERNEL define switches between two personalities of the module
9 * if PARSEKERNEL is defined this module can be used
10 * as kernel module. In this case the time stamps will be
11 * a struct timeval.
12 * when PARSEKERNEL is not defined NTP time stamps will be used.
13 *
14 * Copyright (c) 1992-1998 by Frank Kardel
15 * Friedrich-Alexander Universit�t Erlangen-N�rnberg, Germany
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 *
21 */
22
23#ifdef HAVE_CONFIG_H
24# include <config.h>
25#endif
26
27#if defined(REFCLOCK) && defined(CLOCK_PARSE)
28
29#if	!(defined(lint) || defined(__GNUC__))
30static char rcsid[] = "parse.c,v 4.14 1999/11/28 09:13:52 kardel RELEASE_19991128_A";
31#endif
32
33#include "ntp_fp.h"
34#include "ntp_unixtime.h"
35#include "ntp_calendar.h"
36#include "ntp_stdlib.h"
37#include "ntp_machine.h"
38#include "ntp.h"		/* (get Y2KFixes definitions) 	Y2KFixes */
39
40#include "parse.h"
41
42#ifndef PARSESTREAM
43# include <stdio.h>
44#else
45# include "sys/parsestreams.h"
46#endif
47
48extern clockformat_t *clockformats[];
49extern unsigned short nformats;
50
51static u_long timepacket P((parse_t *));
52
53/*
54 * strings support usually not in kernel - duplicated, but what the heck
55 */
56static int
57Strlen(
58	register const char *s
59	)
60{
61	register int c;
62
63	c = 0;
64	if (s)
65	{
66		while (*s++)
67		{
68			c++;
69		}
70	}
71	return c;
72}
73
74static int
75Strcmp(
76	register const char *s,
77	register const char *t
78	)
79{
80	register int c = 0;
81
82	if (!s || !t || (s == t))
83	{
84		return 0;
85	}
86
87	while (!(c = *s++ - *t++) && *s && *t)
88	    /* empty loop */;
89
90	return c;
91}
92
93int
94parse_timedout(
95	       parse_t *parseio,
96	       timestamp_t *tstamp,
97	       struct timeval *del
98	       )
99{
100	struct timeval delta;
101
102#ifdef PARSEKERNEL
103	delta.tv_sec = tstamp->tv.tv_sec - parseio->parse_lastchar.tv.tv_sec;
104	delta.tv_usec = tstamp->tv.tv_usec - parseio->parse_lastchar.tv.tv_usec;
105	if (delta.tv_usec < 0)
106	{
107		delta.tv_sec  -= 1;
108		delta.tv_usec += 1000000;
109	}
110#else
111	extern long tstouslo[];
112	extern long tstousmid[];
113	extern long tstoushi[];
114
115	l_fp delt;
116
117	delt = tstamp->fp;
118	L_SUB(&delt, &parseio->parse_lastchar.fp);
119	TSTOTV(&delt, &delta);
120#endif
121
122	if (timercmp(&delta, del, >))
123	{
124		parseprintf(DD_PARSE, ("parse: timedout: TRUE\n"));
125		return 1;
126	}
127	else
128	{
129		parseprintf(DD_PARSE, ("parse: timedout: FALSE\n"));
130		return 0;
131	}
132}
133
134/*ARGSUSED*/
135int
136parse_ioinit(
137	register parse_t *parseio
138	)
139{
140	parseprintf(DD_PARSE, ("parse_iostart\n"));
141
142	parseio->parse_plen = 0;
143	parseio->parse_pdata = (void *)0;
144
145	parseio->parse_data = 0;
146	parseio->parse_ldata = 0;
147	parseio->parse_dsize = 0;
148
149	parseio->parse_badformat = 0;
150	parseio->parse_ioflags   = PARSE_IO_CS7;	/* usual unix default */
151	parseio->parse_index     = 0;
152	parseio->parse_ldsize    = 0;
153
154	return 1;
155}
156
157/*ARGSUSED*/
158void
159parse_ioend(
160	register parse_t *parseio
161	)
162{
163	parseprintf(DD_PARSE, ("parse_ioend\n"));
164
165	if (parseio->parse_pdata)
166	    FREE(parseio->parse_pdata, parseio->parse_plen);
167
168	if (parseio->parse_data)
169	    FREE(parseio->parse_data, (unsigned)(parseio->parse_dsize * 2 + 2));
170}
171
172unsigned int
173parse_restart(
174	      parse_t *parseio,
175	      unsigned int ch
176	      )
177{
178	unsigned int updated = PARSE_INP_SKIP;
179
180	/*
181	 * re-start packet - timeout - overflow - start symbol
182	 */
183
184	if (parseio->parse_index)
185	{
186		/*
187		 * filled buffer - thus not end character found
188		 * do processing now
189		 */
190		parseio->parse_data[parseio->parse_index] = '\0';
191		memcpy(parseio->parse_ldata, parseio->parse_data, (unsigned)(parseio->parse_index+1));
192		parseio->parse_ldsize = parseio->parse_index+1;
193		updated = PARSE_INP_TIME;
194	}
195
196	parseio->parse_index = 1;
197	parseio->parse_data[0] = ch;
198	parseprintf(DD_PARSE, ("parse: parse_restart: buffer start (updated = %x)\n", updated));
199	return updated;
200}
201
202unsigned int
203parse_addchar(
204	      parse_t *parseio,
205	      unsigned int ch
206	      )
207{
208	/*
209	 * add to buffer
210	 */
211	if (parseio->parse_index < parseio->parse_dsize)
212	{
213		/*
214		 * collect into buffer
215		 */
216		parseprintf(DD_PARSE, ("parse: parse_addchar: buffer[%d] = 0x%x\n", parseio->parse_index, ch));
217		parseio->parse_data[parseio->parse_index++] = ch;
218		return PARSE_INP_SKIP;
219	}
220	else
221		/*
222		 * buffer overflow - attempt to make the best of it
223		 */
224		return parse_restart(parseio, ch);
225}
226
227unsigned int
228parse_end(
229	  parse_t *parseio
230	  )
231{
232	/*
233	 * message complete processing
234	 */
235	parseio->parse_data[parseio->parse_index] = '\0';
236	memcpy(parseio->parse_ldata, parseio->parse_data, (unsigned)(parseio->parse_index+1));
237	parseio->parse_ldsize = parseio->parse_index+1;
238	parseio->parse_index = 0;
239	parseprintf(DD_PARSE, ("parse: parse_end: buffer end\n"));
240	return PARSE_INP_TIME;
241}
242
243/*ARGSUSED*/
244int
245parse_ioread(
246	register parse_t *parseio,
247	register unsigned int ch,
248	register timestamp_t *tstamp
249	)
250{
251	register unsigned updated = CVT_NONE;
252	/*
253	 * within STREAMS CSx (x < 8) chars still have the upper bits set
254	 * so we normalize the characters by masking unecessary bits off.
255	 */
256	switch (parseio->parse_ioflags & PARSE_IO_CSIZE)
257	{
258	    case PARSE_IO_CS5:
259		ch &= 0x1F;
260		break;
261
262	    case PARSE_IO_CS6:
263		ch &= 0x3F;
264		break;
265
266	    case PARSE_IO_CS7:
267		ch &= 0x7F;
268		break;
269
270	    case PARSE_IO_CS8:
271		ch &= 0xFF;
272		break;
273	}
274
275	parseprintf(DD_PARSE, ("parse_ioread(0x%lx, char=0x%x, ..., ...)\n", (unsigned long)parseio, ch & 0xFF));
276
277	if (!clockformats[parseio->parse_lformat]->convert)
278	{
279		parseprintf(DD_PARSE, ("parse_ioread: input dropped.\n"));
280		return CVT_NONE;
281	}
282
283	if (clockformats[parseio->parse_lformat]->input)
284	{
285		unsigned long input_status;
286
287		input_status = clockformats[parseio->parse_lformat]->input(parseio, ch, tstamp);
288
289		if (input_status & PARSE_INP_SYNTH)
290		{
291			updated = CVT_OK;
292		}
293
294		if (input_status & PARSE_INP_TIME)	/* time sample is available */
295		{
296			updated = timepacket(parseio);
297		}
298
299		if (input_status & PARSE_INP_DATA) /* got additional data */
300		{
301			updated |= CVT_ADDITIONAL;
302		}
303	}
304
305
306	/*
307	 * remember last character time
308	 */
309	parseio->parse_lastchar = *tstamp;
310
311#ifdef DEBUG
312	if ((updated & CVT_MASK) != CVT_NONE)
313	{
314		parseprintf(DD_PARSE, ("parse_ioread: time sample accumulated (status=0x%x)\n", updated));
315	}
316#endif
317
318	parseio->parse_dtime.parse_status = updated;
319
320	return (((updated & CVT_MASK) != CVT_NONE) ||
321		((updated & CVT_ADDITIONAL) != 0));
322}
323
324/*
325 * parse_iopps
326 *
327 * take status line indication and derive synchronisation information
328 * from it.
329 * It can also be used to decode a serial serial data format (such as the
330 * ONE, ZERO, MINUTE sync data stream from DCF77)
331 */
332/*ARGSUSED*/
333int
334parse_iopps(
335	register parse_t *parseio,
336	register int status,
337	register timestamp_t *ptime
338	)
339{
340	register unsigned updated = CVT_NONE;
341
342	/*
343	 * PPS pulse information will only be delivered to ONE clock format
344	 * this is either the last successful conversion module with a ppssync
345	 * routine, or a fixed format with a ppssync routine
346	 */
347	parseprintf(DD_PARSE, ("parse_iopps: STATUS %s\n", (status == SYNC_ONE) ? "ONE" : "ZERO"));
348
349	if (clockformats[parseio->parse_lformat]->syncpps)
350	{
351		updated = clockformats[parseio->parse_lformat]->syncpps(parseio, status == SYNC_ONE, ptime);
352		parseprintf(DD_PARSE, ("parse_iopps: updated = 0x%x\n", updated));
353	}
354
355	return (updated & CVT_MASK) != CVT_NONE;
356}
357
358/*
359 * parse_iodone
360 *
361 * clean up internal status for new round
362 */
363/*ARGSUSED*/
364void
365parse_iodone(
366	register parse_t *parseio
367	)
368{
369	/*
370	 * we need to clean up certain flags for the next round
371	 */
372	parseprintf(DD_PARSE, ("parse_iodone: DONE\n"));
373	parseio->parse_dtime.parse_state = 0; /* no problems with ISRs */
374}
375
376/*---------- conversion implementation --------------------*/
377
378/*
379 * convert a struct clock to UTC since Jan, 1st 1970 0:00 (the UNIX EPOCH)
380 */
381#define days_per_year(x)	((x) % 4 ? 365 : ((x % 400) ? ((x % 100) ? 366 : 365) : 366))
382
383time_t
384parse_to_unixtime(
385	register clocktime_t   *clock_time,
386	register u_long *cvtrtc
387	)
388{
389#define SETRTC(_X_)	{ if (cvtrtc) *cvtrtc = (_X_); }
390	static int days_of_month[] =
391	{
392		0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
393	};
394	register int i;
395	time_t t;
396
397	if (clock_time->utctime)
398	    return clock_time->utctime;	/* if the conversion routine gets it right away - why not */
399
400	if ( clock_time->year < YEAR_PIVOT )			/* Y2KFixes [ */
401	    clock_time->year += 100;	/* convert 20xx%100 to 20xx-1900 */
402	if ( clock_time->year < YEAR_BREAK )	/* expand to full four-digits */
403	    clock_time->year += 1900;
404
405	if (clock_time->year < 1970 )				/* Y2KFixes ] */
406	{
407		SETRTC(CVT_FAIL|CVT_BADDATE);
408		return -1;
409	}
410
411	/*
412	 * sorry, slow section here - but it's not time critical anyway
413	 */
414	t = julian0(clock_time->year) - julian0(1970);		/* Y2kFixes */
415  				/* month */
416	if (clock_time->month <= 0 || clock_time->month > 12)
417	{
418		SETRTC(CVT_FAIL|CVT_BADDATE);
419		return -1;		/* bad month */
420	}
421
422#if 0								/* Y2KFixes */
423				/* adjust leap year */
424	if (clock_time->month < 3 && days_per_year(clock_time->year) == 366)
425	    t--;
426#else								/* Y2KFixes [ */
427	if ( clock_time->month >= 3  &&  isleap_4(clock_time->year) )
428	    t++;		/* add one more if within leap year */
429#endif								/* Y2KFixes ] */
430
431	for (i = 1; i < clock_time->month; i++)
432	{
433		t += days_of_month[i];
434	}
435				/* day */
436	if (clock_time->day < 1 || ((clock_time->month == 2 && days_per_year(clock_time->year) == 366) ?
437			       clock_time->day > 29 : clock_time->day > days_of_month[clock_time->month]))
438	{
439		SETRTC(CVT_FAIL|CVT_BADDATE);
440		return -1;		/* bad day */
441	}
442
443	t += clock_time->day - 1;
444				/* hour */
445	if (clock_time->hour < 0 || clock_time->hour >= 24)
446	{
447		SETRTC(CVT_FAIL|CVT_BADTIME);
448		return -1;		/* bad hour */
449	}
450
451	t = TIMES24(t) + clock_time->hour;
452
453  				/* min */
454	if (clock_time->minute < 0 || clock_time->minute > 59)
455	{
456		SETRTC(CVT_FAIL|CVT_BADTIME);
457		return -1;		/* bad min */
458	}
459
460	t = TIMES60(t) + clock_time->minute;
461				/* sec */
462
463	if (clock_time->second < 0 || clock_time->second > 60)	/* allow for LEAPs */
464	{
465		SETRTC(CVT_FAIL|CVT_BADTIME);
466		return -1;		/* bad sec */
467	}
468
469	t  = TIMES60(t) + clock_time->second;
470
471	t += clock_time->utcoffset;	/* warp to UTC */
472
473				/* done */
474
475	clock_time->utctime = t;		/* documentray only */
476
477	return t;
478}
479
480/*--------------- format conversion -----------------------------------*/
481
482int
483Stoi(
484	const unsigned char *s,
485	long *zp,
486	int cnt
487	)
488{
489	char unsigned const *b = s;
490	int f,z,v;
491	char unsigned c;
492
493	f=z=v=0;
494
495	while(*s == ' ')
496	    s++;
497
498	if (*s == '-')
499	{
500		s++;
501		v = 1;
502	}
503	else
504	    if (*s == '+')
505		s++;
506
507	for(;;)
508	{
509		c = *s++;
510		if (c == '\0' || c < '0' || c > '9' || (cnt && ((s-b) > cnt)))
511		{
512			if (f == 0)
513			{
514				return(-1);
515			}
516			if (v)
517			    z = -z;
518			*zp = z;
519			return(0);
520		}
521		z = (z << 3) + (z << 1) + ( c - '0' );
522		f=1;
523	}
524}
525
526int
527Strok(
528	const unsigned char *s,
529	const unsigned char *m
530	)
531{
532	if (!s || !m)
533	    return 0;
534
535	while(*s && *m)
536	{
537		if ((*m == ' ') ? 1 : (*s == *m))
538		{
539			s++;
540			m++;
541		}
542		else
543		{
544			return 0;
545		}
546	}
547	return !*m;
548}
549
550u_long
551updatetimeinfo(
552	       register parse_t *parseio,
553	       register u_long   flags
554	       )
555{
556#ifdef PARSEKERNEL
557	{
558		int s = splhigh();
559#endif
560
561		parseio->parse_lstate          = parseio->parse_dtime.parse_state | flags | PARSEB_TIMECODE;
562
563		parseio->parse_dtime.parse_state = parseio->parse_lstate;
564
565#ifdef PARSEKERNEL
566		(void)splx((unsigned int)s);
567	}
568#endif
569
570
571#ifdef PARSEKERNEL
572	parseprintf(DD_PARSE, ("updatetimeinfo status=0x%x, time=%x\n", parseio->parse_dtime.parse_state,
573			       parseio->parse_dtime.parse_time.tv.tv_sec));
574#else
575	parseprintf(DD_PARSE, ("updatetimeinfo status=0x%lx, time=%x\n", (long)parseio->parse_dtime.parse_state,
576	                       parseio->parse_dtime.parse_time.fp.l_ui));
577#endif
578
579	return CVT_OK;		/* everything fine and dandy... */
580}
581
582
583/*
584 * syn_simple
585 *
586 * handle a sync time stamp
587 */
588/*ARGSUSED*/
589void
590syn_simple(
591	register parse_t *parseio,
592	register timestamp_t *ts,
593	register struct format *format,
594	register u_long why
595	)
596{
597	parseio->parse_dtime.parse_stime = *ts;
598}
599
600/*
601 * pps_simple
602 *
603 * handle a pps time stamp
604 */
605/*ARGSUSED*/
606u_long
607pps_simple(
608	register parse_t *parseio,
609	register int status,
610	register timestamp_t *ptime
611	)
612{
613	parseio->parse_dtime.parse_ptime  = *ptime;
614	parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
615
616	return CVT_NONE;
617}
618
619/*
620 * pps_one
621 *
622 * handle a pps time stamp in ONE edge
623 */
624/*ARGSUSED*/
625u_long
626pps_one(
627	register parse_t *parseio,
628	register int status,
629	register timestamp_t *ptime
630	)
631{
632	if (status)
633		return pps_simple(parseio, status, ptime);
634
635	return CVT_NONE;
636}
637
638/*
639 * pps_zero
640 *
641 * handle a pps time stamp in ZERO edge
642 */
643/*ARGSUSED*/
644u_long
645pps_zero(
646	register parse_t *parseio,
647	register int status,
648	register timestamp_t *ptime
649	)
650{
651	if (!status)
652		return pps_simple(parseio, status, ptime);
653
654	return CVT_NONE;
655}
656
657/*
658 * timepacket
659 *
660 * process a data packet
661 */
662static u_long
663timepacket(
664	register parse_t *parseio
665	)
666{
667	register unsigned short format;
668	register time_t t;
669	u_long cvtrtc;		/* current conversion result */
670	clocktime_t clock_time;
671
672	memset((char *)&clock_time, 0, sizeof clock_time);
673	format = parseio->parse_lformat;
674
675	if (format == (unsigned short)~0)
676		return CVT_NONE;
677
678	switch ((cvtrtc = clockformats[format]->convert ?
679		 clockformats[format]->convert((unsigned char *)parseio->parse_ldata, parseio->parse_ldsize, (struct format *)(clockformats[format]->data), &clock_time, parseio->parse_pdata) :
680		 CVT_NONE) & CVT_MASK)
681	{
682	case CVT_FAIL:
683		parseio->parse_badformat++;
684		break;
685
686	case CVT_NONE:
687		/*
688		 * too bad - pretend bad format
689		 */
690		parseio->parse_badformat++;
691		break;
692
693	case CVT_OK:
694		break;
695
696	case CVT_SKIP:
697		return CVT_NONE;
698
699	default:
700		/* shouldn't happen */
701#ifndef PARSEKERNEL
702		msyslog(LOG_WARNING, "parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
703#endif
704		return CVT_FAIL|cvtrtc;
705	}
706
707	if ((t = parse_to_unixtime(&clock_time, &cvtrtc)) == -1)
708	{
709		return CVT_FAIL|cvtrtc;
710	}
711
712	/*
713	 * time stamp
714	 */
715#ifdef PARSEKERNEL
716	parseio->parse_dtime.parse_time.tv.tv_sec  = t;
717	parseio->parse_dtime.parse_time.tv.tv_usec = clock_time.usecond;
718#else
719	parseio->parse_dtime.parse_time.fp.l_ui = t + JAN_1970;
720	TVUTOTSF(clock_time.usecond, parseio->parse_dtime.parse_time.fp.l_uf);
721#endif
722
723	parseio->parse_dtime.parse_format       = format;
724
725	return updatetimeinfo(parseio, clock_time.flags);
726}
727
728/*ARGSUSED*/
729int
730parse_timecode(
731	parsectl_t *dct,
732	parse_t    *parse
733	)
734{
735	dct->parsegettc.parse_state  = parse->parse_lstate;
736	dct->parsegettc.parse_format = parse->parse_lformat;
737	/*
738	 * move out current bad packet count
739	 * user program is expected to sum these up
740	 * this is not a problem, as "parse" module are
741	 * exclusive open only
742	 */
743	dct->parsegettc.parse_badformat = parse->parse_badformat;
744	parse->parse_badformat = 0;
745
746	if (parse->parse_ldsize <= PARSE_TCMAX)
747	{
748		dct->parsegettc.parse_count = parse->parse_ldsize;
749		memcpy(dct->parsegettc.parse_buffer, parse->parse_ldata, dct->parsegettc.parse_count);
750		return 1;
751	}
752	else
753	{
754		return 0;
755	}
756}
757
758
759/*ARGSUSED*/
760int
761parse_setfmt(
762	parsectl_t *dct,
763	parse_t    *parse
764	)
765{
766	if (dct->parseformat.parse_count <= PARSE_TCMAX)
767	{
768		if (dct->parseformat.parse_count)
769		{
770			register unsigned short i;
771
772			for (i = 0; i < nformats; i++)
773			{
774				if (!Strcmp(dct->parseformat.parse_buffer, clockformats[i]->name))
775				{
776					if (parse->parse_pdata)
777						FREE(parse->parse_pdata, parse->parse_plen);
778					parse->parse_pdata = 0;
779
780					parse->parse_plen = clockformats[i]->plen;
781
782					if (parse->parse_plen)
783					{
784						parse->parse_pdata = MALLOC(parse->parse_plen);
785						if (!parse->parse_pdata)
786						{
787							parseprintf(DD_PARSE, ("set format failed: malloc for private data area failed\n"));
788							return 0;
789						}
790						memset((char *)parse->parse_pdata, 0, parse->parse_plen);
791					}
792
793					if (parse->parse_data)
794						FREE(parse->parse_data, (unsigned)(parse->parse_dsize * 2 + 2));
795					parse->parse_ldata = parse->parse_data = 0;
796
797					parse->parse_dsize = clockformats[i]->length;
798
799					if (parse->parse_dsize)
800					{
801						parse->parse_data = (char*)MALLOC((unsigned)(parse->parse_dsize * 2 + 2));
802						if (!parse->parse_data)
803						{
804							if (parse->parse_pdata)
805								FREE(parse->parse_pdata, parse->parse_plen);
806							parse->parse_pdata = 0;
807
808							parseprintf(DD_PARSE, ("init failed: malloc for data area failed\n"));
809							return 0;
810						}
811					}
812
813
814					/*
815					 * leave room for '\0'
816					 */
817					parse->parse_ldata     = parse->parse_data + parse->parse_dsize + 1;
818
819					parse->parse_lformat  = i;
820
821					return 1;
822				}
823			}
824		}
825	}
826	return 0;
827}
828
829/*ARGSUSED*/
830int
831parse_getfmt(
832	parsectl_t *dct,
833	parse_t    *parse
834	)
835{
836	if (dct->parseformat.parse_format < nformats &&
837	    Strlen(clockformats[dct->parseformat.parse_format]->name) <= PARSE_TCMAX)
838	{
839		dct->parseformat.parse_count = Strlen(clockformats[dct->parseformat.parse_format]->name)+1;
840		memcpy(dct->parseformat.parse_buffer, clockformats[dct->parseformat.parse_format]->name, dct->parseformat.parse_count);
841		return 1;
842	}
843	else
844	{
845		return 0;
846	}
847}
848
849/*ARGSUSED*/
850int
851parse_setcs(
852	parsectl_t *dct,
853	parse_t    *parse
854	)
855{
856	parse->parse_ioflags &= ~PARSE_IO_CSIZE;
857	parse->parse_ioflags |= dct->parsesetcs.parse_cs & PARSE_IO_CSIZE;
858	return 1;
859}
860
861#else /* not (REFCLOCK && CLOCK_PARSE) */
862int parse_bs;
863#endif /* not (REFCLOCK && CLOCK_PARSE) */
864
865/*
866 * History:
867 *
868 * parse.c,v
869 * Revision 4.14  1999/11/28 09:13:52  kardel
870 * RECON_4_0_98F
871 *
872 * Revision 4.13  1999/02/28 11:50:20  kardel
873 * (timepacket): removed unecessary code
874 *
875 * Revision 4.12  1999/02/21 12:17:44  kardel
876 * 4.91f reconcilation
877 *
878 * Revision 4.11  1999/02/21 11:09:47  kardel
879 * unified debug output
880 *
881 * Revision 4.10  1998/12/20 23:45:30  kardel
882 * fix types and warnings
883 *
884 * Revision 4.9  1998/08/09 22:26:06  kardel
885 * Trimble TSIP support
886 *
887 * Revision 4.8  1998/06/14 21:09:39  kardel
888 * Sun acc cleanup
889 *
890 * Revision 4.7  1998/06/13 15:19:13  kardel
891 * fix mem*() to b*() function macro emulation
892 *
893 * Revision 4.6  1998/06/13 13:24:13  kardel
894 * printf fmt
895 *
896 * Revision 4.5  1998/06/13 13:01:10  kardel
897 * printf fmt
898 *
899 * Revision 4.4  1998/06/13 12:12:10  kardel
900 * bcopy/memcpy cleanup
901 * fix SVSV name clash
902 *
903 * Revision 4.3  1998/06/12 15:22:30  kardel
904 * fix prototypes
905 *
906 * Revision 4.2  1998/06/12 09:13:27  kardel
907 * conditional compile macros fixed
908 * printf prototype
909 *
910 * Revision 4.1  1998/05/24 09:39:55  kardel
911 * implementation of the new IO handling model
912 *
913 * Revision 4.0  1998/04/10 19:45:36  kardel
914 * Start 4.0 release version numbering
915 *
916 * from V3 3.46 log info deleted 1998/04/11 kardel
917 */
918