Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libmseed for openSUSE:Factory checked in at 2026-01-26 11:06:06 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libmseed (Old) and /work/SRC/openSUSE:Factory/.libmseed.new.1928 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libmseed" Mon Jan 26 11:06:06 2026 rev:12 rq:1329080 version:3.2.4 Changes: -------- --- /work/SRC/openSUSE:Factory/libmseed/libmseed.changes 2026-01-03 17:27:02.430806967 +0100 +++ /work/SRC/openSUSE:Factory/.libmseed.new.1928/libmseed.changes 2026-01-26 11:06:13.543784392 +0100 @@ -1,0 +2,7 @@ +Sun Jan 25 17:25:08 UTC 2026 - Andreas Stieger <[email protected]> + +- update to 3.2.4: + * Fix tracking of message count in log registry + * developer visible fixes to functions + +------------------------------------------------------------------- Old: ---- libmseed-3.2.3.tar.gz New: ---- libmseed-3.2.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libmseed.spec ++++++ --- /var/tmp/diff_new_pack.Khtlxo/_old 2026-01-26 11:06:14.151809430 +0100 +++ /var/tmp/diff_new_pack.Khtlxo/_new 2026-01-26 11:06:14.155809595 +0100 @@ -18,7 +18,7 @@ %define sover 3 Name: libmseed -Version: 3.2.3 +Version: 3.2.4 Release: 0 Summary: MiniSEED data format library License: Apache-2.0 ++++++ libmseed-3.2.3.tar.gz -> libmseed-3.2.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libmseed-3.2.3/ChangeLog new/libmseed-3.2.4/ChangeLog --- old/libmseed-3.2.3/ChangeLog 2026-01-01 23:33:34.000000000 +0100 +++ new/libmseed-3.2.4/ChangeLog 2026-01-25 00:20:43.000000000 +0100 @@ -1,3 +1,10 @@ +2026.024: v3.2.4 + - Add ms_rlog_pop() to return messages from the error/warning log registry + - Fix return value from ms_rlog_emit() + - Fix tracking of message count in log registry. + - Add tests for logging facility. + - Add example/lm_extraheaders.c to demonstrate extra header functionality. + 2026.001: v3.2.3 - Add mseh_get_ptr_type() to determine type of an extra header. - Add unsigned integer (uint64_t) support to `mseh_set_ptr_r()` and `mseh_get_ptr_r()`. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libmseed-3.2.3/doc/examples.dox new/libmseed-3.2.4/doc/examples.dox --- old/libmseed-3.2.3/doc/examples.dox 2026-01-01 23:33:34.000000000 +0100 +++ new/libmseed-3.2.4/doc/examples.dox 2026-01-25 00:20:43.000000000 +0100 @@ -98,4 +98,10 @@ @include lm_sids.c +@section example-lm_extraheaders Working with extra headers + +An example of working with extra headers in miniSEED records. + +@include lm_extraheaders.c + */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libmseed-3.2.3/doc/porting.dox new/libmseed-3.2.4/doc/porting.dox --- old/libmseed-3.2.3/doc/porting.dox 2026-01-01 23:33:34.000000000 +0100 +++ new/libmseed-3.2.4/doc/porting.dox 2026-01-25 00:20:43.000000000 +0100 @@ -34,6 +34,10 @@ - MS3TraceList.numtraces renamed to ::MS3TraceList.numtraceids. +- MS3TraceID is no longer a simple linked list, it is now a skip list. +This slightly changes the syntax for traversing the list as illustrated +in the examples. + - The \c fpos and \c last arguments of ::ms3_readmsr(), ::ms3_readmsr_r(), and ::ms3_readmsr_selection() have been removed. The same capability is available using the re-entrant ('_r') functions with ::MS3FileParam (msfp) @@ -47,6 +51,11 @@ required for values used by a program directly (they really _should not_ be). +- Convert all usage of HPTMODULUS to NSTMODULUS abd HPTERROR to NSTERROR. + +- Review usage of NSTERROR for cases where the new NSTUNSET value is used +instead to indicate an unset value. + - Review and change functions with \c hptime in the name to their closest equivalent for the new \c nstime version. See @ref time-related. @@ -64,7 +73,7 @@ @note If the above functions are simply renamed and the flag values are not changed to use the enumerated types, leaving integers as arguments, there may be a **non-obvious bug**. Most compilers will -quiety accept an integer for the enum argument, which may or may not +quietly accept an integer for the enum argument, which may or may not match the desired time format or subsecond handling. Assume _not_. - Adapt to new identification of a record source. The channel @@ -73,7 +82,7 @@ (SID) with the pattern \c FDSN:NN_SSS_LL_B_S_s. The following helper functions are available for mapping between the new URI-based scheme and the traditional SEED network, station, location, channel scheme: - - ms_sid2nslc(): convert SID to traditional SEED identifiers + - ms_sid2nslc_n(): convert SID to traditional SEED identifiers - ms_nslc2sid(): convert traditional SEED identifiers to an SID - ms_seedchan2xchan(): convert SEED channel to SID-style channel components - ms_xchan2seedchan(): convert SID band, source and position to SEED channel @@ -125,26 +134,26 @@ - Activity flags (field 12): - Bit 0 (calibration signals present) => Fixed header flags, bit 0 - Bit 1 (time correction applied), not retained - - Bit 2 (begining of event) => Extra header FDSN.Event.Begin - - Bit 3 (end of event) => Extra header FDSN.Event.End - - Bit 4 (positive leap second included) => Extra header FDSN.Time.LeapSecond - - Bit 5 (negative leap second included) => Extra header FDSN.Time.LeapSecond - - Bit 6 (event in progress) => Extra header FDSN.Event.InProgress + - Bit 2 (begining of event) => Extra header /FDSN/Event/Begin + - Bit 3 (end of event) => Extra header /FDSN/Event/End + - Bit 4 (positive leap second included) => Extra header /FDSN/Time/LeapSecond + - Bit 5 (negative leap second included) => Extra header /FDSN/Time/LeapSecond + - Bit 6 (event in progress) => Extra header /FDSN/Event/InProgress - I/O flags (field 13): - - Bit 0 (Station volume parity error) => Extra header FDSN.Flags.StationVolumeParityError - - Bit 1 (Long record read) => Extra header FDSN.Flags.LongRecordRead - - Bit 2 (Short record read) => Extra header FDSN.Flags.ShortRecordRead - - Bit 3 (Start of time series) => Extra header FDSN.Flags.StartOfTimeSeries - - Bit 4 (End of time series) => Extra header FDSN.Flags.EndOfTimeSeries + - Bit 0 (Station volume parity error) => Extra header /FDSN/Flags/StationVolumeParityError + - Bit 1 (Long record read) => Extra header /FDSN/Flags/LongRecordRead + - Bit 2 (Short record read) => Extra header /FDSN/Flags/ShortRecordRead + - Bit 3 (Start of time series) => Extra header /FDSN/Flags/StartOfTimeSeries + - Bit 4 (End of time series) => Extra header /FDSN/Flags/EndOfTimeSeries - Bit 5 (Clock locked) => Fixed header flags, bit 2 - Data quality flags (field 14): - - Bit 0 (Amplifier saturation detected) => Extra header FDSN.Flags.AmplifierSaturation - - Bit 1 (Digitizer clipping detected) => Extra header FDSN.Flags.DigitizerClipping - - Bit 2 (Spikes detected) => Extra header FDSN.Flags.Spikes - - Bit 3 (Glitches detected) => Extra header FDSN.Flags.Glitches - - Bit 4 (Missing/padded data present) => Extra header FDSN.Flags.MissingData - - Bit 5 (Telemetry synchronization error) => Extra header FDSN.Flags.TelemetrySyncError - - Bit 6 (Digital filter may be charging) => Extra header FDSN.Flags.FilterCharging + - Bit 0 (Amplifier saturation detected) => Extra header /FDSN/Flags/AmplifierSaturation + - Bit 1 (Digitizer clipping detected) => Extra header /FDSN/Flags/DigitizerClipping + - Bit 2 (Spikes detected) => Extra header /FDSN/Flags/Spikes + - Bit 3 (Glitches detected) => Extra header /FDSN/Flags/Glitches + - Bit 4 (Missing/padded data present) => Extra header /FDSN/Flags/MissingData + - Bit 5 (Telemetry synchronization error) => Extra header /FDSN/Flags/TelemetrySyncError + - Bit 6 (Digital filter may be charging) => Extra header /FDSN/Flags/FilterCharging - Bit 7 (Time tag questionable) => Fixed header flags, bit 1 The existence of these boolean extra header values can be tested with mseh_exists(). @@ -169,7 +178,7 @@ - Version 2.x timing blockettes (500) contain one "Clock Model" designation per blockette, potentially allowing for multiple designations in a single record. Upon reading such a record, only - the last clock model is retained (in the \c FDSN.Clock.Model @ref + the last clock model is retained (in the \c /FDSN/Clock/Model @ref extra-headers). - Version 2.x sequence numbers, as six digit ASCII string, are not diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libmseed-3.2.3/example/lm_extraheaders.c new/libmseed-3.2.4/example/lm_extraheaders.c --- old/libmseed-3.2.3/example/lm_extraheaders.c 1970-01-01 01:00:00.000000000 +0100 +++ new/libmseed-3.2.4/example/lm_extraheaders.c 2026-01-25 00:20:43.000000000 +0100 @@ -0,0 +1,220 @@ +/*************************************************************************** + * A program demonstrating manipulation of extra headers in miniSEED records + * + * Extra headers are stored as JSON and accessed using JSON Pointer syntax + * (RFC 6901). + * + * This file is part of the miniSEED Library. + * + * Copyright (c) 2026 Chad Trabant, EarthScope Data Services + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <libmseed.h> + +const char * +header_type (int type) +{ + switch (type) + { + case 'u': + return "unsigned integer"; + case 'i': + return "signed integer"; + case 'n': + return "number (real)"; + case 's': + return "string"; + case 'b': + return "boolean"; + case 'a': + return "array"; + case 'o': + return "object"; + default: + return "unknown or not found"; + } +} + +int +main () +{ + MS3Record *msr = NULL; + + /* Values for setting and getting */ + uint64_t quality; + int64_t leap_second; + double correction; + int event_begin; + + /* Create a new miniSEED record */ + msr = msr3_init (NULL); + if (!msr) + { + ms_log (2, "Error initializing MS3Record\n"); + return -1; + } + + /* Populate basic header fields */ + strcpy (msr->sid, "FDSN:XX_TEST__L_H_Z"); + msr->reclen = 512; + msr->pubversion = 1; + msr->starttime = ms_timestr2nstime ("2024-01-24T12:00:00.000000Z"); + msr->samprate = 1.0; + msr->encoding = DE_STEIM2; + msr->numsamples = 100; + msr->datasize = 0; + + printf ("Setting FDSN and custom headers:\n"); + + quality = 100; + if (mseh_set_uint64 (msr, "/FDSN/Time/Quality", &quality)) + { + ms_log (2, "Error setting /FDSN/Time/Quality header\n"); + } + + leap_second = -1; + if (mseh_set_int64 (msr, "/FDSN/Time/LeapSecond", &leap_second)) + { + ms_log (2, "Error setting /FDSN/Time/LeapSecond header\n"); + } + + correction = 1.234567; + if (mseh_set_number (msr, "/FDSN/Time/Correction", &correction)) + { + ms_log (2, "Error setting /FDSN/Time/Correction header\n"); + } + + event_begin = 1; + if (mseh_set_boolean (msr, "/FDSN/Event/Begin", &event_begin)) + { + ms_log (2, "Error setting /FDSN/Event/Begin header\n"); + } + + /* Set custom headers of string type */ + char *status = "Down"; + if (mseh_set_string (msr, "/Endor/Shield/Status", status)) + { + ms_log (2, "Error setting /Endor/Shield/Status header\n"); + } + char *time_string = "1983-05-25T09:14:00.000000Z"; + if (mseh_set_string (msr, "/Endor/Shield/BootTime", time_string)) + { + ms_log (2, "Error setting /Endor/Shield/BootTime header\n"); + } + + /* Print all extra headers */ + printf ("\n==== Printing all extra headers ====\n"); + if (mseh_print (msr, 2) < 0) + { + ms_log (2, "Error printing extra headers\n"); + } + + printf ("\n==== Checking existence of headers ====\n"); + + if (mseh_exists (msr, "/FDSN/Time/Quality")) + { + printf (" /FDSN/Time/Quality exists\n"); + } + + if (mseh_exists (msr, "/FDSN/Time/MaxEstimatedError") == 0) + { + printf (" /FDSN/Time/MaxEstimatedError DOES NOT exist\n"); + } + + /* Get values */ + if (mseh_get_uint64 (msr, "/FDSN/Time/Quality", &quality) == 0) + { + printf (" Got /FDSN/Time/Quality = %" PRIu64 "\n", quality); + } + + if (mseh_get_int64 (msr, "/FDSN/Time/LeapSecond", &leap_second) == 0) + { + printf (" Got /FDSN/Time/LeapSecond = %lld\n", (long long)leap_second); + } + + if (mseh_get_number (msr, "/FDSN/Time/Correction", &correction) == 0) + { + printf (" Got /FDSN/Time/Correction = %.6f\n", correction); + } + + if (mseh_get_boolean (msr, "/FDSN/Event/Begin", &event_begin) == 0) + { + printf (" Got /FDSN/Event/Begin = %s\n", event_begin ? "true" : "false"); + } + + char get_status[100]; + if (mseh_get_string (msr, "/Endor/Shield/Status", get_status, + sizeof (get_status)) == 0) + { + printf (" Got /Endor/Shield/Status = \"%s\"\n", get_status); + } + + char get_time_string[100]; + if (mseh_get_string (msr, "/Endor/Shield/BootTime", get_time_string, + sizeof (get_time_string)) == 0) + { + printf (" Got /Endor/Shield/BootTime = \"%s\"\n", get_time_string); + } + + printf ("\n==== Checking header types ====\n"); + + int type = mseh_get_ptr_type (msr, "/FDSN/Time", NULL); + printf (" /FDSN/Time type: %s\n", header_type (type)); + + type = mseh_get_ptr_type (msr, "/FDSN/Time/Quality", NULL); + printf (" /FDSN/Time/Quality type: %s\n", header_type (type)); + + type = mseh_get_ptr_type (msr, "/Endor/Shield/BootTime", NULL); + printf (" /Endor/Shield/BootTime type: %s\n", header_type (type)); + + printf ("\n==== Apply JSON Merge Patch to modify headers ====\n"); + + /* Create a merge patch that: + * - Adds /FDSN/Event/End + * - Removes /FDSN/Event/Begin + * - Modifies /FDSN/Time/Quality to 96 */ + char *merge_patch = "{\"FDSN\": {\"Event\": {\"End\": true, \"Begin\": null}, \"Time\": {\"Quality\": 96}}}"; + if (mseh_set_ptr_r (msr, "", merge_patch, 'M', NULL) < 0) + { + ms_log (2, "Error applying merge patch\n"); + } + + if (mseh_print (msr, 2) < 0) + { + ms_log (2, "Error printing extra headers\n"); + } + + printf ("\n==== Replace all extra headers ====\n"); + + char *new_headers = "{\"Operator\": {\"Base\": \"Hoth\", \"Temperature\": -32.1}}"; + if (mseh_replace (msr, new_headers) < 0) + { + ms_log (2, "Error replacing extra headers\n"); + } + else + { + printf ("\nNew extra headers:\n"); + mseh_print (msr, 2); + } + + /* Clean up */ + msr3_free (&msr); + + return 0; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libmseed-3.2.3/libmseed.h new/libmseed-3.2.4/libmseed.h --- old/libmseed-3.2.3/libmseed.h 2026-01-01 23:33:34.000000000 +0100 +++ new/libmseed-3.2.4/libmseed.h 2026-01-25 00:20:43.000000000 +0100 @@ -29,8 +29,8 @@ { #endif -#define LIBMSEED_VERSION "3.2.3" //!< Library version -#define LIBMSEED_RELEASE "2026.001" //!< Library release date +#define LIBMSEED_VERSION "3.2.4" //!< Library version +#define LIBMSEED_RELEASE "2026.024" //!< Library release date /** @defgroup io-functions File and URL I/O */ /** @defgroup miniseed-record Record Handling */ @@ -1277,6 +1277,7 @@ const char *logprefix, void (*diag_print) (const char *), const char *errprefix, int maxmessages); extern int ms_rlog_emit (MSLogParam *logp, int count, int context); +extern int ms_rlog_pop (MSLogParam *logp, char *message, size_t size, int context); extern int ms_rlog_free (MSLogParam *logp); /** @} */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libmseed-3.2.3/logging.c new/libmseed-3.2.4/logging.c --- old/libmseed-3.2.3/logging.c 2026-01-01 23:33:34.000000000 +0100 +++ new/libmseed-3.2.4/logging.c 2026-01-25 00:20:43.000000000 +0100 @@ -454,7 +454,7 @@ strncpy (logentry->message, message, sizeof (logentry->message)); logentry->message[sizeof (logentry->message) - 1] = '\0'; - /* Add entry to registry */ + /* Add entry to registry at the head of the list */ logentry->next = logreg->messages; logreg->messages = logentry; logreg->messagecnt += 1; @@ -473,7 +473,10 @@ logentry->next = NULL; if (count > logreg->maxmessages) + { free (logentry); + logreg->messagecnt -= 1; + } logentry = lognext; } @@ -524,10 +527,10 @@ /** ************************************************************************ * @brief Emit, aka send to print functions, messages from log registry * - * Emit messages from the log registry, using the printing functions - * identified by the ::MSLogParam. + * Emit error and warning messages from the log registry, using the printing + * functions identified by the ::MSLogParam. * - * Messages are printed in order from earliest to latest. + * Messages are printed in order from latest to earliest. * * The maximum number messages to emit, from most recent to earliest, * can be limited using @p count. If the value is 0 all messages are @@ -541,11 +544,11 @@ * @param[in] count Number of messages to emit, 0 to emit all messages * @param[in] context If non-zero include context by prefixing the function name (if available) * - * @returns The number of message emitted on success, and a negative - * value on error. + * @returns The number of messages emitted. * * @see ms_rloginit() * @see ms_rlog_free() + * @see ms_rlog_pop() ***************************************************************************/ int ms_rlog_emit (MSLogParam *logp, int count, int context) @@ -555,6 +558,7 @@ char local_message[MAX_LOG_MSG_LENGTH]; char *message = NULL; int emit = (count > 0) ? count : -1; + int emitted = 0; if (!logp) logp = &gMSLogParam; @@ -595,18 +599,85 @@ logentry = logprint->next; free (logprint); logprint = logentry; + emitted++; } - return 0; + logp->registry.messagecnt -= emitted; + + return emitted; } /* End of ms_rlog_emit() */ /** ************************************************************************ + * @brief Pop error and warning messages from log registry + * + * Messages are formatted and stored in the specified message buffer. + * + * The latest (most recent) message is popped. + * + * @param[in] logp ::MSLogParam for this message or NULL for global parameters + * @param[out] message Message buffer to store the message in + * @param[in] size Size of the message buffer + * @param[in] context If non-zero include context by prefixing the function name (if available) + * + * @retval >0 length of message copied to buffer on success + * @retval 0 if no message is available + * @retval -1 on error + * + * @see ms_rloginit() + * @see ms_rlog_free() + * @see ms_rlog_emit() + ***************************************************************************/ +int +ms_rlog_pop (MSLogParam *logp, char *message, size_t size, int context) +{ + MSLogEntry *logprint = NULL; + char local_message[MAX_LOG_MSG_LENGTH]; + char *message_ptr = NULL; + size_t length = 0; + + if (!message || size == 0) + return -1; + + if (!logp) + logp = &gMSLogParam; + + logprint = logp->registry.messages; + + /* Copy and free message */ + if (logprint) + { + /* Add function name to message if requested and present */ + if (context && logprint->function[0] != '\0') + { + snprintf (local_message, sizeof (local_message), "%s() %.*s", logprint->function, + (int)(MAX_LOG_MSG_LENGTH - sizeof (logprint->function) - 3), logprint->message); + message_ptr = local_message; + } + else + { + message_ptr = logprint->message; + } + + /* Copy message to buffer */ + strncpy (message, message_ptr, size); + message[size - 1] = '\0'; + length = strlen (message); + + /* Remove message from registry */ + logp->registry.messages = logprint->next; + logp->registry.messagecnt -= 1; + free (logprint); + } + + return (int)length; +} /* End of ms_rlog_pop() */ + +/** ************************************************************************ * @brief Free, without emitting, all messages from log registry * * @param[in] logp ::MSLogParam for this message or NULL for global parameters * - * @returns The number of message freed on success, and a negative - * value on error. + * @returns The number of messages freed. ***************************************************************************/ int ms_rlog_free (MSLogParam *logp) @@ -628,5 +699,7 @@ logentry = logp->registry.messages; } + logp->registry.messagecnt = 0; + return freed; } /* End of ms_rlog_free() */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libmseed-3.2.3/test/CMakeLists.txt new/libmseed-3.2.4/test/CMakeLists.txt --- old/libmseed-3.2.3/test/CMakeLists.txt 2026-01-01 23:33:34.000000000 +0100 +++ new/libmseed-3.2.4/test/CMakeLists.txt 2026-01-25 00:20:43.000000000 +0100 @@ -4,6 +4,7 @@ set(TEST_PROGRAMS test-crc test-extraheaders + test-logging test-msrutils test-read test-repack diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libmseed-3.2.3/test/lm_extraheaders.c new/libmseed-3.2.4/test/lm_extraheaders.c --- old/libmseed-3.2.3/test/lm_extraheaders.c 1970-01-01 01:00:00.000000000 +0100 +++ new/libmseed-3.2.4/test/lm_extraheaders.c 2026-01-26 11:06:14.379818820 +0100 @@ -0,0 +1 @@ +symbolic link to ../example/lm_extraheaders.c diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libmseed-3.2.3/test/test-logging.c new/libmseed-3.2.4/test/test-logging.c --- old/libmseed-3.2.3/test/test-logging.c 1970-01-01 01:00:00.000000000 +0100 +++ new/libmseed-3.2.4/test/test-logging.c 2026-01-25 00:20:43.000000000 +0100 @@ -0,0 +1,266 @@ +#include <tau/tau.h> +#include <libmseed.h> +#include <string.h> + +/* Helper variables to track custom print function calls */ +static int log_print_called = 0; +static int diag_print_called = 0; +static char last_log_message[1024] = {0}; +static char last_diag_message[1024] = {0}; + +/* Custom print function for log messages */ +static void +custom_log_print (const char *message) +{ + log_print_called++; + strncpy (last_log_message, message, sizeof (last_log_message) - 1); +} + +/* Custom print function for diagnostic/error messages */ +static void +custom_diag_print (const char *message) +{ + diag_print_called++; + strncpy (last_diag_message, message, sizeof (last_diag_message) - 1); +} + +/* Reset helper variables */ +static void +reset_print_counters (void) +{ + log_print_called = 0; + diag_print_called = 0; + last_log_message[0] = '\0'; + last_diag_message[0] = '\0'; +} + +TEST (logging, rloginit_basic) +{ + /* Test basic initialization with all NULL parameters */ + ms_rloginit (NULL, NULL, NULL, NULL, 0); + + /* Test initialization with custom print functions */ + reset_print_counters (); + ms_rloginit (custom_log_print, "LOG: ", custom_diag_print, "ERROR: ", 0); + + /* Verify custom functions are called */ + ms_log (0, "Test log message"); + CHECK (log_print_called > 0, "Custom log print function was not called"); + CHECK_STREQ (last_log_message, "LOG: Test log message"); + + ms_log (2, "Test error message"); + CHECK (diag_print_called > 0, "Custom diag print function was not called"); + CHECK_STREQ (last_diag_message, "ERROR: Test error message"); + + /* Test initialization with message registry enabled */ + ms_rloginit (NULL, NULL, NULL, NULL, 10); + ms_log (2, "Error to be stored"); + + /* Reset to defaults */ + ms_rloginit (NULL, NULL, NULL, NULL, 0); + + /* Test with very long prefix (should be rejected) */ + char long_prefix[MAX_LOG_MSG_LENGTH + 10]; + memset (long_prefix, 'X', sizeof (long_prefix) - 1); + long_prefix[sizeof (long_prefix) - 1] = '\0'; + + ms_rloginit (custom_log_print, long_prefix, custom_diag_print, NULL, 0); + CHECK_STREQ (last_diag_message, "ERROR: log message prefix is too large"); + + ms_rloginit (custom_log_print, NULL, custom_diag_print, long_prefix, 0); + CHECK_STREQ (last_diag_message, "ERROR: error message prefix is too large"); +} + +TEST (logging, rloginit_l) +{ + MSLogParam *logp; + + /* Test allocation when logp is NULL */ + logp = ms_rloginit_l (NULL, NULL, NULL, NULL, NULL, 0); + REQUIRE (logp != NULL, "ms_rloginit_l failed to allocate MSLogParam"); + + /* Verify initial values */ + CHECK (logp->registry.maxmessages == 0, "maxmessages not initialized correctly"); + CHECK (logp->registry.messagecnt == 0, "messagecnt not initialized correctly"); + CHECK (logp->registry.messages == NULL, "messages not initialized to NULL"); + + /* Test reinitialization with custom values */ + logp = ms_rloginit_l (logp, custom_log_print, "PREFIX: ", custom_diag_print, "ERR: ", 0); + REQUIRE (logp != NULL, "ms_rloginit_l failed to reinitialize"); + CHECK (logp->log_print == custom_log_print, "log_print function not set"); + CHECK (logp->diag_print == custom_diag_print, "diag_print function not set"); + CHECK_STREQ (logp->logprefix, "PREFIX: "); + CHECK_STREQ (logp->errprefix, "ERR: "); + + /* Test that custom functions work */ + reset_print_counters (); + ms_log_l (logp, 0, "Test message"); + CHECK (log_print_called == 1, "Custom log function not used"); + CHECK_STREQ (last_log_message, "PREFIX: Test message"); + ms_log_l (logp, 2, "Test error message"); + CHECK (diag_print_called == 1, "Custom diag function not used"); + CHECK_STREQ (last_diag_message, "ERR: Test error message"); + + /* Test reinitialization with log registry enabled */ + logp = ms_rloginit_l (logp, custom_log_print, "PREFIX: ", custom_diag_print, "ERR: ", 5); + REQUIRE (logp != NULL, "ms_rloginit_l failed to reinitialize"); + CHECK (logp->registry.maxmessages == 5, "maxmessages not set correctly"); + CHECK (logp->registry.messagecnt == 0, "messagecnt not reset to 0"); + CHECK (logp->registry.messages == NULL, "messages not reset to NULL"); + + free (logp); +} + +TEST (logging, loginit_macros) +{ + MSLogParam custom_param = MSLogParam_INITIALIZER; + MSLogParam *logp; + + /* Test ms_loginit macro (wrapper that disables registry) */ + reset_print_counters (); + ms_loginit (custom_log_print, "LOG: ", custom_diag_print, "ERR: "); + + ms_log (0, "Test message"); + CHECK (log_print_called > 0, "ms_loginit macro did not set log function"); + + /* Test ms_loginit_l macro */ + reset_print_counters (); + logp = ms_loginit_l (&custom_param, custom_log_print, NULL, custom_diag_print, NULL); + REQUIRE (logp != NULL, "ms_loginit_l macro failed to allocate MSLogParam"); + + CHECK (logp == &custom_param, "ms_loginit_l macro returned wrong pointer"); + CHECK (logp->registry.maxmessages == 0, "ms_loginit_l should disable registry (maxmessages=0)"); + + ms_log_l (logp, 1, "Warning message"); + CHECK (diag_print_called > 0, "ms_loginit_l macro did not set diag function"); +} + +TEST (logging, logregistry_basic) +{ + MSLogParam custom_param = MSLogParam_INITIALIZER; + MSLogParam *logp; + int freed; + + /* Initialize with registry enabled */ + logp = ms_rloginit_l (&custom_param, custom_log_print, NULL, custom_diag_print, NULL, 10); + REQUIRE (logp != NULL, "ms_rloginit_l failed to allocate MSLogParam"); + + /* Add some messages to registry */ + ms_log_l (logp, 1, "Warning 1"); + ms_log_l (logp, 2, "Error 1"); + ms_log_l (logp, 1, "Warning 2"); + ms_log_l (logp, 2, "Error 2"); + + CHECK (logp->registry.messagecnt == 4, "messagecnt should be 4"); + CHECK (logp->registry.messages != NULL, "messages should not be NULL"); + + /* Emit all messages */ + reset_print_counters (); + int emitted = ms_rlog_emit (logp, 0, 0); + CHECK (emitted == 4, "Should have emitted 4 messages"); + CHECK (diag_print_called == 4, "Custom diag function not used 4 times"); + CHECK_STREQ (last_diag_message, "Error: Error 2"); + CHECK (logp->registry.messagecnt == 0, "messagecnt should be 0 after emit all"); + CHECK (logp->registry.messages == NULL, "messages should be NULL after emit all"); + + /* Add more messages and free them */ + ms_log_l (logp, 2, "Error 2"); + ms_log_l (logp, 2, "Error 3"); + + freed = ms_rlog_free (logp); + CHECK (freed == 2, "Should have freed 2 messages"); + CHECK (logp->registry.messagecnt == 0, "messagecnt should be 0 after free"); + CHECK (logp->registry.messages == NULL, "messages should be NULL after free"); +} + +TEST (logging, logregistry_pop) +{ + MSLogParam custom_param = MSLogParam_INITIALIZER; + MSLogParam *logp; + char message[256]; + int length; + + /* Initialize with registry enabled */ + logp = ms_rloginit_l (&custom_param, custom_log_print, NULL, custom_diag_print, NULL, 10); + REQUIRE (logp != NULL, "ms_rloginit_l failed to allocate MSLogParam"); + + /* Test pop from empty registry */ + length = ms_rlog_pop (logp, message, sizeof (message), 0); + CHECK (length == 0, "Pop from empty registry should return 0"); + + /* Add messages */ + ms_log_l (logp, 2, "First error"); + ms_log_l (logp, 2, "Second error"); + ms_log_l (logp, 2, "Third error"); + + CHECK (logp->registry.messagecnt == 3, "Should have 3 messages"); + + /* Pop latest message (should be "Third error") */ + length = ms_rlog_pop (logp, message, sizeof (message), 0); + CHECK (length > 0, "Pop should return message length"); + CHECK (strstr (message, "Third error") != NULL, "Should pop third message first"); + CHECK (logp->registry.messagecnt == 2, "messagecnt should be 2 after pop"); + + /* Pop next message */ + length = ms_rlog_pop (logp, message, sizeof (message), 0); + CHECK (strstr (message, "Second error") != NULL, "Should pop second message"); + CHECK (logp->registry.messagecnt == 1, "messagecnt should be 1 after second pop"); + + /* Pop last message */ + length = ms_rlog_pop (logp, message, sizeof (message), 0); + CHECK (strstr (message, "First error") != NULL, "Should pop first message last"); + CHECK (logp->registry.messagecnt == 0, "messagecnt should be 0 after popping all"); + CHECK (logp->registry.messages == NULL, "messages should be NULL after popping all"); + + /* Try to pop from empty registry again */ + length = ms_rlog_pop (logp, message, sizeof (message), 0); + CHECK (length == 0, "Pop from empty registry should return 0"); +} + +TEST (logging, logregistry_pop_validation) +{ + MSLogParam custom_param = MSLogParam_INITIALIZER; + MSLogParam *logp; + char message[256]; + int result; + + /* Initialize with registry enabled */ + logp = ms_rloginit_l (&custom_param, NULL, NULL, NULL, NULL, 10); + ms_log_l (logp, 2, "Test error"); + + /* Test with NULL message buffer */ + result = ms_rlog_pop (logp, NULL, sizeof (message), 0); + CHECK (result == -1, "Pop with NULL buffer should return -1"); + + /* Test with zero size */ + result = ms_rlog_pop (logp, message, 0, 0); + CHECK (result == -1, "Pop with zero size should return -1"); + + /* Message should still be in registry */ + CHECK (logp->registry.messagecnt == 1, "Message should not be removed after failed pops"); + + /* Clean up */ + ms_rlog_free (logp); +} + +TEST (logging, logregistry_maxmessages) +{ + MSLogParam custom_param = MSLogParam_INITIALIZER; + MSLogParam *logp; + int i; + + /* Initialize with max 5 messages */ + logp = ms_rloginit_l (&custom_param, custom_log_print, NULL, custom_diag_print, NULL, 5); + + /* Add 10 messages, only 5 should be kept */ + for (i = 0; i < 10; i++) + { + ms_log_l (logp, 2, "Error %d", i); + } + + /* Should only have 5 messages (oldest discarded) */ + CHECK (logp->registry.messagecnt == 5, "Should only have maxmessages (5) in registry"); + + /* Clean up */ + ms_rlog_free (logp); +}
