You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			1158 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C
		
	
			
		
		
	
	
			1158 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C
		
	
| /***************************************************************************
 | |
|  * Routines for dealing with libmseed extra headers stored as JSON.
 | |
|  *
 | |
|  * This file is part of the miniSEED Library.
 | |
|  *
 | |
|  * Copyright (c) 2023 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 "libmseed.h"
 | |
| #include "extraheaders.h"
 | |
| 
 | |
| /* Private allocation wrappers for yyjson's allocator definition */
 | |
| void *_priv_malloc(void *ctx, size_t size) {
 | |
|     UNUSED(ctx);
 | |
|     return libmseed_memory.malloc(size);
 | |
| }
 | |
| 
 | |
| void *_priv_realloc(void *ctx, void *ptr, size_t oldsize, size_t size) {
 | |
|     UNUSED(ctx);
 | |
|     UNUSED(oldsize);
 | |
|     return libmseed_memory.realloc(ptr, size);
 | |
| }
 | |
| 
 | |
| void _priv_free(void *ctx, void *ptr) {
 | |
|     UNUSED(ctx);
 | |
|     libmseed_memory.free(ptr);
 | |
| }
 | |
| 
 | |
| /***************************************************************************
 | |
|  * Internal routine to parse JSON strings into internal state storage.
 | |
|  *
 | |
|  * The state container is allocated if needed and if JSON is supplied,
 | |
|  * it is parsed into the immutable form.
 | |
|  *
 | |
|  * @param[in] jsonstring JSON string to parse
 | |
|  * @param[in] length Length of JSON string
 | |
|  * @param[in] parsed Internal parsed state
 | |
|  *
 | |
|  * @returns A LM_PARSED_JSON* on success or NULL on error
 | |
|  *
 | |
|  * \sa mseh_free_parsestate()
 | |
|  ***************************************************************************/
 | |
| static LM_PARSED_JSON*
 | |
| parse_json (char *jsonstring, size_t length, LM_PARSED_JSON *parsed)
 | |
