1 /*
2  * Copyright(C) 1999-2020 National Technology & Engineering Solutions
3  * of Sandia, LLC (NTESS).  Under the terms of Contract DE-NA0003525 with
4  * NTESS, the U.S. Government retains certain rights in this software.
5  *
6  * See packages/seacas/LICENSE for details
7  */
8 /*****************************************************************************
9  *
10  * exutils - utility routines
11  *
12  *****************************************************************************/
13 
14 #if defined(DEBUG_QSORT)
15 #endif
16 
17 #include <errno.h>
18 #include <stdbool.h>
19 
20 #include "exodusII.h"
21 #include "exodusII_int.h"
22 
23 struct ex__obj_stats *exoII_eb  = 0;
24 struct ex__obj_stats *exoII_ed  = 0;
25 struct ex__obj_stats *exoII_fa  = 0;
26 struct ex__obj_stats *exoII_ns  = 0;
27 struct ex__obj_stats *exoII_es  = 0;
28 struct ex__obj_stats *exoII_fs  = 0;
29 struct ex__obj_stats *exoII_ss  = 0;
30 struct ex__obj_stats *exoII_els = 0;
31 struct ex__obj_stats *exoII_em  = 0;
32 struct ex__obj_stats *exoII_edm = 0;
33 struct ex__obj_stats *exoII_fam = 0;
34 struct ex__obj_stats *exoII_nm  = 0;
35 struct ex__obj_stats *exoII_ass = 0;
36 
37 /*****************************************************************************
38  *
39  * utility routines for string conversions
40  * ex__catstr  - concatenate  string/number (where number is converted to ASCII)
41  * ex__catstr2 - concatenate  string1/number1/string2/number2   "
42  *
43  * NOTE: these routines reuse the same storage over and over to build
44  *        concatenated strings, because the strings are just passed to netCDF
45  *        routines as names used to look up variables.  if the strings returned
46  *        by these routines are needed for any other purpose, they should
47  *        immediately be copied into other storage.
48  *****************************************************************************/
49 
50 static char  ret_string[10 * (MAX_VAR_NAME_LENGTH + 1)];
51 static char *cur_string = &ret_string[0];
52 
53 #ifndef _MSC_VER
54 #if NC_HAS_HDF5
55 extern int H5get_libversion(unsigned *, unsigned *, unsigned *);
56 #endif
57 #endif
58 
59 #if NC_HAS_PNETCDF
60 extern char *ncmpi_inq_libvers();
61 #endif
62 
63 /*!
64   \ingroup Utilities
65   \undoc
66 */
ex_config(void)67 const char *ex_config(void)
68 {
69   static char buffer[2048];
70   int         j =
71       sprintf(buffer, "\tExodus Version %s, Released %s\n", EXODUS_VERSION, EXODUS_RELEASE_DATE);
72 #if defined(PARALLEL_AWARE_EXODUS)
73   j += sprintf(buffer + j, "\t\tParallel enabled\n");
74 #else
75   j += sprintf(buffer + j, "\t\tParallel NOT enabled\n");
76 #endif
77 #if defined(EXODUS_THREADSAFE)
78   j += sprintf(buffer + j, "\t\tThread Safe enabled\n");
79 #else
80   j += sprintf(buffer + j, "\t\tThread Safe NOT enabled\n");
81 #endif
82 #if defined(SEACAS_HIDE_DEPRECATED_CODE)
83   j += sprintf(buffer + j, "\t\tDeprecated Functions NOT built\n\n");
84 #else
85   j += sprintf(buffer + j, "\t\tDeprecated Functions available\n\n");
86 #endif
87 #if defined(NC_VERSION)
88   j += sprintf(buffer + j, "\tNetCDF Version %s\n", NC_VERSION);
89 #else
90   j += sprintf(buffer + j, "\tNetCDF Version < 4.3.3\n");
91 #endif
92 #if NC_HAS_CDF5
93   j += sprintf(buffer + j, "\t\tCDF5 enabled\n");
94 #endif
95 #ifndef _MSC_VER
96 #if NC_HAS_HDF5
97   {
98     unsigned major, minor, release;
99     H5get_libversion(&major, &minor, &release);
100     j += sprintf(buffer + j, "\t\tHDF5 enabled (%u.%u.%u)\n", major, minor, release);
101   }
102   j += sprintf(buffer + j, "\t\tZlib Compression (read/write) enabled\n");
103 #if NC_HAS_SZIP_WRITE == 1
104   j += sprintf(buffer + j, "\t\tSZip Compression (read/write) enabled\n");
105 #else
106   j += sprintf(buffer + j, "\t\tSZip Compression (read/write) NOT enabled\n");
107 #endif
108 #endif
109 #endif
110 #if defined(PARALLEL_AWARE_EXODUS)
111 #if NC_HAS_PARALLEL
112   j += sprintf(buffer + j, "\t\tParallel IO enabled via HDF5 and/or PnetCDF\n");
113 #else
114   j += sprintf(buffer + j,
115                "\t\tParallel IO *NOT* enabled via HDF5 and/or PnetCDF (PROBABLY A BUILD ERROR!)\n");
116 #endif
117 #if NC_HAS_PARALLEL4
118   j += sprintf(buffer + j, "\t\tParallel IO enabled via HDF5\n");
119 #else
120   j += sprintf(buffer + j, "\t\tParallel IO *NOT* enabled via HDF5\n");
121 #endif
122 #if NC_HAS_PAR_FILTERS
123   j += sprintf(buffer + j, "\t\tParallel IO supports filters\n");
124 #endif
125 #if NC_HAS_PNETCDF
126   {
127     char *libver = ncmpi_inq_libvers();
128     j += sprintf(buffer + j, "\t\tParallel IO enabled via PnetCDF (%s)\n", libver);
129   }
130 #else
131   j += sprintf(buffer + j, "\t\tParallel IO *NOT* enabled via PnetCDF\n");
132 #endif
133 #endif /* PARALLEL_AWARE_EXODUS */
134 
135 #if NC_HAS_ERANGE_FILL
136   j += sprintf(buffer + j, "\t\tERANGE_FILL support\n");
137 #endif
138 #if NC_RELAX_COORD_BOUND
139   j += sprintf(buffer + j, "\t\tRELAX_COORD_BOUND defined\n");
140 #endif
141 #if defined(NC_COMPACT)
142   j += sprintf(buffer + j, "\t\tNC_COMPACT defined\n");
143 #endif
144 #if defined(NC_HAVE_META_H)
145   j += sprintf(buffer + j, "\t\tNC_HAVE_META_H defined\n");
146 #endif
147 #if NC_HAS_NC2
148   j += sprintf(buffer + j, "\t\tAPI Version 2 support enabled\n");
149 #else
150   j += sprintf(buffer + j, "\t\tAPI Version 2 support NOT enabled\n");
151 #endif
152   j += sprintf(buffer + j, "\n");
153 
154   assert(j < 2048);
155   return buffer;
156 }
ex_print_config(void)157 void ex_print_config(void) { fprintf(stderr, "%s\n", ex_config()); }
158 
159 /*!
160   \ingroup Utilities
161   \undoc
162 */
ex__check_file_type(const char * path,int * type)163 int ex__check_file_type(const char *path, int *type)
164 {
165   /* Based on (stolen from?) NC_check_file_type from netcdf sources.
166 
167      Type is set to:
168      1 if this is a netcdf classic file,
169      2 if this is a netcdf 64-bit offset file,
170      4 pnetcdf cdf5 file.
171      5 if this is an hdf5 file
172   */
173 
174 #define MAGIC_NUMBER_LEN 4
175 
176   char magic[MAGIC_NUMBER_LEN+1];
177   EX_FUNC_ENTER();
178 
179   *type = 0;
180 
181   /* Get the 4-byte magic from the beginning of the file. */
182   {
183     FILE *fp;
184     int   i;
185 
186     if (!(fp = fopen(path, "r"))) {
187       char errmsg[MAX_ERR_LENGTH];
188       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: Could not open file '%s', error = %s.", path,
189                strerror(errno));
190       ex_err(__func__, errmsg, EX_WRONGFILETYPE);
191       EX_FUNC_LEAVE(EX_FATAL);
192     }
193     i                       = fread(magic, MAGIC_NUMBER_LEN, 1, fp);
194     magic[MAGIC_NUMBER_LEN] = '\0';
195     fclose(fp);
196     if (i != 1) {
197       char errmsg[MAX_ERR_LENGTH];
198       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: Could not read magic data from file '%s', err = %s.",
199                path, strerror(errno));
200       ex_err(__func__, errmsg, EX_WRONGFILETYPE);
201       EX_FUNC_LEAVE(EX_FATAL);
202     }
203   }
204 
205   /* Ignore the first byte for HDF */
206   if (magic[1] == 'H' && magic[2] == 'D' && magic[3] == 'F') {
207     *type = 5;
208   }
209   else if (magic[0] == 'C' && magic[1] == 'D' && magic[2] == 'F') {
210     if (magic[3] == '\001') {
211       *type = 1;
212     }
213     else if (magic[3] == '\002') {
214       *type = 2;
215     }
216     else if (magic[3] == '\005') {
217       *type = 4; /* cdf5 (including pnetcdf) file */
218     }
219   }
220   if (*type == 0) {
221     char errmsg[MAX_ERR_LENGTH];
222     snprintf(
223         errmsg, MAX_ERR_LENGTH,
224         "ERROR: Could not recognize %s as a valid Exodus/NetCDF file variant.  Magic value is '%s'",
225         path, magic);
226     ex_err(__func__, errmsg, EX_WRONGFILETYPE);
227     EX_FUNC_LEAVE(EX_FATAL);
228   }
229   EX_FUNC_LEAVE(EX_NOERR);
230 }
231 
232 /*!
233   \ingroup Utilities
234   \undoc
235 */
ex_set_max_name_length(int exoid,int length)236 int ex_set_max_name_length(int exoid, int length)
237 {
238   char errmsg[MAX_ERR_LENGTH];
239 
240   EX_FUNC_ENTER();
241   if (ex__check_valid_file_id(exoid, __func__) == EX_FATAL) {
242     EX_FUNC_LEAVE(EX_FATAL);
243   }
244   if (length <= 0) {
245     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: Max name length must be positive.");
246     ex_err_fn(exoid, __func__, errmsg, NC_EMAXNAME);
247     EX_FUNC_LEAVE(EX_FATAL);
248   }
249   if (length > NC_MAX_NAME) {
250     snprintf(errmsg, MAX_ERR_LENGTH,
251              "ERROR: Max name length (%d) exceeds netcdf max name size (%d).", length, NC_MAX_NAME);
252     ex_err_fn(exoid, __func__, errmsg, NC_EMAXNAME);
253     EX_FUNC_LEAVE(EX_FATAL);
254   }
255 
256   ex_set_option(exoid, EX_OPT_MAX_NAME_LENGTH, length);
257 
258   EX_FUNC_LEAVE(EX_NOERR);
259 }
260 
261 /*!
262   \ingroup Utilities
263   \undoc
264 */
ex__update_max_name_length(int exoid,int length)265 void ex__update_max_name_length(int exoid, int length)
266 {
267   int status;
268   int db_length = 0;
269   int rootid    = exoid & EX_FILE_ID_MASK;
270 
271   EX_FUNC_ENTER();
272   if (ex__check_valid_file_id(exoid, __func__) == EX_FATAL) {
273     EX_FUNC_VOID();
274   }
275 
276   /* Get current value of the maximum_name_length attribute... */
277   if ((status = nc_get_att_int(rootid, NC_GLOBAL, ATT_MAX_NAME_LENGTH, &db_length)) != NC_NOERR) {
278     char errmsg[MAX_ERR_LENGTH];
279     snprintf(errmsg, MAX_ERR_LENGTH,
280              "ERROR: failed to update 'max_name_length' attribute in file id %d", exoid);
281     ex_err_fn(exoid, __func__, errmsg, status);
282   }
283 
284   if (length > db_length) {
285     /* Update with new value... */
286     ex_set_max_name_length(exoid, length);
287     if ((status = nc_put_att_int(rootid, NC_GLOBAL, ATT_MAX_NAME_LENGTH, NC_INT, 1, &length)) !=
288         NC_NOERR) {
289       char errmsg[MAX_ERR_LENGTH];
290       snprintf(errmsg, MAX_ERR_LENGTH,
291                "ERROR: failed to update 'max_name_length' attribute with new value in file id %d",
292                exoid);
293       ex_err_fn(exoid, __func__, errmsg, status);
294     }
295     nc_sync(rootid);
296   }
297   EX_FUNC_VOID();
298 }
299 
300 /*!
301   \internal
302   \undoc
303 */
ex__put_names(int exoid,int varid,size_t num_names,char ** names,ex_entity_type obj_type,const char * subtype,const char * routine)304 int ex__put_names(int exoid, int varid, size_t num_names, char **names, ex_entity_type obj_type,
305                   const char *subtype, const char *routine)
306 {
307   size_t i;
308   int    status;
309   char   errmsg[MAX_ERR_LENGTH];
310   int    max_name_len = 0;
311   size_t name_length;
312   size_t length;
313   char * int_names  = NULL;
314   size_t idx        = 0;
315   int    found_name = 0;
316 
317   EX_FUNC_ENTER();
318   if (ex__check_valid_file_id(exoid, __func__) == EX_FATAL) {
319     EX_FUNC_LEAVE(EX_FATAL);
320   }
321   /* inquire previously defined dimensions  */
322   name_length = ex_inquire_int(exoid, EX_INQ_DB_MAX_ALLOWED_NAME_LENGTH) + 1;
323 
324   if (!(int_names = calloc(num_names * name_length, 1))) {
325     snprintf(errmsg, MAX_ERR_LENGTH,
326              "ERROR: failed to allocate memory for internal int_names "
327              "array in file id %d",
328              exoid);
329     ex_err_fn(exoid, __func__, errmsg, EX_MEMFAIL);
330     EX_FUNC_LEAVE(EX_FATAL);
331   }
332 
333   for (i = 0; i < num_names; i++) {
334     if (names != NULL && *names != NULL && *names[i] != '\0') {
335       found_name = 1;
336       ex_copy_string(&int_names[idx], names[i], name_length);
337       length = strlen(names[i]) + 1;
338       if (length > name_length) {
339         fprintf(stderr,
340                 "Warning: The %s %s name '%s' is too long.\n\tIt will "
341                 "be truncated from %d to %d characters. [Called from %s]\n",
342                 ex_name_of_object(obj_type), subtype, names[i], (int)length - 1,
343                 (int)name_length - 1, routine);
344         length = name_length;
345       }
346 
347       if (length > max_name_len) {
348         max_name_len = length;
349       }
350     }
351     idx += name_length;
352   }
353 
354   if ((status = nc_put_var_text(exoid, varid, int_names)) != NC_NOERR) {
355     free(int_names);
356     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to store %s names in file id %d",
357              ex_name_of_object(obj_type), exoid);
358     ex_err_fn(exoid, __func__, errmsg, status);
359     EX_FUNC_LEAVE(EX_FATAL);
360   }
361 
362   if (found_name) {
363 
364     /* Update the maximum_name_length attribute on the file. */
365     ex__update_max_name_length(exoid, max_name_len - 1);
366   }
367   free(int_names);
368 
369   EX_FUNC_LEAVE(EX_NOERR);
370 }
371 
372 /*!
373   \internal
374   \undoc
375 */
ex__put_name(int exoid,int varid,size_t index,const char * name,ex_entity_type obj_type,const char * subtype,const char * routine)376 int ex__put_name(int exoid, int varid, size_t index, const char *name, ex_entity_type obj_type,
377                  const char *subtype, const char *routine)
378 {
379   int    status;
380   size_t start[2], count[2];
381   char   errmsg[MAX_ERR_LENGTH];
382   size_t name_length;
383 
384   if (ex__check_valid_file_id(exoid, __func__) == EX_FATAL) {
385     EX_FUNC_LEAVE(EX_FATAL);
386   }
387 
388   /* inquire previously defined dimensions  */
389   name_length = ex_inquire_int(exoid, EX_INQ_DB_MAX_ALLOWED_NAME_LENGTH) + 1;
390 
391   if (name != NULL && *name != '\0') {
392     int too_long = 0;
393     start[0]     = index;
394     start[1]     = 0;
395 
396     count[0] = 1;
397     count[1] = strlen(name) + 1;
398 
399     if (count[1] > name_length) {
400       fprintf(stderr,
401               "Warning: The %s %s name '%s' is too long.\n\tIt will be "
402               "truncated from %d to %d characters. [Called from %s]\n",
403               ex_name_of_object(obj_type), subtype, name, (int)strlen(name), (int)name_length - 1,
404               routine);
405       count[1] = name_length;
406       too_long = 1;
407     }
408 
409     if ((status = nc_put_vara_text(exoid, varid, start, count, name)) != NC_NOERR) {
410       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to store %s name in file id %d",
411                ex_name_of_object(obj_type), exoid);
412       ex_err_fn(exoid, __func__, errmsg, status);
413       return (EX_FATAL);
414     }
415 
416     /* Add the trailing null if the variable name was too long */
417     if (too_long) {
418       start[1] = name_length - 1;
419       nc_put_var1_text(exoid, varid, start, "\0");
420     }
421 
422     /* Update the maximum_name_length attribute on the file. */
423     ex__update_max_name_length(exoid, count[1] - 1);
424   }
425   return (EX_NOERR);
426 }
427 
428 /*!
429   \internal
430   \undoc
431 */
ex__get_names(int exoid,int varid,size_t num_names,char ** names,ex_entity_type obj_type,const char * routine)432 int ex__get_names(int exoid, int varid, size_t num_names, char **names, ex_entity_type obj_type,
433                   const char *routine)
434 {
435   size_t i;
436   int    status;
437 
438   /* Query size of names on file
439    * Use the smaller of the size on file or user-specified length
440    */
441   int db_name_size  = ex_inquire_int(exoid, EX_INQ_DB_MAX_ALLOWED_NAME_LENGTH);
442   int api_name_size = ex_inquire_int(exoid, EX_INQ_MAX_READ_NAME_LENGTH);
443   int name_size     = db_name_size < api_name_size ? db_name_size : api_name_size;
444 
445   for (i = 0; i < num_names; i++) {
446     status = ex__get_name(exoid, varid, i, names[i], name_size, obj_type, routine);
447     if (status != NC_NOERR) {
448       return (status);
449     }
450   }
451   return (EX_NOERR);
452 }
453 
454 /*!
455   \internal
456   \undoc
457 */
ex__get_name(int exoid,int varid,size_t index,char * name,int name_size,ex_entity_type obj_type,const char * routine)458 int ex__get_name(int exoid, int varid, size_t index, char *name, int name_size,
459                  ex_entity_type obj_type, const char *routine)
460 {
461   size_t start[2], count[2];
462   int    status;
463   char   errmsg[MAX_ERR_LENGTH];
464   int    api_name_size = 0;
465 
466   api_name_size = ex_inquire_int(exoid, EX_INQ_MAX_READ_NAME_LENGTH);
467 
468   /* read the name */
469   start[0] = index;
470   count[0] = 1;
471   start[1] = 0;
472   count[1] = name_size + 1;
473 
474   status = nc_get_vara_text(exoid, varid, start, count, name);
475   if (status != NC_NOERR) {
476     snprintf(errmsg, MAX_ERR_LENGTH,
477              "ERROR: failed to get %s name at index %d from file id %d [Called from %s]",
478              ex_name_of_object(obj_type), (int)index, exoid, routine);
479     ex_err_fn(exoid, __func__, errmsg, status);
480     return (EX_FATAL);
481   }
482 
483   name[api_name_size] = '\0';
484 
485   ex__trim(name);
486   return (EX_NOERR);
487 }
488 
489 /*!
490   \internal
491   \undoc
492 */
ex__trim(char * name)493 void ex__trim(char *name)
494 {
495   /* Thread-safe, reentrant */
496   /* Trim trailing spaces... */
497   size_t size;
498   char * end;
499 
500   if (name == NULL) {
501     return;
502   }
503 
504   size = strlen(name);
505   if (size == 0) {
506     return;
507   }
508 
509   end = name + size - 1;
510   while (end >= name && isspace(*end)) {
511     end--;
512   }
513 
514   *(end + 1) = '\0';
515 }
516 
517 /** ex__catstr  - concatenate  string/number (where number is converted to ASCII)
518  */
519 /*!
520   \internal
521   \undoc
522 */
ex__catstr(const char * string,int num)523 char *ex__catstr(const char *string, int num)
524 {
525   /* Only called from an already locked function */
526   char *tmp_string = cur_string;
527   cur_string += sprintf(cur_string, "%s%d", string, num) + 1;
528   if (cur_string - ret_string > 9 * (MAX_VAR_NAME_LENGTH + 1)) {
529     cur_string = ret_string;
530   }
531   return (tmp_string);
532 }
533 
534 /** ex__catstr2 - concatenate  string1num1string2num2   */
535 /*!
536   \internal
537   \undoc
538 */
ex__catstr2(const char * string1,int num1,const char * string2,int num2)539 char *ex__catstr2(const char *string1, int num1, const char *string2, int num2)
540 {
541   /* Only called from an already locked function */
542   char *tmp_string = cur_string;
543   cur_string += sprintf(cur_string, "%s%d%s%d", string1, num1, string2, num2) + 1;
544   if (cur_string - ret_string > 9 * (MAX_VAR_NAME_LENGTH + 1)) {
545     cur_string = ret_string;
546   }
547   return (tmp_string);
548 }
549 
550 /*!
551   \internal
552   \undoc
553 */
ex_name_of_object(ex_entity_type obj_type)554 char *ex_name_of_object(ex_entity_type obj_type)
555 {
556   /* Thread-safe and reentrant */
557   switch (obj_type) {
558   case EX_ASSEMBLY: return "assembly";
559   case EX_BLOB: return "blob";
560   case EX_COORDINATE: /* kluge so some wrapper functions work */ return "coordinate";
561   case EX_NODAL: return "nodal";
562   case EX_EDGE_BLOCK: return "edge block";
563   case EX_FACE_BLOCK: return "face block";
564   case EX_ELEM_BLOCK: return "element block";
565   case EX_NODE_SET: return "node set";
566   case EX_EDGE_SET: return "edge set";
567   case EX_FACE_SET: return "face set";
568   case EX_SIDE_SET: return "side set";
569   case EX_ELEM_SET: return "element set";
570   case EX_ELEM_MAP: return "element map";
571   case EX_NODE_MAP: return "node map";
572   case EX_EDGE_MAP: return "edge map";
573   case EX_FACE_MAP: return "face map";
574   case EX_GLOBAL: return "global";
575   default: return "invalid type";
576   }
577 }
578 
579 /*!
580   \internal
581   \undoc
582 */
ex_var_type_to_ex_entity_type(char var_type)583 ex_entity_type ex_var_type_to_ex_entity_type(char var_type)
584 {
585   /* Thread-safe and reentrant */
586   char var_lower = tolower(var_type);
587   if (var_lower == 'n') {
588     return EX_NODAL;
589   }
590   if (var_lower == 'l') {
591     return EX_EDGE_BLOCK;
592   }
593   if (var_lower == 'f') {
594     return EX_FACE_BLOCK;
595   }
596   if (var_lower == 'e') {
597     return EX_ELEM_BLOCK;
598   }
599   if (var_lower == 'm') {
600     return EX_NODE_SET;
601   }
602   else if (var_lower == 'd') {
603     return EX_EDGE_SET;
604   }
605   else if (var_lower == 'a') {
606     return EX_FACE_SET;
607   }
608   else if (var_lower == 's') {
609     return EX_SIDE_SET;
610   }
611   else if (var_lower == 't') {
612     return EX_ELEM_SET;
613   }
614   else if (var_lower == 'g') {
615     return EX_GLOBAL;
616   }
617   else {
618     return EX_INVALID;
619   }
620 }
621 
622 /*!
623   \internal
624   \undoc
625 */
ex__dim_num_objects(ex_entity_type obj_type)626 char *ex__dim_num_objects(ex_entity_type obj_type)
627 {
628   switch (obj_type) {
629   case EX_NODAL: return DIM_NUM_NODES;
630   case EX_ASSEMBLY: return DIM_NUM_ASSEMBLY;
631   case EX_BLOB: return DIM_NUM_BLOB;
632   case EX_ELEM_BLOCK: return DIM_NUM_EL_BLK;
633   case EX_EDGE_BLOCK: return DIM_NUM_ED_BLK;
634   case EX_FACE_BLOCK: return DIM_NUM_FA_BLK;
635   case EX_NODE_SET: return DIM_NUM_NS;
636   case EX_EDGE_SET: return DIM_NUM_ES;
637   case EX_FACE_SET: return DIM_NUM_FS;
638   case EX_ELEM_SET: return DIM_NUM_ELS;
639   case EX_SIDE_SET: return DIM_NUM_SS;
640   case EX_ELEM_MAP: return DIM_NUM_EM;
641   case EX_FACE_MAP: return DIM_NUM_FAM;
642   case EX_EDGE_MAP: return DIM_NUM_EDM;
643   case EX_NODE_MAP: return DIM_NUM_NM;
644   default: {
645     char errmsg[MAX_ERR_LENGTH];
646     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported in call to %s", obj_type,
647              __func__);
648     ex_err(__func__, errmsg, EX_BADPARAM);
649     return (NULL);
650   }
651   }
652 }
653 
654 /*!
655   \internal
656   \undoc
657 */
ex__dim_num_entries_in_object(ex_entity_type obj_type,int idx)658 char *ex__dim_num_entries_in_object(ex_entity_type obj_type, int idx)
659 {
660   switch (obj_type) {
661   case EX_NODAL: return DIM_NUM_NODES;
662   case EX_ASSEMBLY: return DIM_NUM_ENTITY_ASSEMBLY(idx);
663   case EX_BLOB: return DIM_NUM_VALUES_BLOB(idx);
664   case EX_EDGE_BLOCK: return DIM_NUM_ED_IN_EBLK(idx);
665   case EX_FACE_BLOCK: return DIM_NUM_FA_IN_FBLK(idx);
666   case EX_ELEM_BLOCK: return DIM_NUM_EL_IN_BLK(idx);
667   case EX_NODE_SET: return DIM_NUM_NOD_NS(idx);
668   case EX_EDGE_SET: return DIM_NUM_EDGE_ES(idx);
669   case EX_FACE_SET: return DIM_NUM_FACE_FS(idx);
670   case EX_SIDE_SET: return DIM_NUM_SIDE_SS(idx);
671   case EX_ELEM_SET: return DIM_NUM_ELE_ELS(idx);
672   default: {
673     char errmsg[MAX_ERR_LENGTH];
674     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported in call to %s", obj_type,
675              __func__);
676     ex_err(__func__, errmsg, EX_BADPARAM);
677     return NULL;
678   }
679   }
680 }
681 
682 /*!
683   \internal
684   \undoc
685 */
ex__name_var_of_object(ex_entity_type obj_type,int i,int j)686 char *ex__name_var_of_object(ex_entity_type obj_type, int i, int j)
687 {
688   switch (obj_type) {
689   case EX_ASSEMBLY: return VAR_ASSEMBLY_VAR(i, j);
690   case EX_BLOB: return VAR_BLOB_VAR(i, j);
691   case EX_EDGE_BLOCK: return VAR_EDGE_VAR(i, j);
692   case EX_FACE_BLOCK: return VAR_FACE_VAR(i, j);
693   case EX_ELEM_BLOCK: return VAR_ELEM_VAR(i, j);
694   case EX_NODE_SET: return VAR_NS_VAR(i, j);
695   case EX_EDGE_SET: return VAR_ES_VAR(i, j);
696   case EX_FACE_SET: return VAR_FS_VAR(i, j);
697   case EX_SIDE_SET: return VAR_SS_VAR(i, j);
698   case EX_ELEM_SET: return VAR_ELS_VAR(i, j);
699   default: {
700     char errmsg[MAX_ERR_LENGTH];
701     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported in call to %s", obj_type,
702              __func__);
703     ex_err(__func__, errmsg, EX_BADPARAM);
704     return (NULL);
705   }
706   }
707 }
708 
709 /*!
710   \internal
711   \undoc
712 */
ex__name_red_var_of_object(ex_entity_type obj_type,int id)713 char *ex__name_red_var_of_object(ex_entity_type obj_type, int id)
714 {
715   switch (obj_type) {
716   case EX_ASSEMBLY: return VAR_ASSEMBLY_RED_VAR(id);
717   case EX_BLOB: return VAR_BLOB_RED_VAR(id);
718   case EX_EDGE_BLOCK: return VAR_EDGE_RED_VAR(id);
719   case EX_FACE_BLOCK: return VAR_FACE_RED_VAR(id);
720   case EX_ELEM_BLOCK: return VAR_ELEM_RED_VAR(id);
721   case EX_NODE_SET: return VAR_NS_RED_VAR(id);
722   case EX_EDGE_SET: return VAR_ES_RED_VAR(id);
723   case EX_FACE_SET: return VAR_FS_RED_VAR(id);
724   case EX_SIDE_SET: return VAR_SS_RED_VAR(id);
725   case EX_ELEM_SET: return VAR_ELS_RED_VAR(id);
726   default: {
727     char errmsg[MAX_ERR_LENGTH];
728     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported in call to %s", obj_type,
729              __func__);
730     ex_err(__func__, errmsg, EX_BADPARAM);
731     return (NULL);
732   }
733   }
734 }
735 
736 /*!
737   \internal
738   \undoc
739 */
ex__name_of_map(ex_entity_type map_type,int map_index)740 char *ex__name_of_map(ex_entity_type map_type, int map_index)
741 {
742   switch (map_type) {
743   case EX_NODE_MAP: return VAR_NODE_MAP(map_index);
744   case EX_EDGE_MAP: return VAR_EDGE_MAP(map_index);
745   case EX_FACE_MAP: return VAR_FACE_MAP(map_index);
746   case EX_ELEM_MAP: return VAR_ELEM_MAP(map_index);
747   default: return NULL;
748   }
749 }
750 
751 /*****************************************************************************
752 *
753 * ex__id_lkup - look up id
754 *
755 * entry conditions -
756 *   input parameters:
757 *       int            exoid             exodus file id
758 *       ex_entity_type id_type           id type name:
759 *                                         elem_ss
760 *                                         node_ns
761 2*                                         side_ss
762 *       int     num                     id value
763 *
764 * exit conditions -
765 *       int     return                  index into table (1-based)
766 *
767 *****************************************************************************/
768 
769 /*!
770   \internal
771   \undoc
772 */
ex__id_lkup(int exoid,ex_entity_type id_type,ex_entity_id num)773 int ex__id_lkup(int exoid, ex_entity_type id_type, ex_entity_id num)
774 {
775   char *   id_table;
776   char *   id_dim;
777   char *   stat_table;
778   int      varid, dimid;
779   size_t   dim_len, j;
780   int64_t  i;
781   int64_t *id_vals   = NULL;
782   int *    stat_vals = NULL;
783 
784   static bool           filled     = false;
785   static bool           sequential = false;
786   struct ex__obj_stats *tmp_stats;
787   int                   status;
788   char                  errmsg[MAX_ERR_LENGTH];
789 
790   switch (id_type) {
791   case EX_NODAL: return (0);
792   case EX_GLOBAL: return (0);
793   case EX_ASSEMBLY: return num;
794   case EX_BLOB: return num;
795   case EX_ELEM_BLOCK:
796     id_table   = VAR_ID_EL_BLK;   /* id array name */
797     id_dim     = DIM_NUM_EL_BLK;  /* id array dimension name*/
798     stat_table = VAR_STAT_EL_BLK; /* id status array name */
799     tmp_stats  = ex__get_stat_ptr(exoid, &exoII_eb);
800     break;
801   case EX_NODE_SET:
802     id_table   = VAR_NS_IDS;
803     id_dim     = DIM_NUM_NS;
804     stat_table = VAR_NS_STAT;
805     tmp_stats  = ex__get_stat_ptr(exoid, &exoII_ns);
806     break;
807   case EX_SIDE_SET:
808     id_table   = VAR_SS_IDS;
809     id_dim     = DIM_NUM_SS;
810     stat_table = VAR_SS_STAT;
811     tmp_stats  = ex__get_stat_ptr(exoid, &exoII_ss);
812     break;
813   case EX_EDGE_BLOCK:
814     id_table   = VAR_ID_ED_BLK;
815     id_dim     = DIM_NUM_ED_BLK;
816     stat_table = VAR_STAT_ED_BLK;
817     tmp_stats  = ex__get_stat_ptr(exoid, &exoII_ed);
818     break;
819   case EX_FACE_BLOCK:
820     id_table   = VAR_ID_FA_BLK;
821     id_dim     = DIM_NUM_FA_BLK;
822     stat_table = VAR_STAT_FA_BLK;
823     tmp_stats  = ex__get_stat_ptr(exoid, &exoII_fa);
824     break;
825   case EX_EDGE_SET:
826     id_table   = VAR_ES_IDS;
827     id_dim     = DIM_NUM_ES;
828     stat_table = VAR_ES_STAT;
829     tmp_stats  = ex__get_stat_ptr(exoid, &exoII_es);
830     break;
831   case EX_FACE_SET:
832     id_table   = VAR_FS_IDS;
833     id_dim     = DIM_NUM_FS;
834     stat_table = VAR_FS_STAT;
835     tmp_stats  = ex__get_stat_ptr(exoid, &exoII_fs);
836     break;
837   case EX_ELEM_SET:
838     id_table   = VAR_ELS_IDS;
839     id_dim     = DIM_NUM_ELS;
840     stat_table = VAR_ELS_STAT;
841     tmp_stats  = ex__get_stat_ptr(exoid, &exoII_els);
842     break;
843   case EX_NODE_MAP:
844     id_table   = VAR_NM_PROP(1);
845     id_dim     = DIM_NUM_NM;
846     stat_table = "";
847     tmp_stats  = ex__get_stat_ptr(exoid, &exoII_nm);
848     break;
849   case EX_EDGE_MAP:
850     id_table   = VAR_EDM_PROP(1);
851     id_dim     = DIM_NUM_EDM;
852     stat_table = "";
853     tmp_stats  = ex__get_stat_ptr(exoid, &exoII_edm);
854     break;
855   case EX_FACE_MAP:
856     id_table   = VAR_FAM_PROP(1);
857     id_dim     = DIM_NUM_FAM;
858     stat_table = "";
859     tmp_stats  = ex__get_stat_ptr(exoid, &exoII_fam);
860     break;
861   case EX_ELEM_MAP:
862     id_table   = VAR_EM_PROP(1);
863     id_dim     = DIM_NUM_EM;
864     stat_table = "";
865     tmp_stats  = ex__get_stat_ptr(exoid, &exoII_em);
866     break;
867   default:
868     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unsupported id array type %d for file id %d", id_type,
869              exoid);
870     ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM);
871     return (EX_FATAL);
872   }
873 
874   if ((tmp_stats->id_vals == NULL) || (!(tmp_stats->valid_ids))) {
875 
876     /* first time through or id arrays haven't been completely filled yet */
877 
878     /* get size of id array */
879 
880     /* First get dimension id of id array */
881     if ((status = nc_inq_dimid(exoid, id_dim, &dimid)) != NC_NOERR) {
882       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to locate id array dimension in file id %d",
883                exoid);
884       ex_err_fn(exoid, __func__, errmsg, status);
885       return (EX_FATAL);
886     }
887 
888     /* Next get value of dimension */
889     if ((status = nc_inq_dimlen(exoid, dimid, &dim_len)) != NC_NOERR) {
890       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to locate %s array length in file id %d",
891                id_table, exoid);
892       ex_err_fn(exoid, __func__, errmsg, status);
893       return (EX_FATAL);
894     }
895 
896     /* get variable id of id array */
897     if ((status = nc_inq_varid(exoid, id_table, &varid)) != NC_NOERR) {
898       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to locate %s array in file id %d", id_table,
899                exoid);
900       ex_err_fn(exoid, __func__, errmsg, status);
901       return (EX_FATAL);
902     }
903 
904     /* allocate space for id array and initialize to zero to ensure
905        that the higher bits don't contain garbage while copy from ints */
906     if (!(id_vals = calloc(dim_len, sizeof(int64_t)))) {
907       snprintf(errmsg, MAX_ERR_LENGTH,
908                "ERROR: failed to allocate memory for %s array for file id %d", id_table, exoid);
909       ex_err_fn(exoid, __func__, errmsg, EX_MEMFAIL);
910       return (EX_FATAL);
911     }
912 
913     if (ex_int64_status(exoid) & EX_IDS_INT64_API) {
914       status = nc_get_var_longlong(exoid, varid, (long long *)id_vals);
915     }
916     else {
917       int *id_vals_int;
918       if (!(id_vals_int = malloc(dim_len * sizeof(int)))) {
919         snprintf(errmsg, MAX_ERR_LENGTH,
920                  "ERROR: failed to allocate memory for temporary array "
921                  "id_vals_int for file id %d",
922                  exoid);
923         ex_err_fn(exoid, __func__, errmsg, EX_MEMFAIL);
924         free(id_vals);
925         return (EX_FATAL);
926       }
927       status = nc_get_var_int(exoid, varid, id_vals_int);
928       if (status == NC_NOERR) {
929         for (i = 0; i < dim_len; i++) {
930           id_vals[i] = (int64_t)id_vals_int[i];
931         }
932       }
933       free(id_vals_int);
934     }
935 
936     if (status != NC_NOERR) {
937       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get %s array from file id %d", id_table,
938                exoid);
939       ex_err_fn(exoid, __func__, errmsg, status);
940       free(id_vals);
941       return (EX_FATAL);
942     }
943 
944     /* check if values in stored arrays are filled with non-zeroes */
945     filled     = true;
946     sequential = true;
947     for (i = 0; i < dim_len; i++) {
948       if (id_vals[i] != i + 1) {
949         sequential = false;
950       }
951       if (id_vals[i] == EX_INVALID_ID || id_vals[i] == NC_FILL_INT) {
952         filled     = false;
953         sequential = false;
954         break; /* id array hasn't been completely filled with valid ids yet */
955       }
956     }
957 
958     if (filled) {
959       tmp_stats->valid_ids  = true;
960       tmp_stats->sequential = sequential;
961       tmp_stats->num        = dim_len;
962       tmp_stats->id_vals    = id_vals;
963     }
964   }
965   else {
966     id_vals    = tmp_stats->id_vals;
967     dim_len    = tmp_stats->num;
968     sequential = tmp_stats->sequential;
969   }
970 
971   if (sequential && num < dim_len) {
972     i = num - 1;
973   }
974   else {
975     /* Do a linear search through the id array to find the array value
976        corresponding to the passed index number */
977     for (i = 0; i < dim_len; i++) {
978       if (id_vals[i] == num) {
979         break; /* found the id requested */
980       }
981     }
982   }
983   if (i >= dim_len) /* failed to find id number */
984   {
985     if (!(tmp_stats->valid_ids)) {
986       free(id_vals);
987     }
988     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to locate id %" PRId64 " for file id %d", num,
989              exoid);
990     ex_set_err(__func__, errmsg, EX_LOOKUPFAIL);
991     return (-EX_LOOKUPFAIL); /*if we got here, the id array value doesn't exist */
992   }
993 
994   /* Now check status array to see if object is null */
995   if ((tmp_stats->stat_vals == NULL) || (!(tmp_stats->valid_stat))) {
996 
997     /* allocate space for new status array */
998     if (!(stat_vals = malloc(dim_len * sizeof(int)))) {
999       free(id_vals);
1000       snprintf(errmsg, MAX_ERR_LENGTH,
1001                "ERROR: failed to allocate memory for %s array for file id %d", id_table, exoid);
1002       ex_err_fn(exoid, __func__, errmsg, EX_MEMFAIL);
1003       return (EX_FATAL);
1004     }
1005 
1006     /* first time through or status arrays haven't been filled yet */
1007     if (nc_inq_varid(exoid, stat_table, &varid) == NC_NOERR) {
1008       /* get variable id of status array */
1009       /* if status array exists, use it, otherwise assume object exists
1010          to be backward compatible */
1011 
1012       if ((status = nc_get_var_int(exoid, varid, stat_vals)) != NC_NOERR) {
1013         free(id_vals);
1014         free(stat_vals);
1015         snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to get %s array from file id %d",
1016                  stat_table, exoid);
1017         ex_err_fn(exoid, __func__, errmsg, status);
1018         return (EX_FATAL);
1019       }
1020     }
1021     else {
1022       for (j = 0; j < dim_len; j++) {
1023         stat_vals[j] = 1;
1024       }
1025     }
1026 
1027     if (tmp_stats->valid_ids) {
1028       /* status array is valid only if ids are valid */
1029       tmp_stats->valid_stat = true;
1030       tmp_stats->stat_vals  = stat_vals;
1031     }
1032   }
1033   else {
1034     stat_vals = tmp_stats->stat_vals;
1035   }
1036 
1037   if (!(tmp_stats->valid_ids)) {
1038     free(id_vals);
1039   }
1040 
1041   if (stat_vals[i] == 0) /* is this object null? */ {
1042     ex_err_fn(exoid, __func__, "", EX_NULLENTITY);
1043     if (!(tmp_stats->valid_stat)) {
1044       free(stat_vals);
1045     }
1046     return (-((int)i + 1)); /* return index into id array (1-based) */
1047   }
1048   if (!(tmp_stats->valid_stat)) {
1049     free(stat_vals);
1050   }
1051   return (i + 1); /* return index into id array (1-based) */
1052 }
1053 
1054 /******************************************************************************
1055  *
1056  * ex__get_stat_ptr - returns a pointer to a structure of object ids
1057  *
1058  *****************************************************************************/
1059 
1060 /*! this routine returns a pointer to a structure containing the ids of
1061  * element blocks, node sets, or side sets according to exoid;  if there
1062  * is not a structure that matches the exoid, one is created
1063  * \internal
1064  */
1065 
ex__get_stat_ptr(int exoid,struct ex__obj_stats ** obj_ptr)1066 struct ex__obj_stats *ex__get_stat_ptr(int exoid, struct ex__obj_stats **obj_ptr)
1067 {
1068   struct ex__obj_stats *tmp_ptr;
1069 
1070   tmp_ptr = *obj_ptr;
1071 
1072   while (tmp_ptr) {
1073     if ((tmp_ptr)->exoid == exoid) {
1074       break;
1075     }
1076     tmp_ptr = (tmp_ptr)->next;
1077   }
1078 
1079   if (!tmp_ptr) { /* exoid not found */
1080     tmp_ptr             = (struct ex__obj_stats *)calloc(1, sizeof(struct ex__obj_stats));
1081     tmp_ptr->exoid      = exoid;
1082     tmp_ptr->next       = *obj_ptr;
1083     tmp_ptr->id_vals    = 0;
1084     tmp_ptr->stat_vals  = 0;
1085     tmp_ptr->num        = 0;
1086     tmp_ptr->valid_ids  = 0;
1087     tmp_ptr->valid_stat = 0;
1088     *obj_ptr            = tmp_ptr;
1089   }
1090   return tmp_ptr;
1091 }
1092 
1093 /******************************************************************************
1094  *
1095  * ex__rm_stat_ptr - removes a pointer to a structure of object ids
1096  *
1097  *****************************************************************************/
1098 
1099 /*! this routine removes a pointer to a structure containing the ids of
1100  * element blocks, node sets, or side sets according to exoid;  this
1101  * is necessary to clean up because netCDF reuses file ids;  should be
1102  * called from ex_close
1103  * \internal
1104  */
1105 
ex__rm_stat_ptr(int exoid,struct ex__obj_stats ** obj_ptr)1106 void ex__rm_stat_ptr(int exoid, struct ex__obj_stats **obj_ptr)
1107 {
1108   struct ex__obj_stats *last_head_list_ptr, *tmp_ptr;
1109 
1110   tmp_ptr            = *obj_ptr;
1111   last_head_list_ptr = *obj_ptr; /* save last head pointer */
1112 
1113   while (tmp_ptr) /* Walk linked list of file ids/vals */
1114   {
1115     if (exoid == tmp_ptr->exoid) /* linear search for exodus file id */
1116     {
1117       if (tmp_ptr == *obj_ptr) {     /* Are we at the head of the list? */
1118         *obj_ptr = (*obj_ptr)->next; /*   yes, reset ptr to head of list */
1119       }
1120       else { /*   no, remove this record from chain*/
1121         last_head_list_ptr->next = tmp_ptr->next;
1122       }
1123       free(tmp_ptr->id_vals); /* free up memory */
1124       free(tmp_ptr->stat_vals);
1125       free(tmp_ptr);
1126       break; /* Quit if found */
1127     }
1128     last_head_list_ptr = tmp_ptr;       /* save last head pointer */
1129     tmp_ptr            = tmp_ptr->next; /* Loop back if not */
1130   }
1131 }
1132 
1133 /* structures to hold number of blocks of that type for each file id */
1134 static struct ex__list_item *ed_ctr_list = 0; /* edge blocks */
1135 static struct ex__list_item *fa_ctr_list = 0; /* face blocks */
1136 static struct ex__list_item *eb_ctr_list = 0; /* element blocks */
1137 
1138 /* structures to hold number of sets of that type for each file id */
1139 static struct ex__list_item *ns_ctr_list  = 0; /* node sets */
1140 static struct ex__list_item *es_ctr_list  = 0; /* edge sets */
1141 static struct ex__list_item *fs_ctr_list  = 0; /* face sets */
1142 static struct ex__list_item *ss_ctr_list  = 0; /* side sets */
1143 static struct ex__list_item *els_ctr_list = 0; /* element sets */
1144 
1145 /* structures to hold number of blobs/assemblies for each file id */
1146 static struct ex__list_item *assm_ctr_list = 0; /* assemblies */
1147 static struct ex__list_item *blob_ctr_list = 0; /* blobs */
1148 
1149 /* structures to hold number of maps of that type for each file id */
1150 static struct ex__list_item *nm_ctr_list  = 0; /* node maps */
1151 static struct ex__list_item *edm_ctr_list = 0; /* edge maps */
1152 static struct ex__list_item *fam_ctr_list = 0; /* face maps */
1153 static struct ex__list_item *em_ctr_list  = 0; /* element maps */
1154 
1155 /*!
1156   \internal
1157   \undoc
1158 */
ex__get_counter_list(ex_entity_type obj_type)1159 struct ex__list_item **ex__get_counter_list(ex_entity_type obj_type)
1160 {
1161   /* Thread-safe, but is dealing with globals */
1162   /* Only called from a routine which will be using locks */
1163   switch (obj_type) {
1164   case EX_ASSEMBLY: return &assm_ctr_list;
1165   case EX_BLOB: return &blob_ctr_list;
1166   case EX_ELEM_BLOCK: return &eb_ctr_list;
1167   case EX_NODE_SET: return &ns_ctr_list;
1168   case EX_SIDE_SET: return &ss_ctr_list;
1169   case EX_ELEM_MAP: return &em_ctr_list;
1170   case EX_NODE_MAP: return &nm_ctr_list;
1171   case EX_EDGE_BLOCK: return &ed_ctr_list;
1172   case EX_FACE_BLOCK: return &fa_ctr_list;
1173   case EX_EDGE_SET: return &es_ctr_list;
1174   case EX_FACE_SET: return &fs_ctr_list;
1175   case EX_ELEM_SET: return &els_ctr_list;
1176   case EX_EDGE_MAP: return &edm_ctr_list;
1177   case EX_FACE_MAP: return &fam_ctr_list;
1178   default: return (NULL);
1179   }
1180 }
1181 
1182 /******************************************************************************
1183  *
1184  * ex__inc_file_item - increment file item
1185  *
1186  *****************************************************************************/
1187 
1188 /*! this routine sets up a structure to track and increment a counter for
1189  * each open exodus file.  it is designed to be used by the routines
1190  * ex_put_elem_block() and ex_put_set_param(),
1191  * to keep track of the number of element blocks, and each type of set,
1192  * respectively, for each open exodus II file.
1193  *
1194  * The list structure is used as follows:
1195  *
1196  *   ptr -----------> list item structure
1197  *                    -------------------
1198  *                    exodus file id
1199  *                    item value (int)
1200  *                    ptr to next (NULL if last)
1201  *
1202  *
1203  * NOTE: since netCDF reuses its file ids, and a user may open and close any
1204  *       number of files in one application, items must be taken out of the
1205  *       linked lists in each of the above routines.  these should be called
1206  *       after ncclose().
1207  * \internal
1208  */
1209 
ex__inc_file_item(int exoid,struct ex__list_item ** list_ptr)1210 int ex__inc_file_item(int                    exoid,    /* file id */
1211                       struct ex__list_item **list_ptr) /* ptr to ptr to list_item */
1212 {
1213   struct ex__list_item *tlist_ptr = *list_ptr; /* use temp list ptr to walk linked list */
1214   while (tlist_ptr) {                          /* Walk linked list of file ids/vals */
1215     if (exoid == tlist_ptr->exo_id) {          /* linear search for exodus file id */
1216       break;                                   /* Quit if found */
1217     }
1218     tlist_ptr = tlist_ptr->next; /* Loop back if not */
1219   }
1220 
1221   if (!tlist_ptr) { /* ptr NULL? */
1222     /* allocate space for new structure record */
1223     tlist_ptr         = (struct ex__list_item *)calloc(1, sizeof(struct ex__list_item));
1224     tlist_ptr->exo_id = exoid;     /* insert file id */
1225     tlist_ptr->next   = *list_ptr; /* insert into head of list */
1226     *list_ptr         = tlist_ptr; /* fix up new head of list  */
1227   }
1228   return (tlist_ptr->value++);
1229 }
1230 
1231 /*****************************************************************************
1232  *
1233  * ex__get_file_item - return count
1234  *
1235  *****************************************************************************/
1236 
1237 /*! this routine accesses a structure to track and increment a counter for
1238  * each open exodus file.  it is designed to be used by the routines
1239  * ex_put_elem_block(), and ex_put_set_param(),
1240  * to get the number of element blocks, or a type of set,
1241  * respectively, for an open exodus II file.
1242  *
1243  * The list structure is used as follows:
1244  *
1245  *   ptr -----------> list item structure
1246  *                    -------------------
1247  *                    exodus file id
1248  *                    item value (int)
1249  *                    ptr to next (NULL if last)
1250  *
1251  *
1252  * NOTE: since netCDF reuses its file ids, and a user may open and close any
1253  *       number of files in one application, items must be taken out of the
1254  *       linked lists in each of the above routines.  these should be called
1255  *       after nc_close().
1256  * \internal
1257  */
1258 
ex__get_file_item(int exoid,struct ex__list_item ** list_ptr)1259 int ex__get_file_item(int                    exoid,    /* file id */
1260                       struct ex__list_item **list_ptr) /* ptr to ptr to list_item */
1261 {
1262   /* Not thread-safe: list_ptr passed in is a global
1263    * Would probably work ok with multiple threads since read-only,
1264    * but possible that list_ptr will be modified while being used
1265    */
1266   struct ex__list_item *tlist_ptr = *list_ptr; /* use temp list ptr to walk linked list */
1267   while (tlist_ptr) {                          /* Walk linked list of file ids/vals */
1268     if (exoid == tlist_ptr->exo_id) {          /* linear search for exodus file id */
1269       break;                                   /* Quit if found */
1270     }
1271     tlist_ptr = tlist_ptr->next; /* Loop back if not */
1272   }
1273 
1274   if (!tlist_ptr) { /* ptr NULL? */
1275     return (-1);
1276   }
1277 
1278   return (tlist_ptr->value);
1279 }
1280 
1281 /*****************************************************************************
1282  *
1283  * ex__rm_file_item - remove file item
1284  *
1285  *****************************************************************************/
1286 
1287 /*! this routine removes a structure to track and increment a counter for
1288  * each open exodus file.
1289  *
1290  * The list structure is used as follows:
1291  *
1292  *   ptr -----------> list item structure
1293  *                    -------------------
1294  *                    exodus file id
1295  *                    item value (int)
1296  *                    ptr to next (NULL if last)
1297  *
1298  *
1299  * NOTE: since netCDF reuses its file ids, and a user may open and close any
1300  *       number of files in one application, items must be taken out of the
1301  *       linked lists in each of the above routines.  these should be called
1302  *       after ncclose().
1303  * \internal
1304  */
1305 
ex__rm_file_item(int exoid,struct ex__list_item ** list_ptr)1306 void ex__rm_file_item(int                    exoid,    /* file id */
1307                       struct ex__list_item **list_ptr) /* ptr to ptr to list_item */
1308 
1309 {
1310   struct ex__list_item *last_head_list_ptr = *list_ptr; /* save last head pointer */
1311 
1312   struct ex__list_item *tlist_ptr = *list_ptr;
1313   while (tlist_ptr) {                  /* Walk linked list of file ids/vals */
1314     if (exoid == tlist_ptr->exo_id) {  /* linear search for exodus file id */
1315       if (tlist_ptr == *list_ptr) {    /* Are we at the head of the list? */
1316         *list_ptr = (*list_ptr)->next; /*   yes, reset ptr to head of list */
1317       }
1318       else { /*   no, remove this record from chain*/
1319         last_head_list_ptr->next = tlist_ptr->next;
1320       }
1321       free(tlist_ptr); /* free up memory */
1322       break;           /* Quit if found */
1323     }
1324     last_head_list_ptr = tlist_ptr;       /* save last head pointer */
1325     tlist_ptr          = tlist_ptr->next; /* Loop back if not */
1326   }
1327 }
1328 
1329 /*!
1330   \ingroup Utilities
1331   \undoc
1332 */
ex_get_num_props(int exoid,ex_entity_type obj_type)1333 int ex_get_num_props(int exoid, ex_entity_type obj_type)
1334 {
1335   int   cntr, varid;
1336   char *var_name;
1337   char  errmsg[MAX_ERR_LENGTH];
1338 
1339   EX_FUNC_ENTER();
1340   cntr = 0;
1341 
1342   /* loop until there is not a property variable defined; the name of */
1343   /* the variables begin with an increment of 1 ("xx_prop1") so use cntr+1 */
1344   while (true) {
1345     switch (obj_type) {
1346     case EX_ELEM_BLOCK: var_name = VAR_EB_PROP(cntr + 1); break;
1347     case EX_EDGE_BLOCK: var_name = VAR_ED_PROP(cntr + 1); break;
1348     case EX_FACE_BLOCK: var_name = VAR_FA_PROP(cntr + 1); break;
1349     case EX_NODE_SET: var_name = VAR_NS_PROP(cntr + 1); break;
1350     case EX_EDGE_SET: var_name = VAR_ES_PROP(cntr + 1); break;
1351     case EX_FACE_SET: var_name = VAR_FS_PROP(cntr + 1); break;
1352     case EX_SIDE_SET: var_name = VAR_SS_PROP(cntr + 1); break;
1353     case EX_ELEM_SET: var_name = VAR_ELS_PROP(cntr + 1); break;
1354     case EX_ASSEMBLY: var_name = VAR_ASSEMBLY_PROP(cntr + 1); break;
1355     case EX_BLOB: var_name = VAR_BLOB_PROP(cntr + 1); break;
1356     case EX_ELEM_MAP: var_name = VAR_EM_PROP(cntr + 1); break;
1357     case EX_FACE_MAP: var_name = VAR_FAM_PROP(cntr + 1); break;
1358     case EX_EDGE_MAP: var_name = VAR_EDM_PROP(cntr + 1); break;
1359     case EX_NODE_MAP: var_name = VAR_NM_PROP(cntr + 1); break;
1360     default:
1361       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: object type %d not supported; file id %d", obj_type,
1362                exoid);
1363       ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM);
1364       EX_FUNC_LEAVE(EX_FATAL);
1365     }
1366 
1367     if (nc_inq_varid(exoid, var_name, &varid) != NC_NOERR) {
1368       /*   no variable with this name; return cntr which is now the number of */
1369       /*   properties for this type of entity */
1370       EX_FUNC_LEAVE(cntr);
1371     }
1372     cntr++;
1373   }
1374   EX_FUNC_LEAVE(EX_FATAL);
1375 }
1376 
1377 /*!
1378   \ingroup Utilities
1379   \undoc
1380 */
ex__get_cpu_ws(void)1381 int ex__get_cpu_ws(void) { return (sizeof(float)); }
1382 
1383 /* swap - interchange v[i] and v[j] */
1384 /*!
1385   \internal
1386   \undoc
1387 */
ex_swap(int v[],int64_t i,int64_t j)1388 static void ex_swap(int v[], int64_t i, int64_t j)
1389 {
1390   /* Thread-safe, reentrant */
1391   int temp;
1392 
1393   temp = v[i];
1394   v[i] = v[j];
1395   v[j] = temp;
1396 }
1397 
1398 /*!
1399   \internal
1400   \undoc
1401 */
ex_swap64(int64_t v[],int64_t i,int64_t j)1402 static void ex_swap64(int64_t v[], int64_t i, int64_t j)
1403 {
1404   /* Thread-safe, reentrant */
1405   int64_t temp;
1406 
1407   temp = v[i];
1408   v[i] = v[j];
1409   v[j] = temp;
1410 }
1411 
1412 #define EX_QSORT_CUTOFF 12
1413 
1414 /*!
1415   \internal
1416   \undoc
1417 */
ex_int_median3(int v[],int iv[],int64_t left,int64_t right)1418 static int ex_int_median3(int v[], int iv[], int64_t left, int64_t right)
1419 {
1420   /* Thread-safe, reentrant */
1421   int64_t center;
1422   center = (left + right) / 2;
1423 
1424   if (v[iv[left]] > v[iv[center]]) {
1425     ex_swap(iv, left, center);
1426   }
1427   if (v[iv[left]] > v[iv[right]]) {
1428     ex_swap(iv, left, right);
1429   }
1430   if (v[iv[center]] > v[iv[right]]) {
1431     ex_swap(iv, center, right);
1432   }
1433 
1434   ex_swap(iv, center, right - 1);
1435   return iv[right - 1];
1436 }
1437 
1438 /*!
1439   \internal
1440   \undoc
1441 */
ex_int_median3_64(int64_t v[],int64_t iv[],int64_t left,int64_t right)1442 static int64_t ex_int_median3_64(int64_t v[], int64_t iv[], int64_t left, int64_t right)
1443 {
1444   /* Thread-safe, reentrant */
1445   int64_t center;
1446   center = (left + right) / 2;
1447 
1448   if (v[iv[left]] > v[iv[center]]) {
1449     ex_swap64(iv, left, center);
1450   }
1451   if (v[iv[left]] > v[iv[right]]) {
1452     ex_swap64(iv, left, right);
1453   }
1454   if (v[iv[center]] > v[iv[right]]) {
1455     ex_swap64(iv, center, right);
1456   }
1457 
1458   ex_swap64(iv, center, right - 1);
1459   return iv[right - 1];
1460 }
1461 
1462 /*!
1463   \internal
1464   \undoc
1465 */
ex_int_iqsort(int v[],int iv[],int left,int right)1466 static void ex_int_iqsort(int v[], int iv[], int left, int right)
1467 {
1468   /* Thread-safe, reentrant */
1469   int pivot;
1470   int i, j;
1471 
1472   if (left + EX_QSORT_CUTOFF <= right) {
1473     pivot = ex_int_median3(v, iv, left, right);
1474     i     = left;
1475     j     = right - 1;
1476 
1477     for (;;) {
1478       while (v[iv[++i]] < v[pivot]) {
1479         ;
1480       }
1481       while (v[iv[--j]] > v[pivot]) {
1482         ;
1483       }
1484       if (i < j) {
1485         ex_swap(iv, i, j);
1486       }
1487       else {
1488         break;
1489       }
1490     }
1491 
1492     ex_swap(iv, i, right - 1);
1493     ex_int_iqsort(v, iv, left, i - 1);
1494     ex_int_iqsort(v, iv, i + 1, right);
1495   }
1496 }
1497 
1498 /*!
1499   \internal
1500   \undoc
1501 */
ex_int_iqsort64(int64_t v[],int64_t iv[],int64_t left,int64_t right)1502 static void ex_int_iqsort64(int64_t v[], int64_t iv[], int64_t left, int64_t right)
1503 {
1504   /* Thread-safe, reentrant */
1505   int64_t pivot;
1506   int64_t i, j;
1507 
1508   if (left + EX_QSORT_CUTOFF <= right) {
1509     pivot = ex_int_median3_64(v, iv, left, right);
1510     i     = left;
1511     j     = right - 1;
1512 
1513     for (;;) {
1514       while (v[iv[++i]] < v[pivot]) {
1515         ;
1516       }
1517       while (v[iv[--j]] > v[pivot]) {
1518         ;
1519       }
1520       if (i < j) {
1521         ex_swap64(iv, i, j);
1522       }
1523       else {
1524         break;
1525       }
1526     }
1527 
1528     ex_swap64(iv, i, right - 1);
1529     ex_int_iqsort64(v, iv, left, i - 1);
1530     ex_int_iqsort64(v, iv, i + 1, right);
1531   }
1532 }
1533 
1534 /*!
1535   \internal
1536   \undoc
1537 */
ex_int_iisort(int v[],int iv[],int N)1538 static void ex_int_iisort(int v[], int iv[], int N)
1539 {
1540   /* Thread-safe, reentrant */
1541   int i, j;
1542   int ndx = 0;
1543   int small;
1544   int tmp;
1545 
1546   small = v[iv[0]];
1547   for (i = 1; i < N; i++) {
1548     if (v[iv[i]] < small) {
1549       small = v[iv[i]];
1550       ndx   = i;
1551     }
1552   }
1553   /* Put smallest value in slot 0 */
1554   ex_swap(iv, 0, ndx);
1555 
1556   for (i = 1; i < N; i++) {
1557     tmp = iv[i];
1558     for (j = i; v[tmp] < v[iv[j - 1]]; j--) {
1559       iv[j] = iv[j - 1];
1560     }
1561     iv[j] = tmp;
1562   }
1563 }
1564 
1565 /*!
1566   \internal
1567   \undoc
1568 */
ex_int_iisort64(int64_t v[],int64_t iv[],int64_t N)1569 static void ex_int_iisort64(int64_t v[], int64_t iv[], int64_t N)
1570 {
1571   /* Thread-safe, reentrant */
1572   int64_t i, j;
1573   int64_t ndx = 0;
1574   int64_t small;
1575   int64_t tmp;
1576 
1577   small = v[iv[0]];
1578   for (i = 1; i < N; i++) {
1579     if (v[iv[i]] < small) {
1580       small = v[iv[i]];
1581       ndx   = i;
1582     }
1583   }
1584   /* Put smallest value in slot 0 */
1585   ex_swap64(iv, 0, ndx);
1586 
1587   for (i = 1; i < N; i++) {
1588     tmp = iv[i];
1589     for (j = i; v[tmp] < v[iv[j - 1]]; j--) {
1590       iv[j] = iv[j - 1];
1591     }
1592     iv[j] = tmp;
1593   }
1594 }
1595 
1596 /*!
1597  * \ingroup Utilities
1598  * \internal
1599  * The following 'indexed qsort' routine is modified from Sedgewicks
1600  * algorithm It selects the pivot based on the median of the left,
1601  * right, and center values to try to avoid degenerate cases ocurring
1602  * when a single value is chosen.  It performs a quicksort on
1603  * intervals down to the #EX_QSORT_CUTOFF size and then performs a final
1604  * insertion sort on the almost sorted final array.  Based on data in
1605  * Sedgewick, the #EX_QSORT_CUTOFF value should be between 5 and 20.
1606  *
1607  * See Sedgewick for further details
1608  * Define `DEBUG_QSORT` at the top of this file and recompile to compile
1609  * in code that verifies that the array is sorted.
1610  *
1611  * NOTE: The 'int' implementation below assumes that *both* the items
1612  *       being sorted and the *number* of items being sorted are both
1613  *       representable as 'int'.
1614  * \internal
1615  */
ex__iqsort(int v[],int iv[],int N)1616 void ex__iqsort(int v[], int iv[], int N)
1617 {
1618   /* Thread-safe, reentrant */
1619   ex_int_iqsort(v, iv, 0, N - 1);
1620   ex_int_iisort(v, iv, N);
1621 
1622 #if defined(DEBUG_QSORT)
1623   fprintf(stderr, "Checking sort of %d values\n", N + 1);
1624   int i;
1625   for (i = 1; i < N; i++) {
1626     assert(v[iv[i - 1]] <= v[iv[i]]);
1627   }
1628 #endif
1629 }
1630 
1631 /*! \sa ex__iqsort() */
ex__iqsort64(int64_t v[],int64_t iv[],int64_t N)1632 void ex__iqsort64(int64_t v[], int64_t iv[], int64_t N)
1633 {
1634   /* Thread-safe, reentrant */
1635   ex_int_iqsort64(v, iv, 0, N - 1);
1636   ex_int_iisort64(v, iv, N);
1637 
1638 #if defined(DEBUG_QSORT)
1639   fprintf(stderr, "Checking sort of %" PRId64 " values\n", N + 1);
1640   int i;
1641   for (i = 1; i < N; i++) {
1642     assert(v[iv[i - 1]] <= v[iv[i]]);
1643   }
1644 #endif
1645 }
1646 
1647 /*!
1648  * Determine whether the new large model storage is being used in this
1649  * file, or old method. Basically, the difference is whether the
1650  * coordinates and nodal variables are stored in a blob (xyz
1651  * components together) or as a variable per component per
1652  * nodal_variable.
1653  *
1654  * \ingroup Utilities
1655  */
ex_large_model(int exoid)1656 int ex_large_model(int exoid)
1657 {
1658   if (exoid < 0) {
1659     return (EXODUS_DEFAULT_SIZE); /* Specified in exodusII_int.h */
1660   }
1661 
1662   /* See if the ATT_FILESIZE attribute is defined in the file */
1663   EX_FUNC_ENTER();
1664   int file_size = 0;
1665   int rootid    = exoid & EX_FILE_ID_MASK;
1666   if (nc_get_att_int(rootid, NC_GLOBAL, ATT_FILESIZE, &file_size) != NC_NOERR) {
1667     /* Variable not found; default is 0 */
1668     file_size = 0;
1669   }
1670   EX_FUNC_LEAVE(file_size);
1671 }
1672 
1673 /*!
1674   \internal
1675   \undoc
1676 */
ex__get_dimension(int exoid,const char * DIMENSION,const char * label,size_t * count,int * dimid,const char * routine)1677 int ex__get_dimension(int exoid, const char *DIMENSION, const char *label, size_t *count,
1678                       int *dimid, const char *routine)
1679 {
1680   char errmsg[MAX_ERR_LENGTH];
1681   int  status;
1682 
1683   *count = 0;
1684   *dimid = -1;
1685 
1686   if ((status = nc_inq_dimid(exoid, DIMENSION, dimid)) != NC_NOERR) {
1687     if (routine != NULL) {
1688       if (status == NC_EBADDIM) {
1689         snprintf(errmsg, MAX_ERR_LENGTH, "Warning: no dimension defining '%s' found in file id %d",
1690                  label, exoid);
1691         ex_err_fn(exoid, __func__, errmsg, status);
1692       }
1693       else {
1694         snprintf(errmsg, MAX_ERR_LENGTH,
1695                  "ERROR: failed to locate dimension defining number of '%s' in file id %d", label,
1696                  exoid);
1697         ex_err_fn(exoid, __func__, errmsg, status);
1698       }
1699     }
1700     return status;
1701   }
1702 
1703   if ((status = nc_inq_dimlen(exoid, *dimid, count)) != NC_NOERR) {
1704     if (routine != NULL) {
1705       snprintf(errmsg, MAX_ERR_LENGTH,
1706                "ERROR: failed to get length of dimension defining number of '%s' in file id %d",
1707                label, exoid);
1708       ex_err_fn(exoid, __func__, errmsg, status);
1709     }
1710     return status;
1711   }
1712   return status;
1713 }
1714 
1715 /*!
1716   \deprecated
1717 */
ex_header_size(int exoid)1718 size_t ex_header_size(int exoid)
1719 {
1720   EX_UNUSED(exoid);
1721   return 0;
1722 }
1723 
ex__set_compact_storage(int exoid,int varid)1724 void ex__set_compact_storage(int exoid, int varid)
1725 {
1726   /* Capability was released in version 4.7.4
1727      Only applicable to netcdf-4 files, but will
1728      succeed on other files; just won't do anything
1729   */
1730 #if defined(NC_COMPACT)
1731   nc_def_var_chunking(exoid, varid, NC_COMPACT, NULL);
1732 #else
1733   EX_UNUSED(exoid);
1734   EX_UNUSED(varid);
1735 #endif
1736 }
1737 
1738 /* type = 1 for integer, 2 for real, 3 for character */
1739 /*!
1740   \internal
1741   \undoc
1742 */
ex__compress_variable(int exoid,int varid,int type)1743 void ex__compress_variable(int exoid, int varid, int type)
1744 {
1745 #if NC_HAS_HDF5
1746   struct ex__file_item *file = ex__find_file_item(exoid);
1747 
1748   if (!file) {
1749     char errmsg[MAX_ERR_LENGTH];
1750     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: unknown file id %d for ex__compress_variable().",
1751              exoid);
1752     ex_err_fn(exoid, __func__, errmsg, EX_BADFILEID);
1753   }
1754   else {
1755     /* Compression only supported on HDF5 (NetCDF-4) files; Do not try to compress character data */
1756     if (type != 3 && file->is_hdf5) {
1757       if (file->compression_algorithm == EX_COMPRESS_GZIP) {
1758         int deflate_level = file->compression_level;
1759         int compress      = 1;
1760         int shuffle       = file->shuffle;
1761         if (deflate_level > 0) {
1762           nc_def_var_deflate(exoid, varid, shuffle, compress, deflate_level);
1763         }
1764       }
1765       else if (file->compression_algorithm == EX_COMPRESS_SZIP) {
1766 #if NC_HAS_SZIP_WRITE == 1
1767         /* See: https://support.hdfgroup.org/doc_resource/SZIP/ and
1768                 https://support.hdfgroup.org/HDF5/doc/RM/RM_H5P.html#Property-SetSzip
1769            for details on SZIP library and parameters.
1770         */
1771 
1772         /* const int NC_SZIP_EC = 4; */ /* Selects entropy coding method for szip. */
1773         const int NC_SZIP_NN = 32;      /* Selects nearest neighbor coding method for szip. */
1774         /* Even and between 4 and 32; typical values are 8, 10, 16, 32 */
1775         const int SZIP_PIXELS_PER_BLOCK =
1776             file->compression_level == 0 ? 32 : file->compression_level;
1777         nc_def_var_szip(exoid, varid, NC_SZIP_NN, SZIP_PIXELS_PER_BLOCK);
1778 #else
1779         char errmsg[MAX_ERR_LENGTH];
1780         snprintf(errmsg, MAX_ERR_LENGTH,
1781                  "ERROR: Compression algorithm SZIP is not supported yet (EXPERIMENTAL).");
1782         ex_err_fn(exoid, __func__, errmsg, EX_BADPARAM);
1783 #endif
1784       }
1785 
1786 #if defined(PARALLEL_AWARE_EXODUS)
1787       if (file->is_parallel && file->is_hdf5) {
1788         nc_var_par_access(exoid, varid, NC_COLLECTIVE);
1789       }
1790 #endif
1791     }
1792   }
1793 #endif
1794 }
1795 
1796 /*!
1797   \internal
1798   \undoc
1799 */
ex__leavedef(int exoid,const char * call_rout)1800 int ex__leavedef(int exoid, const char *call_rout)
1801 {
1802   char errmsg[MAX_ERR_LENGTH];
1803   int  status;
1804 
1805   if ((status = nc_enddef(exoid)) != NC_NOERR) {
1806     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to complete definition for file id %d", exoid);
1807     ex_err_fn(exoid, call_rout, errmsg, status);
1808 
1809     return (EX_FATAL);
1810   }
1811   return (EX_NOERR);
1812 }
1813 
1814 static int warning_output = 0;
1815 
ex__check_version(int run_version)1816 int ex__check_version(int run_version)
1817 {
1818   if (run_version != EX_API_VERS_NODOT && warning_output == 0) {
1819     int run_version_major = run_version / 100;
1820     int run_version_minor = run_version % 100;
1821     int lib_version_major = EXODUS_VERSION_MAJOR;
1822     int lib_version_minor = EXODUS_VERSION_MINOR;
1823     fprintf(stderr,
1824             "EXODUS: Warning: This code was compiled with exodus "
1825             "version %d.%02d,\n          but was linked with exodus "
1826             "library version %d.%02d\n          This is probably an "
1827             "error in the build process of this code.\n",
1828             run_version_major, run_version_minor, lib_version_major, lib_version_minor);
1829     warning_output = 1;
1830   }
1831   return warning_output;
1832 }
1833 
1834 /*!
1835   \internal
1836   \undoc
1837 */
ex__handle_mode(unsigned int my_mode,int is_parallel,int run_version)1838 int ex__handle_mode(unsigned int my_mode, int is_parallel, int run_version)
1839 {
1840   char       errmsg[MAX_ERR_LENGTH];
1841   int        nc_mode      = 0;
1842   static int netcdf4_mode = -1;
1843 #if NC_HAS_CDF5
1844   static int netcdf5_mode = -1;
1845 #endif
1846 
1847   int filesiz = 1;
1848   int int64_status;
1849   int pariomode = 0;
1850 
1851   /* Contains a 1 in all bits corresponding to file modes */
1852   /* Do not include EX_64BIT_DATA in this list */
1853   static unsigned int all_modes = EX_NORMAL_MODEL | EX_64BIT_OFFSET | EX_NETCDF4 | EX_PNETCDF;
1854 
1855   ex__check_version(run_version);
1856 
1857 /*
1858  * See if specified mode is supported in the version of netcdf we
1859  * are using
1860  */
1861 #if !NC_HAS_HDF5
1862   if (my_mode & EX_NETCDF4) {
1863     snprintf(errmsg, MAX_ERR_LENGTH,
1864              "EXODUS: ERROR: File format specified as netcdf-4, but the "
1865              "NetCDF library being used was not configured to enable "
1866              "this format\n");
1867     ex_err(__func__, errmsg, EX_BADPARAM);
1868     EX_FUNC_LEAVE(EX_FATAL);
1869   }
1870 #endif
1871 
1872 #if !NC_HAS_CDF5
1873   if (my_mode & EX_64BIT_DATA) {
1874     snprintf(errmsg, MAX_ERR_LENGTH,
1875              "EXODUS: ERROR: File format specified as 64bit_data, but "
1876              "the NetCDF library being used does not support this "
1877              "format\n");
1878     ex_err(__func__, errmsg, EX_BADPARAM);
1879     EX_FUNC_LEAVE(EX_FATAL);
1880   }
1881 #endif
1882 
1883   /* EX_64_BIT_DATA is 64-bit integer version of EX_PNETCDF.  If
1884      EX_64_BIT_DATA and EX_PNETCDF is not set, then set EX_PNETCDF... */
1885   if (my_mode & EX_64BIT_DATA) {
1886     my_mode |= EX_PNETCDF;
1887   }
1888 
1889   /* Check that one and only one format mode is specified... */
1890   {
1891     unsigned int set_modes = all_modes & my_mode;
1892 
1893     if (set_modes == 0) {
1894       my_mode |= EX_64BIT_OFFSET; /* Default if nothing specified */
1895     }
1896     else {
1897       /* Checks that only a single bit is set */
1898       set_modes = !(set_modes & (set_modes - 1));
1899       if (!set_modes) {
1900         snprintf(errmsg, MAX_ERR_LENGTH,
1901                  "EXODUS: ERROR: More than 1 file format "
1902                  "(EX_NORMAL_MODEL, EX_LARGE_MODEL, EX_64BIT_OFFSET, "
1903                  "or EX_NETCDF4)\nwas specified in the "
1904                  "mode argument of the ex_create call. Only a single "
1905                  "format can be specified.\n");
1906         ex_err(__func__, errmsg, EX_BADPARAM);
1907         EX_FUNC_LEAVE(EX_FATAL);
1908       }
1909     }
1910   }
1911 
1912   /*
1913    * See if any integer data is to be stored as int64 (long long). If
1914    * so, then need to set NC_NETCDF4 and unset NC_CLASSIC_MODEL (or
1915    * set EX_NOCLASSIC.  Output meaningful error message if the library
1916    * is not NetCDF-4 enabled...
1917    *
1918    * As of netcdf-4.4.0, can also use NC_64BIT_DATA (CDF5) mode for this...
1919    */
1920   int64_status = my_mode & (EX_ALL_INT64_DB | EX_ALL_INT64_API);
1921 
1922   if ((int64_status & EX_ALL_INT64_DB) != 0) {
1923 #if NC_HAS_HDF5 || NC_HAS_CDF5
1924     /* Library DOES support netcdf4 and/or cdf5 ... See if user
1925      * specified either of these and use that one; if not, pick
1926      * netcdf4, non-classic as default.
1927      */
1928     if (my_mode & EX_NETCDF4) {
1929       my_mode |= EX_NOCLASSIC;
1930     }
1931 #if NC_HAS_CDF5
1932     else if (my_mode & EX_64BIT_DATA) {
1933       ; /* Do nothing, already set */
1934     }
1935     else if (my_mode & EX_PNETCDF) {
1936       my_mode |= EX_64BIT_DATA;
1937     }
1938 #endif
1939     else {
1940       /* Unset the current mode so we don't have multiples specified */
1941       /* ~all_modes sets to 1 all bits not associated with file format */
1942       my_mode &= ~all_modes;
1943 #if NC_HAS_HDF5
1944       /* Pick netcdf4 as default mode for 64-bit integers */
1945       my_mode |= EX_NOCLASSIC;
1946       my_mode |= EX_NETCDF4;
1947 #else
1948       /* Pick 64bit_data as default mode for 64-bit integers */
1949       my_mode |= EX_64BIT_DATA;
1950 #endif
1951     }
1952 #else
1953     /* Library does NOT support netcdf4 or cdf5 */
1954     snprintf(errmsg, MAX_ERR_LENGTH,
1955              "EXODUS: ERROR: 64-bit integer storage requested, but the "
1956              "netcdf library does not support the required netcdf-4 or "
1957              "64BIT_DATA extensions.\n");
1958     ex_err(__func__, errmsg, EX_BADPARAM);
1959     EX_FUNC_LEAVE(EX_FATAL);
1960 #endif
1961   }
1962 
1963 #if defined(PARALLEL_AWARE_EXODUS)
1964   /* Check parallel io mode.  Valid is NC_MPIPOSIX or NC_MPIIO or NC_PNETCDF
1965    * Exodus uses different flag values; map to netcdf values
1966    *
1967    * NOTE: In current versions of NetCDF, MPIPOSIX and MPIIO are ignored and the
1968    *       underlying format is either NC_PNETCDF or NC_NETCDF4 (hdf5-based)
1969    *       They map NC_MPIIO to NC_PNETCDF, but in the past, exodus mapped EX_MPIIO
1970    *       to EX_NETCDF4.
1971    */
1972   if (is_parallel) {
1973     int tmp_mode = 0;
1974     if (my_mode & EX_MPIPOSIX) {
1975       pariomode = NC_MPIIO;
1976       tmp_mode  = EX_NETCDF4;
1977 #if !NC_HAS_HDF5
1978       snprintf(errmsg, MAX_ERR_LENGTH,
1979                "EXODUS: ERROR: EX_MPIPOSIX parallel output requested "
1980                "which requires NetCDF-4 support, but the library does "
1981                "not have that option enabled.\n");
1982       ex_err(__func__, errmsg, EX_BADPARAM);
1983       EX_FUNC_LEAVE(EX_FATAL);
1984 #endif
1985     }
1986     else if (my_mode & EX_MPIIO) {
1987       pariomode = NC_MPIIO;
1988       tmp_mode  = EX_NETCDF4;
1989 #if !NC_HAS_HDF5
1990       snprintf(errmsg, MAX_ERR_LENGTH,
1991                "EXODUS: ERROR: EX_MPIIO parallel output requested which "
1992                "requires NetCDF-4 support, but the library does not "
1993                "have that option enabled.\n");
1994       ex_err(__func__, errmsg, EX_BADPARAM);
1995       EX_FUNC_LEAVE(EX_FATAL);
1996 #endif
1997     }
1998     else if (my_mode & EX_NETCDF4) {
1999       pariomode = NC_MPIIO;
2000       tmp_mode  = EX_NETCDF4;
2001 #if !NC_HAS_HDF5
2002       snprintf(errmsg, MAX_ERR_LENGTH,
2003                "EXODUS: ERROR: EX_NETCDF4 parallel output requested which "
2004                "requires NetCDF-4 support, but the library does not "
2005                "have that option enabled.\n");
2006       ex_err(__func__, errmsg, EX_BADPARAM);
2007       EX_FUNC_LEAVE(EX_FATAL);
2008 #endif
2009     }
2010     else if (my_mode & EX_PNETCDF) {
2011       pariomode = NC_PNETCDF;
2012       /* See if client specified 64-bit or not... */
2013       if ((my_mode & EX_64BIT_DATA) || (int64_status & EX_ALL_INT64_DB)) {
2014         tmp_mode = EX_64BIT_DATA;
2015       }
2016       else {
2017         if (my_mode & EX_64BIT_DATA) {
2018           tmp_mode = EX_64BIT_DATA;
2019         }
2020         else {
2021           tmp_mode = EX_64BIT_OFFSET;
2022         }
2023       }
2024 #if !NC_HAS_PNETCDF
2025       snprintf(errmsg, MAX_ERR_LENGTH,
2026                "EXODUS: ERROR: EX_PNETCDF parallel output requested "
2027                "which requires PNetCDF support, but the library does "
2028                "not have that option enabled.\n");
2029       ex_err(__func__, errmsg, EX_BADPARAM);
2030       EX_FUNC_LEAVE(EX_FATAL);
2031 #endif
2032     }
2033 
2034     /* If tmp_mode was set here, then need to clear any other mode that
2035        was potentially already set in my_mode... */
2036     my_mode &= ~all_modes;
2037     my_mode |= tmp_mode;
2038   }
2039 #else
2040   EX_UNUSED(is_parallel);
2041 #endif /* PARALLEL_AWARE_EXODUS */
2042 
2043   if (my_mode & EX_NETCDF4) {
2044     nc_mode |= NC_NETCDF4;
2045   }
2046   else {
2047     if (netcdf4_mode == -1) {
2048       char *option = getenv("EXODUS_NETCDF4");
2049       if (option != NULL) {
2050         netcdf4_mode = NC_NETCDF4;
2051         if (option[0] != 'q') {
2052           fprintf(stderr, "EXODUS: Using netcdf version 4 selected via "
2053                           "EXODUS_NETCDF4 environment variable\n");
2054         }
2055       }
2056       else {
2057         netcdf4_mode = 0;
2058       }
2059     }
2060     nc_mode |= netcdf4_mode;
2061   }
2062 
2063   if (!(my_mode & EX_NOCLASSIC)) {
2064     nc_mode |= NC_CLASSIC_MODEL;
2065   }
2066 
2067 #if NC_HAS_CDF5
2068   if (my_mode & EX_64BIT_DATA) {
2069     nc_mode |= (NC_64BIT_DATA);
2070   }
2071   else {
2072     if (netcdf5_mode == -1) {
2073       char *option = getenv("EXODUS_NETCDF5");
2074       if (option != NULL) {
2075         netcdf5_mode = NC_64BIT_DATA;
2076         if (option[0] != 'q') {
2077           fprintf(stderr, "EXODUS: Using netcdf version 5 (CDF5) selected via "
2078                           "EXODUS_NETCDF5 environment variable\n");
2079         }
2080       }
2081       else {
2082         netcdf5_mode = 0;
2083       }
2084     }
2085     nc_mode |= netcdf5_mode;
2086   }
2087 #endif
2088 
2089   /*
2090    * Hardwire filesiz to 1 for all created files. Reduce complexity in nodal output routines.
2091    * has been default for a decade or so, but still support it on read...
2092    */
2093   if (
2094 #if NC_HAS_HDF5
2095       !(nc_mode & NC_NETCDF4) &&
2096 #endif
2097 #if NC_HAS_CDF5
2098       !(nc_mode & NC_64BIT_DATA) &&
2099 #endif
2100       filesiz == 1) {
2101     nc_mode |= NC_64BIT_OFFSET;
2102   }
2103 
2104   if (my_mode & EX_SHARE) {
2105     nc_mode |= NC_SHARE;
2106   }
2107 
2108   /*
2109    * set error handling mode to no messages, non-fatal errors
2110    * unless specified differently via environment.
2111    */
2112   {
2113     char *option = getenv("EXODUS_VERBOSE");
2114     if (option != NULL) {
2115       exoptval = EX_VERBOSE;
2116     }
2117     ex_opts(exoptval); /* call required to set ncopts first time through */
2118   }
2119 
2120   if (my_mode & EX_CLOBBER) {
2121     nc_mode |= NC_CLOBBER;
2122   }
2123   else {
2124     nc_mode |= NC_NOCLOBBER;
2125   }
2126 
2127 #if NC_HAS_DISKLESS
2128   /* Use of diskless (in-memory) and parallel is not tested... */
2129   if (my_mode & EX_DISKLESS) {
2130     nc_mode |= NC_DISKLESS;
2131     nc_mode |= NC_WRITE;
2132 #if defined NC_PERSIST
2133     nc_mode |= NC_PERSIST;
2134 #endif
2135   }
2136 #endif
2137   return nc_mode | pariomode;
2138 }
2139 
2140 /*!
2141   \internal
2142   \undoc
2143 */
ex__populate_header(int exoid,const char * path,int my_mode,int is_parallel,int * comp_ws,int * io_ws)2144 int ex__populate_header(int exoid, const char *path, int my_mode, int is_parallel, int *comp_ws,
2145                         int *io_ws)
2146 {
2147   int  status;
2148   int  old_fill;
2149   int  lio_ws;
2150   int  filesiz    = 1;
2151   bool is_hdf5    = false;
2152   bool is_pnetcdf = false;
2153 
2154   float version;
2155   char  errmsg[MAX_ERR_LENGTH];
2156   int   int64_status = my_mode & (EX_ALL_INT64_DB | EX_ALL_INT64_API);
2157 
2158   int format = 0;
2159   int mode;
2160 
2161   /* turn off automatic filling of netCDF variables */
2162   if ((status = nc_set_fill(exoid, NC_NOFILL, &old_fill)) != NC_NOERR) {
2163     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to set nofill mode in file id %d", exoid);
2164     ex_err_fn(exoid, __func__, errmsg, status);
2165     return (EX_FATAL);
2166   }
2167 
2168   /* Verify that there is not an existing file_item struct for this
2169      exoid This could happen (and has) when application calls
2170      ex_open(), but then closes file using nc_close() and then reopens
2171      file.  NetCDF will possibly reuse the exoid which results in
2172      internal corruption in exodus data structures since exodus does
2173      not know that file was closed and possibly new file opened for
2174      this exoid
2175   */
2176   if (ex__find_file_item(exoid) != NULL) {
2177     snprintf(errmsg, MAX_ERR_LENGTH,
2178              "ERROR: There is an existing file already using the file "
2179              "id %d which was also assigned to file %s.\n\tWas "
2180              "nc_close() called instead of ex_close() on an open Exodus "
2181              "file?\n",
2182              exoid, path);
2183     ex_err_fn(exoid, __func__, errmsg, EX_BADFILEID);
2184     nc_close(exoid);
2185     return (EX_FATAL);
2186   }
2187 
2188   /* initialize floating point size conversion.  since creating new file,
2189    * i/o wordsize attribute from file is zero.
2190    */
2191   /* Determine format being used for underlying NetCDF file */
2192   nc_inq_format_extended(exoid, &format, &mode);
2193 
2194   if (format & NC_FORMAT_PNETCDF) {
2195     is_pnetcdf = true;
2196     ;
2197   }
2198 
2199   if (format & NC_FORMAT_NC_HDF5) {
2200     is_hdf5 = true;
2201   }
2202 
2203   if (ex__conv_init(exoid, comp_ws, io_ws, 0, int64_status, is_parallel, is_hdf5, is_pnetcdf,
2204                     my_mode & EX_WRITE) != EX_NOERR) {
2205     snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to init conversion routines in file id %d",
2206              exoid);
2207     ex_err_fn(exoid, __func__, errmsg, EX_LASTERR);
2208     return (EX_FATAL);
2209   }
2210 
2211   /* put the EXODUS version number, and i/o floating point word size as
2212    * netcdf global attributes
2213    */
2214 
2215   /* store Exodus API version # as an attribute */
2216   {
2217     float version_major = EXODUS_VERSION_MAJOR;
2218     float version_minor = EXODUS_VERSION_MINOR;
2219     version             = version_major + version_minor / 100.0;
2220     if ((status = nc_put_att_float(exoid, NC_GLOBAL, ATT_API_VERSION, NC_FLOAT, 1, &version)) !=
2221         NC_NOERR) {
2222       snprintf(errmsg, MAX_ERR_LENGTH,
2223                "ERROR: failed to store Exodus II API version attribute in file id %d", exoid);
2224       ex_err_fn(exoid, __func__, errmsg, status);
2225       return (EX_FATAL);
2226     }
2227   }
2228 
2229   /* store Exodus file version # as an attribute */
2230   {
2231     float version_major = EXODUS_VERSION_MAJOR;
2232     float version_minor = EXODUS_VERSION_MINOR;
2233     version             = version_major + version_minor / 100.0;
2234     if ((status = nc_put_att_float(exoid, NC_GLOBAL, ATT_VERSION, NC_FLOAT, 1, &version)) !=
2235         NC_NOERR) {
2236       snprintf(errmsg, MAX_ERR_LENGTH,
2237                "ERROR: failed to store Exodus II file version attribute in file id %d", exoid);
2238       ex_err_fn(exoid, __func__, errmsg, status);
2239       return (EX_FATAL);
2240     }
2241   }
2242 
2243   /* store Exodus file float word size  as an attribute */
2244   lio_ws = (*io_ws);
2245   if ((status = nc_put_att_int(exoid, NC_GLOBAL, ATT_FLT_WORDSIZE, NC_INT, 1, &lio_ws)) !=
2246       NC_NOERR) {
2247     snprintf(errmsg, MAX_ERR_LENGTH,
2248              "ERROR: failed to store Exodus II file float word size "
2249              "attribute in file id %d",
2250              exoid);
2251     ex_err_fn(exoid, __func__, errmsg, status);
2252     return (EX_FATAL);
2253   }
2254 
2255   /* store Exodus file size (1=large, 0=normal) as an attribute */
2256   if ((status = nc_put_att_int(exoid, NC_GLOBAL, ATT_FILESIZE, NC_INT, 1, &filesiz)) != NC_NOERR) {
2257     snprintf(errmsg, MAX_ERR_LENGTH,
2258              "ERROR: failed to store Exodus II file size attribute in file id %d", exoid);
2259     ex_err_fn(exoid, __func__, errmsg, status);
2260     return (EX_FATAL);
2261   }
2262 
2263   {
2264     int max_so_far = 32;
2265     if ((status = nc_put_att_int(exoid, NC_GLOBAL, ATT_MAX_NAME_LENGTH, NC_INT, 1, &max_so_far)) !=
2266         NC_NOERR) {
2267       snprintf(errmsg, MAX_ERR_LENGTH,
2268                "ERROR: failed to add maximum_name_length attribute in file id %d", exoid);
2269       ex_err_fn(exoid, __func__, errmsg, status);
2270       return (EX_FATAL);
2271     }
2272   }
2273 
2274   {
2275     int int64_db_status = int64_status & EX_ALL_INT64_DB;
2276     if ((status = nc_put_att_int(exoid, NC_GLOBAL, ATT_INT64_STATUS, NC_INT, 1,
2277                                  &int64_db_status)) != NC_NOERR) {
2278       snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to add int64_status attribute in file id %d",
2279                exoid);
2280       ex_err_fn(exoid, __func__, errmsg, status);
2281       return (EX_FATAL);
2282     }
2283   }
2284 
2285 #if 0
2286   /* Testing to see if can eliminate some nc_enddef movement of vars/recs */
2287   if ((status = nc__enddef(exoid, 10000, 4, 10000, 4)) != NC_NOERR) {
2288 #else
2289   if ((status = nc_enddef(exoid)) != NC_NOERR) {
2290 #endif
2291   snprintf(errmsg, MAX_ERR_LENGTH, "ERROR: failed to complete definition for file id %d", exoid);
2292   ex_err_fn(exoid, __func__, errmsg, status);
2293   return (EX_FATAL);
2294 }
2295 return EX_NOERR;
2296 }
2297 
2298 /*!
2299   \internal
2300   \undoc
2301   Safer than strncpy -- guarantees null termination
2302 */
2303 char *ex_copy_string(char *dest, char const *source, size_t elements)
2304 {
2305   char *d;
2306   for (d = dest; d + 1 < dest + elements && *source; d++, source++) {
2307     *d = *source;
2308   }
2309   *d = '\0';
2310   return d;
2311 }
2312