libdsproc3  2.0
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
dsproc_dataset_compare.c
Go to the documentation of this file.
1 /*******************************************************************************
2 *
3 * COPYRIGHT (C) 2012 Battelle Memorial Institute. All Rights Reserved.
4 *
5 ********************************************************************************
6 *
7 * Author:
8 * name: Brian Ermold
9 * phone: (509) 375-2277
10 * email: brian.ermold@pnl.gov
11 *
12 ********************************************************************************
13 *
14 * REPOSITORY INFORMATION:
15 * $Revision: 76749 $
16 * $Author: ermold $
17 * $Date: 2017-02-15 19:06:10 +0000 (Wed, 15 Feb 2017) $
18 *
19 ********************************************************************************
20 *
21 * NOTE: DOXYGEN is used to generate documentation for this file.
22 *
23 *******************************************************************************/
24 
25 /** @file dsproc_dataset_compare.c
26  * Dataset Compare Functions.
27  */
28 
29 #include "dsproc3.h"
30 #include "dsproc_private.h"
31 
32 extern DSProc *_DSProc; /**< Internal DSProc structure */
33 
34 /** @privatesection */
35 
36 /*******************************************************************************
37  * Static Data and Functions Visible Only To This Module
38  */
39 
40 /**
41  * Typedef for structure used to specify attributes and static
42  * data that should be excluded from dod compare checks.
43  */
44 typedef struct ExAtts ExAtts;
45 
46 /**
47  * Structure used to specify attributes and static
48  * data that should be excluded from dod compare checks.
49  */
50 struct ExAtts {
51 
52  ExAtts *next; /**< next structure in list */
53  char *var_name; /**< variable name (NULL for global) */
54  int natts; /**< number of attributes to exclude */
55  char **att_names; /**< names of the attributes to exclude */
56  int exclude_data; /**< exclude static data from dod compare */
57 
58 };
59 
60 /** Linked list of ExAtts */
61 static ExAtts *_ExAtts = (ExAtts *)NULL;
62 
63 /**
64  * Static: Get the ExAtts structure for the specified variable.
65  *
66  * @param var_name - the variable name or NULL for global attributes
67  *
68  * @return
69  * - the ExAtts structure
70  * - NULL if not found
71  */
72 static ExAtts *_dsproc_get_exclude_atts(const char *var_name)
73 {
74  ExAtts *ex_atts;
75 
76  for (ex_atts = _ExAtts; ex_atts; ex_atts = ex_atts->next) {
77 
78  if ((var_name == NULL) || (ex_atts->var_name == NULL) ) {
79  if ((var_name == NULL) && (ex_atts->var_name == NULL) ) {
80  return(ex_atts);
81  }
82  }
83  else if (strcmp(ex_atts->var_name, var_name) == 0) {
84  return(ex_atts);
85  }
86  }
87 
88  return((ExAtts *)NULL);
89 }
90 
91 static const char *_DSName; /**< name of the current dataset */
92 static const char *_Header; /**< metadata change message header */
93 static int _NumChanges; /**< track the number of metadata changes */
94 static int _Warn; /**< flag if warnings should be generated */
95 
96 /**
97  * Initialize metadata change warning messages.
98  */
99 #define INIT_METADATA_WARNINGS(warn, ds_name, header) \
100 _dsproc_init_metadata_warnings(warn, ds_name, header)
101 
102 /**
103  * Generate a metadata change warning messages.
104  */
105 #define METADATA_WARNING(...) \
106 _dsproc_metadata_warning(__func__, __FILE__, __LINE__, __VA_ARGS__)
107 
108 /**
109  * Finish metadata change warning messages.
110  */
111 #define FINISH_METADATA_WARNINGS() \
112 _dsproc_finish_metadata_warnings(__func__, __FILE__, __LINE__)
113 
114 /**
115  * Static: Initialize metadata change warning messages.
116  *
117  * @param warn - generate metadata change warning messages
118  * @param ds_name - the name of the current dataset
119  * @param header - the header to use to start a new metadata change message
120  */
121 static void _dsproc_init_metadata_warnings(
122  int warn,
123  const char *ds_name,
124  const char *header)
125 {
126  _Warn = warn;
127  _DSName = ds_name;
128  _Header = header;
129  _NumChanges = 0;
130 }
131 
132 /**
133  * Static: Finish metadata change warning messages.
134  *
135  * @param func - the name of the function sending the message (__func__)
136  * @param file - the source file the message came from (__FILE__)
137  * @param line - the line number in the source file (__LINE__)
138  *
139  * @return the number of metadata changes found
140  */
141 static int _dsproc_finish_metadata_warnings(
142  const char *func,
143  const char *file,
144  int line)
145 {
146  Mail *warning_mail;
147 
148  if (_NumChanges && _Warn) {
149 
150  warning_mail = msngr_get_mail(MSNGR_WARNING);
151 
152  if (warning_mail) {
153  mail_set_flags(warning_mail, MAIL_ADD_NEWLINE);
154  }
155 
156  msngr_send(
157  DSPROC_LIB_NAME, func, file, line, MSNGR_WARNING,
158  " - number of changes found: %d\n",
159  _NumChanges);
160  }
161 
162  return(_NumChanges);
163 }
164 
165 /**
166  * Static: Generate a metadata change warning messages.
167  *
168  * This function will append a warning message to the log file
169  * and warning mail message.
170  *
171  * @param func - the name of the function sending the message (__func__)
172  * @param file - the source file the message came from (__FILE__)
173  * @param line - the line number in the source file (__LINE__)
174  * @param format - format string (see printf)
175  * @param ... - arguments for the format string
176  */
177 static void _dsproc_metadata_warning(
178  const char *func,
179  const char *file,
180  int line,
181  const char *format, ...)
182 {
183  Mail *warning_mail;
184  va_list args;
185 
186  if (_Warn) {
187 
188  /* Check if this is the first change found */
189 
190  if (_NumChanges == 0) {
191 
192  warning_mail = msngr_get_mail(MSNGR_WARNING);
193 
194  if (warning_mail) {
195  mail_unset_flags(warning_mail, MAIL_ADD_NEWLINE);
196  }
197 
199  "%s: %s\n",
200  _DSName, _Header);
201  }
202 
203  /* Generate the warning message */
204 
205  va_start(args, format);
206 
207  msngr_vsend(
208  DSPROC_LIB_NAME, func, file, line, MSNGR_WARNING, format, args);
209 
210  va_end(args);
211  }
212 
213  _NumChanges++;
214 }
215 
216 /**
217  * Static: Compare the attributes in two attribute lists.
218  *
219  * @param var_name - name of the variable or NULL for globals
220  * @param ex_atts - list of attributes to exclude
221  * @param prev_natts - number of attributes in the previous attributes list
222  * @param prev_atts - previous attributes list
223  * @param curr_natts - number of attributes in the current attributes list
224  * @param curr_atts - current attributes list
225  *
226  * @return
227  * - the number of attribute changes found
228  * - -1 if a memory allocation error occurred generating a warning message
229  */
230 static int _dsproc_compare_atts(
231  const char *var_name,
232  ExAtts *ex_atts,
233  int prev_natts,
234  CDSAtt **prev_atts,
235  int curr_natts,
236  CDSAtt **curr_atts)
237 {
238  CDSAtt *prev_att = (CDSAtt *)NULL;
239  CDSAtt *curr_att;
240  char *curr_value;
241  char *prev_value;
242  size_t nbytes;
243  size_t length;
244  char *null_string;
245  char *indent_string;
246  int nchanges;
247  int ai, aj, xi;
248 
249  null_string = "NULL";
250  indent_string = (var_name) ? " " : "";
251  nchanges = 0;
252 
253  int special_att_count = 0;
254 
255  /* Loop over attributes in the current attributes list */
256 
257  for (ai = 0; ai < curr_natts; ++ai) {
258 
259  curr_att = curr_atts[ai];
260 
261  if (strcmp(curr_att->name, "_Format") == 0 ||
262  strcmp(curr_att->name, "_DeflateLevel") == 0 ||
263  strcmp(curr_att->name, "_ChunkSizes") == 0 ||
264  strcmp(curr_att->name, "_Shuffle") == 0 ||
265  strcmp(curr_att->name, "_Endianness") == 0 ||
266  strcmp(curr_att->name, "_Fletcher32") == 0 ||
267  strcmp(curr_att->name, "_NoFill") == 0) {
268 
269  special_att_count++;
270  continue;
271  }
272 
273  /* Check if we need to exclude this attribute */
274 
275 /* BDE: At some point we may want to skip the long_name and units
276  * attribute checks for the time variable, and let the ncds write
277  * logic do the units conversion when appending to an existing file.
278  *
279  * if (var_name && strcmp(var_name, "time") == 0 &&
280  * (strcmp(curr_att->name, "units") == 0 ||
281  * strcmp(curr_att->name, "long_name") == 0) ) {
282  *
283  * continue;
284  * }
285  */
286 
287  /* Check for user defined attributes to exclude */
288 
289  if (ex_atts) {
290 
291  for (xi = 0; xi < ex_atts->natts; ++xi) {
292  if (strcmp(curr_att->name, ex_atts->att_names[xi]) == 0) {
293  break;
294  }
295  }
296 
297  if (xi != ex_atts->natts) {
298  continue;
299  }
300  }
301 
302  /* Check if this attribute exists in the previous attributes list */
303 
304  for (aj = 0; aj < prev_natts; ++aj) {
305 
306  prev_att = prev_atts[aj];
307 
308  if (strcmp(curr_att->name, prev_att->name) == 0) {
309  break;
310  }
311  }
312 
313  if (aj == prev_natts) {
314 
315  if (_Warn) {
316 
317  if (var_name && !nchanges) {
318 
320  " - %s: variable attribute changes\n",
321  var_name);
322  }
323 
325  "%s - %s: attribute not found in previous dataset\n",
326  indent_string, curr_att->name);
327  }
328  else {
329  _NumChanges++;
330  }
331 
332  nchanges++;
333  continue;
334  }
335 
336  /* Check if the attribute values are equal */
337 
338  nbytes = curr_att->length * cds_data_type_size(curr_att->type);
339 
340  if (prev_att->length == curr_att->length &&
341  prev_att->type == curr_att->type &&
342  memcmp(prev_att->value.vp, curr_att->value.vp, nbytes) == 0) {
343 
344  continue;
345  }
346 
347  /* Generate the warning message */
348 
349  if (_Warn) {
350 
351  prev_value = null_string;
352  curr_value = null_string;
353 
354  if (prev_att->length && prev_att->value.vp) {
355 
356  prev_value = cds_sprint_array(
357  prev_att->type,
358  prev_att->length,
359  prev_att->value.vp,
360  &length, NULL,
361  NULL, 0, 0,
362  0x02 | 0x10);
363  }
364 
365  if (curr_att->length && curr_att->value.vp) {
366 
367  curr_value = cds_sprint_array(
368  curr_att->type,
369  curr_att->length,
370  curr_att->value.vp,
371  &length, NULL,
372  NULL, 0, 0,
373  0x02 | 0x10);
374  }
375 
376  if (!prev_value || !curr_value) {
377 
379  "Could not generate warning message for attribute change\n"
380  " -> memory allocation error\n");
381 
382  if (prev_value && (prev_value != null_string)) free(prev_value);
383  if (curr_value && (curr_value != null_string)) free(curr_value);
384 
386  return(-1);
387  }
388 
389  if (var_name && !nchanges) {
390 
392  " - %s: variable attribute changes\n",
393  var_name);
394  }
395 
397  "%s - %s: attribute value changed\n"
398  "%s - from: %s\n"
399  "%s - to: %s\n",
400  indent_string, curr_att->name,
401  indent_string, prev_value,
402  indent_string, curr_value);
403 
404  if (prev_value != null_string) free(prev_value);
405  if (curr_value != null_string) free(curr_value);
406  }
407  else {
408  _NumChanges++;
409  }
410 
411  nchanges++;
412 
413  } /* end loop over attributes in the current attributes list */
414 
415  /* Check if the number of attributes has changed */
416 
417  if (prev_natts != (curr_natts - special_att_count)) {
418 
419  if (_Warn) {
420 
421  if (var_name && !nchanges) {
422 
424  " - %s: variable attribute changes\n",
425  var_name);
426  }
427 
429  "%s - number of attributes changed from %d to %d\n",
430  indent_string, prev_natts, curr_natts);
431  }
432  else {
433  _NumChanges++;
434  }
435 
436  nchanges++;
437  }
438 
439  return(nchanges);
440 }
441 
442 /*******************************************************************************
443  * Private Functions Visible Only To This Library
444  */
445 
446 /**
447  * Private: Free all memory used by the interval ExAtts list.
448  */
449 void _dsproc_free_exclude_atts(void)
450 {
451  ExAtts *ex_atts;
452  ExAtts *next;
453  int ai;
454 
455  for (ex_atts = _ExAtts; ex_atts; ex_atts = next) {
456 
457  next = ex_atts->next;
458 
459  if (ex_atts->var_name) free(ex_atts->var_name);
460  if (ex_atts->att_names) {
461 
462  for (ai = 0; ai < ex_atts->natts; ai++) {
463  if (ex_atts->att_names[ai]) free(ex_atts->att_names[ai]);
464  }
465 
466  free(ex_atts->att_names);
467  }
468 
469  free(ex_atts);
470  }
471 
472  _ExAtts = (ExAtts *)NULL;
473 }
474 
475 /**
476  * Private: Exclude standard attributes from dod compare.
477  *
478  * If an error occurs in this function it will be appended to the log and
479  * error mail messages, and the process status will be set appropriately.
480  *
481  * @return
482  * - 1 if successful
483  * - 0 if a memory allocation error occurred
484  */
485 int _dsproc_set_standard_exclude_atts(void)
486 {
487  /* Global attributes */
488 
489  const char *global_atts[] = {
490  "command_line",
491  "dod_version", // BDE: Let the library detect true DOD changes.
492  "facility_id", // BDE: temporary hack to prevent file splits during new standards transtion.
493  "input_source",
494  "input_datastreams",
495  "history"
496  };
497  int global_natts = sizeof(global_atts)/sizeof(char *);
498 
499  if (!dsproc_exclude_from_dod_compare(NULL, 0, global_natts, global_atts)) {
500  return(0);
501  }
502 
503  /* base_time variable attributes */
504 
505  const char *bt_atts[] = {
506  "string"
507  };
508  int bt_natts = sizeof(bt_atts)/sizeof(char *);
509 
510  if (!dsproc_exclude_from_dod_compare("base_time", 1, bt_natts, bt_atts)) {
511  return(0);
512  }
513 
514  /* time_offset variable attributes */
515 
516  const char *to_atts[] = {
517  "units"
518  };
519  int to_natts = sizeof(to_atts)/sizeof(char *);
520 
521  if (!dsproc_exclude_from_dod_compare("time_offset", 1, to_natts, to_atts)) {
522  return(0);
523  }
524 
525  /* time variable attributes */
526 
527  const char *time_atts[] = {
528  "units"
529  };
530  int time_natts = sizeof(time_atts)/sizeof(char *);
531 
532  if (!dsproc_exclude_from_dod_compare("time", 1, time_natts, time_atts)) {
533  return(0);
534  }
535 
536  /* time_bounds variable attributes */
537 
538  const char *tb_atts[] = {
539  "units"
540  };
541  int tb_natts = sizeof(tb_atts)/sizeof(char *);
542 
543  if (!dsproc_exclude_from_dod_compare("time_bounds", 1, tb_natts, tb_atts)) {
544  return(0);
545  }
546 
547  return(1);
548 }
549 
550 /** @publicsection */
551 
552 /*******************************************************************************
553  * Internal Functions Visible To The Public
554  */
555 
556 /**
557  * Compare the DOD versions of two datasets.
558  *
559  * @param prev_ds - previous dataset
560  * @param curr_ds - current dataset
561  * @param warn - generate warning message if DOD version changed
562  *
563  * @return
564  * - 1 if the DOD version has changed
565  * - 0 if the DOD version has not changed
566  */
567 int dsproc_compare_dod_versions(CDSGroup *prev_ds, CDSGroup *curr_ds, int warn)
568 {
569  const char *prev_version;
570  const char *curr_version;
571  CDSAtt *att;
572 
573  att = cds_get_att(prev_ds, "dod_version");
574  prev_version = (att && att->type == CDS_CHAR) ? att->value.cp : "NULL";
575 
576  att = cds_get_att(curr_ds, "dod_version");
577  curr_version = (att && att->type == CDS_CHAR) ? att->value.cp : "NULL";
578 
579  if (strcmp(prev_version, curr_version) != 0) {
580 
581  if (warn) {
582 
584  "%s: DOD version changed\n"
585  " - from: %s\n"
586  " - to: %s\n",
587  curr_ds->name, prev_version, curr_version);
588  }
589 
590  return(1);
591  }
592 
593  return(0);
594 }
595 
596 /**
597  * Compare the DOD dimensions of two datasets.
598  *
599  * @param prev_ds - previous dataset
600  * @param curr_ds - current dataset
601  * @param warn - generate warning message if DOD dimensions have changed
602  *
603  * @return the number of changes found
604  */
605 int dsproc_compare_dod_dims(CDSGroup *prev_ds, CDSGroup *curr_ds, int warn)
606 {
607  CDSDim *prev_dim;
608  CDSDim *curr_dim;
609  int nchanges;
610  int di;
611 
612  INIT_METADATA_WARNINGS(warn, curr_ds->name, "DOD dimension changes");
613 
614  /* Loop over dimensions in the current dataset */
615 
616  for (di = 0; di < curr_ds->ndims; ++di) {
617 
618  curr_dim = curr_ds->dims[di];
619  prev_dim = cds_get_dim(prev_ds, curr_dim->name);
620 
621  /* Check if this dimension exists in the previous dataset */
622 
623  if (!prev_dim) {
624 
626  " - %s: dimension not found in previous dataset\n",
627  curr_dim->name);
628 
629  continue;
630  }
631 
632  /* Check if this is an unlimited dimension */
633 
634  if (prev_dim->is_unlimited || curr_dim->is_unlimited) {
635 
636  if (prev_dim->is_unlimited != curr_dim->is_unlimited) {
637 
638  if (prev_dim->is_unlimited) {
639 
641  " - %s: dimension changed from UNLIMITED to %d\n",
642  curr_dim->name, curr_dim->length);
643  }
644  else {
645 
647  " - %s: dimension changed from %d to UNLIMITED\n",
648  curr_dim->name, prev_dim->length);
649  }
650  }
651 
652  continue;
653  }
654 
655  /* Check if the dimension length has changed */
656 
657  if (prev_dim->length != curr_dim->length) {
658 
660  " - %s: length of dimension changed from %d to %d\n",
661  curr_dim->name, prev_dim->length, curr_dim->length);
662 
663  continue;
664  }
665 
666  } /* end loop over dimensions in the current dataset */
667 
668  /* Check if the number of dimensions has changed */
669 
670  if (curr_ds->ndims != prev_ds->ndims) {
671 
673  " - number of dimensions changed from %d to %d\n",
674  prev_ds->ndims, curr_ds->ndims);
675  }
676 
677  nchanges = FINISH_METADATA_WARNINGS();
678 
679  return(nchanges);
680 }
681 
682 /**
683  * Compare the DOD attributes of two datasets.
684  *
685  * @param prev_ds - previous dataset
686  * @param curr_ds - current dataset
687  * @param warn - generate warning message if DOD attributes have changed
688  *
689  * @return
690  * - the number of changes found
691  * - -1 if a memory allocation error occurred generating a warning message
692  */
693 int dsproc_compare_dod_atts(CDSGroup *prev_ds, CDSGroup *curr_ds, int warn)
694 {
695  ExAtts *ex_atts = _dsproc_get_exclude_atts(NULL);
696  int nchanges;
697 
698  INIT_METADATA_WARNINGS(warn, curr_ds->name, "DOD attribute changes");
699 
700  nchanges = _dsproc_compare_atts(
701  NULL, ex_atts,
702  prev_ds->natts, prev_ds->atts,
703  curr_ds->natts, curr_ds->atts);
704 
705  if (nchanges < 0) {
706  return(-1);
707  }
708 
709  nchanges = FINISH_METADATA_WARNINGS();
710 
711  return(nchanges);
712 }
713 
714 /**
715  * Compare the DOD variables of two datasets.
716  *
717  * @param prev_ds - previous dataset
718  * @param curr_ds - current dataset
719  * @param warn - generate warning message if DOD variables have changed
720  *
721  * @return
722  * - the number of changes found
723  * - -1 if a memory allocation error occurred generating a warning message
724  */
725 int dsproc_compare_dod_vars(CDSGroup *prev_ds, CDSGroup *curr_ds, int warn)
726 {
727  CDSVar *prev_var;
728  CDSVar *curr_var;
729  ExAtts *ex_atts;
730  int nchanges;
731  int natt_changes;
732  size_t prev_sample_size;
733  size_t curr_sample_size;
734  size_t nbytes;
735  int di, vi;
736 
737  INIT_METADATA_WARNINGS(warn, curr_ds->name, "DOD variable changes");
738 
739  /* Loop over variables in the current dataset */
740 
741  for (vi = 0; vi < curr_ds->nvars; ++vi) {
742 
743  nchanges = 0;
744  curr_var = curr_ds->vars[vi];
745  prev_var = cds_get_var(prev_ds, curr_var->name);
746 
747  /* Check if this variable exists in the previous dataset */
748 
749  if (!prev_var) {
750 
752  " - %s: variable not found in previous dataset\n",
753  curr_var->name);
754 
755  continue;
756  }
757 
758  /* Check if the variable data type has changed */
759 
760  if (prev_var->type != curr_var->type) {
761 
763  " - %s: variable data type changed from %s to %s\n",
764  curr_var->name,
765  cds_data_type_name(prev_var->type),
766  cds_data_type_name(curr_var->type));
767 
768  nchanges += 1;
769  }
770 
771  /* Check if the variable dimensions have changed */
772 
773  if (prev_var->ndims != curr_var->ndims) {
774 
775  /* The number of dimensions has changed */
776 
778  " - %s: number of variable dimensions changed from %d to %d\n",
779  curr_var->name, prev_var->ndims, curr_var->ndims);
780 
781  nchanges += 1;
782  }
783  else {
784 
785  /* Check if the variable dimensions have changed */
786 
787  for (di = 0; di < curr_var->ndims; ++di) {
788 
789  if (strcmp(curr_var->dims[di]->name,
790  prev_var->dims[di]->name) != 0) {
791 
793  " - %s: variable dimension changed from %s to %s\n",
794  curr_var->name,
795  curr_var->dims[di]->name,
796  prev_var->dims[di]->name);
797 
798  nchanges += 1;
799  }
800  }
801  }
802 
803  /* Check if the variable attributes have changed */
804 
805  ex_atts = _dsproc_get_exclude_atts(curr_var->name);
806 
807  natt_changes = _dsproc_compare_atts(
808  curr_var->name, ex_atts,
809  prev_var->natts, prev_var->atts,
810  curr_var->natts, curr_var->atts);
811 
812  if (natt_changes < 0) {
813  return(-1);
814  }
815 
816  /* Check if we need to compare static data */
817 
818  if (nchanges) {
819 
820  /* variable type or shape has changed */
821  continue;
822  }
823 
824  if ((curr_var->ndims > 0) &&
825  (curr_var->dims[0]->is_unlimited)) {
826 
827  /* not a static variable */
828  continue;
829  }
830 
831  if (ex_atts &&
832  ex_atts->exclude_data) {
833 
834  /* variable excluded from static data check */
835  continue;
836  }
837 
838  /* Make sure the sample count has not changed */
839 
840  if (prev_var->sample_count != curr_var->sample_count) {
841 
842  /* Removed this warning because it is redundant with the
843  * warnings for dimension length changes and only serves
844  * to overly clutter the output mail message.
845  *
846  * METADATA_WARNING(
847  * " - %s: variable sample count changed from %d to %d\n",
848  * curr_var->name,
849  * (int)prev_var->sample_count,
850  * (int)curr_var->sample_count);
851  */
852  continue;
853  }
854 
855  /* Make sure the sample size has not changed */
856 
857  prev_sample_size = cds_var_sample_size(prev_var);
858  curr_sample_size = cds_var_sample_size(curr_var);
859 
860  if (prev_sample_size != curr_sample_size) {
861 
862  /* Removed this warning because it is redundant with the
863  * warnings for dimension length changes and only serves
864  * to overly clutter the output mail message.
865  *
866  * METADATA_WARNING(
867  * " - %s: variable sample sizes changed from %d to %d\n",
868  * curr_var->name,
869  * (int)prev_sample_size,
870  * (int)curr_sample_size);
871  */
872  continue;
873  }
874 
875  /* Compare static data */
876 
877  nbytes = curr_var->sample_count
878  * curr_sample_size
879  * cds_data_type_size(curr_var->type);
880 
881  if (nbytes > 0 &&
882  memcmp(prev_var->data.vp, curr_var->data.vp, nbytes) != 0) {
883 
885  " - %s: static variable data changed\n",
886  curr_var->name);
887  }
888 
889  } /* end loop over variables in the current dataset */
890 
891  /* Check if the number of variables has changed */
892 
893  if (curr_ds->nvars != prev_ds->nvars) {
894 
896  " - number of variables changed from %d to %d\n",
897  prev_ds->nvars, curr_ds->nvars);
898  }
899 
900  nchanges = FINISH_METADATA_WARNINGS();
901 
902  return(nchanges);
903 }
904 
905 /**
906  * Compare the DODs of two datasets.
907  *
908  * @param prev_ds - previous dataset
909  * @param curr_ds - current dataset
910  * @param warn - generate warning message if changes are found
911  *
912  * @return
913  * - the number of changes found
914  * - -1 if a memory allocation error occurred generating a warning message
915  */
916 int dsproc_compare_dods(CDSGroup *prev_ds, CDSGroup *curr_ds, int warn)
917 {
918  int nchanges = 0;
919  int status;
920 
921  nchanges = dsproc_compare_dod_dims(prev_ds, curr_ds, warn);
922 
923  status = dsproc_compare_dod_atts(prev_ds, curr_ds, warn);
924  if (status < 0) return (-1);
925 
926  nchanges += status;
927 
928  status = dsproc_compare_dod_vars(prev_ds, curr_ds, warn);
929  if (status < 0) return (-1);
930 
931  nchanges += status;
932 
933  return(nchanges);
934 }
935 
936 /**
937  * Exclude attributes and/or static data from dod compare.
938  *
939  * If an error occurs in this function it will be appended to the log and
940  * error mail messages, and the process status will be set appropriately.
941  *
942  * @param var_name - the variable name
943  * or NULL for global attributes
944  *
945  * @param exclude_data - flag specifying if static data should be excluded,
946  * (1 == TRUE, 0 == FALSE)
947  *
948  * @param natts - the number of attribute names in the list
949  * @param att_names - the list attribute names
950  *
951  * @return
952  * - 1 if successful
953  * - 0 if a memory allocation error occurred
954  */
956  const char *var_name,
957  int exclude_data,
958  int natts,
959  const char **att_names)
960 {
961  ExAtts *ex_atts = _dsproc_get_exclude_atts(var_name);
962  int new_natts;
963  char **new_att_names;
964  int ai, aj;
965 
966  /* Create a new structure if necessary */
967 
968  if (!ex_atts) {
969 
970  ex_atts = (ExAtts *)calloc(1, sizeof(ExAtts));
971  if (!ex_atts) goto MEMORY_ERROR;
972 
973  if (var_name) {
974  ex_atts->var_name = strdup(var_name);
975  if (!ex_atts->var_name) {
976  free(ex_atts);
977  goto MEMORY_ERROR;
978  }
979  }
980 
981  ex_atts->next = _ExAtts;
982  _ExAtts = ex_atts;
983  }
984 
985  /* Set the exclude data flag */
986 
987  ex_atts->exclude_data = exclude_data;
988 
989  /* Add attribute names */
990 
991  if (natts && att_names) {
992 
993  /* Re-allocate memory for the attribute names list */
994 
995  new_natts = natts + ex_atts->natts;
996  new_att_names = (char **)realloc(
997  ex_atts->att_names, new_natts * sizeof(char *));
998 
999  if (!new_att_names) goto MEMORY_ERROR;
1000 
1001  ex_atts->att_names = new_att_names;
1002 
1003  /* Add new attributes to the list */
1004 
1005  for (ai = 0; ai < natts; ++ai) {
1006 
1007  /* Check if this attribute already exists in the list */
1008 
1009  for (aj = 0; aj < ex_atts->natts; ++aj) {
1010  if (strcmp(att_names[ai], ex_atts->att_names[aj]) == 0) {
1011  break;
1012  }
1013  }
1014 
1015  if (aj != ex_atts->natts) continue;
1016 
1017  /* Add this attribute to the list */
1018 
1019  if (!(ex_atts->att_names[ex_atts->natts] = strdup(att_names[ai]))) {
1020  goto MEMORY_ERROR;
1021  }
1022 
1023  ex_atts->natts += 1;
1024  }
1025  }
1026 
1027  return(1);
1028 
1029 MEMORY_ERROR:
1030 
1031  if (var_name) {
1033  "Could not exclude variable attributes from dod compare for: %s\n"
1034  " -> memory allocation error\n", var_name);
1035  }
1036  else {
1038  "Could not exclude global attributes from dod compare\n"
1039  " -> memory allocation error\n");
1040  }
1041 
1043  return(0);
1044 }