From f454b844aeedd4850e60a153be41fae1db19bd02 Mon Sep 17 00:00:00 2001 From: Jonathan Leffler Date: Fri, 26 Jul 2024 19:11:21 -0600 Subject: [PATCH] Update the library source code. --- etc/soq-head.mk | 4 +- makefile | 2 + src/libsoq/aomcopy.h | 6 +-- src/libsoq/aommngd.h | 6 +-- src/libsoq/aomptr.h | 6 +-- src/libsoq/debug.c | 6 +-- src/libsoq/debug.h | 15 +++++- src/libsoq/gcd.h | 30 ++++++++--- src/libsoq/makefile | 2 +- src/libsoq/mddebug.c | 33 +++++------- src/libsoq/stderr.c | 91 ++++++++++++++++++++++++--------- src/libsoq/timespec_io.c | 101 +++++++++++++++++++------------------ src/libsoq/timespec_io.h | 43 +++++++++++++++- src/libsoq/timespec_math.c | 16 +++--- src/libsoq/timespec_math.h | 8 +-- src/libsoq/timeval_math.c | 16 +++--- src/libsoq/timeval_math.h | 8 +-- 17 files changed, 251 insertions(+), 142 deletions(-) diff --git a/etc/soq-head.mk b/etc/soq-head.mk index bf5ca6c8..82d8ecc2 100644 --- a/etc/soq-head.mk +++ b/etc/soq-head.mk @@ -42,7 +42,7 @@ IFLAG1 = -I${INCDIR} IFLAG2 = #-I${HOME}/inc IFLAGS = ${IFLAG1} ${IFLAG2} OFLAGS = -O3 -SFLAGS = -std=c11 # -pedantic +SFLAGS = -std=c18 # -pedantic TFLAGS = # Set to -DTEST (or similar) when needed UFLAGS = # Set on command line WFLAG1 = -Wall @@ -60,7 +60,7 @@ DXXFLAGS = #-DHAVE_CONFIG_H GXXFLAGS = -g IXXFLAGS = ${IFLAGS} OXXFLAGS = -O3 -SXXFLAGS = -std=c++11 +SXXFLAGS = -std=c++20 TXXFLAGS = # Set to -DTEST (or similar) when needed UXXFLAGS = # Set on comand line WXXFLAG1 = -Wall diff --git a/makefile b/makefile index 437e26da..8e4a9ecf 100644 --- a/makefile +++ b/makefile @@ -5,6 +5,8 @@ include etc/soq-head.mk BASEDIR = . +CC = clang -Wno-nullability-completeness + default: @echo "You must specify a target to build" diff --git a/src/libsoq/aomcopy.h b/src/libsoq/aomcopy.h index 4d82c01c..926b11d1 100644 --- a/src/libsoq/aomcopy.h +++ b/src/libsoq/aomcopy.h @@ -2,8 +2,8 @@ @(#)File: aomcopy.h @(#)Purpose: Array of Memory Blocks - Copy Semantics @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 2017-2023 -@(#)Derivation: aomcopy.h 1.6 2023/01/16 20:34:13 +@(#)Copyright: (C) JLSS 2017-2024 +@(#)Derivation: aomcopy.h 1.7 2024/05/20 14:43:13 */ /*TABSTOP=4*/ @@ -16,7 +16,7 @@ extern "C" { #endif #include /* bool */ -#include "aomcore.h" /* (FILE, size_t), AoM_Block, AoM_SimpleApply, AoM_ContextApply */ +#include "aomcore.h" /* (FILE, size_t), AoM_Block, AoM_SimpleApply, AoM_ContextApply, AoM_PrintData */ /* ** The AoM_Copy structure is for an array of memory blocks which makes a diff --git a/src/libsoq/aommngd.h b/src/libsoq/aommngd.h index 86aa23bf..aed13ad1 100644 --- a/src/libsoq/aommngd.h +++ b/src/libsoq/aommngd.h @@ -2,8 +2,8 @@ @(#)File: aommngd.h @(#)Purpose: Array of Memory Blocks - Memory Managed Data @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 2018-2023 -@(#)Derivation: aommngd.h 1.4 2023/01/16 20:33:37 +@(#)Copyright: (C) JLSS 2018-2024 +@(#)Derivation: aommngd.h 1.5 2024/05/20 14:43:13 */ /*TABSTOP=4*/ @@ -16,7 +16,7 @@ extern "C" { #endif #include /* bool */ -#include "aomcore.h" /* (FILE, size_t), AoM_Block, AoM_SimpleApply, AoM_ContextApply */ +#include "aomcore.h" /* (FILE, size_t), AoM_Block, AoM_SimpleApply, AoM_ContextApply, AoM_PrintData */ /* ** The AoM_Managed structure is for an array of memory blocks which diff --git a/src/libsoq/aomptr.h b/src/libsoq/aomptr.h index 87e92a80..5b7d89e3 100644 --- a/src/libsoq/aomptr.h +++ b/src/libsoq/aomptr.h @@ -2,8 +2,8 @@ @(#)File: aomptr.h @(#)Purpose: Array of Memory Blocks - Pointer Semantics @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 2017-2023 -@(#)Derivation: aomptr.h 1.4 2023/01/16 20:33:02 +@(#)Copyright: (C) JLSS 2017-2024 +@(#)Derivation: aomptr.h 1.5 2024/05/20 14:43:13 */ /*TABSTOP=4*/ @@ -16,7 +16,7 @@ extern "C" { #endif #include /* bool */ -#include "aomcore.h" /* (FILE, size_t), AoM_Block, AoM_SimpleApply, AoM_ContextApply */ +#include "aomcore.h" /* (FILE, size_t), AoM_Block, AoM_SimpleApply, AoM_ContextApply, AoM_PrintData */ /* ** The AoM_Pointer structure is for an array of memory blocks which diff --git a/src/libsoq/debug.c b/src/libsoq/debug.c index 7bdaeb02..49898df0 100644 --- a/src/libsoq/debug.c +++ b/src/libsoq/debug.c @@ -3,7 +3,7 @@ @(#)Purpose: Support for Debugging Printing @(#)Author: J Leffler @(#)Copyright: (C) JLSS 1990-2023 -@(#)Derivation: debug.c 4.1 2023/03/13 17:14:56 +@(#)Derivation: debug.c 4.2 2024/05/31 19:53:09 */ /*TABSTOP=4*/ @@ -196,8 +196,8 @@ int db_setdebug(int level) static void db_test(void) { fprintf(stderr, "Should appear at indent = %d\n", db_newindent() + 1); - TRACE((1, "This should have appeared at debug level %d; %d %f\n", - 1, 3, 3.141593)); + DB_TRACE(1, "This should have appeared at debug level %d; %d %f\n", + 1, 3, 3.141593); DB_TRACE(2, "This should have appeared at debug level %d; %d %f\n", 2, 3, 3.141593); DB_TRACELOC(3, "This should have appeared at debug level %d; %d %f\n", diff --git a/src/libsoq/debug.h b/src/libsoq/debug.h index 3b63e67f..94354c84 100644 --- a/src/libsoq/debug.h +++ b/src/libsoq/debug.h @@ -2,8 +2,8 @@ @(#)File: debug.h @(#)Purpose: Support for Debugging Printing @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 1990-2023 -@(#)Derivation: debug.h 4.1 2023/03/13 17:14:56 +@(#)Copyright: (C) JLSS 1990-2024 +@(#)Derivation: debug.h 4.2 2024/05/21 04:48:42 */ #ifndef DEBUG_H @@ -48,6 +48,17 @@ #define DB_CALL(level, ...)\ do { if (DB_ACTIVE && db_getdebug() >= (level)) { __VA_ARGS__; } } while (0) +/* +** Usage: DB_BEGIN(level); +** ...conditional code... +** DB_END(); +** The semicolon after DB_BEGIN is optional, but harmless. +** If DB_BEGIN/DB_END are not properly matched, all hell will break loose. +*/ + +#define DB_BEGIN(level) do { if (DB_ACTIVE && db_getdebug() >= (level)) { +#define DB_END() } } while (0) + /* ** DB_TRACKING(); uses the FEATURE_FILE macro from klduge.h to embed a ** string in a function identifying that the file is compiled with debug diff --git a/src/libsoq/gcd.h b/src/libsoq/gcd.h index fdce51da..eba9f350 100644 --- a/src/libsoq/gcd.h +++ b/src/libsoq/gcd.h @@ -2,8 +2,8 @@ @(#)File: gcd.h @(#)Purpose: GCD - Greatest Common Denominator @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 2005-2017 -@(#)Derivation: gcd.h 1.5 2017/08/25 22:50:04 +@(#)Copyright: (C) JLSS 2005-2024 +@(#)Derivation: gcd.h 1.6 2024/02/08 05:19:55 */ /*TABSTOP=4*/ @@ -29,16 +29,34 @@ extern "C" { ** ** (A trailing semicolon creates an empty declaration after the ** function; it isn't the end of the world, but it isn't right.) +** +** A recursive GCD function looks like: +** int gcd_recursive(int x, int y) +** { +** if (y == 0) +** return x; +** return gcd_recursive(y, x % y); +** } +** There is no good reason to use recursion; the iterative +** solution is more sensible. */ #define DECLARE_GCD_FUNCTION(storage_class, type, name) \ storage_class type name(type x, type y) #define DEFINE_GCD_FUNCTION(storage_class, type, name) \ - storage_class type name(type x, type y) { type r; \ - if (x <= 0 || y <= 0) return(0); \ - while ((r = x % y) != 0) { x = y; y = r; } \ - return(y); } + storage_class type name(type x, type y) \ + { \ + if (x <= 0 || y <= 0) \ + return(0); \ + type r; \ + while ((r = x % y) != 0) \ + { \ + x = y; \ + y = r; \ + } \ + return(y); \ + } DECLARE_GCD_FUNCTION(extern, int, gcd); DECLARE_GCD_FUNCTION(extern, unsigned long long, gcd_ull); diff --git a/src/libsoq/makefile b/src/libsoq/makefile index 62d3906b..fac283fa 100644 --- a/src/libsoq/makefile +++ b/src/libsoq/makefile @@ -8,7 +8,7 @@ include ../../etc/soq-head.mk #TFLAGS = -DTEST CP = cp CPFLAGS = -fp -CFLAGS += -DHAVE_CONFIG_H +CFLAGS += -DHAVE_NANOSLEEP # FILES.c lists source files for which there is a matching header FILES.c = \ diff --git a/src/libsoq/mddebug.c b/src/libsoq/mddebug.c index ebb63ae0..76054889 100644 --- a/src/libsoq/mddebug.c +++ b/src/libsoq/mddebug.c @@ -2,31 +2,24 @@ @(#)File: mddebug.c @(#)Purpose: Support for Debugging Printing with Multiple Subsystems @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 1990-2023 -@(#)Derivation: mddebug.c 4.1 2023/03/13 17:14:56 +@(#)Copyright: (C) JLSS 1990-2024 +@(#)Derivation: mddebug.c 4.3 2024/05/31 19:53:22 */ /*TABSTOP=4*/ -#include "posixver.h" #undef DEBUG #define DEBUG -#include "mddebug.h" /* SSC: Self-sufficiency check */ -#include "debug.h" -#include "stderr.h" +#include "posixver.h" +#include "mddebug.h" /* SSC: Self-sufficiency check */ #include #include #include #include - -#define USE_JLSS_GETSUBOPT -#define USE_JLSS_GETOPT -#include "getopt.h" -#include "strtoint.h" /* strtoi() */ - -#ifndef GETSUBOPT -#define GETSUBOPT(opt, tokens, value) getsubopt(opt, tokens, value) -#endif +#include "debug.h" +#include "jlss-getopt.h" +#include "stderr.h" +#include "strtoint.h" /* strtoi() */ #define DB_ON 3 #define DB_OFF 0 @@ -89,7 +82,7 @@ void db_mdsubsysnames(char * const *names) /* ** Parse the value of an argument (eg -D subsys1=3,subsys2=9), ** setting the relevant subsystems to the relevant debug levels. -** NB: argument is destroyed by getsubopt(). +** NB: argument is destroyed by jlss_getsubopt(). ** Returns: 0 OK, -1 some fault occurred. */ int db_mdparsearg(char *arg) @@ -146,7 +139,7 @@ int db_mdparsearg(char *arg) /* ** Print debug information if subsys_debug[subsys] set at or above level. */ -void db_mdprint(int subsys, int level, const char *fmt,...) +void db_mdprint(int subsys, int level, const char *fmt, ...) { if (subsys >= 0 && subsys < subsys_max && subsys_debug[subsys] >= level) { @@ -170,7 +163,7 @@ void db_mdprint(int subsys, int level, const char *fmt,...) ** Print debug information if subsys_debug[subsys] set at or above level. */ void db_mdprintloc(int subsys, int level, const char *file, int line, - const char *func, const char *fmt,...) + const char *func, const char *fmt, ...) { if (subsys >= 0 && subsys < subsys_max && subsys_debug[subsys] >= level) { @@ -207,8 +200,8 @@ static char * const ss_names[] = static void db_test(void) { fprintf(stderr, "Should appear at indent = %d\n", db_newindent() + 1); - MDTRACE((TRACE, 1, "This should have appeared at debug level %d; %d %f\n", - 1, 3, 3.141593)); + DB_MDTRACE(TRACE, 1, "This should have appeared at debug level %d; %d %f\n", + 1, 3, 3.141593); DB_MDTRACE(SUBSYS_1, 2, "This should have appeared at debug level %d; %d %f\n", 2, 3, 3.141593); DB_MDTRACE(SUBSYS_2, 3, "This should have appeared at debug level %d; %d %f\n", diff --git a/src/libsoq/stderr.c b/src/libsoq/stderr.c index 4fbb3e8c..2a9ccac2 100644 --- a/src/libsoq/stderr.c +++ b/src/libsoq/stderr.c @@ -2,8 +2,8 @@ @(#)File: stderr.c @(#)Purpose: Error reporting routines @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 1988-2022 -@(#)Derivation: stderr.c 10.30 2022/06/20 19:26:27 +@(#)Copyright: (C) JLSS 1988-2024 +@(#)Derivation: stderr.c 10.32 2024/03/24 20:49:47 */ /*TABSTOP=4*/ @@ -26,6 +26,8 @@ ** HAVE_SYSLOG - Define if syslog() is available */ +/* #define __EXTENSIONS__ // Solaris 11.3 - declare flockfile(), funlockfile() */ + #include "posixver.h" #include "stderr.h" /* Includes config.h if available */ #include @@ -279,7 +281,7 @@ const char *(err_rcs_string)(const char *s2, char *buffer, size_t buflen) /* ** Bother RCS! We've probably been given something like: - ** "$Revision: 10.30 $ ($Date: 2022/06/20 19:26:27 $)" + ** "$Revision: 10.32 $ ($Date: 2024/03/24 20:49:47 $)" ** We only want to emit "7.5 (2001/08/11 06:25:48)". ** Skip the components between '$' and ': ', copy up to ' $', ** repeating as necessary. And we have to test for overflow! @@ -568,7 +570,7 @@ static NORETURN void err_exn_print(int flags, int estat, const char *format, ... va_start(args, format); err_vxn_print(flags, errno, estat, format, args); - va_end(args); /* Superfluous because err_vxn_print() does not return */ + /* va_end(args); // Superfluous - err_vxn_print() does not return */ } /* Print error message to nominated output - returns unless flags say exit */ @@ -644,7 +646,7 @@ void (err_syserr)(const char *format, ...) va_start(args, format); err_vxn_print(ERR_SYSERR | err_getlogopts(), errno, ERR_STAT, format, args); - va_end(args); + /* va_end(args); // Superfluous - err_vxn_print() does not return */ } /* Report error including message from errno */ @@ -654,7 +656,7 @@ void (err_syserror)(int errnum, const char *format, ...) va_start(args, format); err_vxn_print(ERR_SYSERR | err_getlogopts(), errnum, ERR_STAT, format, args); - va_end(args); /* Superflouous - err_vxn_orint() does not return */ + /* va_end(args); // Superfluous - err_vxn_print() does not return */ } /* Report warning - identical to err_warning() */ @@ -687,6 +689,7 @@ void (err_vwarning)(const char *format, va_list args) NORETURN void err_verror(const char *format, va_list args) { err_vxn_print(ERR_ERR | err_getlogopts(), errno, ERR_STAT, format, args); + /* va_end(args); // Superfluous - err_vxn_print() does not return */ } /* Report error */ @@ -696,7 +699,7 @@ NORETURN void (err_error)(const char *format, ...) va_start(args, format); err_vxn_print(ERR_ERR | err_getlogopts(), errno, ERR_STAT, format, args); - va_end(args); /* Superfluous - err_vxn_print() does not return */ + /* va_end(args); // Superfluous - err_vxn_print() does not return */ } /* Report message - sometimes exiting too */ @@ -709,41 +712,79 @@ void (err_report)(int flags, int estat, const char *format, ...) va_end(args); } +/* Format one continuation line of a usage message */ +static int fmt_usage_line(size_t buflen, char *buffer, int length, const char *line) +{ + int arg0_len = (int)strlen(arg0); + const char *name = ""; + if (line[0] == '@') + { + name = arg0; + int whisp = strspn(&line[1], " \t"); + line += whisp + 1; + length -= whisp + 1; + } + int out_len = snprintf(buffer, buflen, "%-*s%*s %.*s\n", + (int)sizeof("Usage:"), "", + arg0_len, name, + length, line); + assert(out_len > 0 && (size_t)out_len < buflen); + return out_len; +} + /* Format possibly multi-line usage message */ -void err_fmt_usage(size_t buflen, char *buffer, const char *s1) +/* +** Given usestr containing "abc\ndef", the output from a command "cmd" would be: +** Usage: cmd abc +** def +** Given usestr containing "abc\n@def \\\nghi\n@jkl", the output would be: +** Usage: cmd abc +** cmd def \ +** ghi +** cmd jkl +** White space (blanks, tabs) after the `@` is skipped. +** White space after the (first) newline suppresses the special formatting; +** the remaining information is printed verbatim. +** In 'real life', the "abc" parts would normally be a lot longer, of course. +*/ + +void err_fmt_usage(size_t buflen, char *buffer, const char *usestr) { - const char *nl = strchr(s1, '\n'); - if (nl != 0 && nl[1] != ' ' && nl[1] != '\0') + const char *newline = strchr(usestr, '\n'); + if (newline != 0 && newline[1] != ' ' && newline[1] != '\0') { - /* Indent second and subsequent lines by length of usage + arg0 */ - int arg0_len = (int)strlen(arg0); char *bufptr = buffer; - int out_len = snprintf(bufptr, buflen, "Usage: %s %.*s\n", arg0, (int)(nl - s1), s1); + int out_len = snprintf(bufptr, buflen, "Usage: %s %.*s\n", arg0, (int)(newline - usestr), usestr); assert(out_len > 0 && (size_t)out_len < buflen); bufptr += out_len; buflen -= (size_t)out_len; - const char *eol; - while ((eol = strchr(nl + 1, '\n')) != 0) + + int eol; + newline++; + while ((eol = strcspn(newline, "\n")) != 0) { - out_len = snprintf(bufptr, buflen, "%-*s%*s %.*s\n", (int)sizeof("Usage:"), "", - arg0_len, "", (int)(eol - (nl + 1)), nl + 1); + out_len = fmt_usage_line(buflen, bufptr, eol, newline); assert(out_len > 0 && (size_t)out_len < buflen); bufptr += out_len; buflen -= (size_t)out_len; - nl = eol; + if (newline[eol] == '\0') + break; + newline += eol + 1; } - out_len = snprintf(bufptr, buflen, "%-*s%*s %s", (int)sizeof("Usage:"), "", - arg0_len, "", nl + 1); } else - snprintf(buffer, buflen, "Usage: %s %s", arg0, s1); + snprintf(buffer, buflen, "Usage: %s %s", arg0, usestr); + + size_t result_len = strlen(buffer); + if (result_len > 0 && buffer[result_len - 1] == '\n') + buffer[result_len - 1] = '\0'; } /* Print usage message and exit with failure status */ -NORETURN void (err_usage)(const char *s1) +NORETURN void (err_usage)(const char *usestr) { char buffer[MAX_MSGLEN]; /* Fairly big chunk of stack! */ - err_fmt_usage(sizeof(buffer), buffer, s1); + err_fmt_usage(sizeof(buffer), buffer, usestr); err_exn_print(ERR_NOARG0|ERR_EXIT, EXIT_FAILURE, "%s\n", buffer); } @@ -754,7 +795,7 @@ NORETURN void (err_abort)(const char *format, ...) va_start(args, format); err_vxn_print(ERR_ABORT | err_getlogopts(), errno, EXIT_FAILURE, format, args); - va_end(args); + /* va_end(args); // Superfluous - err_vxn_print() does not return */ } /* Report version information (no exit), removing embedded RCS keyword strings (but not values) */ @@ -789,7 +830,7 @@ NORETURN void (err_internal)(const char *function, const char *format, ...) va_start(args, format); err_remark("unrecoverable internal error in function %s():\n", function); err_vxn_print(flags | err_getlogopts(), errnum, EXIT_FAILURE, format, args); - va_end(args); /* Superfluous - err_vxn_print() does not return */ + /* va_end(args); // Superfluous - err_vxn_print() does not return */ } #ifdef TEST diff --git a/src/libsoq/timespec_io.c b/src/libsoq/timespec_io.c index 0b8cbd88..bf62df5f 100644 --- a/src/libsoq/timespec_io.c +++ b/src/libsoq/timespec_io.c @@ -2,8 +2,8 @@ @(#)File: timespec_io.c @(#)Purpose: Convert string to timespec and vice versa @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 2015-2019 -@(#)Derivation: timespec_io.c 2.2 2019/08/16 05:25:57 +@(#)Copyright: (C) JLSS 2015-2024 +@(#)Derivation: timespec_io.c 3.1 2024/05/07 04:52:29 */ /*TABSTOP=4*/ @@ -19,21 +19,23 @@ enum { NS_PER_SECOND = 1000000000 }; -int scn_timespec(const char *str, struct timespec *value) +int scn_timespec(const char *string, struct timespec *value) { + const char *str = string; assert(str != 0 && value != 0); if (str == 0 || value == 0) { errno = EINVAL; return -1; } - long sec; + + long sec = 0; long nsec = 0; int sign = +1; char *end; + /* No library routine sets errno to 0 - but this one needs to */ int old_errno = errno; - errno = 0; /* Skip leading white space */ @@ -49,62 +51,65 @@ int scn_timespec(const char *str, struct timespec *value) str++; } - /* Next character must be a digit */ - if (!isdigit((unsigned char)*str)) + /* Next character must be a digit or decimal point */ + if (!isdigit((unsigned char)*str) && *str != '.') { errno = EINVAL; return -1; } - /* Convert seconds part of string */ - sec = strtol(str, &end, 10); - if (end == str || ((sec == LONG_MAX || sec == LONG_MIN) && errno == ERANGE)) + if (isdigit((unsigned char)*str)) + { + /* Convert seconds part of string */ + sec = strtol(str, &end, 10); + if (end == str || ((sec == LONG_MAX || sec == LONG_MIN) && errno == ERANGE)) + { + errno = EINVAL; + return -1; + } + str = end; + } + else if (!isdigit((unsigned char)str[1])) { errno = EINVAL; return -1; } - if (*end != '\0' && !isspace((unsigned char)*end)) + if (str[0] != '.') { - if (*end++ != '.') - { - errno = EINVAL; - return -1; - } - if (*end == '\0') - nsec = 0; - else if (isdigit((unsigned char)*end)) - { - char *frac = end; - nsec = strtol(frac, &end, 10); - if (end == str || - ((nsec == LONG_MAX || nsec == LONG_MIN) && errno == ERANGE) || - (nsec < 0 || nsec >= NS_PER_SECOND) || (end - frac > 9)) - { - errno = EINVAL; - return -1; - } - for (int i = 0; i < 9 - (end - frac); i++) - nsec *= 10; - } + value->tv_sec = sec * sign; + value->tv_nsec = 0; + errno = old_errno; + return end - string; } - /* Allow trailing white space - only */ - unsigned char uc; - while ((uc = (unsigned char)*end++) != '\0') + str++; + if (!isdigit((unsigned char)*str)) { - if (!isspace(uc)) - { - errno = EINVAL; - return -1; - } + value->tv_sec = sec * sign; + value->tv_nsec = 0; + errno = old_errno; + return str - string; } + const char *frac = str; + nsec = strtol(frac, &end, 10); + if (end == frac || + ((nsec == LONG_MAX || nsec == LONG_MIN) && errno == ERANGE) || + (nsec < 0 || nsec >= NS_PER_SECOND) || (end - frac > 9)) + { + errno = EINVAL; + return -1; + } + + for (int i = 0; i < 9 - (end - frac); i++) + nsec *= 10; + /* Success! */ value->tv_sec = sec * sign; value->tv_nsec = nsec * sign; errno = old_errno; - return 0; + return end - string; } int fmt_timespec(const struct timespec *value, int dp, char *buffer, size_t buflen) @@ -173,11 +178,12 @@ static const p1_test_case p1_tests[] = { "0", { .tv_sec = 0, .tv_nsec = 0 }, "0.000000000" }, { "0.", { .tv_sec = 0, .tv_nsec = 0 }, "0.000000000" }, { "0. ", { .tv_sec = 0, .tv_nsec = 0 }, "0.000000000" }, + { ".0 ", { .tv_sec = 0, .tv_nsec = 0 }, "0.000000000" }, { "0.0", { .tv_sec = 0, .tv_nsec = 0 }, "0.000000000" }, { "0.01", { .tv_sec = 0, .tv_nsec = 10000000 }, "0.010000000" }, { "0.012", { .tv_sec = 0, .tv_nsec = 12000000 }, "0.012000000" }, { "0.0123", { .tv_sec = 0, .tv_nsec = 12300000 }, "0.012300000" }, - { "0.01234 \t\n", { .tv_sec = 0, .tv_nsec = 12340000 }, "0.012340000" }, + { "0.01234XXXXX", { .tv_sec = 0, .tv_nsec = 12340000 }, "0.012340000" }, { "0.012345 ", { .tv_sec = 0, .tv_nsec = 12345000 }, "0.012345000" }, { "0.0123456 ", { .tv_sec = 0, .tv_nsec = 12345600 }, "0.012345600" }, { "0.01234567", { .tv_sec = 0, .tv_nsec = 12345670 }, "0.012345670" }, @@ -187,7 +193,7 @@ static const p1_test_case p1_tests[] = { "0.000000078 ", { .tv_sec = 0, .tv_nsec = 78 }, "0.000000078" }, { "-0.000000008 ", { .tv_sec = 0, .tv_nsec = -8 }, "-0.000000008" }, { "0.000000000", { .tv_sec = 0, .tv_nsec = 0 }, "0.000000000" }, - { "-0.000000000", { .tv_sec = 0, .tv_nsec = 0 }, "0.000000000" }, + { "-0.000000001", { .tv_sec = 0, .tv_nsec = -1 }, "-0.000000001" }, { "+0.000000000", { .tv_sec = 0, .tv_nsec = 0 }, "0.000000000" }, { "1.012345678", { .tv_sec = 1, .tv_nsec = 12345678 }, "1.012345678" }, { "12.012345678", { .tv_sec = 12, .tv_nsec = 12345678 }, "12.012345678" }, @@ -203,8 +209,10 @@ static void p1_tester(const void *data) struct timespec t_out; char buffer1[32]; int len; + int nbytes; - if (scn_timespec(test->input, &t_out) != 0) + nbytes = scn_timespec(test->input, &t_out); + if (nbytes <= 0) pt_fail("failed to convert [%s]\n", test->input); else if ((len = fmt_timespec(&t_out, 9, buffer1, sizeof(buffer1))) < 0) pt_fail("failed to format [%s]\n", test->input); @@ -216,7 +224,7 @@ static void p1_tester(const void *data) char buffer3[32]; str_cstrlit(test->input, buffer2, sizeof(buffer2)); snprintf(buffer3, sizeof(buffer3), "[%.*s]", (int)(sizeof(buffer3)-3), buffer2); - pt_pass("%-22s formats to (%2d) [%s]\n", buffer3, len, buffer1); + pt_pass("%-22s formats to (%2d) [%s] residue [%s]\n", buffer3, len, buffer1, test->input + nbytes); } } @@ -272,9 +280,6 @@ static const p3_test_case p3_tests[] = { "" }, { " " }, { " ." }, - { " .0" }, - { " 1 0" }, - { "1.00X" }, { "X1.00X" }, { "12345678901234567890.112233" }, { "1234.1234567890" }, diff --git a/src/libsoq/timespec_io.h b/src/libsoq/timespec_io.h index 8fa04020..d910c9d6 100644 --- a/src/libsoq/timespec_io.h +++ b/src/libsoq/timespec_io.h @@ -2,8 +2,8 @@ @(#)File: timespec_io.h @(#)Purpose: Convert string to timespec and vice versa @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 2015-2019 -@(#)Derivation: timespec_io.h 2.2 2019/08/16 05:25:57 +@(#)Copyright: (C) JLSS 2015-2024 +@(#)Derivation: timespec_io.h 3.2 2024/05/07 05:05:02 */ /*TABSTOP=4*/ @@ -17,6 +17,45 @@ extern "C" { #include +/* +** scn_timespec() reports how many bytes were consumed by the scan. +** const char str[] = "12.13213"; +** struct timespec ts; +** int nbytes = scn_timespec(str, &ts); +** if (nbytes <= 0 || str[nbytes] != '\0') +** err_error("failed to consume entire string [%s]\n", str); +** The returned length allows you to determine what character caused the +** scan to stop. As long as a valid time value was read, it will not +** report failure. A string like "-.123" is valid too; so is "12." and +** the dot is deemed to be consumed. +** +** fmt_timespec() reports how many bytes were written to the buffer, not +** including the terminal null byte. Errors are reported by returning +** -1, with errno = EINVAL if buffer is null or dp is negative or larger +** than 9, or errno = ENOSPC if buflen is zero or too small. A return +** value of 0 indicates no valid value was found. The fraction is +** optional. Leading white space is skipped. +** +** FMT_TIMESPEC_BUFFER_SIZE is the recommended buffer size - it is big +** enough for a full-size 64-bit signed integer (19 decimal digits) and +** 9 decimal places, plus a negative sign, decimal point and null byte. +** +** NB: It is assumed that negative values are stored with a negative (or zero) +** value for tv_sec and a negative (or zero) value for tv_nsec. POSIX +** (https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html) +** says in section 2.8.5 Clocks and Timers: +** - The tv_nsec member [of a struct timespec value] is only valid if +** greater than or equal to zero, and less than the number of +** nanoseconds in a second (1000 million). The time interval +** described by this structure is (tv_sec * 10^9 + tv_nsec) +** nanoseconds. +** If applied literally to negative values of tv_sec, then a value like +** -1.234 would be represented by tv_sec = -2 and tv_nsec = 766000000. +** This is not coonvenient or easy to understand or print. +*/ + +enum { FMT_TIMESPEC_BUFFER_SIZE = 32 }; + extern int scn_timespec(const char *str, struct timespec *value); extern int fmt_timespec(const struct timespec *value, int dp, char *buffer, size_t buflen); diff --git a/src/libsoq/timespec_math.c b/src/libsoq/timespec_math.c index 9680fb6c..6573ea99 100644 --- a/src/libsoq/timespec_math.c +++ b/src/libsoq/timespec_math.c @@ -2,8 +2,8 @@ @(#)File: timespec_math.c @(#)Purpose: Add, subtract, compare two struct timespec values @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 2015-2019 -@(#)Derivation: timespec_math.c 2.2 2019/08/16 05:25:57 +@(#)Copyright: (C) JLSS 2015-2024 +@(#)Derivation: timespec_math.c 2.3 2024/04/04 03:22:36 */ /*TABSTOP=4*/ @@ -13,10 +13,10 @@ enum { NS_PER_SECOND = 1000000000 }; -void sub_timespec(struct timespec t1, struct timespec t2, struct timespec *td) +void sub_timespec(struct timespec t_old, struct timespec t_new, struct timespec *td) { - td->tv_nsec = t2.tv_nsec - t1.tv_nsec; - td->tv_sec = t2.tv_sec - t1.tv_sec; + td->tv_nsec = t_new.tv_nsec - t_old.tv_nsec; + td->tv_sec = t_new.tv_sec - t_old.tv_sec; if (td->tv_sec > 0 && td->tv_nsec < 0) { td->tv_nsec += NS_PER_SECOND; @@ -29,10 +29,10 @@ void sub_timespec(struct timespec t1, struct timespec t2, struct timespec *td) } } -void add_timespec(struct timespec t1, struct timespec t2, struct timespec *td) +void add_timespec(struct timespec t_old, struct timespec t_new, struct timespec *td) { - td->tv_nsec = t2.tv_nsec + t1.tv_nsec; - td->tv_sec = t2.tv_sec + t1.tv_sec; + td->tv_nsec = t_new.tv_nsec + t_old.tv_nsec; + td->tv_sec = t_new.tv_sec + t_old.tv_sec; if (td->tv_nsec >= NS_PER_SECOND) { td->tv_nsec -= NS_PER_SECOND; diff --git a/src/libsoq/timespec_math.h b/src/libsoq/timespec_math.h index 8704cd3b..7a3cfa62 100644 --- a/src/libsoq/timespec_math.h +++ b/src/libsoq/timespec_math.h @@ -2,8 +2,8 @@ @(#)File: timespec_math.h @(#)Purpose: Add, subtract, compare two struct timespec values @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 2015-2019 -@(#)Derivation: timespec_math.h 2.2 2019/08/16 05:25:57 +@(#)Copyright: (C) JLSS 2015-2024 +@(#)Derivation: timespec_math.h 2.3 2024/04/04 03:22:36 */ /*TABSTOP=4*/ @@ -17,8 +17,8 @@ extern "C" { #include -extern void sub_timespec(struct timespec t1, struct timespec t2, struct timespec *td); -extern void add_timespec(struct timespec t1, struct timespec t2, struct timespec *td); +extern void sub_timespec(struct timespec t_old, struct timespec t_new, struct timespec *td); +extern void add_timespec(struct timespec t_old, struct timespec t_new, struct timespec *td); extern int cmp_timespec(struct timespec t1, struct timespec t2); #ifdef __cplusplus diff --git a/src/libsoq/timeval_math.c b/src/libsoq/timeval_math.c index 51337eb3..8ceaac91 100644 --- a/src/libsoq/timeval_math.c +++ b/src/libsoq/timeval_math.c @@ -2,8 +2,8 @@ @(#)File: timeval_math.c @(#)Purpose: Add, subtract, compare two struct timeval values @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 2019 -@(#)Derivation: timeval_math.c 2.1 2019/06/02 05:22:35 +@(#)Copyright: (C) JLSS 2019-2024 +@(#)Derivation: timeval_math.c 2.2 2024/04/04 03:22:36 */ /*TABSTOP=4*/ @@ -13,10 +13,10 @@ enum { US_PER_SECOND = 1000000 }; -void sub_timeval(struct timeval t1, struct timeval t2, struct timeval *td) +void sub_timeval(struct timeval t_old, struct timeval t_new, struct timeval *td) { - td->tv_usec = t2.tv_usec - t1.tv_usec; - td->tv_sec = t2.tv_sec - t1.tv_sec; + td->tv_usec = t_new.tv_usec - t_old.tv_usec; + td->tv_sec = t_new.tv_sec - t_old.tv_sec; if (td->tv_sec > 0 && td->tv_usec < 0) { td->tv_usec += US_PER_SECOND; @@ -29,10 +29,10 @@ void sub_timeval(struct timeval t1, struct timeval t2, struct timeval *td) } } -void add_timeval(struct timeval t1, struct timeval t2, struct timeval *td) +void add_timeval(struct timeval t_old, struct timeval t_new, struct timeval *td) { - td->tv_usec = t2.tv_usec + t1.tv_usec; - td->tv_sec = t2.tv_sec + t1.tv_sec; + td->tv_usec = t_new.tv_usec + t_old.tv_usec; + td->tv_sec = t_new.tv_sec + t_old.tv_sec; if (td->tv_usec >= US_PER_SECOND) { td->tv_usec -= US_PER_SECOND; diff --git a/src/libsoq/timeval_math.h b/src/libsoq/timeval_math.h index 0ae5f91d..eea805bb 100644 --- a/src/libsoq/timeval_math.h +++ b/src/libsoq/timeval_math.h @@ -2,8 +2,8 @@ @(#)File: timeval_math.h @(#)Purpose: Add, subtract, compare two struct timeval values @(#)Author: J Leffler -@(#)Copyright: (C) JLSS 2019 -@(#)Derivation: timeval_math.h 2.1 2019/06/02 05:22:44 +@(#)Copyright: (C) JLSS 2019-2024 +@(#)Derivation: timeval_math.h 2.2 2024/04/04 03:22:36 */ /*TABSTOP=4*/ @@ -17,8 +17,8 @@ extern "C" { #include -extern void sub_timeval(struct timeval t1, struct timeval t2, struct timeval *td); -extern void add_timeval(struct timeval t1, struct timeval t2, struct timeval *td); +extern void sub_timeval(struct timeval t_old, struct timeval t_new, struct timeval *td); +extern void add_timeval(struct timeval t_old, struct timeval t_new, struct timeval *td); extern int cmp_timeval(struct timeval t1, struct timeval t2); #ifdef __cplusplus