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);
+}

Reply via email to