1 /* This is part of the netCDF package. Copyright 2005 University
2    Corporation for Atmospheric Research/Unidata See COPYRIGHT file for
3    conditions of use. See www.unidata.ucar.edu for more info.
4 
5    Test netcdf-4 cross platform compound type.
6 
7    $Id: tst_xplatform2.c,v 1.18 2009/10/26 18:44:59 russ Exp $
8 */
9 
10 #include <nc_tests.h>
11 #include "err_macros.h"
12 #include "netcdf.h"
13 #include <hdf5.h>
14 
15 #define FILE_NAME_1 "tst_xplatform2_1.nc"
16 #define REF_FILE_NAME_1 "ref_tst_xplatform2_1.nc"
17 #define FILE_NAME_2 "tst_xplatform2_2.nc"
18 #define REF_FILE_NAME_2 "ref_tst_xplatform2_2.nc"
19 #define FILE_NAME_3 "tst_xplatform2_3.nc"
20 #define REF_FILE_NAME_3 "ref_tst_xplatform2_3.nc"
21 #define FILE_NAME_4 "tst_xplatform2_4.nc"
22 #define REF_FILE_NAME_4 "ref_tst_xplatform2_4.nc"
23 
24 #define S1_TYPE_NAME "cmp_t"
25 #define X_NAME "x"
26 #define Y_NAME "y"
27 #define S1_NAME "s1"
28 #define S2_ATT_NAME "Runnymede"
29 #define S2_TYPE_NAME "date_1215"
30 
31 #define DIM1_LEN 5
32 #define DIM2_LEN 3
33 #define VLEN_NAME "Magna_Carta_VLEN"
34 #define VLEN_ATT_NAME "We_will_sell_to_no_man_we_will_not_deny_or_defer_to_any_man_either_Justice_or_Right"
35 #define TWO_TYPES 2
36 #define NUM_S1 4
37 
38 #define DIM3_LEN 1
39 #define DIM3_NAME "DIMENSION->The city of London shall enjoy all its ancient liberties and free customs, both by land and by water."
40 #define VAR3_NAME "VARIABLE->In future we will allow no one to levy an `aid' from his free men, except to ransom his person, to make his eldest son a knight, and (once) to marry his eldest daughter."
41 
42 #define NUM_VL 1
43 #define S3_ATT_NAME "King_John"
44 #define S3_TYPE_NAME "barons"
45 #define VL_NAME "No scutage or aid may be levied in our kingdom without its general consent"
46 #define THREE_TYPES 3
47 
48 struct s1
49 {
50    float x;
51    double y;
52 };
53 struct s2
54 {
55    struct s1 data[NUM_S1];
56 };
57 struct s3
58 {
59    nc_vlen_t data[NUM_VL];
60 };
61 
62 int
check_file_1(int ncid,nc_vlen_t * data_out)63 check_file_1(int ncid, nc_vlen_t *data_out)
64 {
65    int ntypes_in, ndims_in;
66    char name_in[NC_MAX_NAME + 1];
67    size_t size_in, nfields_in, offset_in;
68    nc_type field_type_in;
69    nc_type typeids_in[TWO_TYPES], base_nc_type_in;
70    nc_vlen_t data_in[DIM1_LEN];
71    int i, j;
72 
73    /* There should be two types. */
74    if (nc_inq_typeids(ncid, &ntypes_in, typeids_in)) ERR;
75    if (ntypes_in != 2) ERR;
76 
77    /* The compound type is first: check it out. */
78    if (nc_inq_compound(ncid, typeids_in[0], name_in, &size_in, &nfields_in)) ERR;
79    if (nfields_in != 2 || strcmp(name_in, S1_TYPE_NAME) || size_in != sizeof(struct s1)) ERR;
80    if (nc_inq_compound_field(ncid, typeids_in[0], 0, name_in, &offset_in,
81 			     &field_type_in, &ndims_in, NULL)) ERR;
82    if (strcmp(name_in, X_NAME) || offset_in != 0 || field_type_in != NC_FLOAT ||
83        ndims_in) ERR;
84    if (nc_inq_compound_field(ncid, typeids_in[0], 1, name_in, &offset_in,
85 			     &field_type_in, &ndims_in, NULL)) ERR;
86    if (strcmp(name_in, Y_NAME) || offset_in != NC_COMPOUND_OFFSET(struct s1, y) || field_type_in != NC_DOUBLE ||
87        ndims_in) ERR;
88 
89    /* How does the vlen type look? */
90    if (nc_inq_vlen(ncid, typeids_in[1], name_in, &size_in, &base_nc_type_in)) ERR;
91    if (strcmp(name_in, VLEN_NAME) || size_in != sizeof(nc_vlen_t) ||
92        base_nc_type_in != typeids_in[0]) ERR;
93 
94    /* Now read the attribute. */
95    if (nc_get_att(ncid, NC_GLOBAL, VLEN_ATT_NAME, data_in)) ERR;
96 
97    /* Did we get the correct data? */
98    for (i = 0; i < DIM1_LEN; i++)
99    {
100       if (data_in[i].len != data_out[i].len) ERR;
101       for (j = 0; j < data_in[i].len; j++)
102 	 if (((struct s1 *)data_in[i].p)->x != ((struct s1 *)data_out[i].p)->x ||
103 	     ((struct s1 *)data_in[i].p)->y != ((struct s1 *)data_out[i].p)->y) ERR_RET;
104    }
105 
106    /* Free the memory that was malloced when the VLEN was read. */
107    for (i = 0; i < DIM1_LEN; i++)
108       free(data_in[i].p);
109 
110    /* We're done! */
111    return NC_NOERR;
112 }
113 
114 int
check_file_2(int ncid,struct s2 * data_out)115 check_file_2(int ncid, struct s2 *data_out)
116 {
117    int ntypes_in, ndims_in;
118    char name_in[NC_MAX_NAME + 1];
119    size_t size_in, nfields_in, offset_in;
120    nc_type field_type_in;
121    nc_type typeids_in[TWO_TYPES];
122    struct s2 data_in[DIM2_LEN];
123    int field_dims_in[1];
124    int i, j;
125 
126    /* There should be two types. */
127    if (nc_inq_typeids(ncid, &ntypes_in, typeids_in)) ERR;
128    if (ntypes_in != TWO_TYPES) ERR;
129 
130    /* The compound type is first: check it out. */
131    if (nc_inq_compound(ncid, typeids_in[0], name_in, &size_in, &nfields_in)) ERR;
132    if (nfields_in != 2 || strcmp(name_in, S1_TYPE_NAME) || size_in != sizeof(struct s1)) ERR;
133    if (nc_inq_compound_field(ncid, typeids_in[0], 0, name_in, &offset_in,
134 			     &field_type_in, &ndims_in, NULL)) ERR;
135    if (strcmp(name_in, X_NAME) || offset_in != 0 || field_type_in != NC_FLOAT ||
136        ndims_in) ERR;
137    if (nc_inq_compound_field(ncid, typeids_in[0], 1, name_in, &offset_in,
138 			     &field_type_in, &ndims_in, NULL)) ERR;
139    if (strcmp(name_in, Y_NAME) || offset_in != NC_COMPOUND_OFFSET(struct s1, y) || field_type_in != NC_DOUBLE ||
140        ndims_in) ERR;
141 
142    /* How does the containing compound type look? */
143    if (nc_inq_compound(ncid, typeids_in[1], name_in, &size_in, &nfields_in)) ERR;
144    if (strcmp(name_in, S2_TYPE_NAME) || size_in != sizeof(struct s2) ||
145        nfields_in != 1) ERR;
146    if (nc_inq_compound_field(ncid, typeids_in[1], 0, name_in, &offset_in, &field_type_in,
147 			     &ndims_in, field_dims_in)) ERR;
148    if (strcmp(name_in, S1_NAME) || offset_in != NC_COMPOUND_OFFSET(struct s2, data) ||
149        field_type_in != typeids_in[0] || ndims_in != 1 || field_dims_in[0] != NUM_S1) ERR;
150 
151    /* Now read the attribute. */
152    if (nc_get_att(ncid, NC_GLOBAL, S2_ATT_NAME, data_in)) ERR;
153 
154    /* Did we get the correct data? */
155    for (i = 0; i < DIM2_LEN; i++)
156       for (j = 0; j < NUM_S1; j++)
157 	 if (data_out[i].data[j].x != data_in[i].data[j].x ||
158 	     data_out[i].data[j].y != data_in[i].data[j].y) ERR;
159 
160    /* We're done! */
161    return NC_NOERR;
162 }
163 
164 int
check_file_3(int ncid,struct s3 * data_out)165 check_file_3(int ncid, struct s3 *data_out)
166 {
167    int ntypes_in, ndims_in;
168    char name_in[NC_MAX_NAME + 1];
169    size_t size_in, nfields_in, offset_in;
170    nc_type field_type_in, base_nc_type_in;
171    nc_type typeids_in[THREE_TYPES];
172    struct s3 data_in[DIM3_LEN];
173    int field_dims_in[1];
174    int i, j, k;
175 
176    /* There should be three types. */
177    if (nc_inq_typeids(ncid, &ntypes_in, typeids_in)) ERR;
178    if (ntypes_in != THREE_TYPES) ERR;
179 
180    /* The s1 compound type is first: check it out. */
181    if (nc_inq_compound(ncid, typeids_in[0], name_in, &size_in, &nfields_in)) ERR;
182    if (nfields_in != 2 || strcmp(name_in, S1_TYPE_NAME) || size_in != sizeof(struct s1)) ERR;
183    if (nc_inq_compound_field(ncid, typeids_in[0], 0, name_in, &offset_in,
184 			     &field_type_in, &ndims_in, NULL)) ERR;
185    if (strcmp(name_in, X_NAME) || offset_in != 0 || field_type_in != NC_FLOAT ||
186        ndims_in) ERR;
187    if (nc_inq_compound_field(ncid, typeids_in[0], 1, name_in, &offset_in,
188 			     &field_type_in, &ndims_in, NULL)) ERR;
189    if (strcmp(name_in, Y_NAME) || offset_in != NC_COMPOUND_OFFSET(struct s1, y) || field_type_in != NC_DOUBLE ||
190        ndims_in) ERR;
191 
192    /* How does the vlen type look? */
193    if (nc_inq_vlen(ncid, typeids_in[1], name_in, &size_in, &base_nc_type_in)) ERR;
194    if (strcmp(name_in, VLEN_NAME) || size_in != sizeof(nc_vlen_t) ||
195        base_nc_type_in != typeids_in[0]) ERR;
196 
197    /* How does the containing compound type look? */
198    if (nc_inq_compound(ncid, typeids_in[2], name_in, &size_in, &nfields_in)) ERR;
199    if (strcmp(name_in, S3_TYPE_NAME) || size_in != sizeof(struct s3) ||
200        nfields_in != 1) ERR;
201    if (nc_inq_compound_field(ncid, typeids_in[2], 0, name_in, &offset_in, &field_type_in,
202 			     &ndims_in, field_dims_in)) ERR;
203    if (strcmp(name_in, VL_NAME) || offset_in != NC_COMPOUND_OFFSET(struct s3, data) ||
204        field_type_in != typeids_in[1] || ndims_in != 1 || field_dims_in[0] != NUM_VL) ERR;
205 
206    /* Now read the attribute. */
207    if (nc_get_att(ncid, NC_GLOBAL, S3_ATT_NAME, data_in)) ERR;
208 
209    /* Did we get the correct data? */
210    for (i = 0; i < DIM3_LEN; i++)
211       for (j = 0; j < NUM_VL; j++)
212       {
213 	 if (data_in[i].data[j].len != data_out[i].data[j].len) ERR;
214 	 for (k = 0; k < data_out[i].data[j].len; k++)
215 	    if (((struct s1 *)data_in[i].data[j].p)[k].x != ((struct s1 *)data_out[i].data[j].p)[k].x ||
216 		((struct s1 *)data_in[i].data[j].p)[k].y != ((struct s1 *)data_out[i].data[j].p)[k].y) ERR;
217       }
218 
219    /* Free our vlens. */
220    for (i = 0; i < DIM3_LEN; i++)
221       for (j = 0; j < NUM_VL; j++)
222 	 nc_free_vlen(&(data_in[i].data[j]));
223 
224    /* We're done! */
225    return NC_NOERR;
226 }
227 
228 int
check_file_4(int ncid,struct s3 * data_out)229 check_file_4(int ncid, struct s3 *data_out)
230 {
231    int ntypes_in, ndims_in;
232    char name_in[NC_MAX_NAME + 1];
233    size_t size_in, nfields_in, offset_in;
234    nc_type field_type_in, base_nc_type_in;
235    nc_type typeids_in[THREE_TYPES];
236    struct s3 data_in[DIM3_LEN];
237    int field_dims_in[1];
238    int i, j, k;
239 
240    /* There should be three types. */
241    if (nc_inq_typeids(ncid, &ntypes_in, typeids_in)) ERR;
242    if (ntypes_in != THREE_TYPES) ERR;
243 
244    /* The s1 compound type is first: check it out. */
245    if (nc_inq_compound(ncid, typeids_in[0], name_in, &size_in, &nfields_in)) ERR;
246    if (nfields_in != 2 || strcmp(name_in, S1_TYPE_NAME) || size_in != sizeof(struct s1)) ERR;
247    if (nc_inq_compound_field(ncid, typeids_in[0], 0, name_in, &offset_in,
248 			     &field_type_in, &ndims_in, NULL)) ERR;
249    if (strcmp(name_in, X_NAME) || offset_in != 0 || field_type_in != NC_FLOAT ||
250        ndims_in) ERR;
251    if (nc_inq_compound_field(ncid, typeids_in[0], 1, name_in, &offset_in,
252 			     &field_type_in, &ndims_in, NULL)) ERR;
253    if (strcmp(name_in, Y_NAME) || offset_in != NC_COMPOUND_OFFSET(struct s1, y) || field_type_in != NC_DOUBLE ||
254        ndims_in) ERR;
255 
256    /* How does the vlen type look? */
257    if (nc_inq_vlen(ncid, typeids_in[1], name_in, &size_in, &base_nc_type_in)) ERR;
258    if (strcmp(name_in, VLEN_NAME) || size_in != sizeof(nc_vlen_t) ||
259        base_nc_type_in != typeids_in[0]) ERR;
260 
261    /* How does the containing compound type look? */
262    if (nc_inq_compound(ncid, typeids_in[2], name_in, &size_in, &nfields_in)) ERR;
263    if (strcmp(name_in, S3_TYPE_NAME) || size_in != sizeof(struct s3) ||
264        nfields_in != 1) ERR;
265    if (nc_inq_compound_field(ncid, typeids_in[2], 0, name_in, &offset_in, &field_type_in,
266 			     &ndims_in, field_dims_in)) ERR;
267    if (strcmp(name_in, VL_NAME) || offset_in != NC_COMPOUND_OFFSET(struct s3, data) ||
268        field_type_in != typeids_in[1] || ndims_in != 1 || field_dims_in[0] != NUM_VL) ERR;
269 
270    /* Now read the variable. */
271    if (nc_get_var(ncid, 0, data_in)) ERR;
272 
273    /* Did we get the correct data? */
274    for (i = 0; i < DIM3_LEN; i++)
275       for (j = 0; j < NUM_VL; j++)
276       {
277 	 if (data_in[i].data[j].len != data_out[i].data[j].len) ERR;
278 	 for (k = 0; k < data_out[i].data[j].len; k++)
279 	    if (((struct s1 *)data_in[i].data[j].p)[k].x != ((struct s1 *)data_out[i].data[j].p)[k].x ||
280 		((struct s1 *)data_in[i].data[j].p)[k].y != ((struct s1 *)data_out[i].data[j].p)[k].y) ERR;
281       }
282 
283    /* Free our vlens. */
284    for (i = 0; i < DIM3_LEN; i++)
285       for (j = 0; j < NUM_VL; j++)
286 	 nc_free_vlen(&(data_in[i].data[j]));
287 
288    /* We're done! */
289    return NC_NOERR;
290 }
291 
292 int
main(int argc,char ** argv)293 main(int argc, char **argv)
294 {
295    int ncid;
296    int i, j, k;
297    nc_vlen_t *vlen_of_comp_out;
298    struct s2 *comp_array_of_comp_out;
299    struct s3 *comp_array_of_vlen_of_comp_out;
300 
301    printf("\nTesting nested types across platforms.\n");
302 
303    if (!(vlen_of_comp_out = calloc(sizeof(nc_vlen_t), DIM1_LEN))) ERR;
304    if (!(comp_array_of_comp_out = calloc(sizeof(struct s2), DIM2_LEN))) ERR;
305    if (!(comp_array_of_vlen_of_comp_out = calloc(sizeof(struct s3), DIM3_LEN))) ERR;
306 
307    /* Create some output data: a vlen of struct s1. */
308    for (i = 0; i < DIM1_LEN; i++)
309    {
310       vlen_of_comp_out[i].len = i + 1;
311       if (!(vlen_of_comp_out[i].p = malloc(sizeof(struct s1) * vlen_of_comp_out[i].len)))
312 	 return NC_ENOMEM;
313       for (j = 0; j < vlen_of_comp_out[i].len; j++)
314       {
315 	 ((struct s1 *)vlen_of_comp_out[i].p)[j].x = 42.42;
316 	 ((struct s1 *)vlen_of_comp_out[i].p)[j].y = 2.0;
317       }
318    }
319 
320    /* Create some output data: a struct which holds an array of
321     * struct s1. */
322    for (i = 0; i < DIM2_LEN; i++)
323       for (j = 0; j < NUM_S1; j++)
324       {
325 	 comp_array_of_comp_out[i].data[j].x = 42.42;
326 	 comp_array_of_comp_out[i].data[j].y = 2.0;
327       }
328 
329    /* Create some output data: a struct which holds an array of
330     * vlen of struct s1. */
331    for (i = 0; i < DIM3_LEN; i++)
332       for (j = 0; j < NUM_VL; j++)
333       {
334 	 comp_array_of_vlen_of_comp_out[i].data[j].len = i + 1;
335 	 if (!(comp_array_of_vlen_of_comp_out[i].data[j].p = malloc(sizeof(struct s1) * comp_array_of_vlen_of_comp_out[i].data[j].len)))
336 	    return NC_ENOMEM;
337 	 for (k = 0; k < comp_array_of_vlen_of_comp_out[i].data[j].len; k++)
338 	 {
339 	    ((struct s1 *)comp_array_of_vlen_of_comp_out[i].data[j].p)[k].x = 42.42;
340 	    ((struct s1 *)comp_array_of_vlen_of_comp_out[i].data[j].p)[k].y = 2.0;
341 	 }
342       }
343 
344    printf("*** testing of vlen of compound type...");
345    {
346       nc_type s1_typeid, vlen_typeid;
347 
348       /* Create a netCDF-4 file. */
349       if (nc_create(FILE_NAME_1, NC_NETCDF4, &ncid)) ERR;
350 
351       /* Create a simple compound type which has different sizes on
352        * different platforms - our old friend struct s1. */
353       if (nc_def_compound(ncid, sizeof(struct s1), S1_TYPE_NAME, &s1_typeid)) ERR;
354       if (nc_insert_compound(ncid, s1_typeid, X_NAME,
355    			     NC_COMPOUND_OFFSET(struct s1, x), NC_FLOAT)) ERR;
356       if (nc_insert_compound(ncid, s1_typeid, Y_NAME,
357    			     NC_COMPOUND_OFFSET(struct s1, y), NC_DOUBLE)) ERR;
358 
359       /* Now make a new type: a vlen of our compound type. */
360       if (nc_def_vlen(ncid, VLEN_NAME, s1_typeid, &vlen_typeid)) ERR;
361 
362       /* Write the output data as an attribute. */
363       if (nc_put_att(ncid, NC_GLOBAL, VLEN_ATT_NAME, vlen_typeid,
364       		     DIM1_LEN, vlen_of_comp_out)) ERR;
365 
366       /* How does it look? */
367       if (check_file_1(ncid, vlen_of_comp_out)) ERR;
368 
369       /* We're done - wasn't that easy? */
370       if (nc_close(ncid)) ERR;
371 
372       /* Check it out. */
373       if (nc_open(FILE_NAME_1, NC_NOWRITE, &ncid)) ERR;
374       if (check_file_1(ncid, vlen_of_comp_out)) ERR;
375       if (nc_close(ncid)) ERR;
376    }
377    SUMMARIZE_ERR;
378    printf("*** testing Solaris-written vlen of compound type...");
379    {
380       char file_in[NC_MAX_NAME + 1];
381 
382       strcpy(file_in, "");
383       if (getenv("srcdir"))
384       {
385    	 strcat(file_in, getenv("srcdir"));
386    	 strcat(file_in, "/");
387       }
388       strcat(file_in, REF_FILE_NAME_1);
389 
390       /* Check out the same file, generated on buddy and included with
391        * the distribution. */
392       if (nc_open(file_in, NC_NOWRITE, &ncid)) ERR;
393       if (check_file_1(ncid, vlen_of_comp_out)) ERR;
394       if (nc_close(ncid)) ERR;
395    }
396    SUMMARIZE_ERR;
397    printf("*** testing compound type containing array of compound type...");
398    {
399       nc_type s1_typeid, s2_typeid;
400       int dimsizes[1] = {NUM_S1};
401 
402       /* Create a netCDF-4 file. */
403       if (nc_create(FILE_NAME_2, NC_NETCDF4, &ncid)) ERR;
404 
405       /* Create a simple compound type which has different sizes on
406        * different platforms - our old friend struct s1. */
407       if (nc_def_compound(ncid, sizeof(struct s1), S1_TYPE_NAME, &s1_typeid)) ERR;
408       if (nc_insert_compound(ncid, s1_typeid, X_NAME,
409    			     NC_COMPOUND_OFFSET(struct s1, x), NC_FLOAT)) ERR;
410       if (nc_insert_compound(ncid, s1_typeid, Y_NAME,
411    			     NC_COMPOUND_OFFSET(struct s1, y), NC_DOUBLE)) ERR;
412 
413       /* Now make a compound type that holds an array of the struct s1
414        * type. */
415       if (nc_def_compound(ncid, sizeof(struct s2), S2_TYPE_NAME, &s2_typeid)) ERR;
416       if (nc_insert_array_compound(ncid, s2_typeid, S1_NAME,
417    				   NC_COMPOUND_OFFSET(struct s2, data),
418    				   s1_typeid, 1, dimsizes)) ERR;
419 
420 
421       /* Write the output data as an attribute. */
422       if (nc_put_att(ncid, NC_GLOBAL, S2_ATT_NAME, s2_typeid,
423    		     DIM2_LEN, comp_array_of_comp_out)) ERR;
424 
425       /* How does it look? */
426       if (check_file_2(ncid, comp_array_of_comp_out)) ERR;
427 
428       /* We're done - wasn't that easy? */
429       if (nc_close(ncid)) ERR;
430 
431       /* Check it out. */
432       if (nc_open(FILE_NAME_2, NC_NOWRITE, &ncid)) ERR;
433       if (check_file_2(ncid, comp_array_of_comp_out)) ERR;
434       if (nc_close(ncid)) ERR;
435    }
436    SUMMARIZE_ERR;
437    printf("*** testing Solaris-written compound type containing array of compound type...");
438    {
439       char file_in[NC_MAX_NAME + 1];
440 
441       strcpy(file_in, "");
442       if (getenv("srcdir"))
443       {
444    	 strcat(file_in, getenv("srcdir"));
445    	 strcat(file_in, "/");
446       }
447       strcat(file_in, REF_FILE_NAME_2);
448 
449       /* Check out the same file, generated on buddy and included with
450        * the distribution. */
451       if (nc_open(file_in, NC_NOWRITE, &ncid)) ERR;
452       if (check_file_2(ncid, comp_array_of_comp_out)) ERR;
453       if (nc_close(ncid)) ERR;
454    }
455    SUMMARIZE_ERR;
456    printf("*** testing compound attribute containing array of vlen of compound type...");
457    {
458       nc_type vlen_typeid, s3_typeid, s1_typeid;
459       int dimsizes[1] = {NUM_VL};
460 
461       /* Create a netCDF-4 file. */
462       if (nc_create(FILE_NAME_3, NC_NETCDF4, &ncid)) ERR;
463 
464       /* Create a simple compound type which has different sizes on
465        * different platforms - our old friend struct s1. */
466       if (nc_def_compound(ncid, sizeof(struct s1), S1_TYPE_NAME, &s1_typeid)) ERR;
467       if (nc_insert_compound(ncid, s1_typeid, X_NAME,
468    			     NC_COMPOUND_OFFSET(struct s1, x), NC_FLOAT)) ERR;
469       if (nc_insert_compound(ncid, s1_typeid, Y_NAME,
470    			     NC_COMPOUND_OFFSET(struct s1, y), NC_DOUBLE)) ERR;
471 
472       /* Now make a new type: a vlen of our s1 compound type. */
473       if (nc_def_vlen(ncid, VLEN_NAME, s1_typeid, &vlen_typeid)) ERR;
474 
475       /* Now make a compound type that holds an array of the VLEN
476        * type. */
477       if (nc_def_compound(ncid, sizeof(struct s3), S3_TYPE_NAME, &s3_typeid)) ERR;
478       if (nc_insert_array_compound(ncid, s3_typeid, VL_NAME,
479    				   NC_COMPOUND_OFFSET(struct s3, data),
480    				   vlen_typeid, 1, dimsizes)) ERR;
481 
482 
483       /* Write the output data as an attribute. */
484       if (nc_put_att(ncid, NC_GLOBAL, S3_ATT_NAME, s3_typeid,
485    		     DIM3_LEN, comp_array_of_vlen_of_comp_out)) ERR;
486 
487       /* How does it look? Uncomment this line to see memory issue. */
488       /* if (check_file_3(ncid, comp_array_of_vlen_of_comp_out)) ERR; */
489 
490       /* We're done - wasn't that easy? */
491       if (nc_close(ncid)) ERR;
492 
493       /* Check it out. */
494       if (nc_open(FILE_NAME_3, NC_NOWRITE, &ncid)) ERR;
495       if (check_file_3(ncid, comp_array_of_vlen_of_comp_out)) ERR;
496       if (nc_close(ncid)) ERR;
497    }
498    SUMMARIZE_ERR;
499    printf("*** testing compound variable containing array of vlen of compound type...");
500    {
501       nc_type vlen_typeid, s3_typeid, s1_typeid;
502       int varid, dimid;
503       int dimsizes[1] = {NUM_VL};
504 
505       /* Create a netCDF-4 file. */
506       if (nc_create(FILE_NAME_4, NC_NETCDF4, &ncid)) ERR;
507 
508       /* Create a simple compound type which has different sizes on
509        * different platforms - our old friend struct s1. */
510       if (nc_def_compound(ncid, sizeof(struct s1), S1_TYPE_NAME, &s1_typeid)) ERR;
511       if (nc_insert_compound(ncid, s1_typeid, X_NAME,
512    			     NC_COMPOUND_OFFSET(struct s1, x), NC_FLOAT)) ERR;
513       if (nc_insert_compound(ncid, s1_typeid, Y_NAME,
514    			     NC_COMPOUND_OFFSET(struct s1, y), NC_DOUBLE)) ERR;
515 
516       /* Now make a new type: a vlen of our s1 compound type. */
517       if (nc_def_vlen(ncid, VLEN_NAME, s1_typeid, &vlen_typeid)) ERR;
518 
519       /* Now make a compound type that holds an array of the VLEN
520        * type. */
521       if (nc_def_compound(ncid, sizeof(struct s3), S3_TYPE_NAME, &s3_typeid)) ERR;
522       if (nc_insert_array_compound(ncid, s3_typeid, VL_NAME,
523    				   NC_COMPOUND_OFFSET(struct s3, data),
524    				   vlen_typeid, 1, dimsizes)) ERR;
525 
526       /* Create a dimension and a var of s3 type, then write the
527        * data. */
528       if (nc_def_dim(ncid, DIM3_NAME, DIM3_LEN, &dimid)) ERR;
529       if (nc_def_var(ncid, VAR3_NAME, s3_typeid, 1, &dimid, &varid)) ERR;
530       if (nc_put_var(ncid, varid, comp_array_of_vlen_of_comp_out)) ERR;
531 
532       /* How does it look? */
533       if (check_file_4(ncid, comp_array_of_vlen_of_comp_out)) ERR;
534 
535       /* We're done - wasn't that easy? */
536       if (nc_close(ncid)) ERR;
537 
538       /* Check it out. */
539       if (nc_open(FILE_NAME_4, NC_NOWRITE, &ncid)) ERR;
540       if (check_file_4(ncid, comp_array_of_vlen_of_comp_out)) ERR;
541       if (nc_close(ncid)) ERR;
542    }
543    SUMMARIZE_ERR;
544 
545    /* Free our mallocs. */
546    for (i = 0; i < DIM1_LEN; i++)
547       free(vlen_of_comp_out[i].p);
548    for (i = 0; i < DIM3_LEN; i++)
549       for (j = 0; j < NUM_VL; j++)
550 	 free(comp_array_of_vlen_of_comp_out[i].data[j].p);
551 
552    free(comp_array_of_comp_out);
553    free(comp_array_of_vlen_of_comp_out);
554    free(vlen_of_comp_out);
555 
556    /* Now run the tests formerly in tst_h_atts2.c. */
557 #define REF_FILE_NAME "tst_xplatform2_3.nc"
558 #define NUM_OBJ 3
559 
560    printf("\n*** Checking HDF5 attribute functions some more.\n");
561    printf("*** Opening tst_xplatform2_3.nc...");
562    {
563       hid_t fileid, grpid, attid;
564       hid_t file_typeid1[NUM_OBJ], native_typeid1[NUM_OBJ];
565       hid_t file_typeid2, native_typeid2;
566       hsize_t num_obj, i;
567       H5O_info_t obj_info;
568       char obj_name[NC_MAX_NAME + 1];
569 
570       /* Open one of the netCDF test files. */
571       if ((fileid = H5Fopen(REF_FILE_NAME, H5F_ACC_RDWR, H5P_DEFAULT)) < 0) ERR;
572       if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;
573 
574       /* How many objects in this group? */
575       if (H5Gget_num_objs(grpid, &num_obj) < 0) ERR;
576       if (num_obj != NUM_OBJ) ERR;
577 
578       /* For each object in the group... */
579       for (i = 0; i < num_obj; i++)
580       {
581 	 /* Get the name. */
582 	 if (H5Oget_info_by_idx(grpid, ".", H5_INDEX_CRT_ORDER, H5_ITER_INC,
583 				i, &obj_info, H5P_DEFAULT) < 0) ERR_RET;
584 	 if (H5Lget_name_by_idx(grpid, ".", H5_INDEX_NAME, H5_ITER_INC, i,
585 				obj_name, NC_MAX_NAME + 1, H5P_DEFAULT) < 0) ERR_RET;
586 	 printf(" reading type %s ", obj_name);
587 	 if (obj_info.type != H5O_TYPE_NAMED_DATATYPE) ERR_RET;
588 
589 	 /* Get the typeid. */
590 	 if ((file_typeid1[i] = H5Topen2(grpid, obj_name, H5P_DEFAULT)) < 0) ERR_RET;
591 	 if ((native_typeid1[i] = H5Tget_native_type(file_typeid1[i], H5T_DIR_DEFAULT)) < 0) ERR_RET;
592       }
593 
594       /* There is one att: open it by index. */
595       if ((attid = H5Aopen_idx(grpid, 0)) < 0) ERR;
596 
597       /* Get file and native typeids. */
598       if ((file_typeid2 = H5Aget_type(attid)) < 0) ERR;
599       if ((native_typeid2 = H5Tget_native_type(file_typeid2, H5T_DIR_DEFAULT)) < 0) ERR;
600 
601       /* Close the attribute. */
602       if (H5Aclose(attid) < 0) ERR;
603 
604       /* Close the typeids. */
605       if (H5Tclose(file_typeid2) < 0) ERR_RET;
606       if (H5Tclose(native_typeid2) < 0) ERR_RET;
607       for (i = 0; i < NUM_OBJ; i++)
608       {
609 	 if (H5Tclose(file_typeid1[i]) < 0) ERR_RET;
610 	 if (H5Tclose(native_typeid1[i]) < 0) ERR_RET;
611       }
612 
613       /* Close the group and file. */
614       if (H5Gclose(grpid) < 0 ||
615 	  H5Fclose(fileid) < 0) ERR;
616    }
617 
618    SUMMARIZE_ERR;
619    printf("*** Opening tst_xplatform2_3.nc again...");
620    {
621       hid_t fileid, grpid, attid, file_typeid, native_typeid;
622       hid_t file_typeid2, native_typeid2;
623 
624       /* Open one of the netCDF test files. */
625       if ((fileid = H5Fopen(REF_FILE_NAME, H5F_ACC_RDWR, H5P_DEFAULT)) < 0) ERR;
626       if ((grpid = H5Gopen(fileid, "/")) < 0) ERR;
627 
628       /* There is one att: open it by index. */
629       if ((attid = H5Aopen_idx(grpid, 0)) < 0) ERR;
630 
631       /* Get file and native typeids. */
632       if ((file_typeid = H5Aget_type(attid)) < 0) ERR;
633       if ((native_typeid = H5Tget_native_type(file_typeid, H5T_DIR_DEFAULT)) < 0) ERR;
634 
635       /* Now getting another copy of the native typeid will fail! WTF? */
636       if ((file_typeid2 = H5Aget_type(attid)) < 0) ERR;
637       if ((native_typeid2 = H5Tget_native_type(file_typeid, H5T_DIR_DEFAULT)) < 0) ERR;
638 
639       /* Close the attribute. */
640       if (H5Aclose(attid) < 0) ERR;
641 
642       /* Close the typeids. */
643       if (H5Tclose(file_typeid) < 0) ERR;
644       if (H5Tclose(native_typeid) < 0) ERR;
645       if (H5Tclose(file_typeid2) < 0) ERR;
646       if (H5Tclose(native_typeid2) < 0) ERR;
647 
648       /* Close the group and file. */
649       if (H5Gclose(grpid) < 0 ||
650 	  H5Fclose(fileid) < 0) ERR;
651    }
652    SUMMARIZE_ERR;
653    FINAL_RESULTS;
654 }
655