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