1 /***************************************************************************
2  *
3  * Routines to parse Mini-SEED.
4  *
5  * Written by Chad Trabant
6  *   IRIS Data Management Center
7  *
8  * modified: 2015.108
9  ***************************************************************************/
10 
11 #include <errno.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <time.h>
16 
17 #include "libmseed.h"
18 
19 /**********************************************************************
20  * msr_parse:
21  *
22  * This routine will attempt to parse (detect and unpack) a Mini-SEED
23  * record from a specified memory buffer and populate a supplied
24  * MSRecord structure.
25  *
26  * If reclen is less than or equal to 0 the length of record is
27  * automatically detected otherwise reclen should be the correct
28  * record length.
29  *
30  * For auto detection of record length the record should include a
31  * 1000 blockette or be followed by another record header in the
32  * buffer.
33  *
34  * dataflag will be passed directly to msr_unpack().
35  *
36  * Return values:
37  *   0 : Success, populates the supplied MSRecord.
38  *  >0 : Data record detected but not enough data is present, the
39  *       return value is a hint of how many more bytes are needed.
40  *  <0 : libmseed error code (listed in libmseed.h) is returned.
41  *********************************************************************/
42 int
msr_parse(char * record,int recbuflen,MSRecord ** ppmsr,int reclen,flag dataflag,flag verbose)43 msr_parse (char *record, int recbuflen, MSRecord **ppmsr, int reclen,
44            flag dataflag, flag verbose)
45 {
46   int detlen  = 0;
47   int retcode = 0;
48 
49   if (!ppmsr)
50     return MS_GENERROR;
51 
52   if (!record)
53     return MS_GENERROR;
54 
55   /* Sanity check: record length cannot be larger than buffer */
56   if (reclen > 0 && reclen > recbuflen)
57   {
58     ms_log (2, "ms_parse() Record length (%d) cannot be larger than buffer (%d)\n",
59             reclen, recbuflen);
60     return MS_GENERROR;
61   }
62 
63   /* Autodetect the record length */
64   if (reclen <= 0)
65   {
66     detlen = ms_detect (record, recbuflen);
67 
68     /* No data record detected */
69     if (detlen < 0)
70     {
71       return MS_NOTSEED;
72     }
73 
74     /* Found record but could not determine length */
75     if (detlen == 0)
76     {
77       return MINRECLEN;
78     }
79 
80     if (verbose > 2)
81     {
82       ms_log (1, "Detected record length of %d bytes\n", detlen);
83     }
84 
85     reclen = detlen;
86   }
87 
88   /* Check that record length is in supported range */
89   if (reclen < MINRECLEN || reclen > MAXRECLEN)
90   {
91     ms_log (2, "Record length is out of range: %d (allowed: %d to %d)\n",
92             reclen, MINRECLEN, MAXRECLEN);
93 
94     return MS_OUTOFRANGE;
95   }
96 
97   /* Check if more data is required, return hint */
98   if (reclen > recbuflen)
99   {
100     if (verbose > 2)
101       ms_log (1, "Detected %d byte record, need %d more bytes\n",
102               reclen, (reclen - recbuflen));
103 
104     return (reclen - recbuflen);
105   }
106 
107   /* Unpack record */
108   if ((retcode = msr_unpack (record, reclen, ppmsr, dataflag, verbose)) != MS_NOERROR)
109   {
110     msr_free (ppmsr);
111 
112     return retcode;
113   }
114 
115   return MS_NOERROR;
116 } /* End of msr_parse() */
117 
118 /**********************************************************************
119  * msr_parse_selection:
120  *
121  * This routine wraps msr_parse() to parse and return the first record
122  * from a memory buffer that matches optional Selections.  If the
123  * selections pointer is NULL the effect is to search the buffer for
124  * the first parsable record.
125  *
126  * The offset value specifies the starting offset in the buffer and,
127  * on success, the offset in the buffer to record parsed.
128  *
129  * The caller should manage the value of the offset in two ways:
130  *
131  * 1) on subsequent calls after a record has been parsed the caller
132  * should increment the offset by the record length returned or
133  * properly manipulate the record buffer pointer, buffer length and
134  * offset to the same effect.
135  *
136  * 2) when the end of the buffer is reached MS_GENERROR (-1) is
137  * returned, the caller should check the offset value against the
138  * record buffer length to determine when the entire buffer has been
139  * searched.
140  *
141  * Return values: same as msr_parse() except that MS_GENERROR is
142  * returned when end-of-buffer is reached.
143  *********************************************************************/
144 int
msr_parse_selection(char * recbuf,int recbuflen,int64_t * offset,MSRecord ** ppmsr,int reclen,Selections * selections,flag dataflag,flag verbose)145 msr_parse_selection (char *recbuf, int recbuflen, int64_t *offset,
146                      MSRecord **ppmsr, int reclen,
147                      Selections *selections, flag dataflag, flag verbose)
148 {
149   int retval = MS_GENERROR;
150   int unpackretval;
151   flag dataswapflag  = 0;
152   flag bigendianhost = ms_bigendianhost ();
153 
154   if (!ppmsr)
155     return MS_GENERROR;
156 
157   if (!recbuf)
158     return MS_GENERROR;
159 
160   if (!offset)
161     return MS_GENERROR;
162 
163   while (*offset < recbuflen)
164   {
165     retval = msr_parse (recbuf + *offset, (int)(recbuflen - *offset), ppmsr, reclen, 0, verbose);
166 
167     if (retval)
168     {
169       if (verbose)
170         ms_log (2, "Error parsing record at offset %" PRId64 "\n", *offset);
171 
172       *offset += MINRECLEN;
173     }
174     else
175     {
176       if (selections && !msr_matchselect (selections, *ppmsr, NULL))
177       {
178         *offset += (*ppmsr)->reclen;
179         retval = MS_GENERROR;
180       }
181       else
182       {
183         if (dataflag)
184         {
185           /* If BE host and LE data need swapping */
186           if (bigendianhost && (*ppmsr)->byteorder == 0)
187             dataswapflag = 1;
188           /* If LE host and BE data (or bad byte order value) need swapping */
189           else if (!bigendianhost && (*ppmsr)->byteorder > 0)
190             dataswapflag = 1;
191 
192           unpackretval = msr_unpack_data (*ppmsr, dataswapflag, verbose);
193 
194           if (unpackretval < 0)
195             return unpackretval;
196           else
197             (*ppmsr)->numsamples = unpackretval;
198         }
199 
200         break;
201       }
202     }
203   }
204 
205   return retval;
206 } /* End of msr_parse_selection() */
207 
208 /********************************************************************
209  * ms_detect:
210  *
211  * Determine SEED data record length with the following steps:
212  *
213  * 1) determine that the buffer contains a SEED data record by
214  * verifying known signatures (fields with known limited values)
215  *
216  * 2) search the record up to recbuflen bytes for a 1000 blockette.
217  *
218  * 3) If no blockette 1000 is found search at MINRECLEN-byte offsets
219  * for the fixed section of the next header or blank/noise record,
220  * thereby implying the record length.
221  *
222  * Returns:
223  * -1 : data record not detected or error
224  *  0 : data record detected but could not determine length
225  * >0 : size of the record in bytes
226  *********************************************************************/
227 int
ms_detect(const char * record,int recbuflen)228 ms_detect (const char *record, int recbuflen)
229 {
230   uint16_t blkt_offset;  /* Byte offset for next blockette */
231   uint8_t swapflag = 0;  /* Byte swapping flag */
232   uint8_t foundlen = 0;  /* Found record length */
233   int32_t reclen   = -1; /* Size of record in bytes */
234 
235   uint16_t blkt_type;
236   uint16_t next_blkt;
237 
238   struct fsdh_s *fsdh;
239   struct blkt_1000_s *blkt_1000;
240   const char *nextfsdh;
241 
242   /* Buffer must be at least 48 bytes (the fixed section) */
243   if (recbuflen < 48)
244     return -1;
245 
246   /* Check for valid fixed section of header */
247   if (!MS_ISVALIDHEADER (record))
248     return -1;
249 
250   fsdh = (struct fsdh_s *)record;
251 
252   /* Check to see if byte swapping is needed by checking for sane year and day */
253   if (!MS_ISVALIDYEARDAY (fsdh->start_time.year, fsdh->start_time.day))
254     swapflag = 1;
255 
256   blkt_offset = fsdh->blockette_offset;
257 
258   /* Swap order of blkt_offset if needed */
259   if (swapflag)
260     ms_gswap2 (&blkt_offset);
261 
262   /* Loop through blockettes as long as number is non-zero and viable */
263   while (blkt_offset != 0 &&
264          blkt_offset <= recbuflen)
265   {
266     memcpy (&blkt_type, record + blkt_offset, 2);
267     memcpy (&next_blkt, record + blkt_offset + 2, 2);
268 
269     if (swapflag)
270     {
271       ms_gswap2 (&blkt_type);
272       ms_gswap2 (&next_blkt);
273     }
274 
275     /* Found a 1000 blockette, not truncated */
276     if (blkt_type == 1000 &&
277         (int)(blkt_offset + 4 + sizeof (struct blkt_1000_s)) <= recbuflen)
278     {
279       blkt_1000 = (struct blkt_1000_s *)(record + blkt_offset + 4);
280 
281       foundlen = 1;
282 
283       /* Calculate record size in bytes as 2^(blkt_1000->reclen) */
284       reclen = (unsigned int)1 << blkt_1000->reclen;
285 
286       break;
287     }
288 
289     /* Safety check for invalid offset */
290     if (next_blkt != 0 && (next_blkt < 4 || (next_blkt - 4) <= blkt_offset))
291     {
292       ms_log (2, "Invalid blockette offset (%d) less than or equal to current offset (%d)\n",
293               next_blkt, blkt_offset);
294       return -1;
295     }
296 
297     blkt_offset = next_blkt;
298   }
299 
300   /* If record length was not determined by a 1000 blockette scan the buffer
301    * and search for the next record */
302   if (reclen == -1)
303   {
304     nextfsdh = record + MINRECLEN;
305 
306     /* Check for record header or blank/noise record at MINRECLEN byte offsets */
307     while (((nextfsdh - record) + 48) < recbuflen)
308     {
309       if (MS_ISVALIDHEADER (nextfsdh) || MS_ISVALIDBLANK (nextfsdh))
310       {
311         foundlen = 1;
312         reclen   = nextfsdh - record;
313         break;
314       }
315 
316       nextfsdh += MINRECLEN;
317     }
318   }
319 
320   if (!foundlen)
321     return 0;
322   else
323     return reclen;
324 } /* End of ms_detect() */
325 
326 /***************************************************************************
327  * ms_parse_raw:
328  *
329  * Parse and verify a SEED data record header (fixed section and
330  * blockettes) at the lowest level, printing error messages for
331  * invalid header values and optionally print raw header values.  The
332  * memory at 'record' is assumed to be a Mini-SEED record.  Not every
333  * possible test is performed, common errors and those causing
334  * libmseed parsing to fail should be detected.
335  *
336  * The 'details' argument is interpreted as follows:
337  *
338  * details:
339  *  0 = only print error messages for invalid header fields
340  *  1 = print basic fields in addition to invalid field errors
341  *  2 = print all fields in addition to invalid field errors
342  *
343  * The 'swapflag' argument is interpreted as follows:
344  *
345  * swapflag:
346  *  1 = swap multibyte quantities
347  *  0 = do no swapping
348  * -1 = autodetect byte order using year test, swap if needed
349  *
350  * Any byte swapping performed by this routine is applied directly to
351  * the memory reference by the record pointer.
352  *
353  * This routine is primarily intended to diagnose invalid Mini-SEED headers.
354  *
355  * Returns 0 when no errors were detected or a positive count of
356  * errors detected.
357  ***************************************************************************/
358 int
ms_parse_raw(char * record,int maxreclen,flag details,flag swapflag)359 ms_parse_raw (char *record, int maxreclen, flag details, flag swapflag)
360 {
361   struct fsdh_s *fsdh;
362   double nomsamprate;
363   char srcname[50];
364   char *X;
365   char b;
366   int retval          = 0;
367   int b1000encoding   = -1;
368   int b1000reclen     = -1;
369   int endofblockettes = -1;
370   int idx;
371 
372   if (!record)
373     return 1;
374 
375   /* Generate a source name string */
376   srcname[0] = '\0';
377   ms_recsrcname (record, srcname, 1);
378 
379   fsdh = (struct fsdh_s *)record;
380 
381   /* Check to see if byte swapping is needed by testing the year and day */
382   if (swapflag == -1 && !MS_ISVALIDYEARDAY (fsdh->start_time.year, fsdh->start_time.day))
383     swapflag = 1;
384   else
385     swapflag = 0;
386 
387   if (details > 1)
388   {
389     if (swapflag == 1)
390       ms_log (0, "Swapping multi-byte quantities in header\n");
391     else
392       ms_log (0, "Not swapping multi-byte quantities in header\n");
393   }
394 
395   /* Swap byte order */
396   if (swapflag)
397   {
398     MS_SWAPBTIME (&fsdh->start_time);
399     ms_gswap2a (&fsdh->numsamples);
400     ms_gswap2a (&fsdh->samprate_fact);
401     ms_gswap2a (&fsdh->samprate_mult);
402     ms_gswap4a (&fsdh->time_correct);
403     ms_gswap2a (&fsdh->data_offset);
404     ms_gswap2a (&fsdh->blockette_offset);
405   }
406 
407   /* Validate fixed section header fields */
408   X = record; /* Pointer of convenience */
409 
410   /* Check record sequence number, 6 ASCII digits */
411   if (!isdigit ((int)*(X)) || !isdigit ((int)*(X + 1)) ||
412       !isdigit ((int)*(X + 2)) || !isdigit ((int)*(X + 3)) ||
413       !isdigit ((int)*(X + 4)) || !isdigit ((int)*(X + 5)))
414   {
415     ms_log (2, "%s: Invalid sequence number: '%c%c%c%c%c%c'\n", srcname, X, X + 1, X + 2, X + 3, X + 4, X + 5);
416     retval++;
417   }
418 
419   /* Check header/quality indicator */
420   if (!MS_ISDATAINDICATOR (*(X + 6)))
421   {
422     ms_log (2, "%s: Invalid header indicator (DRQM): '%c'\n", srcname, X + 6);
423     retval++;
424   }
425 
426   /* Check reserved byte, space or NULL */
427   if (!(*(X + 7) == ' ' || *(X + 7) == '\0'))
428   {
429     ms_log (2, "%s: Invalid fixed section reserved byte (Space): '%c'\n", srcname, X + 7);
430     retval++;
431   }
432 
433   /* Check station code, 5 alphanumerics or spaces */
434   if (!(isalnum ((unsigned char)*(X + 8)) || *(X + 8) == ' ') ||
435       !(isalnum ((unsigned char)*(X + 9)) || *(X + 9) == ' ') ||
436       !(isalnum ((unsigned char)*(X + 10)) || *(X + 10) == ' ') ||
437       !(isalnum ((unsigned char)*(X + 11)) || *(X + 11) == ' ') ||
438       !(isalnum ((unsigned char)*(X + 12)) || *(X + 12) == ' '))
439   {
440     ms_log (2, "%s: Invalid station code: '%c%c%c%c%c'\n", srcname, X + 8, X + 9, X + 10, X + 11, X + 12);
441     retval++;
442   }
443 
444   /* Check location ID, 2 alphanumerics or spaces */
445   if (!(isalnum ((unsigned char)*(X + 13)) || *(X + 13) == ' ') ||
446       !(isalnum ((unsigned char)*(X + 14)) || *(X + 14) == ' '))
447   {
448     ms_log (2, "%s: Invalid location ID: '%c%c'\n", srcname, X + 13, X + 14);
449     retval++;
450   }
451 
452   /* Check channel codes, 3 alphanumerics or spaces */
453   if (!(isalnum ((unsigned char)*(X + 15)) || *(X + 15) == ' ') ||
454       !(isalnum ((unsigned char)*(X + 16)) || *(X + 16) == ' ') ||
455       !(isalnum ((unsigned char)*(X + 17)) || *(X + 17) == ' '))
456   {
457     ms_log (2, "%s: Invalid channel codes: '%c%c%c'\n", srcname, X + 15, X + 16, X + 17);
458     retval++;
459   }
460 
461   /* Check network code, 2 alphanumerics or spaces */
462   if (!(isalnum ((unsigned char)*(X + 18)) || *(X + 18) == ' ') ||
463       !(isalnum ((unsigned char)*(X + 19)) || *(X + 19) == ' '))
464   {
465     ms_log (2, "%s: Invalid network code: '%c%c'\n", srcname, X + 18, X + 19);
466     retval++;
467   }
468 
469   /* Check start time fields */
470   if (fsdh->start_time.year < 1900 || fsdh->start_time.year > 2100)
471   {
472     ms_log (2, "%s: Unlikely start year (1900-2100): '%d'\n", srcname, fsdh->start_time.year);
473     retval++;
474   }
475   if (fsdh->start_time.day < 1 || fsdh->start_time.day > 366)
476   {
477     ms_log (2, "%s: Invalid start day (1-366): '%d'\n", srcname, fsdh->start_time.day);
478     retval++;
479   }
480   if (fsdh->start_time.hour > 23)
481   {
482     ms_log (2, "%s: Invalid start hour (0-23): '%d'\n", srcname, fsdh->start_time.hour);
483     retval++;
484   }
485   if (fsdh->start_time.min > 59)
486   {
487     ms_log (2, "%s: Invalid start minute (0-59): '%d'\n", srcname, fsdh->start_time.min);
488     retval++;
489   }
490   if (fsdh->start_time.sec > 60)
491   {
492     ms_log (2, "%s: Invalid start second (0-60): '%d'\n", srcname, fsdh->start_time.sec);
493     retval++;
494   }
495   if (fsdh->start_time.fract > 9999)
496   {
497     ms_log (2, "%s: Invalid start fractional seconds (0-9999): '%d'\n", srcname, fsdh->start_time.fract);
498     retval++;
499   }
500 
501   /* Check number of samples, max samples in 4096-byte Steim-2 encoded record: 6601 */
502   if (fsdh->numsamples > 20000)
503   {
504     ms_log (2, "%s: Unlikely number of samples (>20000): '%d'\n", srcname, fsdh->numsamples);
505     retval++;
506   }
507 
508   /* Sanity check that there is space for blockettes when both data and blockettes are present */
509   if (fsdh->numsamples > 0 && fsdh->numblockettes > 0 && fsdh->data_offset <= fsdh->blockette_offset)
510   {
511     ms_log (2, "%s: No space for %d blockettes, data offset: %d, blockette offset: %d\n", srcname,
512             fsdh->numblockettes, fsdh->data_offset, fsdh->blockette_offset);
513     retval++;
514   }
515 
516   /* Print raw header details */
517   if (details >= 1)
518   {
519     /* Determine nominal sample rate */
520     nomsamprate = ms_nomsamprate (fsdh->samprate_fact, fsdh->samprate_mult);
521 
522     /* Print header values */
523     ms_log (0, "RECORD -- %s\n", srcname);
524     ms_log (0, "        sequence number: '%c%c%c%c%c%c'\n", fsdh->sequence_number[0], fsdh->sequence_number[1], fsdh->sequence_number[2],
525             fsdh->sequence_number[3], fsdh->sequence_number[4], fsdh->sequence_number[5]);
526     ms_log (0, " data quality indicator: '%c'\n", fsdh->dataquality);
527     if (details > 0)
528       ms_log (0, "               reserved: '%c'\n", fsdh->reserved);
529     ms_log (0, "           station code: '%c%c%c%c%c'\n", fsdh->station[0], fsdh->station[1], fsdh->station[2], fsdh->station[3], fsdh->station[4]);
530     ms_log (0, "            location ID: '%c%c'\n", fsdh->location[0], fsdh->location[1]);
531     ms_log (0, "          channel codes: '%c%c%c'\n", fsdh->channel[0], fsdh->channel[1], fsdh->channel[2]);
532     ms_log (0, "           network code: '%c%c'\n", fsdh->network[0], fsdh->network[1]);
533     ms_log (0, "             start time: %d,%d,%d:%d:%d.%04d (unused: %d)\n", fsdh->start_time.year, fsdh->start_time.day,
534             fsdh->start_time.hour, fsdh->start_time.min, fsdh->start_time.sec, fsdh->start_time.fract, fsdh->start_time.unused);
535     ms_log (0, "      number of samples: %d\n", fsdh->numsamples);
536     ms_log (0, "     sample rate factor: %d  (%.10g samples per second)\n",
537             fsdh->samprate_fact, nomsamprate);
538     ms_log (0, " sample rate multiplier: %d\n", fsdh->samprate_mult);
539 
540     /* Print flag details if requested */
541     if (details > 1)
542     {
543       /* Activity flags */
544       b = fsdh->act_flags;
545       ms_log (0, "         activity flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
546               bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
547               bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
548       if (b & 0x01)
549         ms_log (0, "                         [Bit 0] Calibration signals present\n");
550       if (b & 0x02)
551         ms_log (0, "                         [Bit 1] Time correction applied\n");
552       if (b & 0x04)
553         ms_log (0, "                         [Bit 2] Beginning of an event, station trigger\n");
554       if (b & 0x08)
555         ms_log (0, "                         [Bit 3] End of an event, station detrigger\n");
556       if (b & 0x10)
557         ms_log (0, "                         [Bit 4] A positive leap second happened in this record\n");
558       if (b & 0x20)
559         ms_log (0, "                         [Bit 5] A negative leap second happened in this record\n");
560       if (b & 0x40)
561         ms_log (0, "                         [Bit 6] Event in progress\n");
562       if (b & 0x80)
563         ms_log (0, "                         [Bit 7] Undefined bit set\n");
564 
565       /* I/O and clock flags */
566       b = fsdh->io_flags;
567       ms_log (0, "    I/O and clock flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
568               bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
569               bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
570       if (b & 0x01)
571         ms_log (0, "                         [Bit 0] Station volume parity error possibly present\n");
572       if (b & 0x02)
573         ms_log (0, "                         [Bit 1] Long record read (possibly no problem)\n");
574       if (b & 0x04)
575         ms_log (0, "                         [Bit 2] Short record read (record padded)\n");
576       if (b & 0x08)
577         ms_log (0, "                         [Bit 3] Start of time series\n");
578       if (b & 0x10)
579         ms_log (0, "                         [Bit 4] End of time series\n");
580       if (b & 0x20)
581         ms_log (0, "                         [Bit 5] Clock locked\n");
582       if (b & 0x40)
583         ms_log (0, "                         [Bit 6] Undefined bit set\n");
584       if (b & 0x80)
585         ms_log (0, "                         [Bit 7] Undefined bit set\n");
586 
587       /* Data quality flags */
588       b = fsdh->dq_flags;
589       ms_log (0, "     data quality flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
590               bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
591               bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
592       if (b & 0x01)
593         ms_log (0, "                         [Bit 0] Amplifier saturation detected\n");
594       if (b & 0x02)
595         ms_log (0, "                         [Bit 1] Digitizer clipping detected\n");
596       if (b & 0x04)
597         ms_log (0, "                         [Bit 2] Spikes detected\n");
598       if (b & 0x08)
599         ms_log (0, "                         [Bit 3] Glitches detected\n");
600       if (b & 0x10)
601         ms_log (0, "                         [Bit 4] Missing/padded data present\n");
602       if (b & 0x20)
603         ms_log (0, "                         [Bit 5] Telemetry synchronization error\n");
604       if (b & 0x40)
605         ms_log (0, "                         [Bit 6] A digital filter may be charging\n");
606       if (b & 0x80)
607         ms_log (0, "                         [Bit 7] Time tag is questionable\n");
608     }
609 
610     ms_log (0, "   number of blockettes: %d\n", fsdh->numblockettes);
611     ms_log (0, "        time correction: %ld\n", (long int)fsdh->time_correct);
612     ms_log (0, "            data offset: %d\n", fsdh->data_offset);
613     ms_log (0, " first blockette offset: %d\n", fsdh->blockette_offset);
614   } /* Done printing raw header details */
615 
616   /* Validate and report information in the blockette chain */
617   if (fsdh->blockette_offset > 46 && fsdh->blockette_offset < maxreclen)
618   {
619     int blkt_offset = fsdh->blockette_offset;
620     int blkt_count  = 0;
621     int blkt_length;
622     uint16_t blkt_type;
623     uint16_t next_blkt;
624     char *blkt_desc;
625 
626     /* Traverse blockette chain */
627     while (blkt_offset != 0 && blkt_offset < maxreclen)
628     {
629       /* Every blockette has a similar 4 byte header: type and next */
630       memcpy (&blkt_type, record + blkt_offset, 2);
631       memcpy (&next_blkt, record + blkt_offset + 2, 2);
632 
633       if (swapflag)
634       {
635         ms_gswap2 (&blkt_type);
636         ms_gswap2 (&next_blkt);
637       }
638 
639       /* Print common header fields */
640       if (details >= 1)
641       {
642         blkt_desc = ms_blktdesc (blkt_type);
643         ms_log (0, "          BLOCKETTE %u: (%s)\n", blkt_type, (blkt_desc) ? blkt_desc : "Unknown");
644         ms_log (0, "              next blockette: %u\n", next_blkt);
645       }
646 
647       blkt_length = ms_blktlen (blkt_type, record + blkt_offset, swapflag);
648       if (blkt_length == 0)
649       {
650         ms_log (2, "%s: Unknown blockette length for type %d\n", srcname, blkt_type);
651         retval++;
652       }
653 
654       /* Track end of blockette chain */
655       endofblockettes = blkt_offset + blkt_length - 1;
656 
657       /* Sanity check that the blockette is contained in the record */
658       if (endofblockettes > maxreclen)
659       {
660         ms_log (2, "%s: Blockette type %d at offset %d with length %d does not fix in record (%d)\n",
661                 srcname, blkt_type, blkt_offset, blkt_length, maxreclen);
662         retval++;
663         break;
664       }
665 
666       if (blkt_type == 100)
667       {
668         struct blkt_100_s *blkt_100 = (struct blkt_100_s *)(record + blkt_offset + 4);
669 
670         if (swapflag)
671           ms_gswap4 (&blkt_100->samprate);
672 
673         if (details >= 1)
674         {
675           ms_log (0, "          actual sample rate: %.10g\n", blkt_100->samprate);
676 
677           if (details > 1)
678           {
679             b = blkt_100->flags;
680             ms_log (0, "             undefined flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
681                     bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
682                     bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
683 
684             ms_log (0, "          reserved bytes (3): %u,%u,%u\n",
685                     blkt_100->reserved[0], blkt_100->reserved[1], blkt_100->reserved[2]);
686           }
687         }
688       }
689 
690       else if (blkt_type == 200)
691       {
692         struct blkt_200_s *blkt_200 = (struct blkt_200_s *)(record + blkt_offset + 4);
693 
694         if (swapflag)
695         {
696           ms_gswap4 (&blkt_200->amplitude);
697           ms_gswap4 (&blkt_200->period);
698           ms_gswap4 (&blkt_200->background_estimate);
699           MS_SWAPBTIME (&blkt_200->time);
700         }
701 
702         if (details >= 1)
703         {
704           ms_log (0, "            signal amplitude: %g\n", blkt_200->amplitude);
705           ms_log (0, "               signal period: %g\n", blkt_200->period);
706           ms_log (0, "         background estimate: %g\n", blkt_200->background_estimate);
707 
708           if (details > 1)
709           {
710             b = blkt_200->flags;
711             ms_log (0, "       event detection flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
712                     bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
713                     bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
714             if (b & 0x01)
715               ms_log (0, "                         [Bit 0] 1: Dilatation wave\n");
716             else
717               ms_log (0, "                         [Bit 0] 0: Compression wave\n");
718             if (b & 0x02)
719               ms_log (0, "                         [Bit 1] 1: Units after deconvolution\n");
720             else
721               ms_log (0, "                         [Bit 1] 0: Units are digital counts\n");
722             if (b & 0x04)
723               ms_log (0, "                         [Bit 2] Bit 0 is undetermined\n");
724             ms_log (0, "               reserved byte: %u\n", blkt_200->reserved);
725           }
726 
727           ms_log (0, "           signal onset time: %d,%d,%d:%d:%d.%04d (unused: %d)\n", blkt_200->time.year, blkt_200->time.day,
728                   blkt_200->time.hour, blkt_200->time.min, blkt_200->time.sec, blkt_200->time.fract, blkt_200->time.unused);
729           ms_log (0, "               detector name: %.24s\n", blkt_200->detector);
730         }
731       }
732 
733       else if (blkt_type == 201)
734       {
735         struct blkt_201_s *blkt_201 = (struct blkt_201_s *)(record + blkt_offset + 4);
736 
737         if (swapflag)
738         {
739           ms_gswap4 (&blkt_201->amplitude);
740           ms_gswap4 (&blkt_201->period);
741           ms_gswap4 (&blkt_201->background_estimate);
742           MS_SWAPBTIME (&blkt_201->time);
743         }
744 
745         if (details >= 1)
746         {
747           ms_log (0, "            signal amplitude: %g\n", blkt_201->amplitude);
748           ms_log (0, "               signal period: %g\n", blkt_201->period);
749           ms_log (0, "         background estimate: %g\n", blkt_201->background_estimate);
750 
751           b = blkt_201->flags;
752           ms_log (0, "       event detection flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
753                   bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
754                   bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
755           if (b & 0x01)
756             ms_log (0, "                         [Bit 0] 1: Dilation wave\n");
757           else
758             ms_log (0, "                         [Bit 0] 0: Compression wave\n");
759 
760           if (details > 1)
761             ms_log (0, "               reserved byte: %u\n", blkt_201->reserved);
762           ms_log (0, "           signal onset time: %d,%d,%d:%d:%d.%04d (unused: %d)\n", blkt_201->time.year, blkt_201->time.day,
763                   blkt_201->time.hour, blkt_201->time.min, blkt_201->time.sec, blkt_201->time.fract, blkt_201->time.unused);
764           ms_log (0, "                  SNR values: ");
765           for (idx = 0; idx < 6; idx++)
766             ms_log (0, "%u  ", blkt_201->snr_values[idx]);
767           ms_log (0, "\n");
768           ms_log (0, "              loopback value: %u\n", blkt_201->loopback);
769           ms_log (0, "              pick algorithm: %u\n", blkt_201->pick_algorithm);
770           ms_log (0, "               detector name: %.24s\n", blkt_201->detector);
771         }
772       }
773 
774       else if (blkt_type == 300)
775       {
776         struct blkt_300_s *blkt_300 = (struct blkt_300_s *)(record + blkt_offset + 4);
777 
778         if (swapflag)
779         {
780           MS_SWAPBTIME (&blkt_300->time);
781           ms_gswap4 (&blkt_300->step_duration);
782           ms_gswap4 (&blkt_300->interval_duration);
783           ms_gswap4 (&blkt_300->amplitude);
784           ms_gswap4 (&blkt_300->reference_amplitude);
785         }
786 
787         if (details >= 1)
788         {
789           ms_log (0, "      calibration start time: %d,%d,%d:%d:%d.%04d (unused: %d)\n", blkt_300->time.year, blkt_300->time.day,
790                   blkt_300->time.hour, blkt_300->time.min, blkt_300->time.sec, blkt_300->time.fract, blkt_300->time.unused);
791           ms_log (0, "      number of calibrations: %u\n", blkt_300->numcalibrations);
792 
793           b = blkt_300->flags;
794           ms_log (0, "           calibration flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
795                   bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
796                   bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
797           if (b & 0x01)
798             ms_log (0, "                         [Bit 0] First pulse is positive\n");
799           if (b & 0x02)
800             ms_log (0, "                         [Bit 1] Calibration's alternate sign\n");
801           if (b & 0x04)
802             ms_log (0, "                         [Bit 2] Calibration was automatic\n");
803           if (b & 0x08)
804             ms_log (0, "                         [Bit 3] Calibration continued from previous record(s)\n");
805 
806           ms_log (0, "               step duration: %u\n", blkt_300->step_duration);
807           ms_log (0, "           interval duration: %u\n", blkt_300->interval_duration);
808           ms_log (0, "            signal amplitude: %g\n", blkt_300->amplitude);
809           ms_log (0, "        input signal channel: %.3s", blkt_300->input_channel);
810           if (details > 1)
811             ms_log (0, "               reserved byte: %u\n", blkt_300->reserved);
812           ms_log (0, "         reference amplitude: %u\n", blkt_300->reference_amplitude);
813           ms_log (0, "                    coupling: %.12s\n", blkt_300->coupling);
814           ms_log (0, "                     rolloff: %.12s\n", blkt_300->rolloff);
815         }
816       }
817 
818       else if (blkt_type == 310)
819       {
820         struct blkt_310_s *blkt_310 = (struct blkt_310_s *)(record + blkt_offset + 4);
821 
822         if (swapflag)
823         {
824           MS_SWAPBTIME (&blkt_310->time);
825           ms_gswap4 (&blkt_310->duration);
826           ms_gswap4 (&blkt_310->period);
827           ms_gswap4 (&blkt_310->amplitude);
828           ms_gswap4 (&blkt_310->reference_amplitude);
829         }
830 
831         if (details >= 1)
832         {
833           ms_log (0, "      calibration start time: %d,%d,%d:%d:%d.%04d (unused: %d)\n", blkt_310->time.year, blkt_310->time.day,
834                   blkt_310->time.hour, blkt_310->time.min, blkt_310->time.sec, blkt_310->time.fract, blkt_310->time.unused);
835           if (details > 1)
836             ms_log (0, "               reserved byte: %u\n", blkt_310->reserved1);
837 
838           b = blkt_310->flags;
839           ms_log (0, "           calibration flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
840                   bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
841                   bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
842           if (b & 0x04)
843             ms_log (0, "                         [Bit 2] Calibration was automatic\n");
844           if (b & 0x08)
845             ms_log (0, "                         [Bit 3] Calibration continued from previous record(s)\n");
846           if (b & 0x10)
847             ms_log (0, "                         [Bit 4] Peak-to-peak amplitude\n");
848           if (b & 0x20)
849             ms_log (0, "                         [Bit 5] Zero-to-peak amplitude\n");
850           if (b & 0x40)
851             ms_log (0, "                         [Bit 6] RMS amplitude\n");
852 
853           ms_log (0, "        calibration duration: %u\n", blkt_310->duration);
854           ms_log (0, "               signal period: %g\n", blkt_310->period);
855           ms_log (0, "            signal amplitude: %g\n", blkt_310->amplitude);
856           ms_log (0, "        input signal channel: %.3s", blkt_310->input_channel);
857           if (details > 1)
858             ms_log (0, "               reserved byte: %u\n", blkt_310->reserved2);
859           ms_log (0, "         reference amplitude: %u\n", blkt_310->reference_amplitude);
860           ms_log (0, "                    coupling: %.12s\n", blkt_310->coupling);
861           ms_log (0, "                     rolloff: %.12s\n", blkt_310->rolloff);
862         }
863       }
864 
865       else if (blkt_type == 320)
866       {
867         struct blkt_320_s *blkt_320 = (struct blkt_320_s *)(record + blkt_offset + 4);
868 
869         if (swapflag)
870         {
871           MS_SWAPBTIME (&blkt_320->time);
872           ms_gswap4 (&blkt_320->duration);
873           ms_gswap4 (&blkt_320->ptp_amplitude);
874           ms_gswap4 (&blkt_320->reference_amplitude);
875         }
876 
877         if (details >= 1)
878         {
879           ms_log (0, "      calibration start time: %d,%d,%d:%d:%d.%04d (unused: %d)\n", blkt_320->time.year, blkt_320->time.day,
880                   blkt_320->time.hour, blkt_320->time.min, blkt_320->time.sec, blkt_320->time.fract, blkt_320->time.unused);
881           if (details > 1)
882             ms_log (0, "               reserved byte: %u\n", blkt_320->reserved1);
883 
884           b = blkt_320->flags;
885           ms_log (0, "           calibration flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
886                   bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
887                   bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
888           if (b & 0x04)
889             ms_log (0, "                         [Bit 2] Calibration was automatic\n");
890           if (b & 0x08)
891             ms_log (0, "                         [Bit 3] Calibration continued from previous record(s)\n");
892           if (b & 0x10)
893             ms_log (0, "                         [Bit 4] Random amplitudes\n");
894 
895           ms_log (0, "        calibration duration: %u\n", blkt_320->duration);
896           ms_log (0, "      peak-to-peak amplitude: %g\n", blkt_320->ptp_amplitude);
897           ms_log (0, "        input signal channel: %.3s", blkt_320->input_channel);
898           if (details > 1)
899             ms_log (0, "               reserved byte: %u\n", blkt_320->reserved2);
900           ms_log (0, "         reference amplitude: %u\n", blkt_320->reference_amplitude);
901           ms_log (0, "                    coupling: %.12s\n", blkt_320->coupling);
902           ms_log (0, "                     rolloff: %.12s\n", blkt_320->rolloff);
903           ms_log (0, "                  noise type: %.8s\n", blkt_320->noise_type);
904         }
905       }
906 
907       else if (blkt_type == 390)
908       {
909         struct blkt_390_s *blkt_390 = (struct blkt_390_s *)(record + blkt_offset + 4);
910 
911         if (swapflag)
912         {
913           MS_SWAPBTIME (&blkt_390->time);
914           ms_gswap4 (&blkt_390->duration);
915           ms_gswap4 (&blkt_390->amplitude);
916         }
917 
918         if (details >= 1)
919         {
920           ms_log (0, "      calibration start time: %d,%d,%d:%d:%d.%04d (unused: %d)\n", blkt_390->time.year, blkt_390->time.day,
921                   blkt_390->time.hour, blkt_390->time.min, blkt_390->time.sec, blkt_390->time.fract, blkt_390->time.unused);
922           if (details > 1)
923             ms_log (0, "               reserved byte: %u\n", blkt_390->reserved1);
924 
925           b = blkt_390->flags;
926           ms_log (0, "           calibration flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
927                   bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
928                   bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
929           if (b & 0x04)
930             ms_log (0, "                         [Bit 2] Calibration was automatic\n");
931           if (b & 0x08)
932             ms_log (0, "                         [Bit 3] Calibration continued from previous record(s)\n");
933 
934           ms_log (0, "        calibration duration: %u\n", blkt_390->duration);
935           ms_log (0, "            signal amplitude: %g\n", blkt_390->amplitude);
936           ms_log (0, "        input signal channel: %.3s", blkt_390->input_channel);
937           if (details > 1)
938             ms_log (0, "               reserved byte: %u\n", blkt_390->reserved2);
939         }
940       }
941 
942       else if (blkt_type == 395)
943       {
944         struct blkt_395_s *blkt_395 = (struct blkt_395_s *)(record + blkt_offset + 4);
945 
946         if (swapflag)
947           MS_SWAPBTIME (&blkt_395->time);
948 
949         if (details >= 1)
950         {
951           ms_log (0, "        calibration end time: %d,%d,%d:%d:%d.%04d (unused: %d)\n", blkt_395->time.year, blkt_395->time.day,
952                   blkt_395->time.hour, blkt_395->time.min, blkt_395->time.sec, blkt_395->time.fract, blkt_395->time.unused);
953           if (details > 1)
954             ms_log (0, "          reserved bytes (2): %u,%u\n",
955                     blkt_395->reserved[0], blkt_395->reserved[1]);
956         }
957       }
958 
959       else if (blkt_type == 400)
960       {
961         struct blkt_400_s *blkt_400 = (struct blkt_400_s *)(record + blkt_offset + 4);
962 
963         if (swapflag)
964         {
965           ms_gswap4 (&blkt_400->azimuth);
966           ms_gswap4 (&blkt_400->slowness);
967           ms_gswap4 (&blkt_400->configuration);
968         }
969 
970         if (details >= 1)
971         {
972           ms_log (0, "      beam azimuth (degrees): %g\n", blkt_400->azimuth);
973           ms_log (0, "  beam slowness (sec/degree): %g\n", blkt_400->slowness);
974           ms_log (0, "               configuration: %u\n", blkt_400->configuration);
975           if (details > 1)
976             ms_log (0, "          reserved bytes (2): %u,%u\n",
977                     blkt_400->reserved[0], blkt_400->reserved[1]);
978         }
979       }
980 
981       else if (blkt_type == 405)
982       {
983         struct blkt_405_s *blkt_405 = (struct blkt_405_s *)(record + blkt_offset + 4);
984         uint16_t firstvalue         = blkt_405->delay_values[0]; /* Work on a private copy */
985 
986         if (swapflag)
987           ms_gswap2 (&firstvalue);
988 
989         if (details >= 1)
990           ms_log (0, "           first delay value: %u\n", firstvalue);
991       }
992 
993       else if (blkt_type == 500)
994       {
995         struct blkt_500_s *blkt_500 = (struct blkt_500_s *)(record + blkt_offset + 4);
996 
997         if (swapflag)
998         {
999           ms_gswap4 (&blkt_500->vco_correction);
1000           MS_SWAPBTIME (&blkt_500->time);
1001           ms_gswap4 (&blkt_500->exception_count);
1002         }
1003 
1004         if (details >= 1)
1005         {
1006           ms_log (0, "              VCO correction: %g%%\n", blkt_500->vco_correction);
1007           ms_log (0, "           time of exception: %d,%d,%d:%d:%d.%04d (unused: %d)\n", blkt_500->time.year, blkt_500->time.day,
1008                   blkt_500->time.hour, blkt_500->time.min, blkt_500->time.sec, blkt_500->time.fract, blkt_500->time.unused);
1009           ms_log (0, "                        usec: %d\n", blkt_500->usec);
1010           ms_log (0, "           reception quality: %u%%\n", blkt_500->reception_qual);
1011           ms_log (0, "             exception count: %u\n", blkt_500->exception_count);
1012           ms_log (0, "              exception type: %.16s\n", blkt_500->exception_type);
1013           ms_log (0, "                 clock model: %.32s\n", blkt_500->clock_model);
1014           ms_log (0, "                clock status: %.128s\n", blkt_500->clock_status);
1015         }
1016       }
1017 
1018       else if (blkt_type == 1000)
1019       {
1020         struct blkt_1000_s *blkt_1000 = (struct blkt_1000_s *)(record + blkt_offset + 4);
1021         char order[40];
1022 
1023         /* Calculate record size in bytes as 2^(blkt_1000->rec_len) */
1024         b1000reclen = (unsigned int)1 << blkt_1000->reclen;
1025 
1026         /* Big or little endian? */
1027         if (blkt_1000->byteorder == 0)
1028           strncpy (order, "Little endian", sizeof (order) - 1);
1029         else if (blkt_1000->byteorder == 1)
1030           strncpy (order, "Big endian", sizeof (order) - 1);
1031         else
1032           strncpy (order, "Unknown value", sizeof (order) - 1);
1033 
1034         if (details >= 1)
1035         {
1036           ms_log (0, "                    encoding: %s (val:%u)\n",
1037                   (char *)ms_encodingstr (blkt_1000->encoding), blkt_1000->encoding);
1038           ms_log (0, "                  byte order: %s (val:%u)\n",
1039                   order, blkt_1000->byteorder);
1040           ms_log (0, "               record length: %d (val:%u)\n",
1041                   b1000reclen, blkt_1000->reclen);
1042 
1043           if (details > 1)
1044             ms_log (0, "               reserved byte: %u\n", blkt_1000->reserved);
1045         }
1046 
1047         /* Save encoding format */
1048         b1000encoding = blkt_1000->encoding;
1049 
1050         /* Sanity check encoding format */
1051         if (!(b1000encoding >= 0 && b1000encoding <= 5) &&
1052             !(b1000encoding >= 10 && b1000encoding <= 19) &&
1053             !(b1000encoding >= 30 && b1000encoding <= 33))
1054         {
1055           ms_log (2, "%s: Blockette 1000 encoding format invalid (0-5,10-19,30-33): %d\n", srcname, b1000encoding);
1056           retval++;
1057         }
1058 
1059         /* Sanity check byte order flag */
1060         if (blkt_1000->byteorder != 0 && blkt_1000->byteorder != 1)
1061         {
1062           ms_log (2, "%s: Blockette 1000 byte order flag invalid (0 or 1): %d\n", srcname, blkt_1000->byteorder);
1063           retval++;
1064         }
1065       }
1066 
1067       else if (blkt_type == 1001)
1068       {
1069         struct blkt_1001_s *blkt_1001 = (struct blkt_1001_s *)(record + blkt_offset + 4);
1070 
1071         if (details >= 1)
1072         {
1073           ms_log (0, "              timing quality: %u%%\n", blkt_1001->timing_qual);
1074           ms_log (0, "                micro second: %d\n", blkt_1001->usec);
1075 
1076           if (details > 1)
1077             ms_log (0, "               reserved byte: %u\n", blkt_1001->reserved);
1078 
1079           ms_log (0, "                 frame count: %u\n", blkt_1001->framecnt);
1080         }
1081       }
1082 
1083       else if (blkt_type == 2000)
1084       {
1085         struct blkt_2000_s *blkt_2000 = (struct blkt_2000_s *)(record + blkt_offset + 4);
1086         char order[40];
1087 
1088         if (swapflag)
1089         {
1090           ms_gswap2 (&blkt_2000->length);
1091           ms_gswap2 (&blkt_2000->data_offset);
1092           ms_gswap4 (&blkt_2000->recnum);
1093         }
1094 
1095         /* Big or little endian? */
1096         if (blkt_2000->byteorder == 0)
1097           strncpy (order, "Little endian", sizeof (order) - 1);
1098         else if (blkt_2000->byteorder == 1)
1099           strncpy (order, "Big endian", sizeof (order) - 1);
1100         else
1101           strncpy (order, "Unknown value", sizeof (order) - 1);
1102 
1103         if (details >= 1)
1104         {
1105           ms_log (0, "            blockette length: %u\n", blkt_2000->length);
1106           ms_log (0, "                 data offset: %u\n", blkt_2000->data_offset);
1107           ms_log (0, "               record number: %u\n", blkt_2000->recnum);
1108           ms_log (0, "                  byte order: %s (val:%u)\n",
1109                   order, blkt_2000->byteorder);
1110           b = blkt_2000->flags;
1111           ms_log (0, "                  data flags: [%u%u%u%u%u%u%u%u] 8 bits\n",
1112                   bit (b, 0x01), bit (b, 0x02), bit (b, 0x04), bit (b, 0x08),
1113                   bit (b, 0x10), bit (b, 0x20), bit (b, 0x40), bit (b, 0x80));
1114 
1115           if (details > 1)
1116           {
1117             if (b & 0x01)
1118               ms_log (0, "                         [Bit 0] 1: Stream oriented\n");
1119             else
1120               ms_log (0, "                         [Bit 0] 0: Record oriented\n");
1121             if (b & 0x02)
1122               ms_log (0, "                         [Bit 1] 1: Blockette 2000s may NOT be packaged\n");
1123             else
1124               ms_log (0, "                         [Bit 1] 0: Blockette 2000s may be packaged\n");
1125             if (!(b & 0x04) && !(b & 0x08))
1126               ms_log (0, "                      [Bits 2-3] 00: Complete blockette\n");
1127             else if (!(b & 0x04) && (b & 0x08))
1128               ms_log (0, "                      [Bits 2-3] 01: First blockette in span\n");
1129             else if ((b & 0x04) && (b & 0x08))
1130               ms_log (0, "                      [Bits 2-3] 11: Continuation blockette in span\n");
1131             else if ((b & 0x04) && !(b & 0x08))
1132               ms_log (0, "                      [Bits 2-3] 10: Final blockette in span\n");
1133             if (!(b & 0x10) && !(b & 0x20))
1134               ms_log (0, "                      [Bits 4-5] 00: Not file oriented\n");
1135             else if (!(b & 0x10) && (b & 0x20))
1136               ms_log (0, "                      [Bits 4-5] 01: First blockette of file\n");
1137             else if ((b & 0x10) && !(b & 0x20))
1138               ms_log (0, "                      [Bits 4-5] 10: Continuation of file\n");
1139             else if ((b & 0x10) && (b & 0x20))
1140               ms_log (0, "                      [Bits 4-5] 11: Last blockette of file\n");
1141           }
1142 
1143           ms_log (0, "           number of headers: %u\n", blkt_2000->numheaders);
1144 
1145           /* Crude display of the opaque data headers */
1146           if (details > 1)
1147             ms_log (0, "                     headers: %.*s\n",
1148                     (blkt_2000->data_offset - 15), blkt_2000->payload);
1149         }
1150       }
1151 
1152       else
1153       {
1154         ms_log (2, "%s: Unrecognized blockette type: %d\n", srcname, blkt_type);
1155         retval++;
1156       }
1157 
1158       /* Sanity check the next blockette offset */
1159       if (next_blkt && next_blkt <= endofblockettes)
1160       {
1161         ms_log (2, "%s: Next blockette offset (%d) is within current blockette ending at byte %d\n",
1162                 srcname, next_blkt, endofblockettes);
1163         blkt_offset = 0;
1164       }
1165       else
1166       {
1167         blkt_offset = next_blkt;
1168       }
1169 
1170       blkt_count++;
1171     } /* End of looping through blockettes */
1172 
1173     /* Check that the blockette offset is within the maximum record size */
1174     if (blkt_offset > maxreclen)
1175     {
1176       ms_log (2, "%s: Blockette offset (%d) beyond maximum record length (%d)\n", srcname, blkt_offset, maxreclen);
1177       retval++;
1178     }
1179 
1180     /* Check that the data and blockette offsets are within the record */
1181     if (b1000reclen && fsdh->data_offset > b1000reclen)
1182     {
1183       ms_log (2, "%s: Data offset (%d) beyond record length (%d)\n", srcname, fsdh->data_offset, b1000reclen);
1184       retval++;
1185     }
1186     if (b1000reclen && fsdh->blockette_offset > b1000reclen)
1187     {
1188       ms_log (2, "%s: Blockette offset (%d) beyond record length (%d)\n", srcname, fsdh->blockette_offset, b1000reclen);
1189       retval++;
1190     }
1191 
1192     /* Check that the data offset is beyond the end of the blockettes */
1193     if (fsdh->numsamples && fsdh->data_offset <= endofblockettes)
1194     {
1195       ms_log (2, "%s: Data offset (%d) is within blockette chain (end of blockettes: %d)\n", srcname, fsdh->data_offset, endofblockettes);
1196       retval++;
1197     }
1198 
1199     /* Check that the correct number of blockettes were parsed */
1200     if (fsdh->numblockettes != blkt_count)
1201     {
1202       ms_log (2, "%s: Specified number of blockettes (%d) not equal to those parsed (%d)\n", srcname, fsdh->numblockettes, blkt_count);
1203       retval++;
1204     }
1205   }
1206 
1207   return retval;
1208 } /* End of ms_parse_raw() */
1209