SDDS ToolKit Programs and Libraries for C and Python
Loading...
Searching...
No Matches
sddscheck.c File Reference

Detailed Description

Validates and checks an SDDS file for corruption or issues.

This program reads one or more SDDS (Self Describing Data Set) files and determines their validity. It processes each file by verifying its structure, pages, and data, and outputs the status:

  • "ok" if the file is valid.
  • "nonexistent" if the file does not exist.
  • "badHeader" if the file has an invalid header.
  • "corrupted" if the file contains errors. For multiple files, each output line is prefixed with the corresponding file name.

Usage

sddscheck [options] <filename|directory> [<filename|directory>...]

Options

Option Description
-printErrors Outputs detailed error messages to stderr.
-threads=<number> Number of input files to check concurrently.
-summary Prints aggregate counts after per-file output.
-failuresOnly Suppresses ok results.
-failOnError Exits nonzero if any checked file is not ok.
-recursive Recursively expands directory arguments.
-pattern=<glob> Filters directory expansion by file-name wildcard.
-maxErrors=<number> Stops after the specified number of failed files.
-showPages Adds the number of pages read to each result line.
-checkDefinitionsOnly Validates only the SDDS header and definitions.
-verbose Adds file size and SDDS layout metadata to each result line.
-stdin Reads file or directory names from standard input.
License
This file is distributed under the terms of the Software License Agreement found in the file LICENSE included with this distribution.
Author
M. Borland, C. Saunders, R. Soliday

Definition in file sddscheck.c.

#include "mdb.h"
#include "SDDS.h"
#include "scan.h"
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>

Go to the source code of this file.

Functions

static void * checkedMalloc (size_t size)
 
static void * checkedRealloc (void *ptr, size_t size)
 
static char * checkedStrdup (const char *text)
 
static int parseLongOptionValue (const char *text, long minimum, long maximum, long *value)
 
static void stringListAppend (StringList *list, const char *text)
 
static void stringListFree (StringList *list)
 
static int compareStringPointers (const void *left, const void *right)
 
static const char * checkStatusString (CheckStatus status)
 
static const char * dataModeString (int mode)
 
static const char * baseName (const char *path)
 
static int matchesPatterns (const char *path, const StringList *patterns)
 
static int isDirectory (const char *path)
 
static char * joinPath (const char *directory, const char *name)
 
static void collectDirectoryFiles (const char *directory, long recursive, const StringList *patterns, StringList *files)
 
static void readInputNamesFromStdin (StringList *input)
 
static CheckResult checkFile (char *input, const CheckOptions *options, long print_errors)
 
static void printCheckResult (const char *input, const CheckResult *result, long singleResult, long showPages, long verbose)
 
static void printSummary (long checked, long *statusCounts)
 
int main (int argc, char **argv)
 

Function Documentation

◆ baseName()

static const char * baseName ( const char * path)
static

Definition at line 266 of file sddscheck.c.

266 {
267 const char *slash;
268 const char *backslash;
269
270 slash = strrchr(path, '/');
271 backslash = strrchr(path, '\\');
272 if (backslash && (!slash || backslash > slash))
273 slash = backslash;
274 return slash ? slash + 1 : path;
275}

◆ checkedMalloc()

static void * checkedMalloc ( size_t size)
static

Definition at line 173 of file sddscheck.c.

173 {
174 void *ptr;
175 if (size == 0)
176 size = 1;
177 ptr = malloc(size);
178 if (!ptr)
179 SDDS_Bomb("memory allocation failure");
180 return ptr;
181}
void SDDS_Bomb(char *message)
Terminates the program after printing an error message and recorded errors.
Definition SDDS_utils.c:380

◆ checkedRealloc()

static void * checkedRealloc ( void * ptr,
size_t size )
static

Definition at line 183 of file sddscheck.c.

183 {
184 if (size == 0)
185 size = 1;
186 ptr = realloc(ptr, size);
187 if (!ptr)
188 SDDS_Bomb("memory allocation failure");
189 return ptr;
190}

