154 {
155 SCANNED_ARG *s_arg;
157 char *file1, *file2;
158 long different = 0, i, pages1, pages2, pagediff = 0, i_arg, absolute = 0;
159 int32_t *columnDataType, *parDataType, *arrayDataType;
160 int32_t columns, parameters, arrays, columnMatches, parameterMatches, arrayMatches;
161 int64_t rows1, rows2;
162 char **columnName, **parameterName, **arrayName, **columnMatch, **parameterMatch, **arrayMatch;
163 long column_provided, parameter_provided, array_provided, precision, labelFromSecondFile = 0, rowLabelType = 0, rowLabelIndex = -1, notCompareRowLabel = 0;
164 long double tolerance = 0.0L, relativeTolerance = 0.0L, precisionTolerance = 0.0L;
165 unsigned long compareCommonFlags = 0, dummyFlags = 0;
166 char *floatFormat, *doubleFormat, *ldoubleFormat, *stringFormat, *rowLabelColumn;
167 void *rowLabel;
168 short ignoreUnits = 0;
169
170 rowLabelColumn = NULL;
171 rowLabel = NULL;
172 floatFormat = doubleFormat = ldoubleFormat = stringFormat = NULL;
173 precision = 0;
174 columnDataType = parDataType = arrayDataType = NULL;
175 columnName = parameterName = arrayName = NULL;
176 columnMatch = parameterMatch = arrayMatch = NULL;
177 columnMatches = parameterMatches = arrayMatches = 0;
178 columns = parameters = arrays = 0;
179 file1 = file2 = NULL;
180 pages1 = pages2 = 0;
181 column_provided = parameter_provided = array_provided = 0;
183 argc =
scanargs(&s_arg, argc, argv);
184 if (argc < 3) {
185 fprintf(stderr, "%s%s", USAGE1, USAGE2);
186 exit(EXIT_FAILURE);
187 }
188 for (i_arg = 1; i_arg < argc; i_arg++) {
189 if (s_arg[i_arg].arg_type == OPTION) {
191 switch (
match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
192 case CLO_ROWLABEL:
193 if (s_arg[i_arg].n_items < 2)
195 rowLabelColumn = s_arg[i_arg].list[1];
196 if (s_arg[i_arg].n_items > 2 &&
198 notCompareRowLabel = 1;
199 break;
200 case CLO_EXACT:
201 tolerance = -1.0L;
202 break;
203 case CLO_ABSOLUTE:
204 absolute = 1;
205 break;
206 case CLO_TOLERANCE:
207 if (s_arg[i_arg].n_items != 2)
210 SDDS_Bomb(
"Invalid -tolerance syntax (not a number given)");
211 break;
212 case CLO_RELATIVE_TOLERANCE:
213 if (s_arg[i_arg].n_items != 2)
214 SDDS_Bomb(
"Invalid -relativeTolerance syntax");
216 SDDS_Bomb(
"Invalid -relativeTolerance syntax (not a number given)");
217 break;
218 case CLO_PRECISION:
219 if (s_arg[i_arg].n_items != 2)
221 if (!
get_long(&precision, s_arg[i_arg].list[1]))
222 SDDS_Bomb(
"Invalid -precision syntax (not a number given)");
223 if (precision < 0)
224 precision = 0;
225 break;
226 case CLO_FORMAT:
227 if (s_arg[i_arg].n_items < 2)
229 s_arg[i_arg].n_items--;
230 if (!
scanItemList(&dummyFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
236 s_arg[i_arg].n_items++;
238 fprintf(stderr, "Error: Given print format (\"%s\") for float data is invalid.\n", floatFormat);
239 exit(EXIT_FAILURE);
240 }
242 fprintf(stderr, "Error: Given print format (\"%s\") for double data is invalid.\n", doubleFormat);
243 exit(EXIT_FAILURE);
244 }
246 fprintf(stderr, "Error: Given print format (\"%s\") for long double data is invalid.\n", ldoubleFormat);
247 exit(EXIT_FAILURE);
248 }
250 fprintf(stderr, "Error: Given print format (\"%s\") for string data is invalid.\n", stringFormat);
251 exit(EXIT_FAILURE);
252 }
253 break;
254 case CLO_COMPARECOMMON:
255 if (s_arg[i_arg].n_items == 1)
256 compareCommonFlags |= COMPARE_COMMON_COLUMN | COMPARE_COMMON_PARAMETER | COMPARE_COMMON_ARRAY;
257 else {
258 s_arg[i_arg].n_items--;
259 if (!
scanItemList(&compareCommonFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
260 "column", -1, NULL, 0, COMPARE_COMMON_COLUMN,
261 "parameter", -1, NULL, 0, COMPARE_COMMON_PARAMETER,
262 "array", -1, NULL, 0, COMPARE_COMMON_ARRAY, NULL))
263 SDDS_Bomb(
"Invalid -compareCommon syntax");
264 s_arg[i_arg].n_items++;
265 }
266 break;
267 case CLO_COLUMNS:
268 if (s_arg[i_arg].n_items < 2)
270 columnMatch =
tmalloc(
sizeof(*columnMatch) * (columnMatches = s_arg[i_arg].n_items - 1));
271 for (i = 0; i < columnMatches; i++)
272 columnMatch[i] = s_arg[i_arg].list[i + 1];
273 column_provided = 1;
274 break;
275 case CLO_PARAMETERS:
276 if (s_arg[i_arg].n_items < 2)
278 parameterMatch =
tmalloc(
sizeof(*parameterMatch) * (parameterMatches = s_arg[i_arg].n_items - 1));
279 for (i = 0; i < parameterMatches; i++)
280 parameterMatch[i] = s_arg[i_arg].list[i + 1];
281 parameter_provided = 1;
282 break;
283 case CLO_ARRAYS:
284 if (s_arg[i_arg].n_items < 2)
286 arrayMatch =
tmalloc(
sizeof(*arrayMatch) * (arrayMatches = s_arg[i_arg].n_items - 1));
287 for (i = 0; i < arrayMatches; i++)
288 arrayMatch[i] = s_arg[i_arg].list[i + 1];
289 array_provided = 1;
290 break;
291 case CLO_IGNORE_UNITS:
292 ignoreUnits = 1;
293 break;
294 default:
295 fprintf(stderr, "Unknown option given (sddsdiff): %s\n", s_arg[i_arg].list[0]);
296 exit(EXIT_FAILURE);
297 break;
298 }
299 } else {
300 if (!file1)
301 file1 = s_arg[i_arg].list[0];
302 else if (!file2)
303 file2 = s_arg[i_arg].list[0];
304 else
306 }
307 }
308 if (!floatFormat)
310 if (!doubleFormat)
312 if (!ldoubleFormat)
314 if (!stringFormat)
316
317 if ((tolerance || relativeTolerance) && precision > 0) {
318 SDDS_Bomb(
"Tolerance, relativeTolerance, and precision options are not compatible. Only one of tolerance, relativeTolerance, precision, or exact may be given.");
319 }
320 if (tolerance && relativeTolerance) {
321 SDDS_Bomb(
"Tolerance and relativeTolerance options are not compatible. Only one may be given.");
322 }
323 if (!file1 || !file2) {
324 fprintf(stderr, "Error: Two files must be provided for comparison.\n");
325 exit(EXIT_FAILURE);
326 }
327 if (strcmp(file1, file2) == 0) {
328 printf("\"%s\" and \"%s\" are identical.\n", file1, file2);
329 return EXIT_SUCCESS;
330 }
333 exit(EXIT_FAILURE);
334 }
337 exit(EXIT_FAILURE);
338 }
339 if (rowLabelColumn) {
342 fprintf(stdout, "Warning: Row label column \"%s\" does not exist in the input files. The number of rows will be labeled instead.\n", rowLabelColumn);
343 rowLabelColumn = NULL;
344 } else {
345 labelFromSecondFile = 1;
346 notCompareRowLabel = 1;
347 }
348 } else {
350 notCompareRowLabel = 1;
351 }
352
353 if (rowLabelColumn) {
354 if (labelFromSecondFile)
356 else
358 }
359 }
360 if (!precision) {
361 precisionTolerance = powl(10L, -1L * fabsl(log10l(LDBL_EPSILON)));
362 } else {
363 precisionTolerance = powl(10L, -1L * precision);
364 }
365 if (column_provided) {
366 columnName =
getMatchingSDDSNames(&table1, columnMatch, columnMatches, &columns, SDDS_MATCH_COLUMN);
367 if (CompareDefinitions(&table1, &table2, file1, file2, &columns, &columnName, &columnDataType, SDDS_COLUMN_TYPE, compareCommonFlags & COMPARE_COMMON_COLUMN, rowLabelColumn, notCompareRowLabel, ignoreUnits))
368 different = 1;
369 }
370 if (parameter_provided) {
371 parameterName =
getMatchingSDDSNames(&table1, parameterMatch, parameterMatches, ¶meters, SDDS_MATCH_PARAMETER);
372
373 if (CompareDefinitions(&table1, &table2, file1, file2, ¶meters, ¶meterName, &parDataType, SDDS_PARAMETER_TYPE, compareCommonFlags & COMPARE_COMMON_PARAMETER, NULL, 1, ignoreUnits))
374 different = 1;
375 }
376 if (array_provided) {
378 if (CompareDefinitions(&table1, &table2, file1, file2, &arrays, &arrayName, &arrayDataType, SDDS_ARRAY_TYPE, compareCommonFlags & COMPARE_COMMON_ARRAY, NULL, 1, ignoreUnits))
379 different = 1;
380 }
381 if (!columns && !parameters && !arrays) {
382 if (!compareCommonFlags || compareCommonFlags & COMPARE_COMMON_COLUMN)
383 different += CompareDefinitions(&table1, &table2, file1, file2, &columns, &columnName, &columnDataType, SDDS_COLUMN_TYPE, compareCommonFlags & COMPARE_COMMON_COLUMN, rowLabelColumn, notCompareRowLabel, ignoreUnits);
384 if (!compareCommonFlags || compareCommonFlags & COMPARE_COMMON_PARAMETER)
385 different += CompareDefinitions(&table1, &table2, file1, file2, ¶meters, ¶meterName, &parDataType, SDDS_PARAMETER_TYPE, compareCommonFlags & COMPARE_COMMON_PARAMETER, NULL, 1, ignoreUnits);
386 if (!compareCommonFlags || compareCommonFlags & COMPARE_COMMON_ARRAY)
387 different += CompareDefinitions(&table1, &table2, file1, file2, &arrays, &arrayName, &arrayDataType, SDDS_ARRAY_TYPE, compareCommonFlags & COMPARE_COMMON_ARRAY, NULL, 1, ignoreUnits);
388 }
389 if (!different) {
390 if (!columns && !parameters && !arrays) {
391 fprintf(stderr, "There are no common columns, parameters, or arrays in the two files.\n");
392 different = 1;
393 } else {
394
395 while (1) {
396 pagediff = 0;
399 if (pages1 > 0 && pages2 > 0) {
400
403 if (rows1 != rows2) {
404 pagediff = 1;
405 different = 1;
406 fprintf(stderr, "The two files have different numbers of rows on page %ld: \"%s\" has %" PRId64 " rows, while \"%s\" has %" PRId64 " rows.\n",
407 pages1, file1, rows1, file2, rows2);
408 break;
409 } else {
410 if (parameters)
411 pagediff += CompareData(&table1, &table2, file1, file2, parameters, parameterName, parDataType, SDDS_PARAMETER_TYPE, pages1, tolerance, relativeTolerance, precisionTolerance, floatFormat, doubleFormat, ldoubleFormat, stringFormat, absolute, NULL, rowLabelType, NULL);
412
413 if (columns && rows1) {
414 if (rowLabelColumn) {
415 if (labelFromSecondFile) {
418 } else {
421 }
422 }
423 pagediff += CompareData(&table1, &table2, file1, file2, columns, columnName, columnDataType, SDDS_COLUMN_TYPE, pages1, tolerance, relativeTolerance, precisionTolerance, floatFormat, doubleFormat, ldoubleFormat, stringFormat, absolute, rowLabel, rowLabelType, rowLabelColumn);
424 if (rowLabelColumn) {
427 else
428 free(rowLabel);
429 rowLabel = NULL;
430 }
431 }
432 if (arrays)
433 pagediff += CompareData(&table1, &table2, file1, file2, arrays, arrayName, arrayDataType, SDDS_ARRAY_TYPE, pages1, tolerance, relativeTolerance, precisionTolerance, floatFormat, doubleFormat, ldoubleFormat, stringFormat, absolute, NULL, rowLabelType, NULL);
434 different += pagediff;
435 }
436 } else if (pages1 > 0 && pages2 <= 0) {
437 fprintf(stderr, "\"%s\" has fewer pages than \"%s\".\n", file2, file1);
438 different = 1;
439 break;
440 } else if (pages1 < 0 && pages2 > 0) {
441 different = 1;
442 fprintf(stderr, "\"%s\" has fewer pages than \"%s\".\n", file1, file2);
443 break;
444 } else {
445 break;
446 }
447 }
448 }
449 } else {
450 different = 1;
451 }
452 if (!different)
453 printf("\"%s\" and \"%s\" are identical.\n", file1, file2);
454 else
455 fprintf(stderr, "\"%s\" and \"%s\" are different.\n", file1, file2);
456
457 if (columns) {
458 for (i = 0; i < columns; i++)
459 free(columnName[i]);
460 free(columnName);
461 free(columnDataType);
462 free(columnMatch);
463 }
464 if (parameters) {
465 for (i = 0; i < parameters; i++)
466 free(parameterName[i]);
467 free(parameterName);
468 free(parDataType);
469 free(parameterMatch);
470 }
471 if (arrays) {
472 for (i = 0; i < arrays; i++)
473 free(arrayName[i]);
474 free(arrayName);
475 free(arrayDataType);
476 free(arrayMatch);
477 }
479 free(stringFormat);
480 free(floatFormat);
481 free(doubleFormat);
482 free(ldoubleFormat);
483
486 exit(EXIT_FAILURE);
487 }
488 return EXIT_SUCCESS;
489}
int32_t SDDS_FreeStringArray(char **string, int64_t strings)
Frees an array of strings by deallocating each individual string.
int32_t SDDS_VerifyPrintfFormat(const char *string, int32_t type)
Verifies that a printf format string is compatible with a specified data type.
char ** getMatchingSDDSNames(SDDS_DATASET *dataset, char **matchName, int32_t matches, int32_t *names, short type)
Retrieves an array of matching SDDS entity names based on specified criteria.
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
void SDDS_Bomb(char *message)
Terminates the program after printing an error message and recorded errors.
void * tmalloc(uint64_t size_of_block)
Allocates a memory block of the specified size with zero initialization.
int get_longdouble(long double *dptr, char *s)
Parses a long double value from the given string.
int get_long(long *iptr, char *s)
Parses a long integer value from the given string.
char * delete_chars(char *s, char *t)
Removes all occurrences of characters found in string t from string s.
int strncmp_case_insensitive(char *s1, char *s2, long n)
Compares up to a specified number of characters of two strings in a case-insensitive manner.
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
void free_scanargs(SCANNED_ARG **scanned, int argc)
long scanItemList(unsigned long *flags, char **item, long *items, unsigned long mode,...)
Scans a list of items and assigns values based on provided keywords and types.