diff --git a/src/libCom/Makefile.Host b/src/libCom/Makefile.Host index b972adc6f..e2c35cb1b 100644 --- a/src/libCom/Makefile.Host +++ b/src/libCom/Makefile.Host @@ -58,7 +58,6 @@ LIBSRCS += fdmgr.c LIBSRCS += freeListLib.c LIBSRCS += gpHashLib.c LIBSRCS += memDebugLib.c -LIBSRCS += nextFieldSubr.c LIBSRCS += postfix.c LIBSRCS += realpath.c LIBSRCS += tsSubr.c @@ -99,9 +98,9 @@ LIBTYPE:=SHARED # libs needed for PROD and TESTPRODUCT PROD_LIBS = Com -# tsTest does not use the default tsTest.c: +#USR_CFLAGS += -DTS_TEST #TESTPROD_SRCS=tsSubr.c -#TESTPROD=tsTest +#TESTPROD=testtsLib #TESTPROD=envtest #TESTPROD=osiTimeTest fdManagerTest diff --git a/src/libCom/Makefile.Vx b/src/libCom/Makefile.Vx index 475fb0a7c..37638525c 100644 --- a/src/libCom/Makefile.Vx +++ b/src/libCom/Makefile.Vx @@ -13,7 +13,6 @@ SRCS.c += ../ellLib.c SRCS.c += ../envSubr.c SRCS.c += envData.c SRCS.c += ../errSymLib.c -SRCS.c += ../nextFieldSubr.c SRCS.c += ../postfix.c SRCS.c += ../bucketLib.c SRCS.c += ../memDebugLib.c @@ -41,7 +40,6 @@ LIBOBJS += envData.o LIBOBJS += envSubr.o LIBOBJS += errSymLib.o LIBOBJS += errSymTbl.o -LIBOBJS += nextFieldSubr.o LIBOBJS += postfix.o LIBOBJS += bucketLib.o LIBOBJS += tsSubr.o diff --git a/src/libCom/nextFieldSubr.c b/src/libCom/nextFieldSubr.c deleted file mode 100644 index ac95d4be7..000000000 --- a/src/libCom/nextFieldSubr.c +++ /dev/null @@ -1,369 +0,0 @@ -/* $Id$ - * Author: Roger A. Cole - * Date: 10-10-90 - * - * Experimental Physics and Industrial Control System (EPICS) - * - * Copyright 1991-92, the Regents of the University of California, - * and the University of Chicago Board of Governors. - * - * This software was produced under U.S. Government contracts: - * (W-7405-ENG-36) at the Los Alamos National Laboratory, - * and (W-31-109-ENG-38) at Argonne National Laboratory. - * - * Initial development by: - * The Controls and Automation Group (AT-8) - * Ground Test Accelerator - * Accelerator Technology Division - * Los Alamos National Laboratory - * - * Co-developed with - * The Controls and Computing Group - * Accelerator Systems Division - * Advanced Photon Source - * Argonne National Laboratory - * - * Modification Log: - * ----------------- - * .00 10-10-90 rac initial version - * .01 06-18-91 rac installed in SCCS - * .02 06-25-91 rac changed nextNonSpaceField to handle quotes - * .03 03-04-92 rac fixed nextNonSpaceField to honor '\0' - * - * make options - * -DvxWorks makes a version for VxWorks - * -DNDEBUG don't compile assert() checking - * -DDEBUG compile various debug code, including checks on - * malloc'd memory - */ -/*+/mod*********************************************************************** -* TITLE nextFieldSubr.c - text field scanning routines -* -* GENERAL DESCRIPTION -* The routines in this module provide for scanning fields in text -* strings. They can be used as the basis for parsing text input -* to a program. -* -* QUICK REFERENCE -* char (*pText)[]; -* char (*pField)[]; -* char *pDelim; -* -* int nextAlphField( <>pText, >pField, >pDelim ) -* int nextAlph1UCField( <>pText, >pField, >pDelim ) -* int nextANField( <>pText, >pField, >pDelim ) -* int nextChanNameField( <>pText, >pField, >pDelim ) -* int nextFltField( <>pText, >pField, >pDelim ) -* int nextFltFieldAsDbl( <>pText, >pDblVal, >pDelim ) -* int nextIntField( <>pText, >pField, >pDelim ) -* int nextIntFieldAsInt( <>pText, >pIntVal, >pDelim ) -* int nextIntFieldAsLong( <>pText, >pLongVal, >pDelim ) -* int nextNonSpace( <>pText ) -* int nextNonSpaceField( <>pText, >pField, >pDelim ) -* -* DESCRIPTION -* The input text string is scanned to identify the beginning and -* end of a field. At return, the input pointer points to the character -* following the delimiter and the delimiter has been returned through -* its pointer; the field contents are `returned' either as a pointer -* to the first character of the field or as a value returned through -* a pointer. -* -* For nextNonSpaceField, the field is defined as either: -* 1) from the next non-white-space character up to the following -* white-space character, or -* 2) from a " through a matching ", with the "s not being included -* in the characters returned to the caller. The delimeter will -* be the character following the closing ". -* -* In the input string, a '\0' is stored in place of the delimiter, -* so that standard string handling tools can be used for text fields. -* -* nextAlphField scans the next alphabetic field -* nextAlph1UCField scans the next alphabetic field, changes -* the first character to upper case, and -* changes the rest to lower case -* nextANField scans the next alpha-numeric field -* nextChanNameField scans the next field as a channel name, -* delimited by white space or a comma -* nextFltField scans the next float field -* nextFltFieldAsDbl scans the next float field as a double -* nextIntField scans the next integer field -* nextIntFieldAsInt scans the next integer field as an int -* nextIntFieldAsLong scans the next integer field as a long -* nextNonSpace scans to the next non-space character -* nextNonSpaceField scans the next field, delimited by white -* space -* -* RETURNS -* count of characters in field, including the delimiter. A special -* case exists when only '\0' is encountered; in this case 0 is returned. -* (For quoted alpha strings, the count will include the " characters.) -* -* BUGS -* o use of type checking macros isn't protected by isascii() -* o for nextANField, with string in quotes: embedded " isn't handled; -* if " isn't encountered before end of line, no error message is -* generated. -* -* SEE ALSO -* tsTextToStamp() -* -* NOTES -* 1. fields involving alpha types consider underscore ('_') to be -* alphabetic. -* -* EXAMPLE -* char text[]="process 30 samples" -* char *pText; pointer into text string -* char *pCmd; pointer to first field, to use as a command -* int count; value of second field, number of items to process -* char *pUnits; pointer to third field, needed for command processing -* int length; length of field -* char delim; delimiter for field -* -* pText = text; -* if (nextAlphField(&pText, &pCmd, &delim) <= 1) -* error action if empty field -* if (nextIntFieldAsInt(&pText, &count, &delim) <= 1) -* error action if empty field -* if (nextAlphField(&pText, &pUnits, &delim) <= 1) -* error action if empty field -* printf("command=%s, count=%d, units=%s\n", pCmd, count, pUnits); -* -*-***************************************************************************/ -#ifdef vxWorks -# include -# include -# include -#else -# include -# include -#endif - -#define epicsExportSharedSymbols -#include "epicsAssert.h" -#include "tsDefs.h" - - -/*----------------------------------------------------------------------------- -* the preamble skips over leading white space, stopping either at -* end of string or at a non-white-space character. If EOS is encountered, -* then the appropriate return conditions are set up and a return 0 is done. -*----------------------------------------------------------------------------*/ -#define NEXT_PREAMBLE \ - char *pDlm; /* pointer to field delimiter */ \ - int count; /* count of characters (plus delim) */ \ - \ - assert(ppText != NULL); \ - assert(*ppText != NULL); \ - assert(ppField != NULL); \ - assert(pDelim != NULL); \ - \ - if (**ppText == '\0') { \ - *ppField = *ppText; \ - *pDelim = **ppText; \ - return 0; \ - } \ - while (**ppText != '\0' && isspace(**ppText)) \ - (*ppText)++; /* skip leading white space */ \ - pDlm = *ppField = *ppText; \ - if (*pDlm == '\0') { \ - *pDelim = **ppText; \ - return 0; \ - } \ - count = 1; /* include delimiter in count */ -/*----------------------------------------------------------------------------- -* the postamble is called for each character in the field. It moves -* the pointers and increments the count. If the loop terminates, then -* the caller is informed of the delimiter and the source character -* string has '\0' inserted in place of the delimiter, so that the field -* is now a proper '\0' terminated string. -*----------------------------------------------------------------------------*/ -#define NEXT_POSTAMBLE \ - pDlm++; \ - count++; \ - } \ - *pDelim = *pDlm; \ - *ppText = pDlm; \ - if (*pDlm != '\0') { \ - (*ppText)++; /* point to next available character */ \ - *pDlm = '\0'; \ - } - -int -epicsShareAPI nextAlphField(ppText, ppField, pDelim) -char **ppText; /* I/O pointer to pointer to text to scan */ -char **ppField; /* O pointer to pointer to field */ -char *pDelim; /* O pointer to return field's delimiter */ -{ - NEXT_PREAMBLE - while (isalpha(*pDlm) || *pDlm == '_') { - NEXT_POSTAMBLE - return count; -} -epicsShareFunc int epicsShareAPI -nextAlph1UCField(ppText, ppField, pDelim) -char **ppText; /* I/O pointer to pointer to text to scan */ -char **ppField; /* O pointer to pointer to field */ -char *pDelim; /* O pointer to return field's delimiter */ -{ - NEXT_PREAMBLE - while (isalpha(*pDlm) || *pDlm == '_') { - if (count == 1) { - if (islower(*pDlm)) - *pDlm = toupper(*pDlm); - } - else { - if (isupper(*pDlm)) - *pDlm = tolower(*pDlm); - } - NEXT_POSTAMBLE - return count; -} -int -epicsShareAPI nextANField(ppText, ppField, pDelim) -char **ppText; /* I/O pointer to pointer to text to scan */ -char **ppField; /* O pointer to pointer to field */ -char *pDelim; /* O pointer to return field's delimiter */ -{ - NEXT_PREAMBLE - while (isalnum(*pDlm) || *pDlm == '_') { - NEXT_POSTAMBLE - return count; -} -int -epicsShareAPI nextChanNameField(ppText, ppField, pDelim) -char **ppText; /* I/O pointer to pointer to text to scan */ -char **ppField; /* O pointer to pointer to field */ -char *pDelim; /* O pointer to return field's delimiter */ -{ - NEXT_PREAMBLE - while (!isspace(*pDlm)) { - if (*pDlm == '\0') - break; - else if (*pDlm == ',') - break; - NEXT_POSTAMBLE - return count; -} -int -epicsShareAPI nextFltField(ppText, ppField, pDelim) -char **ppText; /* I/O pointer to pointer to text to scan */ -char **ppField; /* O pointer to pointer to field */ -char *pDelim; /* O pointer to return field's delimiter */ -{ - NEXT_PREAMBLE - while (isdigit(*pDlm) || *pDlm=='-' || *pDlm=='+' || *pDlm=='.') { - NEXT_POSTAMBLE - return count; -} -int -epicsShareAPI nextFltFieldAsDbl(ppText, pDblVal, pDelim) -char **ppText; /* I/O pointer to pointer to text to scan */ -double *pDblVal; /* O pointer to return field's value */ -char *pDelim; /* O pointer to return field's delimiter */ -{ - char *pField; /* pointer to field */ - int count; /* count of char in field, including delim */ - - assert(pDblVal != NULL); - - count = nextFltField(ppText, &pField, pDelim); - if (count > 1) { - if (sscanf(pField, "%lf", pDblVal) != 1) - assert(0); - } - - return count; -} -int -epicsShareAPI nextIntField(ppText, ppField, pDelim) -char **ppText; /* I/O pointer to pointer to text to scan */ -char **ppField; /* O pointer to pointer to field */ -char *pDelim; /* O pointer to return field's delimiter */ -{ - NEXT_PREAMBLE - while (isdigit(*pDlm) || ((*pDlm=='-' || *pDlm=='+') && count==1)) { - NEXT_POSTAMBLE - return count; -} -epicsShareFunc int epicsShareAPI -nextIntFieldAsInt(ppText, pIntVal, pDelim) -char **ppText; /* I/O pointer to pointer to text to scan */ -int *pIntVal; /* O pointer to return field's value */ -char *pDelim; /* O pointer to return field's delimiter */ -{ - char *pField; /* pointer to field */ - int count; /* count of char in field, including delim */ - - assert(pIntVal != NULL); - - count = nextIntField(ppText, &pField, pDelim); - if (count > 1) { - if (sscanf(pField, "%d", pIntVal) != 1) - assert(0); - } - - return count; -} -epicsShareFunc int epicsShareAPI -nextIntFieldAsLong(ppText, pLongVal, pDelim) -char **ppText; /* I/O pointer to pointer to text to scan */ -long *pLongVal; /* O pointer to return field's value */ -char *pDelim; /* O pointer to return field's delimiter */ -{ - char *pField; /* pointer to field */ - int count; /* count of char in field, including delim */ - - assert(pLongVal != NULL); - - count = nextIntField(ppText, &pField, pDelim); - if (count > 1) { - if (sscanf(pField, "%ld", pLongVal) != 1) - assert(0); - } - - return count; -} -int -epicsShareAPI nextNonSpace(ppText) -char **ppText; /* I/O pointer to pointer to text to scan */ -{ - while (isspace(**ppText)) - (*ppText)++; - return 0; -} -int -epicsShareAPI nextNonSpaceField(ppText, ppField, pDelim) -char **ppText; /* I/O pointer to pointer to text to scan */ -char **ppField; /* O pointer to pointer to field */ -char *pDelim; /* O pointer to return field's delimiter */ -{ - NEXT_PREAMBLE - if (**ppField == '"') { - (*ppField)++; /* skip over leading double quote */ - count++; - pDlm++; - while (*pDlm != '"') { /* scan until find another " */ - NEXT_POSTAMBLE - if (*pDelim == '"') { - pDlm++; - while (isspace(*pDlm)) - pDlm++; /* skip trailing white space */ - if (*pDlm == '"') - pDlm--; - *pDelim = *pDlm; /* give caller the delim and set */ - *ppText = pDlm; /* for scanning the next field */ - if (*pDlm != '\0') - (*ppText)++; - } - } - else { - while (!isspace(*pDlm)) { - if (*pDlm == '\0') - break; - NEXT_POSTAMBLE - } - return count; -} diff --git a/src/libCom/tsDefs.h b/src/libCom/tsDefs.h index 908f4aa18..a702f9f04 100644 --- a/src/libCom/tsDefs.h +++ b/src/libCom/tsDefs.h @@ -31,6 +31,8 @@ * .02 06-18-91 rac installed in SCCS * .03 08-03-92 rac added tsRound routines * .04 07-02-96 joh added ANSI prototypes + * .05 02-05-98 mrk move pvt stuff to source file; + * TsAddDouble just invokes tsAddDouble * */ /*+/mod*********************************************************************** @@ -49,12 +51,13 @@ * 1990 are treated as `dateless' times. * *-***************************************************************************/ - + #ifdef __cplusplus extern "C" { #endif #include "shareLib.h" +#include "epicsTypes.h" #include "errMdef.h" /* get M_ts for this subsystem's `number' */ @@ -72,8 +75,8 @@ extern "C" { *---------------------------------------------------------------------------- */ typedef struct { - unsigned secPastEpoch; /* seconds since 0000 Jan 1, 1990 */ - unsigned nsec; /* nanoseconds within second */ + epicsUInt32 secPastEpoch; /* seconds since 0000 Jan 1, 1990 */ + epicsUInt32 nsec; /* nanoseconds within second */ } TS_STAMP; /*---------------------------------------------------------------------------- @@ -89,146 +92,16 @@ enum tsTextType{ TS_TEXT_MMDDYY }; -/*/subhead configuration------------------------------------------------------ -* C O N F I G U R A T I O N D E F I N I T I O N S -* -* TS_DST_BEGIN the day number for starting DST -* TS_DST_END the day number for ending DST -* TS_MIN_WEST the number of minutes west of GMT for time zone (zones east -* will have negative values) -* TS_DST_HOUR_ON the hour (standard time) when DST turns on -* TS_DST_HOUR_OFF the hour (standard time) when DST turns off -* TS_DST_HRS_ADD hours to add when DST is on -* -* day numbers start with 0 for Jan 1; day numbers in these defines are -* based on a NON-leap year. The start and end days for DST are assumed to be -* Sundays. A negative day indicates that the following Sunday is to be used; -* a positive day indicates the prior Sunday. If the begin date is larger than -* the end date, then DST overlaps the change of the year (e.g., for southern -* hemisphere). -* -* Note well that TS_DST_HOUR_ON and TS_DST_HOUR_OFF are both STANDARD time. -* So, if dst begins at 2 a.m. (standard time) and ends at 2 a.m. (daylight -* time), the two values would be 2 and 1, respectively (assuming -* TS_DST_HRS_ADD is 1). -*----------------------------------------------------------------------------*/ -#define TS_DST_BEGIN -90 /* first Sun in Apr (Apr 1 = 90) */ -#define TS_DST_END 303 /* last Sun in Oct (Oct 31 = 303) */ -#define TS_DST_HOUR_ON 2 /* 2 a.m. (standard time) */ -#define TS_DST_HOUR_OFF 1 /* 2 a.m. (1 a.m. standard time) */ -#define TS_DST_HRS_ADD 1 /* add one hour */ -#define TS_MIN_WEST 7 * 60 /* USA mountain time zone */ -#if 0 /* first set is for testing only */ -#define TS_EPOCH_YEAR 1989 -#define TS_EPOCH_SEC_PAST_1970 6940*86400 /* 1/1/89 19 yr (5 leap) of seconds */ -#define TS_EPOCH_WDAY_NUM 0 /* Jan 1 1989 was Sun (wkday num = 0) */ -#else -#define TS_EPOCH_YEAR 1990 -#define TS_EPOCH_SEC_PAST_1970 7305*86400 /* 1/1/90 20 yr (5 leap) of seconds */ -#define TS_EPOCH_WDAY_NUM 1 /* Jan 1 1990 was Mon (wkday num = 1) */ -#endif -#define TS_MAX_YEAR TS_EPOCH_YEAR+134 /* ULONG can handle 135 years */ -#define TS_TRUNC 1000000 /* truncate to milli-second significance */ - -/*/subhead struct tsDetail---------------------------------------------------- -* breakdown structure for working with secPastEpoch -*----------------------------------------------------------------------------*/ - -struct tsDetail { - int year; /* 4 digit year */ - int dayYear; /* day number in year; 0 = Jan 1 */ - int monthNum; /* month number; 0 = Jan */ - int dayMonth; /* day number; 0 = 1st of month */ - int hours; /* hours within day */ - int minutes; /* minutes within hour */ - int seconds; /* seconds within minute */ - int dayOfWeek; /* weekday number; 0 = Sun */ - int leapYear; /* (0, 1) for year (isn't, is) a leap year */ - char dstOverlapChar; /* indicator for distinguishing duplicate - times in the `switch to standard' time period: - ':'--time isn't ambiguous; - 's'--time is standard time - 'd'--time is daylight time */ -}; - -/*/subhead status codes------------------------------------------------------- -* S T A T U S C O D E S -* -* -* Status codes for time stamp routines have the form S_ts_briefMessage . -* -* The macro TsStatusToText(stat) can be used to obtain a text string -* corresponding to a status code. -* -*----------------------------------------------------------------------------*/ -#define S_ts_OK 0 -#define S_ts_sysTimeError (M_ts|1| 1<<1) /* error getting system time */ -#define S_ts_badTextCode (M_ts|1| 2<<1) /* invalid TS_TEXT_xxx code */ -#define S_ts_inputTextError (M_ts|1| 3<<1) /* error in text date or time */ -#define S_ts_timeSkippedDST (M_ts|1| 4<<1) /* time skipped on switch to DST */ -#define S_ts_badRoundInterval (M_ts|1| 5<<1) /* invalid rounding interval */ - -#define TS_S_PAST 6 /* one past last legal code */ - -#define TsStatusToIndex(status) \ - ( ((status&0xffff0000)!=M_ts) \ - ? TS_S_PAST \ - : ( (((status&0xffff)>>1)>=TS_S_PAST) \ - ? TS_S_PAST \ - : ((status&0xffff)>>1) \ - ) \ - ) - -#define TsStatusToText(status) \ - (glTsStatText[TsStatusToIndex(status)]) - -#ifndef TS_PRIVATE_DATA - epicsShareExtern char *glTsStatText[7]; -#else - epicsShareDef char *glTsStatText[] = { - /* S_ts_OK */ "success", - /* S_ts_sysTimeError */ "error getting system time", - /* S_ts_badTextCode */ "invalid TS_TEXT_xxx code", - /* S_ts_inputTextError */ "error in text date or time", - /* S_ts_timeSkippedDST */ "time skipped on switch to DST", - /* S_ts_badRoundInterval */ "rounding interval is invalid", - - /* TS_S_PAST */ "illegal TS status code", - }; -#endif - /*/subhead macros------------------------------------------------------------- * arithmetic macros -* * care has been taken in writing these macros that the result can * be stored back into either operand time stamp. For example, * both of the following (as well as other variations) are legal: * TsAddStamps(pS1, pS1, pS2); * TsDiffAsStamp(pS2, pS1, pS2); *----------------------------------------------------------------------------*/ -#if _WIN32 /* the microsoft compiler will not compile the TsAddDouble() macro */ +/*TsAddDouble was originally a macro. Now just use tsAddDouble */ #define TsAddDouble(pSum, pS1, dbl) tsAddDouble(pSum, pS1, dbl) -#else /*_WIN32*/ -#define TsAddDouble(pSum, pS1, dbl) \ - (void)( \ - dbl >= 0. \ - ? ((*pSum).secPastEpoch = (*pS1).secPastEpoch + (unsigned long)dbl, \ - ( ((*pSum).nsec = (*pS1).nsec + (unsigned long)(1000000000. * \ - (dbl - (double)((unsigned long)dbl))) ) ) \ - >= 1000000000 \ - ?((*pSum).secPastEpoch += (*pSum).nsec/1000000000, \ - (*pSum).nsec %= 1000000000) \ - :0) \ - : ((*pSum).secPastEpoch = (*pS1).secPastEpoch - (unsigned long)(-dbl), \ - (*pS1).nsec >= (unsigned long)(1000000000. * \ - ((-dbl) - (double)((unsigned long)(-dbl)))) \ - ?( (*pSum).nsec = (*pS1).nsec - (unsigned long)(1000000000.* \ - ((-dbl) - (double)((unsigned long)(-dbl))))) \ - :( (*pSum).nsec = (*pS1).nsec + 1000000000 - \ - (unsigned long)(1000000000.* \ - ((-dbl) - (double)((unsigned long)(-dbl)))), \ - (*pSum).secPastEpoch -= 1) ) ) -#endif /*_WIN32*/ #define TsAddStamps(pSum, pS1, pS2) \ (void)( \ ((*pSum).secPastEpoch = (*pS1).secPastEpoch + (*pS2).secPastEpoch) , \ @@ -281,30 +154,13 @@ struct tsDetail { :((((*pS1).secPastEpoch) == ((*pS2).secPastEpoch)) \ ?((((*pS1).nsec) <= ((*pS2).nsec)) ? 1 : 0) \ :0 )) + /*---------------------------------------------------------------------------- * `prototypes' *----------------------------------------------------------------------------*/ #if defined(__STDC__) || defined(__cplusplus) -epicsShareFunc int epicsShareAPI nextIntFieldAsInt( -char **ppText, /* I/O pointer to pointer to text to scan */ -int *pIntVal, /* O pointer to return field's value */ -char *pDelim /* O pointer to return field's delimiter */ -); - -epicsShareFunc int epicsShareAPI nextAlph1UCField( -char **ppText, /* I/O pointer to pointer to text to scan */ -char **ppField, /* O pointer to pointer to field */ -char *pDelim /* O pointer to return field's delimiter */ -); - -epicsShareFunc int epicsShareAPI nextIntFieldAsLong( -char **ppText, /* I/O pointer to pointer to text to scan */ -long *pLongVal, /* O pointer to return field's value */ -char *pDelim /* O pointer to return field's delimiter */ -); - epicsShareFunc long epicsShareAPI tsLocalTime (TS_STAMP *pStamp); epicsShareFunc void epicsShareAPI tsAddDouble( @@ -346,9 +202,6 @@ char **pText /* IO ptr to ptr to string containing time and date */ #else /* !defined(__STDC__) && !defined(__cplusplus) */ -epicsShareFunc int epicsShareAPI nextIntFieldAsInt(); -epicsShareFunc int epicsShareAPI nextAlph1UCField(); -epicsShareFunc int epicsShareAPI nextIntFieldAsLong(); epicsShareFunc long epicsShareAPI tsLocalTime (); epicsShareFunc void epicsShareAPI tsAddDouble(); epicsShareFunc int epicsShareAPI tsCmpStamps(); @@ -358,10 +211,56 @@ epicsShareFunc char * epicsShareAPI tsStampToText(); epicsShareFunc long epicsShareAPI tsTextToStamp(); epicsShareFunc long epicsShareAPI tsTimeTextToStamp(); -#endif +#endif /*defined(__STDC__) || defined(__cplusplus) */ + +/*/subhead status codes------------------------------------------------------- +* S T A T U S C O D E S +* +* +* Status codes for time stamp routines have the form S_ts_briefMessage . +* +* The macro TsStatusToText(stat) can be used to obtain a text string +* corresponding to a status code. +* +*----------------------------------------------------------------------------*/ +#define S_ts_OK 0 +#define S_ts_sysTimeError (M_ts|1| 1<<1) /* error getting system time */ +#define S_ts_badTextCode (M_ts|1| 2<<1) /* invalid TS_TEXT_xxx code */ +#define S_ts_inputTextError (M_ts|1| 3<<1) /* error in text date or time */ +#define S_ts_timeSkippedDST (M_ts|1| 4<<1) /* time skipped on switch to DST */ +#define S_ts_badRoundInterval (M_ts|1| 5<<1) /* invalid rounding interval */ + +#define TS_S_PAST 6 /* one past last legal code */ + +#define TsStatusToIndex(status) \ + ( ((status&0xffff0000)!=M_ts) \ + ? TS_S_PAST \ + : ( (((status&0xffff)>>1)>=TS_S_PAST) \ + ? TS_S_PAST \ + : ((status&0xffff)>>1) \ + ) \ + ) + +#define TsStatusToText(status) \ + (glTsStatText[TsStatusToIndex(status)]) + +#ifndef TS_PRIVATE_DATA + epicsShareExtern char *glTsStatText[7]; +#else + epicsShareDef char *glTsStatText[] = { + /* S_ts_OK */ "success", + /* S_ts_sysTimeError */ "error getting system time", + /* S_ts_badTextCode */ "invalid TS_TEXT_xxx code", + /* S_ts_inputTextError */ "error in text date or time", + /* S_ts_timeSkippedDST */ "time skipped on switch to DST", + /* S_ts_badRoundInterval */ "rounding interval is invalid", + + /* TS_S_PAST */ "illegal TS status code", + }; +#endif /*TS_PRIVATE_DATA*/ #ifdef __cplusplus } -#endif +#endif /*__cplusplus */ -#endif +#endif /*INC_tsDefs_h*/ diff --git a/src/libCom/tsSubr.c b/src/libCom/tsSubr.c index 0c70b063d..1bd8db7d6 100644 --- a/src/libCom/tsSubr.c +++ b/src/libCom/tsSubr.c @@ -45,12 +45,12 @@ * so I left TsAddDouble() code - which works - * at the end of the file but commented it out * - * make options - * -DvxWorks makes a version for VxWorks - * -DNDEBUG don't compile assert() checking - * -DDEBUG compile various debug code, including checks on * malloc'd memory + * .05 02-05-98 mrk move pvt stuff from tsDefs.h to here + * move code for nextXXX to here and made static + * use standard C function declarations everywhere */ + /*+/mod*********************************************************************** * TITLE tsSubr.c - time stamp routines * @@ -113,17 +113,19 @@ * (Some of the code here is based somewhat on time-related routines from * 4.2 BSD.) *-***************************************************************************/ - + #include #include #include #include +#define TS_PRIVATE_DATA +#include "tsDefs.h" +#undef TS_PRIVATE_DATA #if defined(vxWorks) # include -# include -# include +# include "drvTS.h" #elif defined(VMS) # include # include @@ -139,13 +141,78 @@ #define epicsExportSharedSymbols #include "epicsAssert.h" #include "envDefs.h" -#define TS_PRIVATE_DATA -#include "tsDefs.h" + +/*/subhead configuration------------------------------------------------------ +* C O N F I G U R A T I O N D E F I N I T I O N S +* +* TS_DST_BEGIN the day number for starting DST +* TS_DST_END the day number for ending DST +* TS_MIN_WEST the number of minutes west of GMT for time zone (zones east +* will have negative values) +* TS_DST_HOUR_ON the hour (standard time) when DST turns on +* TS_DST_HOUR_OFF the hour (standard time) when DST turns off +* TS_DST_HRS_ADD hours to add when DST is on +* +* day numbers start with 0 for Jan 1; day numbers in these defines are +* based on a NON-leap year. The start and end days for DST are assumed to be +* Sundays. A negative day indicates that the following Sunday is to be used; +* a positive day indicates the prior Sunday. If the begin date is larger than +* the end date, then DST overlaps the change of the year (e.g., for southern +* hemisphere). +* +* Note well that TS_DST_HOUR_ON and TS_DST_HOUR_OFF are both STANDARD time. +* So, if dst begins at 2 a.m. (standard time) and ends at 2 a.m. (daylight +* time), the two values would be 2 and 1, respectively (assuming +* TS_DST_HRS_ADD is 1). +*----------------------------------------------------------------------------*/ +#define TS_DST_BEGIN -90 /* first Sun in Apr (Apr 1 = 90) */ +#define TS_DST_END 303 /* last Sun in Oct (Oct 31 = 303) */ +#define TS_DST_HOUR_ON 2 /* 2 a.m. (standard time) */ +#define TS_DST_HOUR_OFF 1 /* 2 a.m. (1 a.m. standard time) */ +#define TS_DST_HRS_ADD 1 /* add one hour */ +#define TS_MIN_WEST 7 * 60 /* USA mountain time zone */ +#if 0 /* first set is for testing only */ +#define TS_EPOCH_YEAR 1989 +#define TS_EPOCH_SEC_PAST_1970 6940*86400 /* 1/1/89 19 yr (5 leap) of seconds */ +#define TS_EPOCH_WDAY_NUM 0 /* Jan 1 1989 was Sun (wkday num = 0) */ +#else +#define TS_EPOCH_YEAR 1990 +#define TS_EPOCH_SEC_PAST_1970 7305*86400 /* 1/1/90 20 yr (5 leap) of seconds */ +#define TS_EPOCH_WDAY_NUM 1 /* Jan 1 1990 was Mon (wkday num = 1) */ +#endif +#define TS_MAX_YEAR TS_EPOCH_YEAR+134 /* ULONG can handle 135 years */ +#define TS_TRUNC 1000000 /* truncate to milli-second significance */ -static void tsStampFromLocal(); -static void tsStampToLocal(); -static void tsStampToLocalZone(); -static void tsInitMinWest(); +/*/subhead struct tsDetail---------------------------------------------------- +* breakdown structure for working with secPastEpoch +*----------------------------------------------------------------------------*/ + +struct tsDetail { + int year; /* 4 digit year */ + int dayYear; /* day number in year; 0 = Jan 1 */ + int monthNum; /* month number; 0 = Jan */ + int dayMonth; /* day number; 0 = 1st of month */ + int hours; /* hours within day */ + int minutes; /* minutes within hour */ + int seconds; /* seconds within minute */ + int dayOfWeek; /* weekday number; 0 = Sun */ + int leapYear; /* (0, 1) for year (isn't, is) a leap year */ + char dstOverlapChar; /* indicator for distinguishing duplicate + times in the `switch to standard' time period: + ':'--time isn't ambiguous; + 's'--time is standard time + 'd'--time is daylight time */ +}; + +static void tsStampFromLocal(TS_STAMP *pStamp, struct tsDetail *pT); +static void tsStampToLocal(TS_STAMP stamp,struct tsDetail *pT); +static void tsStampToLocalZone(TS_STAMP *pStamp,struct tsDetail *pT); +static void tsInitMinWest(void); + +static int nextAlph1UCField(char **ppText,char **ppField, char *pDelim); +static int nextIntField(char **ppText,char **ppField,char *pDelim); +static int nextIntFieldAsInt(char **ppText,int *pIntVal,char *pDelim); +static int nextIntFieldAsLong(char **ppText,long *pLongVal,char *pDelim); static int needToInitMinWest=1; static long tsMinWest=TS_MIN_WEST; @@ -154,6 +221,7 @@ static int daysInMonth[] = {31,28,31,30, 31, 30, 31, 31, 30, 31, 30, 31}; static int dayYear1stOfMon[] = {0,31,59,90,120,151,181,212,243,273,304,334}; static char monthText[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; +#define TEXTBUFSIZE 40 /*/subhead tsTest------------------------------------------------------------- * some test code. @@ -161,12 +229,13 @@ static char monthText[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; #if TS_TEST || LINT # ifndef vxWorks - main() +int tsTest(void); +int main() { return tsTest(); } # endif -tsTest() +int tsTest() { long stat; double dbl; @@ -326,9 +395,9 @@ tsTest() (void)printf("%s\n", TsStatusToText(stat)); } else { - (void)printf("stamp is: %lu %lu %s\n", inStamp.secPastEpoch, + (void)printf("stamp is: %u %u %s\n", inStamp.secPastEpoch, inStamp.nsec, - tsStampToText(&inStamp, TS_TEXT_MMDDYY, inStampTxt)); + tsStampToText(&inStamp, TS_TEXT_MONDDYYYY, inStampTxt)); } } } @@ -349,8 +418,7 @@ tsTest() * void * *-*/ -void -date() +void date() { TS_STAMP now; char nowText[32]; @@ -372,12 +440,11 @@ date() * day of year for the desired Sunday * *-*/ -static -int sunday(day, leap, dayYear, dayOfWeek) -int day; /* I day of year to find closest Sunday */ -int leap; /* I 0, 1 for not leap year, leap year, respectively */ -int dayYear; /* I known day of year */ -int dayOfWeek; /* I day of week for dayYear */ +static int sunday( +int day, /* I day of year to find closest Sunday */ +int leap, /* I 0, 1 for not leap year, leap year, respectively */ +int dayYear, /* I known day of year */ +int dayOfWeek) /* I day of week for dayYear */ { int offset; /* controls direction of offset */ @@ -458,10 +525,7 @@ int dayOfWeek; /* I day of week for dayYear */ * *-*/ void epicsShareAPI -tsAddDouble(pSum, pStamp, dbl) -TS_STAMP *pSum; /* O sum time stamp */ -TS_STAMP *pStamp; /* I addend time stamp */ -double dbl; /* I number of seconds to add */ +tsAddDouble(TS_STAMP *pSum,TS_STAMP *pStamp, double dbl) { TS_STAMP stamp; /* stamp equivalent of the double */ @@ -523,10 +587,7 @@ double dbl; /* I number of seconds to add */ * printf("first stamp is later\n"); * *-*/ -int epicsShareAPI -tsCmpStamps(pStamp1, pStamp2) -TS_STAMP *pStamp1; /* pointer to first stamp */ -TS_STAMP *pStamp2; /* pointer to second stamp */ +int epicsShareAPI tsCmpStamps(TS_STAMP *pStamp1,TS_STAMP *pStamp2) { if (pStamp1->secPastEpoch < pStamp2->secPastEpoch) return -1; @@ -599,51 +660,54 @@ TS_STAMP *pStamp2; /* pointer to second stamp */ *-*/ long epicsShareAPI tsLocalTime(TS_STAMP *pStamp) { - long retStat=S_ts_OK;/* return status to caller */ + long retStat=S_ts_OK;/* return status to caller */ + + assert(pStamp != NULL); + + { +# if defined(vxWorks) + retStat = TScurrentTimeStamp((struct timespec*)pStamp); + if (retStat == 0) { + return S_ts_OK; + } + else { + return S_ts_sysTimeError; + } +# elif defined(_WIN32) + DWORD win_sys_time_ms; /* time (ms) since windows started */ + static DWORD prev_time_ms; /* time (ms) of previous call */ + static long start_time_s=0; /* time (sec) from 1990 when windows started */ + + if (start_time_s == 0) { + prev_time_ms = timeGetTime(); + start_time_s = time(NULL) + - TS_EPOCH_SEC_PAST_1970 - (long)prev_time_ms/1000; + } + win_sys_time_ms = timeGetTime(); + if (prev_time_ms > win_sys_time_ms) { + /* must have been a timer roll-over */ + + start_time_s += 4294967; /* add number of seconds in 49.7 days */ + } + /* time (sec) since 1990 */ + pStamp->secPastEpoch = (long)win_sys_time_ms/1000 + start_time_s; + pStamp->nsec = (long)((win_sys_time_ms % 1000) * 1000000); + prev_time_ms = win_sys_time_ms; +# else + struct timeval curtime; assert(pStamp != NULL); - - { -# if defined(vxWorks) - retStat = TScurrentTimeStamp((struct timespec*)pStamp); - if (retStat == 0) { - return S_ts_OK; - } - else { - return S_ts_sysTimeError; - } -# elif defined(_WIN32) - DWORD win_sys_time_ms; /* time (ms) since windows started */ - static DWORD prev_time_ms; /* time (ms) of previous call */ - static long start_time_s=0; /* time (sec) from 1990 when windows started */ - - if (start_time_s == 0) { - prev_time_ms = timeGetTime(); - start_time_s = time(NULL) - TS_EPOCH_SEC_PAST_1970 - (long)prev_time_ms/1000; - } - win_sys_time_ms = timeGetTime(); - if (prev_time_ms > win_sys_time_ms) { /* must have been a timer roll-over */ - start_time_s += 4294967; /* add number of seconds in 49.7 days */ - } - pStamp->secPastEpoch = (long)win_sys_time_ms/1000 + start_time_s; /* time (sec) since 1990 */ - pStamp->nsec = (long)((win_sys_time_ms % 1000) * 1000000); - prev_time_ms = win_sys_time_ms; -# else - struct timeval curtime; - - assert(pStamp != NULL); - if (gettimeofday(&curtime, (struct timezone *)NULL) == -1) - retStat = S_ts_sysTimeError; - else { - pStamp->nsec = ( curtime.tv_usec/1000 ) * 1000000; - pStamp->secPastEpoch = curtime.tv_sec - - TS_EPOCH_SEC_PAST_1970; - } -# endif + if (gettimeofday(&curtime, (struct timezone *)NULL) == -1) + retStat = S_ts_sysTimeError; + else { + pStamp->nsec = ( curtime.tv_usec/1000 ) * 1000000; + pStamp->secPastEpoch = curtime.tv_sec - TS_EPOCH_SEC_PAST_1970; } +# endif + } - pStamp->nsec = pStamp->nsec - (pStamp->nsec % TS_TRUNC); - return retStat; + pStamp->nsec = pStamp->nsec - (pStamp->nsec % TS_TRUNC); + return retStat; } /*+/subr********************************************************************** @@ -665,10 +729,7 @@ long epicsShareAPI tsLocalTime(TS_STAMP *pStamp) * tsRoundDownLocal(&now, 86400); * *-*/ -long epicsShareAPI -tsRoundDownLocal(pStamp, interval) -TS_STAMP *pStamp; /* IO pointer to time stamp buffer */ -unsigned long interval; /* I rounding interval, in seconds */ +long epicsShareAPI tsRoundDownLocal(TS_STAMP *pStamp, unsigned long interval) { long retStat=S_ts_OK;/* return status to caller */ struct tsDetail detail; @@ -710,10 +771,7 @@ unsigned long interval; /* I rounding interval, in seconds */ * tsRoundUpLocal(&now, 86400); * *-*/ -long epicsShareAPI -tsRoundUpLocal(pStamp, interval) -TS_STAMP *pStamp; /* IO pointer to time stamp buffer */ -unsigned long interval; /* I rounding interval, in seconds */ +long epicsShareAPI tsRoundUpLocal(TS_STAMP *pStamp, unsigned long interval) { long retStat=S_ts_OK;/* return status to caller */ struct tsDetail detail; @@ -752,10 +810,7 @@ unsigned long interval; /* I rounding interval, in seconds */ * o doesn't handle 0 time stamps for time zones west of Greenwich * *-*/ -static void -tsStampFromLocal(pStamp, pT) -TS_STAMP *pStamp;/* O EPICS time stamp resulting from conversion */ -struct tsDetail *pT; /* I pointer to time structure to convert */ +static void tsStampFromLocal(TS_STAMP *pStamp, struct tsDetail *pT) { long retStat=S_ts_OK;/* return status to caller */ @@ -830,7 +885,7 @@ struct tsDetail *pT; /* I pointer to time structure to convert */ stamp.secPastEpoch += tsMinWest*60; *pStamp = stamp; } - + /*+/internal****************************************************************** * NAME tsStampToLocal - convert time stamp to local time * @@ -845,10 +900,7 @@ struct tsDetail *pT; /* I pointer to time structure to convert */ * o doesn't handle 0 time stamps for time zones west of Greenwich * *-*/ -static void -tsStampToLocal(stamp, pT) -TS_STAMP stamp; /* I EPICS time stamp to convert */ -struct tsDetail *pT; /* O pointer to time structure for conversion */ +static void tsStampToLocal(TS_STAMP stamp,struct tsDetail *pT) { int dstBegin; /* day DST begins */ int dstEnd; /* day DST ends */ @@ -905,9 +957,8 @@ struct tsDetail *pT; /* O pointer to time structure for conversion */ return; } - -static void -tsInitMinWest() + +static void tsInitMinWest() { int error=0; @@ -925,10 +976,7 @@ EPICS_TS_MIN_WEST.name, tsMinWest); needToInitMinWest = 0; } -static void -tsStampToLocalZone(pStamp, pT) -TS_STAMP *pStamp;/* pointer to EPICS time stamp to convert */ -struct tsDetail *pT; /* pointer to time structure for conversion */ +static void tsStampToLocalZone(TS_STAMP *pStamp,struct tsDetail *pT) { int ndays; /* number of days in this month or year */ unsigned long secPastEpoch; /* time from stamp, in local zone */ @@ -1164,10 +1212,9 @@ char *textBuffer; /* O buffer to receive text */ * tsTextToStamp(&equiv, &text); * *-*/ -long epicsShareAPI -tsTextToStamp(pStamp, pText) -TS_STAMP *pStamp; /* O time stamp corresponding to text */ -char **pText; /* IO ptr to ptr to string containing time and date */ +long epicsShareAPI tsTextToStamp( +TS_STAMP *pStamp, /* O time stamp corresponding to text */ +char **pCallerText) /* IO ptr to ptr to string containing time and date */ { long retStat=S_ts_OK;/* status return to caller */ long stat; /* status from calls */ @@ -1177,14 +1224,20 @@ char **pText; /* IO ptr to ptr to string containing time and date */ char delim; /* delimiter character */ long nsec; /* temp for nano-seconds */ int count; /* count from scan of next field */ + char textbuf[TEXTBUFSIZE]; + char *ptextbuf; + char **pText; if (needToInitMinWest) tsInitMinWest(); - assert(pStamp != NULL); - assert(pText != NULL); - assert(*pText != NULL); - + assert(pCallerText != NULL); + assert(*pCallerText != NULL); + /*Lets be nice and not modify *pCallerText or **pCallerText */ + strncpy(textbuf,*pCallerText,TEXTBUFSIZE); + textbuf[TEXTBUFSIZE-1] = 0; + ptextbuf = textbuf; + pText = &ptextbuf; /*---------------------------------------------------------------------------- * skip over leading white space *----------------------------------------------------------------------------*/ @@ -1410,9 +1463,9 @@ char **pText; /* IO ptr to ptr to string containing time and date */ * *-*/ long epicsShareAPI -tsTimeTextToStamp(pStamp, pText) +tsTimeTextToStamp(pStamp, pCallerText) TS_STAMP *pStamp; /* O time stamp corresponding to text */ -char **pText; /* IO ptr to ptr to string containing time and date */ +char **pCallerText; /* IO ptr to ptr to string containing time and date */ { long retStat=S_ts_OK;/* status return to caller */ struct tsDetail t; /* detailed breakdown of text */ @@ -1420,11 +1473,18 @@ char **pText; /* IO ptr to ptr to string containing time and date */ char delim; /* delimiter character */ int count; /* count from scan of next field */ long nsec; /* temp for nano-seconds */ + char textbuf[TEXTBUFSIZE]; + char *ptextbuf; + char **pText; assert(pStamp != NULL); - assert(pText != NULL); - assert(*pText != NULL); - + assert(pCallerText != NULL); + assert(*pCallerText != NULL); + /*Lets be nice and not modify *pCallerText or **pCallerText */ + strncpy(textbuf,*pCallerText,TEXTBUFSIZE); + textbuf[TEXTBUFSIZE-1] = 0; + ptextbuf = textbuf; + pText = &ptextbuf; /*---------------------------------------------------------------------------- * skip over leading white space *----------------------------------------------------------------------------*/ @@ -1504,4 +1564,171 @@ char **pText; /* IO ptr to ptr to string containing time and date */ *pStamp = stamp; return retStat; } + +/*+/mod*********************************************************************** +* TITLE nextFieldSubr.c - text field scanning routines +* +* GENERAL DESCRIPTION +* The routines in this module provide for scanning fields in text +* strings. They can be used as the basis for parsing text input +* to a program. +* +* QUICK REFERENCE +* char (*pText)[]; +* char (*pField)[]; +* char *pDelim; +* +* int nextAlph1UCField( <>pText, >pField, >pDelim ) +* int nextIntField( <>pText, >pField, >pDelim ) +* int nextIntFieldAsInt( <>pText, >pIntVal, >pDelim ) +* int nextIntFieldAsLong( <>pText, >pLongVal, >pDelim ) +* +* DESCRIPTION +* The input text string is scanned to identify the beginning and +* end of a field. At return, the input pointer points to the character +* following the delimiter and the delimiter has been returned through +* its pointer; the field contents are `returned' either as a pointer +* to the first character of the field or as a value returned through +* a pointer. +* +* In the input string, a '\0' is stored in place of the delimiter, +* so that standard string handling tools can be used for text fields. +* +* nextAlph1UCField scans the next alphabetic field, changes +* the first character to upper case, and +* changes the rest to lower case +* nextIntField scans the next integer field +* nextIntFieldAsInt scans the next integer field as an int +* nextIntFieldAsLong scans the next integer field as a long +* +* RETURNS +* count of characters in field, including the delimiter. A special +* case exists when only '\0' is encountered; in this case 0 is returned. +* (For quoted alpha strings, the count will include the " characters.) +* +* BUGS +* o use of type checking macros isn't protected by isascii() +* +* SEE ALSO +* tsTextToStamp() +* +* NOTES +* 1. fields involving alpha types consider underscore ('_') to be +* alphabetic. +* +* EXAMPLE +* char text[]="process 30 samples" +* char *pText; pointer into text string +* char *pCmd; pointer to first field, to use as a command +* int count; value of second field, number of items to process +* char *pUnits; pointer to third field, needed for command processing +* int length; length of field +* char delim; delimiter for field +* +* pText = text; +* if (nextIntFieldAsInt(&pText, &count, &delim) <= 1) +* error action if empty field +* printf("command=%s, count=%d, units=%s\n", pCmd, count, pUnits); +* +*-***************************************************************************/ + +/*----------------------------------------------------------------------------- +* the preamble skips over leading white space, stopping either at +* end of string or at a non-white-space character. If EOS is encountered, +* then the appropriate return conditions are set up and a return 0 is done. +*----------------------------------------------------------------------------*/ +#define NEXT_PREAMBLE \ + char *pDlm; /* pointer to field delimiter */ \ + int count; /* count of characters (plus delim) */ \ + \ + assert(ppText != NULL); \ + assert(*ppText != NULL); \ + assert(ppField != NULL); \ + assert(pDelim != NULL); \ + \ + if (**ppText == '\0') { \ + *ppField = *ppText; \ + *pDelim = **ppText; \ + return 0; \ + } \ + while (**ppText != '\0' && isspace(**ppText)) \ + (*ppText)++; /* skip leading white space */ \ + pDlm = *ppField = *ppText; \ + if (*pDlm == '\0') { \ + *pDelim = **ppText; \ + return 0; \ + } \ + count = 1; /* include delimiter in count */ +/*----------------------------------------------------------------------------- +* the postamble is called for each character in the field. It moves +* the pointers and increments the count. If the loop terminates, then +* the caller is informed of the delimiter and the source character +* string has '\0' inserted in place of the delimiter, so that the field +* is now a proper '\0' terminated string. +*----------------------------------------------------------------------------*/ +#define NEXT_POSTAMBLE \ + pDlm++; \ + count++; \ + } \ + *pDelim = *pDlm; \ + *ppText = pDlm; \ + if (*pDlm != '\0') { \ + (*ppText)++; /* point to next available character */ \ + *pDlm = '\0'; \ + } + +static int nextAlph1UCField(char **ppText,char **ppField, char *pDelim) +{ + NEXT_PREAMBLE + while (isalpha(*pDlm) || *pDlm == '_') { + if (count == 1) { + if (islower(*pDlm)) + *pDlm = toupper(*pDlm); + } + else { + if (isupper(*pDlm)) + *pDlm = tolower(*pDlm); + } + NEXT_POSTAMBLE + return count; +} +static int nextIntField(char **ppText,char **ppField,char *pDelim) +{ + NEXT_PREAMBLE + while (isdigit(*pDlm) || ((*pDlm=='-' || *pDlm=='+') && count==1)) { + NEXT_POSTAMBLE + return count; +} + +static int nextIntFieldAsInt(char **ppText,int *pIntVal,char *pDelim) +{ + char *pField; /* pointer to field */ + int count; /* count of char in field, including delim */ + + assert(pIntVal != NULL); + + count = nextIntField(ppText, &pField, pDelim); + if (count > 1) { + if (sscanf(pField, "%d", pIntVal) != 1) + assert(0); + } + + return count; +} + +static int nextIntFieldAsLong(char **ppText,long *pLongVal,char *pDelim) +{ + char *pField; /* pointer to field */ + int count; /* count of char in field, including delim */ + + assert(pLongVal != NULL); + + count = nextIntField(ppText, &pField, pDelim); + if (count > 1) { + if (sscanf(pField, "%ld", pLongVal) != 1) + assert(0); + } + + return count; +}