◆ checkedStrdup()

static char * checkedStrdup ( const char * text)
static

Definition at line 192 of file sddscheck.c.

192 {
193 char *copy;
194 size_t length;
195 if (!text)
196 text = "";
197 length = strlen(text) + 1;
198 copy = checkedMalloc(length);
199 memcpy(copy, text, length);
200 return copy;
201}

◆ checkFile()

static CheckResult checkFile ( char * input,
const CheckOptions * options,
long print_errors )
static

Definition at line 409 of file sddscheck.c.

409 {
410 SDDS_DATASET SDDS_input;
411 CheckResult result;
412 struct stat statBuffer;
413 long retval;
414
415 memset(&result, 0, sizeof(result));
416 result.checked = 1;
417 result.status = CHECK_CORRUPTED;
418 result.dataMode = 0;
419
420 if (stat(input, &statBuffer) != 0) {
421 result.status = CHECK_NONEXISTENT;
422 return result;
423 }
424 result.sizeKnown = 1;
425 result.sizeBytes = (long long)statBuffer.st_size;
426
427 if (!SDDS_InitializeInput(&SDDS_input, input)) {
428 if (print_errors)
429 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
430 else
432 result.status = CHECK_BADHEADER;
433 return result;
434 }
435
436 result.metadataKnown = 1;
437 result.dataMode = SDDS_input.layout.data_mode.mode;
438 result.columns = SDDS_input.layout.n_columns;
439 result.parameters = SDDS_input.layout.n_parameters;
440 result.arrays = SDDS_input.layout.n_arrays;
441 result.associates = SDDS_input.layout.n_associates;
442
443 if (options->checkDefinitionsOnly) {
444 result.status = CHECK_OK;
445 SDDS_Terminate(&SDDS_input);
446 return result;
447 }
448
449 while ((retval = SDDS_ReadPage(&SDDS_input)) > 0) {
450 /* Loop continues until EOF or an error occurs. */
451 result.pagesRead++;
452 }
453
454 if (retval == -1) {
455 SDDS_Terminate(&SDDS_input);
456 result.status = CHECK_OK;
457 return result;
458 }
459
460 if (print_errors)
461 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
462 else
464 SDDS_Terminate(&SDDS_input);
465 result.status = CHECK_CORRUPTED;
466 return result;
467}
int32_t SDDS_InitializeInput(SDDS_DATASET *SDDS_dataset, char *filename)
Definition SDDS_input.c:50
int32_t SDDS_Terminate(SDDS_DATASET *SDDS_dataset)
int32_t SDDS_ReadPage(SDDS_DATASET *SDDS_dataset)
void SDDS_PrintErrors(FILE *fp, int32_t mode)
Prints recorded error messages to a specified file stream.
Definition SDDS_utils.c:474
void SDDS_ClearErrors()
Clears all recorded error messages from the SDDS error stack.
Definition SDDS_utils.c:354

◆ checkStatusString()

static const char * checkStatusString ( CheckStatus status)
static

Definition at line 239 of file sddscheck.c.

239 {
240 switch (status) {
241 case CHECK_OK:
242 return "ok";
243 case CHECK_NONEXISTENT:
244 return "nonexistent";
245 case CHECK_BADHEADER:
246 return "badHeader";
247 case CHECK_CORRUPTED:
248 return "corrupted";
249 }
250 return "corrupted";
251}

◆ collectDirectoryFiles()

static void collectDirectoryFiles ( const char * directory,
long recursive,
const StringList * patterns,
StringList * files )
static

Definition at line 312 of file sddscheck.c.