| {
 | |
|   yyjson_read_flag flg = YYJSON_READ_NOFLAG;
 | |
|   yyjson_read_err err;
 | |
|   yyjson_alc alc = {_priv_malloc, _priv_realloc, _priv_free, NULL};
 | |
| 
 | |
|   /* Allocate parsed state if needed */
 | |
|   if (!parsed)
 | |
|   {
 | |
|     if ((parsed = libmseed_memory.malloc (sizeof (LM_PARSED_JSON))) == NULL)
 | |
|     {
 | |
|       ms_log (2, "%s() Cannot allocate memory for internal JSON parsing state\n", __func__);
 | |
|       return NULL;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       parsed->doc     = NULL;
 | |
|       parsed->mut_doc = NULL;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* Nothing to parse */
 | |
|   if (jsonstring == NULL || length == 0)
 | |
|   {
 | |
|     return parsed;
 | |
|   }
 | |
| 
 | |
|   /* Free existing immutable doc */
 | |
|   if (parsed->doc)
 | |
|   {
 | |
|     yyjson_doc_free (parsed->doc);
 | |
|     parsed->doc     = NULL;
 | |
|   }
 | |
| 
 | |
|   /* Free existing mutable document */
 | |
|   if (parsed->mut_doc != NULL)
 | |
|   {
 | |
|     yyjson_mut_doc_free (parsed->mut_doc);
 | |
|     parsed->mut_doc = NULL;
 | |
|   }
 | |
| 
 | |
|   /* Parse JSON into immutable form */
 | |
|   if ((parsed->doc = yyjson_read_opts (jsonstring, length, flg, &alc, &err)) == NULL)
 | |
|   {
 | |
|     ms_log (2, "%s() Cannot parse extra header JSON: %s\n",
 | |
|             __func__, (err.msg) ? err.msg : "Unknown error");
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   return parsed;
 | |
| }
 | |
| 
 | |
| /**********************************************************************/ /**
 | |
|  * @brief Search for and return an extra header value.
 | |
|  *
 | |
|  * The extra header value is specified as a JSON Pointer (RFC 6901), e.g.
 | |
|  * \c '/objectA/objectB/header'.
 | |
|  *
 | |
|  * This routine can get used to test for the existence of a value
 | |
|  * without returning the value by setting \a value to NULL.
 | |
|  *
 | |
|  * If the target item is found (and \a value parameter is set) the
 | |
|  * value will be copied into the memory specified by \c value.  The
 | |
|  * \a type value specifies the data type expected.
 | |
|  *
 | |
|  * If a \a parsestate pointer is supplied, the parsed (deserialized) JSON
 | |
|  * data are stored here.  This value may be used in subsequent calls to
 | |
|  * avoid re-parsing the JSON.  The data must be freed with
 | |
|  * mseh_free_parsestate() when done reading the JSON.  If this value
 | |
|  * is NULL the parse state will be created and destroyed on each call.
 | |
|  *
 | |
|  * @param[in] msr Parsed miniSEED record to search
 | |
|  * @param[in] ptr Header value desired, as JSON Pointer
 | |
|  * @param[out] value Buffer for value, of type \c type
 | |
|  * @param[in] type Type of value expected, one of:
 | |
|  * @parblock
 | |
|  * - \c 'n' - \a value is type \a double
 | |
|  * - \c 'i' - \a value is type \a int64_t
 | |
|  * - \c 's' - \a value is type \a char* (maximum length is: \c maxlength - 1)
 | |
|  * - \c 'b' - \a value of type \a int (boolean value of 0 or 1)
 | |
|  * @endparblock
 | |
|  * @param[in] maxlength Maximum length of string value
 | |
|  * @param[in] parsestate Parsed state for multiple operations, can be NULL
 | |
|  *
 | |
|  * @retval 0 on success
 | |
|  * @retval 1 when the value was not found
 | |
|  * @retval 2 when the value is of a different type
 | |
|  * @returns A (negative) libmseed error code on error
 | |
|  *
 | |
|  * \ref MessageOnError - this function logs a message on error
 | |
|  *
 | |
|  * \sa mseh_free_parsestate()
 | |
|  ***************************************************************************/
 | |
| int
 | |
| mseh_get_ptr_r (const MS3Record *msr, const char *ptr,
 | |
|                  void *value, char type, size_t maxlength,
 | |
|                  LM_PARSED_JSON **parsestate)
 | |
| {
 | |
|   LM_PARSED_JSON *parsed  = (parsestate) ? *parsestate : NULL;
 | |
| 
 | |
|   yyjson_alc alc = {_priv_malloc, _priv_realloc, _priv_free, NULL};
 | |
|   yyjson_val *extravalue  = NULL;
 | |
|   const char *stringvalue = NULL;
 | |
| 
 | |
|   int retval = 0;
 | |
| 
 | |
|   if (!msr || !ptr)
 | |
|   {
 | |
|     ms_log (2, "%s() Required argument not defined: 'msr' or 'ptr'\n", __func__);
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   /* Nothing can be found in no headers */
 | |
|   if (!msr->extralength)
 | |
|   {
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   if (!msr->extra)
 | |
|   {
 | |
|     ms_log (2, "%s() Expected extra headers (msr->extra) are not present\n", __func__);
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   /* Detect invalid JSON Pointer, i.e. with no root '/' designation */
 | |
|   if (ptr[0] != '/')
 | |
|   {
 | |
|     ms_log (2, "%s() Unsupported ptr notation: %s\n", __func__, ptr);
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   /* Parse JSON extra headers if not available in state */
 | |
|   if (parsed == NULL)
 | |
|   {
 | |
|     /* Parse to immutable state */
 | |
|     parsed = parse_json(msr->extra, msr->extralength, parsed);
 | |
| 
 | |
|     if (parsed == NULL)
 | |
|     {
 | |
|       return MS_GENERROR;
 | |
|     }
 | |
| 
 | |
|     /* Set supplied state pointer */
 | |
|     if (parsestate != NULL)
 | |
|     {
 | |
|       *parsestate = parsed;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* Create immutable document from mutable if needed */
 | |
|   if (parsed->mut_doc != NULL && parsed->doc == NULL)
 | |
|   {
 | |
|     if ((parsed->doc = yyjson_mut_doc_imut_copy (parsed->mut_doc, &alc)) == NULL)
 | |
|     {
 | |
|       ms_log (2, "%s() Cannot create immutable document from mutable\n", __func__);
 | |
| 
 | |
|       if (parsestate == NULL)
 | |
|       {
 | |
|         mseh_free_parsestate (&parsed);
 | |
|       }
 | |
| 
 | |
|       return MS_GENERROR;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* Get target value */
 | |
|   extravalue = yyjson_doc_ptr_get(parsed->doc, ptr);
 | |
| 
 | |
|   if (extravalue == NULL)
 | |
|   {
 | |
|     retval = 1;
 | |
|   }
 | |
|   else if (type == 'n' && yyjson_is_num (extravalue))
 | |
|   {
 | |
|     if (value)
 | |
|       *((double *)value) = unsafe_yyjson_get_num (extravalue);
 | |
|   }
 | |
|   else if (type == 'i' && yyjson_is_int (extravalue))
 | |
|   {
 | |
|     if (value)
 | |
|       *((int64_t *)value) = unsafe_yyjson_get_int (extravalue);
 | |
|   }
 | |
|   else if (type == 's' && yyjson_is_str (extravalue))
 | |
|   {
 | |
|     if (value)
 | |
|     {
 | |
|       stringvalue = unsafe_yyjson_get_str (extravalue);
 | |
|       strncpy ((char *)value, stringvalue, maxlength - 1);
 | |
|       ((char *)value)[maxlength - 1] = '\0';
 | |
|     }
 | |
|   }
 | |
|   else if (type == 'b' && yyjson_is_bool(extravalue))
 | |
|   {
 | |
|     if (value)
 | |
|       *((int *)value) = (unsafe_yyjson_get_bool (extravalue)) ? 1 : 0;
 | |
|   }
 | |
|   /* Return wrong type indicator if a value was requested */
 | |
|   else if (value)
 | |
|   {
 | |
|     retval = 2;
 | |
|   }
 | |
| 
 | |
|   /* Free parse state if not being retained */
 | |
|   if (parsestate == NULL)
 | |
|   {
 | |
|     mseh_free_parsestate (&parsed);
 | |
|   }
 | |
| 
 | |
|   return retval;
 | |
| } /* End of mseh_get_ptr_r() */
 | |
| 
 | |
| /**********************************************************************/ /**
 | |
|  * @brief Set the value of extra header values
 | |
|  *
 | |
|  * The extra header value is specified as a JSON Pointer (RFC 6901), e.g.
 | |
|  * \c '/objectA/objectB/header'.
 | |
|  *
 | |
|  * For most value types, if the \a ptr or final header values do not exist
 | |
|  * they will be created.  If the header value exists it will be replaced.
 | |
|  * When the value type is 'M', for Merge Patch (RFC 7386), the location
 | |
|  * indicated by \a ptr must exist.
 | |
|  *
 | |
|  * The \a type value specifies the data type expected for \c value.
 | |
|  *
 | |
|  * If a \a parsestate pointer is supplied, the parsed (deserialized) JSON
 | |
|  * data are stored here.  This value may be used in subsequent calls to
 | |
|  * avoid re-parsing the JSON.  When done setting headers using this
 | |
|  * functionality the following \a must be done:
 | |
|  * 1. call mseh_serialize() to create the JSON headers before writing the record
 | |
|  * 2. free the \a parsestate data with mseh_free_parsestate()
 | |
|  * If this value is NULL the parse state will be created and destroyed
 | |
|  * on each call.
 | |
|  *
 | |
|  * @param[in] msr Parsed miniSEED record to modify
 | |
|  * @param[in] ptr Header value to set as JSON Pointer, or JSON Merge Patch
 | |
|  * @param[in] value Buffer for value, of type \c type
 | |
|  * @param[in] type Type of value expected, one of:
 | |
|  * @parblock
 | |
|  * - \c 'n' - \a value is type \a double
 | |
|  * - \c 'i' - \a value is type \a int64_t
 | |
|  * - \c 's' - \a value is type \a char*
 | |
|  * - \c 'b' - \a value is type \a int (boolean value of 0 or 1)
 | |
|  * - \c 'M' - \a value is type \a char* and a Merge Patch to apply at \a ptr
 | |
|  * - \c 'V' - \a value is type \a yyjson_mut_val* to _set/replace_ (internal use)
 | |
|  * - \c 'A' - \a value is type \a yyjson_mut_val* to _append to array_ (internal use)
 | |
|  * @endparblock
 | |
|  * @param[in] parsestate Parsed state for multiple operations, can be NULL
 | |
|  *
 | |
|  * @retval 0 on success, otherwise a (negative) libmseed error code.
 | |
|  *
 | |
|  * \ref MessageOnError - this function logs a message on error
 | |
|  *
 | |
|  * \sa mseh_free_parsestate()
 | |
|  * \sa mseh_serialize()
 | |
|  ***************************************************************************/
 | |
| int
 | |
| mseh_set_ptr_r (MS3Record *msr, const char *ptr,
 | |
|                  void *value, char type,
 | |
|                  LM_PARSED_JSON **parsestate)
 | |
| {
 | |
|   LM_PARSED_JSON *parsed = (parsestate) ? *parsestate : NULL;
 | |
|   yyjson_alc alc = {_priv_malloc, _priv_realloc, _priv_free, NULL};
 | |
|   yyjson_doc *patch_idoc = NULL;
 | |
|   yyjson_mut_doc *patch_doc = NULL;
 | |
|   yyjson_mut_val *merged_val = NULL;
 | |
|   yyjson_mut_val *target_val = NULL;
 | |
|   yyjson_mut_val *array_val = NULL;
 | |
|   bool rv = false;
 | |
| 
 | |
|   if (!msr || !ptr || !value)
 | |
|   {
 | |
|     ms_log (2, "%s() Required argument not defined: 'msr', 'ptr', or 'value'\n", __func__);
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   /* Detect invalid JSON Pointer, i.e. with no root '/' designation */
 | |
|   if (ptr[0] != '/' && type != 'M')
 | |
|   {
 | |
|     ms_log (2, "%s() Unsupported JSON Pointer notation: %s\n", __func__, ptr);
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   /* Parse JSON extra headers if not available in state */
 | |
|   if (parsed == NULL)
 | |
|   {
 | |
|     /* Allocate state container and parse to immutable form */
 | |
|     parsed = parse_json (msr->extra, msr->extralength, parsed);
 | |
| 
 | |
|     if (parsed == NULL)
 | |
|     {
 | |
|       return MS_GENERROR;
 | |
|     }
 | |
| 
 | |
|     /* Set supplied state pointer */
 | |
|     if (parsestate != NULL)
 | |
|     {
 | |
|       *parsestate = parsed;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* Generate mutable document from immutable form if needed */
 | |
|   if (parsed->doc != NULL && parsed->mut_doc == NULL)
 | |
|   {
 | |
|     if ((parsed->mut_doc = yyjson_doc_mut_copy (parsed->doc, &alc)) == NULL)
 | |
|     {
 | |
|       ms_log (2, "%s() Cannot create mutable JSON document\n", __func__);
 | |
| 
 | |
|       if (parsestate == NULL)
 | |
|       {
 | |
|         mseh_free_parsestate (&parsed);
 | |
|       }
 | |
| 
 | |
|       return MS_GENERROR;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* Initialize empty mutable document if needed */
 | |
|   if (parsed->mut_doc == NULL)
 | |
|   {
 | |
|     if ((parsed->mut_doc = yyjson_mut_doc_new (&alc)) == NULL)
 | |
|     {
 | |
|       ms_log (2, "%s() Cannot initialize mutable JSON document\n", __func__);
 | |
| 
 | |
|       if (parsestate == NULL)
 | |
|       {
 | |
|         mseh_free_parsestate (&parsed);
 | |
|       }
 | |
| 
 | |
|       return MS_GENERROR;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* Set (or replace) header value at ptr */
 | |
|   switch (type)
 | |
|   {
 | |
|   case 'n':
 | |
|     rv = yyjson_mut_doc_ptr_set (parsed->mut_doc, ptr,
 | |
|                                  yyjson_mut_real (parsed->mut_doc, *((double *)value)));
 | |
|     break;
 | |
|   case 'i':
 | |
|     rv = yyjson_mut_doc_ptr_set (parsed->mut_doc, ptr,
 | |
|                                  yyjson_mut_sint (parsed->mut_doc, *((int64_t *)value)));
 | |
|     break;
 | |
|   case 's':
 | |
|     rv = yyjson_mut_doc_ptr_set (parsed->mut_doc, ptr,
 | |
|                                  yyjson_mut_strcpy (parsed->mut_doc, (const char *)value));
 | |
|     break;
 | |
|   case 'b':
 | |
|     rv = yyjson_mut_doc_ptr_set (parsed->mut_doc, ptr,
 | |
|                                  yyjson_mut_bool (parsed->mut_doc, *((int *)value) ? true : false));
 | |
|     break;
 | |
|   case 'M':
 | |
|     /* Parse supplied patch */
 | |
|     if ((patch_idoc = yyjson_read_opts ((char *)value, strlen ((char *)value), 0, &alc, NULL)))
 | |
|     {
 | |
|       if ((patch_doc = yyjson_doc_mut_copy (patch_idoc, &alc)))
 | |
|       {
 | |
|         /* Get patch target value */
 | |
|         if ((target_val = yyjson_mut_doc_ptr_get (parsed->mut_doc, ptr)))
 | |
|         {
 | |
|           /* Generate merged value */
 | |
|           if ((merged_val = yyjson_mut_merge_patch (parsed->mut_doc,
 | |
|                                                     target_val,
 | |
|                                                     yyjson_mut_doc_get_root (patch_doc))))
 | |
|           {
 | |
|             /* Replace value at pointer with merged value */
 | |
|             rv = yyjson_mut_doc_ptr_replace (parsed->mut_doc, ptr, merged_val);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       ms_log (2, "%s() Cannot parse JSON Merge Patch: %s'\n", __func__, (char *)value);
 | |
|     }
 | |
| 
 | |
|     yyjson_doc_free(patch_idoc);
 | |
|     yyjson_mut_doc_free (patch_doc);
 | |
| 
 | |
|     break;
 | |
|   case 'V':
 | |
|     rv = yyjson_mut_doc_ptr_set (parsed->mut_doc, ptr,
 | |
|                                  yyjson_mut_val_mut_copy (parsed->mut_doc, (yyjson_mut_val *)value));
 | |
|     break;
 | |
|   case 'A':
 | |
|     /* Search for existing array, create if necessary */
 | |
|     if ((array_val = yyjson_mut_doc_ptr_get (parsed->mut_doc, ptr)) == NULL)
 | |
|     {
 | |
|       if ((array_val = yyjson_mut_arr (parsed->mut_doc)))
 | |
|       {
 | |
|         if (yyjson_mut_doc_ptr_set (parsed->mut_doc, ptr, array_val) == false)
 | |
|         {
 | |
|           array_val = NULL;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     /* Append supplied value, will return false if array_val == NULL */
 | |
|     rv = yyjson_mut_arr_append (array_val,
 | |
|                                 yyjson_mut_val_mut_copy (parsed->mut_doc, (yyjson_mut_val *)value));
 | |
|     break;
 | |
|   default:
 | |
|     ms_log (2, "%s() Unrecognized value type '%d'\n", __func__, type);
 | |
|     rv = false;
 | |
|   }
 | |
| 
 | |
|   /* Serialized extra headers and free parse state if not being retained */
 | |
|   if (parsestate == NULL)
 | |
|   {
 | |
|     mseh_serialize (msr, &parsed);
 | |
|     mseh_free_parsestate (&parsed);
 | |
|   }
 | |
|   /* If changes were applied, the immutable form of the document is now invalid */
 | |
|   else if (rv ==true && parsed->doc != NULL)
 | |
|   {
 | |
|     yyjson_doc_free (parsed->doc);
 | |
|     parsed->doc = NULL;
 | |
|   }
 | |
| 
 | |
|   return (rv == true) ? 0 : MS_GENERROR;
 | |
| } /* End of mseh_set_ptr_r() */
 | |
| 
 | |
| /**********************************************************************/ /**
 | |
|  * @brief Add event detection to the extra headers of the given record.
 | |
|  *
 | |
|  * If \a ptr is NULL, the default is \c '/FDSN/Event/Detection'.
 | |
|  *
 | |
|  * @param[in] msr Parsed miniSEED record to query
 | |
|  * @param[in] ptr Header value desired, specified in dot notation
 | |
|  * @param[in] eventdetection Structure with event detection values
 | |
|  * @param[in] parsestate Parsed state for multiple operations, can be NULL
 | |
|  *
 | |
|  * @returns 0 on success, otherwise a (negative) libmseed error code.
 | |
|  *
 | |
|  * \ref MessageOnError - this function logs a message on error
 | |
|  ***************************************************************************/
 | |
| int
 | |
| mseh_add_event_detection_r (MS3Record *msr, const char *ptr,
 | |
|                             MSEHEventDetection *eventdetection,
 | |
|                             LM_PARSED_JSON **parsestate)
 | |
| {
 | |
|   yyjson_mut_val entry;
 | |
|   yyjson_mut_val type_key, type;
 | |
|   yyjson_mut_val sigamp_key, sigamp;
 | |
|   yyjson_mut_val sigper_key, sigper;
 | |
|   yyjson_mut_val bgest_key, bgest;
 | |
|   yyjson_mut_val wave_key, wave;
 | |
|   yyjson_mut_val units_key, units;
 | |
|   yyjson_mut_val onset_key, onset;
 | |
|   yyjson_mut_val medsnr_key, medsnr;
 | |
|   yyjson_mut_val medsnr_value[6];
 | |
|   yyjson_mut_val medlookback_key, medlookback;
 | |
|   yyjson_mut_val medpickalg_key, medpickalg;
 | |
|   yyjson_mut_val detector_key, detector;
 | |
| 
 | |
|   char timestring[40];
 | |
|   int idx;
 | |
| 
 | |
|   if (!msr || !eventdetection)
 | |
|   {
 | |
|     ms_log (2, "%s() Required argument not defined: 'msr' or 'eventdetection'\n", __func__);
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   yyjson_mut_set_obj (&entry);
 | |
| 
 | |
|   /* Add elements to new object */
 | |
|   if (eventdetection->type[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&type_key, "Type");
 | |
|     yyjson_mut_set_str (&type, eventdetection->type);
 | |
|     yyjson_mut_obj_add (&entry, &type_key, &type);
 | |
|   }
 | |
|   if (eventdetection->signalamplitude != 0.0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&sigamp_key, "SignalAmplitude");
 | |
|     yyjson_mut_set_real (&sigamp, eventdetection->signalamplitude);
 | |
|     yyjson_mut_obj_add (&entry, &sigamp_key, &sigamp);
 | |
|   }
 | |
|   if (eventdetection->signalperiod != 0.0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&sigper_key, "SignalPeriod");
 | |
|     yyjson_mut_set_real (&sigper, eventdetection->signalperiod);
 | |
|     yyjson_mut_obj_add (&entry, &sigper_key, &sigper);
 | |
|   }
 | |
|   if (eventdetection->backgroundestimate != 0.0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&bgest_key, "BackgroundEstimate");
 | |
|     yyjson_mut_set_real (&bgest, eventdetection->backgroundestimate);
 | |
|     yyjson_mut_obj_add (&entry, &bgest_key, &bgest);
 | |
|   }
 | |
|   if (eventdetection->wave[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&wave_key, "Wave");
 | |
|     yyjson_mut_set_str (&wave, eventdetection->wave);
 | |
|     yyjson_mut_obj_add (&entry, &wave_key, &wave);
 | |
|   }
 | |
|   if (eventdetection->units[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&units_key, "Units");
 | |
|     yyjson_mut_set_str (&units, eventdetection->units);
 | |
|     yyjson_mut_obj_add (&entry, &units_key, &units);
 | |
|   }
 | |
|   if (eventdetection->onsettime != NSTERROR && eventdetection->onsettime != NSTUNSET)
 | |
|   {
 | |
|     if (ms_nstime2timestr (eventdetection->onsettime, timestring, ISOMONTHDAY_Z, NANO_MICRO_NONE))
 | |
|     {
 | |
|       yyjson_mut_set_str (&onset_key, "OnsetTime");
 | |
|       yyjson_mut_set_str (&onset, timestring);
 | |
|       yyjson_mut_obj_add (&entry, &onset_key, &onset);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       ms_log (2, "%s() Cannot create time string for %"PRId64"\n", __func__, eventdetection->onsettime);
 | |
|       return MS_GENERROR;
 | |
|     }
 | |
|   }
 | |
|   if (memcmp (eventdetection->medsnr, (uint8_t[]){0, 0, 0, 0, 0, 0}, 6))
 | |
|   {
 | |
|     yyjson_mut_set_str (&medsnr_key, "MEDSNR");
 | |
|     yyjson_mut_set_arr (&medsnr);
 | |
|     yyjson_mut_obj_add (&entry, &medsnr_key, &medsnr);
 | |
| 
 | |
|     /* Array containing the 6 SNR values */
 | |
|     for (idx = 0; idx < 6; idx++)
 | |
|     {
 | |
|       yyjson_mut_set_uint (&(medsnr_value[idx]), eventdetection->medsnr[idx]);
 | |
|       yyjson_mut_arr_append (&medsnr, &(medsnr_value[idx]));
 | |
|     }
 | |
|   }
 | |
|   if (eventdetection->medlookback >= 0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&medlookback_key, "MEDLookback");
 | |
|     yyjson_mut_set_sint (&medlookback, eventdetection->medlookback);
 | |
|     yyjson_mut_obj_add (&entry, &medlookback_key, &medlookback);
 | |
|   }
 | |
|   if (eventdetection->medpickalgorithm >= 0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&medpickalg_key, "MEDPickAlgorithm");
 | |
|     yyjson_mut_set_sint (&medpickalg, eventdetection->medpickalgorithm);
 | |
|     yyjson_mut_obj_add (&entry, &medpickalg_key, &medpickalg);
 | |
|   }
 | |
|   if (eventdetection->detector[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&detector_key, "Detector");
 | |
|     yyjson_mut_set_str (&detector, eventdetection->detector);
 | |
|     yyjson_mut_obj_add (&entry, &detector_key, &detector);
 | |
|   }
 | |
| 
 | |
|   /* Add new object to array, created 'value' will be free'd on successful return */
 | |
|   if (mseh_set_ptr_r (msr, (ptr) ? ptr : "/FDSN/Event/Detection", &entry, 'A', parsestate))
 | |
|   {
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| } /* End of mseh_add_event_detection_r() */
 | |
| 
 | |
| /**********************************************************************/ /**
 | |
|  * @brief Add calibration to the extra headers of the given record.
 | |
|  *
 | |
|  * If \a ptr is NULL, the default is \c '/FDSN/Calibration/Sequence'.
 | |
|  *
 | |
|  * @param[in] msr Parsed miniSEED record to query
 | |
|  * @param[in] ptr Header value desired, specified in dot notation
 | |
|  * @param[in] calibration Structure with calibration values
 | |
|  * @param[in] parsestate Parsed state for multiple operations, can be NULL
 | |
|  *
 | |
|  * @returns 0 on success, otherwise a (negative) libmseed error code.
 | |
|  *
 | |
|  * \ref MessageOnError - this function logs a message on error
 | |
|  ***************************************************************************/
 | |
| int
 | |
| mseh_add_calibration_r (MS3Record *msr, const char *ptr,
 | |
|                         MSEHCalibration *calibration,
 | |
|                         LM_PARSED_JSON **parsestate)
 | |
| {
 | |
|   yyjson_mut_val entry;
 | |
|   yyjson_mut_val type_key, type;
 | |
|   yyjson_mut_val begintime_key, begintime;
 | |
|   yyjson_mut_val endtime_key, endtime;
 | |
|   yyjson_mut_val steps_key, steps;
 | |
|   yyjson_mut_val firstpp_key, firstpp;
 | |
|   yyjson_mut_val altsign_key, altsign;
 | |
|   yyjson_mut_val trigger_key, trigger;
 | |
|   yyjson_mut_val continued_key, continued;
 | |
|   yyjson_mut_val amplitude_key, amplitude;
 | |
|   yyjson_mut_val inputunits_key, inputunits;
 | |
|   yyjson_mut_val amprange_key, amprange;
 | |
|   yyjson_mut_val duration_key, duration;
 | |
|   yyjson_mut_val sineperiod_key, sineperiod;
 | |
|   yyjson_mut_val stepbetween_key, stepbetween;
 | |
|   yyjson_mut_val inputchannel_key, inputchannel;
 | |
|   yyjson_mut_val refamp_key, refamp;
 | |
|   yyjson_mut_val coupling_key, coupling;
 | |
|   yyjson_mut_val rolloff_key, rolloff;
 | |
|   yyjson_mut_val noise_key, noise;
 | |
| 
 | |
|   char beginstring[40];
 | |
|   char endstring[40];
 | |
| 
 | |
|   if (!msr || !calibration)
 | |
|   {
 | |
|     ms_log (2, "%s() Required argument not defined: 'msr' or 'calibration'\n", __func__);
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   yyjson_mut_set_obj (&entry);
 | |
| 
 | |
|   /* Add elements to new object */
 | |
|   if (calibration->type[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&type_key, "Type");
 | |
|     yyjson_mut_set_str (&type, calibration->type);
 | |
|     yyjson_mut_obj_add (&entry, &type_key, &type);
 | |
|   }
 | |
|   if (calibration->begintime != NSTERROR && calibration->begintime != NSTUNSET)
 | |
|   {
 | |
|     if (ms_nstime2timestr (calibration->begintime, beginstring, ISOMONTHDAY_Z, NANO_MICRO_NONE))
 | |
|     {
 | |
|       yyjson_mut_set_str (&begintime_key, "BeginTime");
 | |
|       yyjson_mut_set_str (&begintime, beginstring);
 | |
|       yyjson_mut_obj_add (&entry, &begintime_key, &begintime);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       ms_log (2, "%s() Cannot create time string for %" PRId64 "\n", __func__, calibration->begintime);
 | |
|       return MS_GENERROR;
 | |
|     }
 | |
|   }
 | |
|   if (calibration->endtime != NSTERROR && calibration->endtime != NSTUNSET)
 | |
|   {
 | |
|     if (ms_nstime2timestr (calibration->endtime, endstring, ISOMONTHDAY_Z, NANO_MICRO_NONE))
 | |
|     {
 | |
|       yyjson_mut_set_str (&endtime_key, "EndTime");
 | |
|       yyjson_mut_set_str (&endtime, endstring);
 | |
|       yyjson_mut_obj_add (&entry, &endtime_key, &endtime);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       ms_log (2, "%s() Cannot create time string for %" PRId64 "\n", __func__, calibration->endtime);
 | |
|       return MS_GENERROR;
 | |
|     }
 | |
|   }
 | |
|   if (calibration->steps >= 0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&steps_key, "Steps");
 | |
|     yyjson_mut_set_sint (&steps, calibration->steps);
 | |
|     yyjson_mut_obj_add (&entry, &steps_key, &steps);
 | |
|   }
 | |
|   if (calibration->firstpulsepositive >= 0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&firstpp_key, "StepFirstPulsePositive");
 | |
|     yyjson_mut_set_bool (&firstpp, calibration->firstpulsepositive);
 | |
|     yyjson_mut_obj_add (&entry, &firstpp_key, &firstpp);
 | |
|   }
 | |
|   if (calibration->alternatesign >= 0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&altsign_key, "StepAlternateSign");
 | |
|     yyjson_mut_set_bool (&altsign, calibration->alternatesign);
 | |
|     yyjson_mut_obj_add (&entry, &altsign_key, &altsign);
 | |
|   }
 | |
|   if (calibration->trigger[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&trigger_key, "Trigger");
 | |
|     yyjson_mut_set_str (&trigger, calibration->trigger);
 | |
|     yyjson_mut_obj_add (&entry, &trigger_key, &trigger);
 | |
|   }
 | |
|   if (calibration->continued >= 0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&continued_key, "Continued");
 | |
|     yyjson_mut_set_bool (&continued, calibration->continued);
 | |
|     yyjson_mut_obj_add (&entry, &continued_key, &continued);
 | |
|   }
 | |
|   if (calibration->amplitude != 0.0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&litude_key, "Amplitude");
 | |
|     yyjson_mut_set_real (&litude, calibration->amplitude);
 | |
|     yyjson_mut_obj_add (&entry, &litude_key, &litude);
 | |
|   }
 | |
|   if (calibration->inputunits[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&inputunits_key, "InputUnits");
 | |
|     yyjson_mut_set_str (&inputunits, calibration->inputunits);
 | |
|     yyjson_mut_obj_add (&entry, &inputunits_key, &inputunits);
 | |
|   }
 | |
|   if (calibration->amplituderange[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&range_key, "AmplitudeRange");
 | |
|     yyjson_mut_set_str (&range, calibration->amplituderange);
 | |
|     yyjson_mut_obj_add (&entry, &range_key, &range);
 | |
|   }
 | |
|   if (calibration->duration != 0.0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&duration_key, "Duration");
 | |
|     yyjson_mut_set_real (&duration, calibration->duration);
 | |
|     yyjson_mut_obj_add (&entry, &duration_key, &duration);
 | |
|   }
 | |
|   if (calibration->sineperiod != 0.0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&sineperiod_key, "SinePeriod");
 | |
|     yyjson_mut_set_real (&sineperiod, calibration->sineperiod);
 | |
|     yyjson_mut_obj_add (&entry, &sineperiod_key, &sineperiod);
 | |
|   }
 | |
|   if (calibration->stepbetween != 0.0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&stepbetween_key, "StepBetween");
 | |
|     yyjson_mut_set_real (&stepbetween, calibration->stepbetween);
 | |
|     yyjson_mut_obj_add (&entry, &stepbetween_key, &stepbetween);
 | |
|   }
 | |
|   if (calibration->inputchannel[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&inputchannel_key, "InputChannel");
 | |
|     yyjson_mut_set_str (&inputchannel, calibration->inputchannel);
 | |
|     yyjson_mut_obj_add (&entry, &inputchannel_key, &inputchannel);
 | |
|   }
 | |
|   if (calibration->refamplitude != 0.0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&refamp_key, "ReferenceAmplitude");
 | |
|     yyjson_mut_set_real (&refamp, calibration->refamplitude);
 | |
|     yyjson_mut_obj_add (&entry, &refamp_key, &refamp);;
 | |
|   }
 | |
|   if (calibration->coupling[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&coupling_key, "Coupling");
 | |
|     yyjson_mut_set_str (&coupling, calibration->coupling);
 | |
|     yyjson_mut_obj_add (&entry, &coupling_key, &coupling);
 | |
|   }
 | |
|   if (calibration->rolloff[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&rolloff_key, "Rolloff");
 | |
|     yyjson_mut_set_str (&rolloff, calibration->rolloff);
 | |
|     yyjson_mut_obj_add (&entry, &rolloff_key, &rolloff);
 | |
|   }
 | |
|   if (calibration->noise[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&noise_key, "Noise");
 | |
|     yyjson_mut_set_str (&noise, calibration->noise);
 | |
|     yyjson_mut_obj_add (&entry, &noise_key, &noise);
 | |
|   }
 | |
| 
 | |
|   /* Add new object to array, created 'value' will be free'd on successful return */
 | |
|   if (mseh_set_ptr_r (msr, (ptr) ? ptr : "/FDSN/Calibration/Sequence", &entry, 'A', parsestate))
 | |
|   {
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| } /* End of mseh_add_calibration_r() */
 | |
| 
 | |
| /**********************************************************************/ /**
 | |
|  * @brief Add timing exception to the extra headers of the given record.
 | |
|  *
 | |
|  * If \a ptr is NULL, the default is \c '/FDSN/Time/Exception'.
 | |
|  *
 | |
|  * @param[in] msr Parsed miniSEED record to query
 | |
|  * @param[in] ptr Header value desired, specified in dot notation
 | |
|  * @param[in] exception Structure with timing exception values
 | |
|  * @param[in] parsestate Parsed state for multiple operations, can be NULL
 | |
|  *
 | |
|  * @returns 0 on success, otherwise a (negative) libmseed error code.
 | |
|  *
 | |
|  * \ref MessageOnError - this function logs a message on error
 | |
|  ***************************************************************************/
 | |
| int
 | |
| mseh_add_timing_exception_r (MS3Record *msr, const char *ptr,
 | |
|                              MSEHTimingException *exception,
 | |
|                              LM_PARSED_JSON **parsestate)
 | |
| {
 | |
|   yyjson_mut_val entry;
 | |
|   yyjson_mut_val etime_key, etime;
 | |
|   yyjson_mut_val vcocorr_key, vcocorr;
 | |
|   yyjson_mut_val recqual_key, recqual;
 | |
|   yyjson_mut_val count_key, count;
 | |
|   yyjson_mut_val type_key, type;
 | |
|   yyjson_mut_val clockstatus_key, clockstatus;
 | |
| 
 | |
|   char timestring[40];
 | |
| 
 | |
|   if (!msr || !exception)
 | |
|   {
 | |
|     ms_log (2, "%s() Required argument not defined: 'msr' or 'exception'\n", __func__);
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   yyjson_mut_set_obj (&entry);
 | |
| 
 | |
|   /* Add elements to new object */
 | |
|   if (exception->time != NSTERROR && exception->time != NSTUNSET)
 | |
|   {
 | |
|     if (ms_nstime2timestr (exception->time, timestring, ISOMONTHDAY_Z, NANO_MICRO_NONE))
 | |
|     {
 | |
|       yyjson_mut_set_str (&etime_key, "Time");
 | |
|       yyjson_mut_set_str (&etime, timestring);
 | |
|       yyjson_mut_obj_add (&entry, &etime_key, &etime);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       ms_log (2, "%s() Cannot create time string for %" PRId64 "\n", __func__, exception->time);
 | |
|       return MS_GENERROR;
 | |
|     }
 | |
|   }
 | |
|   if (exception->vcocorrection >= 0.0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&vcocorr_key, "VCOCorrection");
 | |
|     yyjson_mut_set_real (&vcocorr, exception->vcocorrection);
 | |
|     yyjson_mut_obj_add (&entry, &vcocorr_key, &vcocorr);
 | |
|   }
 | |
|   if (exception->receptionquality >= 0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&recqual_key, "ReceptionQuality");
 | |
|     yyjson_mut_set_sint (&recqual, exception->receptionquality);
 | |
|     yyjson_mut_obj_add (&entry, &recqual_key, &recqual);
 | |
|   }
 | |
|   if (exception->count > 0)
 | |
|   {
 | |
|     yyjson_mut_set_str (&count_key, "Count");
 | |
|     yyjson_mut_set_sint (&count, exception->count);
 | |
|     yyjson_mut_obj_add (&entry, &count_key, &count);
 | |
|   }
 | |
|   if (exception->type[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&type_key, "Type");
 | |
|     yyjson_mut_set_str (&type, exception->type);
 | |
|     yyjson_mut_obj_add (&entry, &type_key, &type);
 | |
|   }
 | |
|   if (exception->clockstatus[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&clockstatus_key, "ClockStatus");
 | |
|     yyjson_mut_set_str (&clockstatus, exception->clockstatus);
 | |
|     yyjson_mut_obj_add (&entry, &clockstatus_key, &clockstatus);
 | |
|   }
 | |
| 
 | |
|   /* Add new object to array, created 'value' will be free'd on successful return */
 | |
|   if (mseh_set_ptr_r (msr, (ptr) ? ptr : "/FDSN/Time/Exception", &entry, 'A', parsestate))
 | |
|   {
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| } /* End of mseh_add_timing_exception_r() */
 | |
| 
 | |
| /**********************************************************************/ /**
 | |
|  * @brief Add recenter event to the extra headers of the given record.
 | |
|  *
 | |
|  * If \a ptr is NULL, the default is \c '/FDSN/Recenter/Sequence'.
 | |
|  *
 | |
|  * @param[in] msr Parsed miniSEED record to query
 | |
|  * @param[in] ptr Header value desired, specified in dot notation
 | |
|  * @param[in] recenter Structure with recenter values
 | |
|  * @param[in] parsestate Parsed state for multiple operations, can be NULL
 | |
|  *
 | |
|  * @returns 0 on success, otherwise a (negative) libmseed error code.
 | |
|  *
 | |
|  * \ref MessageOnError - this function logs a message on error
 | |
|  ***************************************************************************/
 | |
| int
 | |
| mseh_add_recenter_r (MS3Record *msr, const char *ptr, MSEHRecenter *recenter,
 | |
|                      LM_PARSED_JSON **parsestate)
 | |
| {
 | |
|   yyjson_mut_val entry;
 | |
|   yyjson_mut_val type_key, type;
 | |
|   yyjson_mut_val begin_key, begin;
 | |
|   yyjson_mut_val end_key, end;
 | |
|   yyjson_mut_val trigger_key, trigger;
 | |
|   char beginstring[40];
 | |
|   char endstring[40];
 | |
| 
 | |
|   if (!msr || !recenter)
 | |
|   {
 | |
|     ms_log (2, "%s() Required argument not defined: 'msr' or 'recenter'\n", __func__);
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   yyjson_mut_set_obj (&entry);
 | |
| 
 | |
|   /* Add elements to new object */
 | |
|   if (recenter->type[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&type_key, "Type");
 | |
|     yyjson_mut_set_str (&type, recenter->type);
 | |
|     yyjson_mut_obj_add (&entry, &type_key, &type);
 | |
|   }
 | |
|   if (recenter->begintime != NSTERROR && recenter->begintime != NSTUNSET)
 | |
|   {
 | |
|     if (ms_nstime2timestr (recenter->begintime, beginstring, ISOMONTHDAY_Z, NANO_MICRO_NONE))
 | |
|     {
 | |
|       yyjson_mut_set_str (&begin_key, "BeginTime");
 | |
|       yyjson_mut_set_str (&begin, beginstring);
 | |
|       yyjson_mut_obj_add (&entry, &begin_key, &begin);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       ms_log (2, "%s() Cannot create time string for %" PRId64 "\n", __func__, recenter->begintime);
 | |
|       return MS_GENERROR;
 | |
|     }
 | |
|   }
 | |
|   if (recenter->endtime != NSTERROR && recenter->endtime != NSTUNSET)
 | |
|   {
 | |
|     if (ms_nstime2timestr (recenter->endtime, endstring, ISOMONTHDAY_Z, NANO_MICRO_NONE))
 | |
|     {
 | |
|       yyjson_mut_set_str (&end_key, "EndTime");
 | |
|       yyjson_mut_set_str (&end, endstring);
 | |
|       yyjson_mut_obj_add (&entry, &end_key, &end);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       ms_log (2, "%s() Cannot create time string for %" PRId64 "\n", __func__, recenter->endtime);
 | |
|       return MS_GENERROR;
 | |
|     }
 | |
|   }
 | |
|   if (recenter->trigger[0])
 | |
|   {
 | |
|     yyjson_mut_set_str (&trigger_key, "Trigger");
 | |
|     yyjson_mut_set_str (&trigger, recenter->trigger);
 | |
|     yyjson_mut_obj_add (&entry, &trigger_key, &trigger);
 | |
|   }
 | |
| 
 | |
|   /* Add new object to array, created 'value' will be free'd on successful return */
 | |
|   if (mseh_set_ptr_r (msr, (ptr) ? ptr : "/FDSN/Recenter/Sequence", &entry, 'A', parsestate))
 | |
|   {
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| } /* End of mseh_add_recenter_r() */
 | |
| 
 | |
| /**********************************************************************/ /**
 | |
|  * @brief Generate extra headers string (serialize) from internal state
 | |
|  *
 | |
|  * Generate the extra headers JSON string from the internal parse state
 | |
|  * created by mseh_set_ptr_r().
 | |
|  *
 | |
|  * @param[in] msr ::MS3Record to generate extra headers for
 | |
|  * @param[in] parsestate Internal parsed state associated with \a msr
 | |
|  *
 | |
|  * @returns Length of extra headers on success, otherwise a (negative) libmseed error code
 | |
|  *
 | |
|  * \sa mseh_set_ptr_r()
 | |
|  ***************************************************************************/
 | |
| int
 | |
| mseh_serialize (MS3Record *msr, LM_PARSED_JSON **parsestate)
 | |
| {
 | |
|   yyjson_write_flag flg = YYJSON_WRITE_NOFLAG;
 | |
|   yyjson_write_err err;
 | |
|   yyjson_alc alc = {_priv_malloc, _priv_realloc, _priv_free, NULL};
 | |
| 
 | |
|   LM_PARSED_JSON *parsed = NULL;
 | |
|   char *serialized    = NULL;
 | |
|   size_t serialsize   = 0;
 | |
| 
 | |
|   if (!msr || !parsestate)
 | |
|     return MS_GENERROR;
 | |
| 
 | |
|   parsed = *parsestate;
 | |
| 
 | |
|   if (!parsed || !parsed->mut_doc)
 | |
|     return 0;
 | |
| 
 | |
|   /* Serialize new JSON string */
 | |
|   serialized = yyjson_mut_write_opts (parsed->mut_doc, flg, &alc, &serialsize, &err);
 | |
| 
 | |
|   if (serialized == NULL)
 | |
|   {
 | |
|     ms_log (2, "%s() Cannot write extra header JSON: %s\n",
 | |
|             __func__, (err.msg) ? err.msg : "Unknown error");
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   if (serialsize > UINT16_MAX)
 | |
|   {
 | |
|     ms_log (2, "%s() New serialization size exceeds limit of %d bytes: %" PRIu64 "\n",
 | |
|             __func__, UINT16_MAX, (uint64_t)serialsize);
 | |
|     libmseed_memory.free(serialized);
 | |
|     return MS_GENERROR;
 | |
|   }
 | |
| 
 | |
|   /* Set new extra headers, replacing existing headers */
 | |
|   if (msr->extra)
 | |
|     libmseed_memory.free (msr->extra);
 | |
|   msr->extra       = serialized;
 | |
|   msr->extralength = (uint16_t)serialsize;
 | |
| 
 | |
|   return (int)serialsize;
 | |
| }
 | |
| 
 | |
| /**********************************************************************/ /**
 | |
|  * @brief Free internally parsed (deserialized) JSON data
 | |
|  *
 | |
|  * Free the memory associated with JSON data parsed by mseh_get_ptr_r()
 | |
|  * or mseh_set_ptr_r(), specifically the data at the \a parsestate pointer.
 | |
|  *
 | |
|  * @param[in] parsestate Internal parsed state associated with \a msr
 | |
|  *
 | |
|  * \sa mseh_get_ptr_r()
 | |
|  * \sa mseh_set_ptr_r()
 | |
|  ***************************************************************************/
 | |
| void
 | |
| mseh_free_parsestate (LM_PARSED_JSON **parsestate)
 | |
| {
 | |
|   LM_PARSED_JSON *parsed = NULL;
 | |
| 
 | |
|   if (!parsestate || !*parsestate)
 | |
|     return;
 | |
| 
 | |
|   parsed = *parsestate;
 | |
| 
 | |
|   if (parsed->doc)
 | |
|     yyjson_doc_free (parsed->doc);
 | |
| 
 | |
|   if (parsed->mut_doc)
 | |
|     yyjson_mut_doc_free (parsed->mut_doc);
 | |
| 
 | |
|   libmseed_memory.free(parsed);
 | |
| 
 | |
|   *parsestate = NULL;
 | |
| }
 | |
| 
 | |
| /**********************************************************************/ /**
 | |
|  * @brief Print the extra header structure for the specified MS3Record.
 | |
|  *
 | |
|  * Output is printed in a pretty, formatted form for readability and
 | |
|  * the anonymous, root object container (the outer {}) is not printed.
 | |
|  *
 | |
|  * @param[in] msr ::MS3Record with extra headers to pring
 | |
|  * @param[in] indent Number of spaces to indent output
 | |
|  *
 | |
|  * @returns 0 on success and a (negative) libmseed error code on error.
 | |
|  ***************************************************************************/
 | |
| int
 | |
| mseh_print (const MS3Record *msr, int indent)
 | |
| {
 | |
|   char *extra;
 | |
|   int idx;
 | |
|   int instring = 0;
 | |
| 
 | |
|   if (!msr)
 | |
|     return MS_GENERROR;
 | |
| 
 | |
|   if (!msr->extra || !msr->extralength)
 | |
|     return MS_NOERROR;
 | |
| 
 | |
|   extra = msr->extra;
 | |
| 
 | |
|   if (extra[0] != '{' || extra[msr->extralength - 1] != '}')
 | |
|   {
 | |
|     ms_log (1, "%s() Warning, something is wrong, extra headers are not a clean {object}\n", __func__);
 | |
|   }
 | |
| 
 | |
|   /* Print JSON character-by-character, inserting
 | |
|    * indentation, spaces and newlines for readability. */
 | |
|   ms_log (0, "%*s", indent, "");
 | |
|   for (idx = 1; idx < (msr->extralength - 1); idx++)
 | |
|   {
 | |
|     /* Toggle "in string" flag for double quotes */
 | |
|     if (extra[idx] == '"')
 | |
|       instring = (instring) ? 0 : 1;
 | |
| 
 | |
|     if (!instring)
 | |
|     {
 | |
|       if (extra[idx] == ':')
 | |
|       {
 | |
|         ms_log (0, ": ");
 | |
|       }
 | |
|       else if (extra[idx] == ',')
 | |
|       {
 | |
|         ms_log (0, ",\n%*s", indent, "");
 | |
|       }
 | |
|       else if (extra[idx] == '{')
 | |
|       {
 | |
|         indent += 2;
 | |
|         ms_log (0, "{\n%*s", indent, "");
 | |
|       }
 | |
|       else if (extra[idx] == '[')
 | |
|       {
 | |
|         indent += 2;
 | |
|         ms_log (0, "[\n%*s", indent, "");
 | |
|       }
 | |
|       else if (extra[idx] == '}')
 | |
|       {
 | |
|         indent -= 2;
 | |
|         ms_log (0, "\n%*s}", indent, "");
 | |
|       }
 | |
|       else if (extra[idx] == ']')
 | |
|       {
 | |
|         indent -= 2;
 | |
|         ms_log (0, "\n%*s]", indent, "");
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         ms_log (0, "%c", extra[idx]);
 | |
|       }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       ms_log (0, "%c", extra[idx]);
 | |
|     }
 | |
|   }
 | |
|   ms_log (0, "\n");
 | |
| 
 | |
|   return MS_NOERROR;
 | |
| } /* End of mseh_print() */
 |