115 {
116 SCANNED_ARG *s_arg;
118 char *file1, *file2;
119 long different = 0, i, pages1, pages2, pagediff = 0, i_arg, absolute = 0;
120 int32_t *columnDataType, *parDataType, *arrayDataType;
121 int32_t columns, parameters, arrays, columnMatches, parameterMatches, arrayMatches;
122 int64_t rows1, rows2;
123 char **columnName, **parameterName, **arrayName, **columnMatch, **parameterMatch, **arrayMatch;
124 long column_provided, parameter_provided, array_provided, precision, labelFromSecondFile = 0, rowLabelType = 0, rowLabelIndex = -1, notCompareRowLabel = 0;
125 long double tolerance = 0.0L, precisionTolerance = 0.0L;
126 unsigned long compareCommonFlags = 0, dummyFlags = 0;
127 char *floatFormat, *doubleFormat, *ldoubleFormat, *stringFormat, *rowLabelColumn;
128 void *rowLabel;
129 short ignoreUnits = 0;
130
131 rowLabelColumn = NULL;
132 rowLabel = NULL;
133 floatFormat = doubleFormat = ldoubleFormat = stringFormat = NULL;
134 precision = 0;
135 columnDataType = parDataType = arrayDataType = NULL;
136 columnName = parameterName = arrayName = NULL;
137 columnMatch = parameterMatch = arrayMatch = NULL;
138 columnMatches = parameterMatches = arrayMatches = 0;
139 columns = parameters = arrays = 0;
140 file1 = file2 = NULL;
141 pages1 = pages2 = 0;
142 column_provided = parameter_provided = array_provided = 0;
144 argc =
scanargs(&s_arg, argc, argv);
145 if (argc < 3) {
146 fprintf(stderr, "%s%s", USAGE1, USAGE2);
147 exit(EXIT_FAILURE);
148 }
149 for (i_arg = 1; i_arg < argc; i_arg++) {
150 if (s_arg[i_arg].arg_type == OPTION) {
152 switch (
match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
153 case CLO_ROWLABEL:
154 if (s_arg[i_arg].n_items < 2)
156 rowLabelColumn = s_arg[i_arg].list[1];
157 if (s_arg[i_arg].n_items > 2 &&
159 notCompareRowLabel = 1;
160 break;
161 case CLO_EXACT:
162 tolerance = -1.0L;
163 break;
164 case CLO_ABSOLUTE:
165 absolute = 1;
166 break;
167 case CLO_TOLERANCE:
168 if (s_arg[i_arg].n_items != 2)
171 SDDS_Bomb(
"Invalid -tolerance syntax (not a number given)");
172 break;
173 case CLO_PRECISION:
174 if (s_arg[i_arg].n_items != 2)
176 if (!
get_long(&precision, s_arg[i_arg].list[1]))
177 SDDS_Bomb(
"Invalid -precision syntax (not a number given)");
178 if (precision < 0)
179 precision = 0;
180 break;
181 case CLO_FORMAT:
182 if (s_arg[i_arg].n_items < 2)
184 s_arg[i_arg].n_items--;
185 if (!
scanItemList(&dummyFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
191 s_arg[i_arg].n_items++;
193 fprintf(stderr, "Error: Given print format (\"%s\") for float data is invalid.\n", floatFormat);
194 exit(EXIT_FAILURE);
195 }
197 fprintf(stderr, "Error: Given print format (\"%s\") for double data is invalid.\n", doubleFormat);
198 exit(EXIT_FAILURE);
199 }
201 fprintf(stderr, "Error: Given print format (\"%s\") for long double data is invalid.\n", ldoubleFormat);
202 exit(EXIT_FAILURE);
203 }
205 fprintf(stderr, "Error: Given print format (\"%s\") for string data is invalid.\n", stringFormat);
206 exit(EXIT_FAILURE);
207 }
208 break;
209 case CLO_COMPARECOMMON:
210 if (s_arg[i_arg].n_items == 1)
211 compareCommonFlags |= COMPARE_COMMON_COLUMN | COMPARE_COMMON_PARAMETER | COMPARE_COMMON_ARRAY;
212 else {
213 s_arg[i_arg].n_items--;
214 if (!
scanItemList(&compareCommonFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
215 "column", -1, NULL, 0, COMPARE_COMMON_COLUMN,
216 "parameter", -1, NULL, 0, COMPARE_COMMON_PARAMETER,
217 "array", -1, NULL, 0, COMPARE_COMMON_ARRAY, NULL))
218 SDDS_Bomb(
"Invalid -compareCommon syntax");
219 s_arg[i_arg].n_items++;
220 }
221 break;
222 case CLO_COLUMNS:
223 if (s_arg[i_arg].n_items < 2)
225 columnMatch =
tmalloc(
sizeof(*columnMatch) * (columnMatches = s_arg[i_arg].n_items - 1));
226 for (i = 0; i < columnMatches; i++)
227 columnMatch[i] = s_arg[i_arg].list[i + 1];
228 column_provided = 1;
229 break;
230 case CLO_PARAMETERS:
231 if (s_arg[i_arg].n_items < 2)
233 parameterMatch =
tmalloc(
sizeof(*parameterMatch) * (parameterMatches = s_arg[i_arg].n_items - 1));
234 for (i = 0; i < parameterMatches; i++)
235 parameterMatch[i] = s_arg[i_arg].list[i + 1];
236 parameter_provided = 1;
237 break;
238 case CLO_ARRAYS:
239 if (s_arg[i_arg].n_items < 2)
241 arrayMatch =
tmalloc(
sizeof(*arrayMatch) * (arrayMatches = s_arg[i_arg].n_items - 1));
242 for (i = 0; i < arrayMatches; i++)
243 arrayMatch[i] = s_arg[i_arg].list[i + 1];
244 array_provided = 1;
245 break;
246 case CLO_IGNORE_UNITS:
247 ignoreUnits = 1;
248 break;
249 default:
250 fprintf(stderr, "Unknown option given (sddsdiff): %s\n", s_arg[i_arg].list[0]);
251 exit(EXIT_FAILURE);
252 break;
253 }
254 } else {
255 if (!file1)
256 file1 = s_arg[i_arg].list[0];
257 else if (!file2)
258 file2 = s_arg[i_arg].list[0];
259 else
261 }
262 }
263 if (!floatFormat)
265 if (!doubleFormat)
267 if (!ldoubleFormat)
269 if (!stringFormat)
271
272 if (tolerance && precision > 0) {
273 SDDS_Bomb(
"Tolerance and precision options are not compatible. Only one of tolerance, precision, or exact may be given.");
274 }
275 if (!file1 || !file2) {
276 fprintf(stderr, "Error: Two files must be provided for comparison.\n");
277 exit(EXIT_FAILURE);
278 }
279 if (strcmp(file1, file2) == 0) {
280 printf("\"%s\" and \"%s\" are identical.\n", file1, file2);
281 return EXIT_SUCCESS;
282 }
285 exit(EXIT_FAILURE);
286 }
289 exit(EXIT_FAILURE);
290 }
291 if (rowLabelColumn) {
294 fprintf(stdout, "Warning: Row label column \"%s\" does not exist in the input files. The number of rows will be labeled instead.\n", rowLabelColumn);
295 rowLabelColumn = NULL;
296 } else {
297 labelFromSecondFile = 1;
298 notCompareRowLabel = 1;
299 }
300 } else {
302 notCompareRowLabel = 1;
303 }
304
305 if (rowLabelColumn) {
306 if (labelFromSecondFile)
308 else
310 }
311 }
312 if (!precision) {
313 precisionTolerance = powl(10L, -1L * fabsl(log10l(LDBL_EPSILON)));
314 } else {
315 precisionTolerance = powl(10L, -1L * precision);
316 }
317 if (column_provided) {
318 columnName =
getMatchingSDDSNames(&table1, columnMatch, columnMatches, &columns, SDDS_MATCH_COLUMN);
319 if (CompareDefinitions(&table1, &table2, file1, file2, &columns, &columnName, &columnDataType, SDDS_COLUMN_TYPE, compareCommonFlags & COMPARE_COMMON_COLUMN, rowLabelColumn, notCompareRowLabel, ignoreUnits))
320 different = 1;
321 }
322 if (parameter_provided) {
323 parameterName =
getMatchingSDDSNames(&table1, parameterMatch, parameterMatches, ¶meters, SDDS_MATCH_PARAMETER);
324
325 if (CompareDefinitions(&table1, &table2, file1, file2, ¶meters, ¶meterName, &parDataType, SDDS_PARAMETER_TYPE, compareCommonFlags & COMPARE_COMMON_PARAMETER, NULL, 1, ignoreUnits))
326 different = 1;
327 }
328 if (array_provided) {
330 if (CompareDefinitions(&table1, &table2, file1, file2, &arrays, &arrayName, &arrayDataType, SDDS_ARRAY_TYPE, compareCommonFlags & COMPARE_COMMON_ARRAY, NULL, 1, ignoreUnits))
331 different = 1;
332 }
333 if (!columns && !parameters && !arrays) {
334 if (!compareCommonFlags || compareCommonFlags & COMPARE_COMMON_COLUMN)
335 different += CompareDefinitions(&table1, &table2, file1, file2, &columns, &columnName, &columnDataType, SDDS_COLUMN_TYPE, compareCommonFlags & COMPARE_COMMON_COLUMN, rowLabelColumn, notCompareRowLabel, ignoreUnits);
336 if (!compareCommonFlags || compareCommonFlags & COMPARE_COMMON_PARAMETER)
337 different += CompareDefinitions(&table1, &table2, file1, file2, ¶meters, ¶meterName, &parDataType, SDDS_PARAMETER_TYPE, compareCommonFlags & COMPARE_COMMON_PARAMETER, NULL, 1, ignoreUnits);
338 if (!compareCommonFlags || compareCommonFlags & COMPARE_COMMON_ARRAY)
339 different += CompareDefinitions(&table1, &table2, file1, file2, &arrays, &arrayName, &arrayDataType, SDDS_ARRAY_TYPE, compareCommonFlags & COMPARE_COMMON_ARRAY, NULL, 1, ignoreUnits);
340 }
341 if (!different) {
342 if (!columns && !parameters && !arrays) {
343 fprintf(stderr, "There are no common columns, parameters, or arrays in the two files.\n");
344 different = 1;
345 } else {
346
347 while (1) {
348 pagediff = 0;
351 if (pages1 > 0 && pages2 > 0) {
352
355 if (rows1 != rows2) {
356 pagediff = 1;
357 different = 1;
358 fprintf(stderr, "The two files have different numbers of rows on page %ld: \"%s\" has %" PRId64 " rows, while \"%s\" has %" PRId64 " rows.\n",
359 pages1, file1, rows1, file2, rows2);
360 break;
361 } else {
362 if (parameters)
363 pagediff += CompareData(&table1, &table2, file1, file2, parameters, parameterName, parDataType, SDDS_PARAMETER_TYPE, pages1, tolerance, precisionTolerance, floatFormat, doubleFormat, ldoubleFormat, stringFormat, absolute, NULL, rowLabelType, NULL);
364 if (columns && rows1) {
365 if (rowLabelColumn) {
366 if (labelFromSecondFile) {
369 } else {
372 }
373 }
374 pagediff += CompareData(&table1, &table2, file1, file2, columns, columnName, columnDataType, SDDS_COLUMN_TYPE, pages1, tolerance, precisionTolerance, floatFormat, doubleFormat, ldoubleFormat, stringFormat, absolute, rowLabel, rowLabelType, rowLabelColumn);
375 if (rowLabelColumn) {
378 else
379 free(rowLabel);
380 rowLabel = NULL;
381 }
382 }
383 if (arrays)
384 pagediff += CompareData(&table1, &table2, file1, file2, arrays, arrayName, arrayDataType, SDDS_ARRAY_TYPE, pages1, tolerance, precisionTolerance, floatFormat, doubleFormat, ldoubleFormat, stringFormat, absolute, NULL, rowLabelType, NULL);
385 different += pagediff;
386 }
387 } else if (pages1 > 0 && pages2 <= 0) {
388 fprintf(stderr, "\"%s\" has fewer pages than \"%s\".\n", file2, file1);
389 different = 1;
390 break;
391 } else if (pages1 < 0 && pages2 > 0) {
392 different = 1;
393 fprintf(stderr, "\"%s\" has fewer pages than \"%s\".\n", file1, file2);
394 break;
395 } else {
396 break;
397 }
398 }
399 }
400 } else {
401 different = 1;
402 }
403 if (!different)
404 printf("\"%s\" and \"%s\" are identical.\n", file1, file2);
405 else
406 fprintf(stderr, "\"%s\" and \"%s\" are different.\n", file1, file2);
407
408 if (columns) {
409 for (i = 0; i < columns; i++)
410 free(columnName[i]);
411 free(columnName);
412 free(columnDataType);
413 free(columnMatch);
414 }
415 if (parameters) {
416 for (i = 0; i < parameters; i++)
417 free(parameterName[i]);
418 free(parameterName);
419 free(parDataType);
420 free(parameterMatch);
421 }
422 if (arrays) {
423 for (i = 0; i < arrays; i++)
424 free(arrayName[i]);
425 free(arrayName);
426 free(arrayDataType);
427 free(arrayMatch);
428 }
430 free(stringFormat);
431 free(floatFormat);
432 free(doubleFormat);
433 free(ldoubleFormat);
434
437 exit(EXIT_FAILURE);
438 }
439 return EXIT_SUCCESS;
440}
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.