312 {
313 StringList entries = {NULL, 0, 0};
314 long i;
315
316#if SDDSCHECK_USE_DIRENT
317 DIR *dir;
318 struct dirent *entry;
319
320 dir = opendir(directory);
321 if (!dir) {
322 fprintf(stderr, "warning: unable to read directory %s: %s\n", directory, strerror(errno));
323 return;
324 }
325
326 while ((entry = readdir(dir))) {
327 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
328 continue;
329 stringListAppend(&entries, entry->d_name);
330 }
331 closedir(dir);
332#elif defined(_WIN32)
333 WIN32_FIND_DATAA entry;
334 HANDLE findHandle;
335 char *searchPattern;
336 DWORD error;
337
338 searchPattern = joinPath(directory, "*");
339 findHandle = FindFirstFileA(searchPattern, &entry);
340 if (findHandle == INVALID_HANDLE_VALUE) {
341 error = GetLastError();
342 if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND)
343 fprintf(stderr, "warning: unable to read directory %s: Windows error %lu\n",
344 directory, (unsigned long)error);
345 free(searchPattern);
346 return;
347 }
348
349 do {
350 if (strcmp(entry.cFileName, ".") == 0 || strcmp(entry.cFileName, "..") == 0 ||
351 (entry.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
352 continue;
353 stringListAppend(&entries, entry.cFileName);
354 } while (FindNextFileA(findHandle, &entry));
355 error = GetLastError();
356 if (error != ERROR_NO_MORE_FILES)
357 fprintf(stderr, "warning: unable to finish reading directory %s: Windows error %lu\n",
358 directory, (unsigned long)error);
359 FindClose(findHandle);
360 free(searchPattern);
361#else
362 (void)directory;
363 (void)recursive;
364 (void)patterns;
365 (void)files;
366 SDDS_Bomb("-recursive and directory -pattern expansion are not supported on this platform");
367#endif
368
369 if (entries.items > 1)
370 qsort(entries.item, entries.items, sizeof(*entries.item), compareStringPointers);
371
372 for (i = 0; i < entries.items; i++) {
373 char *path;
374 struct stat statBuffer;
375
376 path = joinPath(directory, entries.item[i]);
377#if SDDSCHECK_USE_DIRENT
378 if (lstat(path, &statBuffer) != 0) {
379#else
380 if (stat(path, &statBuffer) != 0) {
381#endif
382 free(path);
383 continue;
384 }
385 if (S_ISDIR(statBuffer.st_mode)) {
386 if (recursive)
387 collectDirectoryFiles(path, recursive, patterns, files);
388 } else if (S_ISREG(statBuffer.st_mode) && matchesPatterns(path, patterns)) {
389 stringListAppend(files, path);
390 }
391 free(path);
392 }
393 stringListFree(&entries);
394}

◆ compareStringPointers()

static int compareStringPointers ( const void * left,
const void * right )
static

Definition at line 233 of file sddscheck.c.

233 {
234 const char *const *leftString = left;
235 const char *const *rightString = right;
236 return strcmp(*leftString, *rightString);
237}

◆ dataModeString()

static const char * dataModeString ( int mode)
static

Definition at line 253 of file sddscheck.c.

253 {
254 switch (mode) {
255 case SDDS_BINARY:
256 return "binary";
257 case SDDS_ASCII:
258 return "ascii";
259 case SDDS_PARALLEL:
260 return "parallel";
261 default:
262 return "unknown";
263 }
264}

◆ isDirectory()

static int isDirectory ( const char * path)
static

Definition at line 291 of file sddscheck.c.

291 {
292 struct stat statBuffer;
293 if (stat(path, &statBuffer) != 0)
294 return 0;
295 return S_ISDIR(statBuffer.st_mode);
296}

◆ joinPath()

static char * joinPath ( const char * directory,
const char * name )
static

Definition at line 298 of file sddscheck.c.

298 {
299 char *path;
300 size_t directoryLength, nameLength, length;
301 int needsSlash;
302
303 directoryLength = strlen(directory);
304 nameLength = strlen(name);
305 needsSlash = directoryLength && directory[directoryLength - 1] != '/' && directory[directoryLength - 1] != '\\';
306 length = directoryLength + needsSlash + nameLength + 1;
307 path = checkedMalloc(length);
308 snprintf(path, length, "%s%s%s", directory, needsSlash ? "/" : "", name);
309 return path;
310}

◆ main()

int main ( int argc,
char ** argv )

Definition at line 494 of file sddscheck.c.

494 {
495 StringList rawInput = {NULL, 0, 0};
496 StringList input = {NULL, 0, 0};
497 StringList patterns = {NULL, 0, 0};
498 CheckOptions checkOptions;
499 CheckResult *result;
500 long i_arg, print_errors, threads, summary, failuresOnly, failOnError, recursive, useStdin, maxErrors, showPages, verbose;
501 long checked, failures, statusCounts[4];
502 SCANNED_ARG *s_arg;
503
504 /* Register the program name for error reporting. */
506
507 /* Parse command-line arguments. */
508 argc = scanargs(&s_arg, argc, argv);
509 if (!s_arg || argc < 2) {
510 bomb(NULL, usage); /* Display usage and exit if arguments are insufficient. */
511 }
512
513 memset(&checkOptions, 0, sizeof(checkOptions));
514 memset(statusCounts, 0, sizeof(statusCounts));
515 print_errors = 0;
516 threads = 1;
517 summary = 0;
518 failuresOnly = 0;
519 failOnError = 0;
520 recursive = 0;
521 useStdin = 0;
522 maxErrors = 0;
523 showPages = 0;
524 verbose = 0;
525
526 /* Process each command-line argument. */
527 for (i_arg = 1; i_arg < argc; i_arg++) {
528 if (s_arg[i_arg].arg_type == OPTION) {
529 /* Match recognized options. */
530 switch (match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
531 case CLO_PRINTERRORS:
532 print_errors = 1;
533 break;
534 case CLO_THREADS:
535 if (s_arg[i_arg].n_items != 2 ||
536 !parseLongOptionValue(s_arg[i_arg].list[1], 1, INT_MAX, &threads))
537 SDDS_Bomb("invalid -threads syntax");
538 break;
539 case CLO_SUMMARY:
540 summary = 1;
541 break;
542 case CLO_FAILURESONLY:
543 failuresOnly = 1;
544 break;
545 case CLO_FAILONERROR:
546 failOnError = 1;
547 break;
548 case CLO_RECURSIVE:
549 recursive = 1;
550 break;
551 case CLO_PATTERN:
552 if (s_arg[i_arg].n_items != 2 || !strlen(s_arg[i_arg].list[1]))
553 SDDS_Bomb("invalid -pattern syntax");
554 stringListAppend(&patterns, s_arg[i_arg].list[1]);
555 break;
556 case CLO_MAXERRORS:
557 if (s_arg[i_arg].n_items != 2 ||
558 !parseLongOptionValue(s_arg[i_arg].list[1], 1, LONG_MAX, &maxErrors))
559 SDDS_Bomb("invalid -maxErrors syntax");
560 break;
561 case CLO_SHOWPAGES:
562 showPages = 1;
563 break;
564 case CLO_CHECKDEFINITIONSONLY:
565 checkOptions.checkDefinitionsOnly = 1;
566 break;
567 case CLO_VERBOSE:
568 verbose = 1;
569 break;
570 case CLO_STDIN:
571 useStdin = 1;
572 break;
573 default:
574 SDDS_Bomb("unknown option given"); /* Handle unrecognized options. */
575 break;
576 }
577 } else {
578 stringListAppend(&rawInput, s_arg[i_arg].list[0]);
579 }
580 }
581
582 if (useStdin)
583 readInputNamesFromStdin(&rawInput);
584
585 if (rawInput.items < 1)
586 bomb(NULL, usage);
587
588 for (i_arg = 0; i_arg < rawInput.items; i_arg++) {
589 if ((recursive || patterns.items) && isDirectory(rawInput.item[i_arg]))
590 collectDirectoryFiles(rawInput.item[i_arg], recursive, &patterns, &input);
591 else
592 stringListAppend(&input, rawInput.item[i_arg]);
593 }
594
595 result = checkedMalloc(sizeof(*result) * input.items);
596 memset(result, 0, sizeof(*result) * input.items);
597 checked = 0;
598 failures = 0;
599
600 if (input.items == 1 && !summary && !failuresOnly && !showPages && !verbose) {
601 result[0] = checkFile(input.item[0], &checkOptions, 0);
602 checked = 1;
603 statusCounts[result[0].status]++;
604 failures = result[0].status != CHECK_OK;
605 if (print_errors && result[0].status == CHECK_CORRUPTED)
606 checkFile(input.item[0], &checkOptions, 1);
607 printCheckResult(input.item[0], &result[0], 1, showPages, verbose);
608 if (print_errors && result[0].status == CHECK_BADHEADER)
609 checkFile(input.item[0], &checkOptions, 1);
610 free(result);
611 stringListFree(&patterns);
612 stringListFree(&rawInput);
613 stringListFree(&input);
614 return (failOnError && failures) ? 1 : 0;
615 }
616
617 if (threads <= 1 || input.items <= 1) {
618 for (i_arg = 0; i_arg < input.items; i_arg++) {
619 result[i_arg] = checkFile(input.item[i_arg], &checkOptions, print_errors);
620 checked++;
621 statusCounts[result[i_arg].status]++;
622 if (result[i_arg].status != CHECK_OK) {
623 failures++;
624 if (maxErrors && failures >= maxErrors)
625 break;
626 }
627 }
628 } else {
629 long sharedFailures = 0;
630 if (threads > input.items)
631 threads = input.items;
632#if SDDSCHECK_USE_OPENMP
633 omp_set_num_threads((int)threads);
634#pragma omp parallel for schedule(dynamic)
635 for (i_arg = 0; i_arg < input.items; i_arg++) {
636 long failuresSeen = 0;
637 if (maxErrors) {
638#pragma omp critical(sddscheck_failures)
639 {
640 failuresSeen = sharedFailures;
641 }
642 if (failuresSeen >= maxErrors)
643 continue;
644 }
645 result[i_arg] = checkFile(input.item[i_arg], &checkOptions, 0);
646 if (maxErrors && result[i_arg].status != CHECK_OK) {
647#pragma omp critical(sddscheck_failures)
648 {
649 sharedFailures++;
650 }
651 }
652 }
653#else
654 for (i_arg = 0; i_arg < input.items; i_arg++) {
655 if (maxErrors && sharedFailures >= maxErrors)
656 break;
657 result[i_arg] = checkFile(input.item[i_arg], &checkOptions, 0);
658 if (result[i_arg].status != CHECK_OK)
659 sharedFailures++;
660 }
661#endif
662 if (print_errors) {
663 for (i_arg = 0; i_arg < input.items; i_arg++) {
664 if (result[i_arg].checked && result[i_arg].status != CHECK_OK && result[i_arg].status != CHECK_NONEXISTENT)
665 checkFile(input.item[i_arg], &checkOptions, 1);
666 }
667 }
668 for (i_arg = 0; i_arg < input.items; i_arg++) {
669 if (!result[i_arg].checked)
670 continue;
671 checked++;
672 statusCounts[result[i_arg].status]++;
673 if (result[i_arg].status != CHECK_OK)
674 failures++;
675 }
676 }
677
678 for (i_arg = 0; i_arg < input.items; i_arg++) {
679 if (!result[i_arg].checked)
680 continue;
681 if (failuresOnly && result[i_arg].status == CHECK_OK)
682 continue;
683 printCheckResult(input.item[i_arg], &result[i_arg], input.items == 1, showPages, verbose);
684 }
685 if (summary)
686 printSummary(checked, statusCounts);
687
688 free(result);
689 stringListFree(&patterns);
690 stringListFree(&rawInput);
691 stringListFree(&input);
692 return (failOnError && failures) ? 1 : 0;
693}
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
Definition SDDS_utils.c:318
void bomb(char *error, char *usage)
Reports error messages to the terminal and aborts the program.
Definition bomb.c:26
long match_string(char *string, char **option, long n_options, long mode)
Matches a given string against an array of option strings based on specified modes.
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
Definition scanargs.c:36

◆ matchesPatterns()

static int matchesPatterns ( const char * path,
const StringList * patterns )
static

Definition at line 277 of file sddscheck.c.

277 {
278 long i;
279 const char *name;
280
281 if (!patterns->items)
282 return 1;
283 name = baseName(path);
284 for (i = 0; i < patterns->items; i++) {
285 if (wild_match((char *)name, patterns->item[i]))
286 return 1;
287 }
288 return 0;
289}
int wild_match(char *string, char *template)
Determine whether one string is a wildcard match for another.
Definition wild_match.c:49

◆ parseLongOptionValue()

static int parseLongOptionValue ( const char * text,
long minimum,
long maximum,
long * value )
static

Definition at line 203 of file sddscheck.c.

203 {
204 char *endptr;
205 long parsed;
206
207 if (!text || !*text)
208 return 0;
209 errno = 0;
210 parsed = strtol(text, &endptr, 10);
211 if (errno == ERANGE || endptr == text || *endptr || parsed < minimum || parsed > maximum)
212 return 0;
213 *value = parsed;
214 return 1;
215}

◆ printCheckResult()

static void printCheckResult ( const char * input,
const CheckResult * result,
long singleResult,
long showPages,
long verbose )
static

Definition at line 469 of file sddscheck.c.

469 {
470 if (singleResult && !showPages && !verbose) {
471 puts(checkStatusString(result->status));
472 return;
473 }
474
475 printf("%s: %s", input, checkStatusString(result->status));
476 if (showPages || verbose)
477 printf(" pagesRead=%ld", result->pagesRead);
478 if (verbose) {
479 if (result->sizeKnown)
480 printf(" sizeBytes=%lld", result->sizeBytes);
481 if (result->metadataKnown) {
482 printf(" dataMode=%s columns=%d parameters=%d arrays=%d associates=%d",
483 dataModeString(result->dataMode), result->columns, result->parameters, result->arrays, result->associates);
484 }
485 }
486 putchar('\n');
487}

◆ printSummary()

static void printSummary ( long checked,
long * statusCounts )
static

Definition at line 489 of file sddscheck.c.

489 {
490 printf("checked: %ld ok: %ld nonexistent: %ld badHeader: %ld corrupted: %ld\n",
491 checked, statusCounts[CHECK_OK], statusCounts[CHECK_NONEXISTENT], statusCounts[CHECK_BADHEADER], statusCounts[CHECK_CORRUPTED]);
492}

◆ readInputNamesFromStdin()

static void readInputNamesFromStdin ( StringList * input)
static

Definition at line 396 of file sddscheck.c.

396 {
397 char buffer[4096];
398
399 while (fgets(buffer, sizeof(buffer), stdin)) {
400 size_t length = strlen(buffer);
401 while (length && (buffer[length - 1] == '\n' || buffer[length - 1] == '\r'))
402 buffer[--length] = 0;
403 if (!length)
404 continue;
405 stringListAppend(input, buffer);
406 }
407}

◆ stringListAppend()

static void stringListAppend ( StringList * list,
const char * text )
static

Definition at line 217 of file sddscheck.c.

217 {
218 if (list->items >= list->allocated) {
219 list->allocated = list->allocated ? 2 * list->allocated : 16;
220 list->item = checkedRealloc(list->item, sizeof(*list->item) * list->allocated);
221 }
222 list->item[list->items++] = checkedStrdup(text);
223}

◆ stringListFree()

static void stringListFree ( StringList * list)
static

Definition at line 225 of file sddscheck.c.

225 {
226 long i;
227 for (i = 0; i < list->items; i++)
228 free(list->item[i]);
229 free(list->item);
230 memset(list, 0, sizeof(*list));
231}