1 /** \file \internal
2 Basic NC_INMEMORY API tests both for netcdf-3 and netcdf-4
3 
4 Copyright 2011, UCAR/Unidata. See COPYRIGHT file for copying and
5 redistribution conditions.
6 */
7 
8 #undef DDBG
9 
10 #include "config.h"
11 #include <stdio.h>
12 #include <stdlib.h>
13 #ifdef HAVE_UNISTD_H
14 #include <unistd.h>
15 #endif
16 
17 #include "netcdf.h"
18 #include "netcdf_mem.h"
19 #include "ncbytes.h"
20 #include "nc_tests.h"
21 #include "err_macros.h"
22 
23 #ifdef USE_NETCDF4
24 #include <hdf5.h>
25 extern int H5Eprint1(FILE * stream);
26 #endif
27 
28 #define FLAGS4 (NC_INMEMORY|NC_NETCDF4|NC_CLOBBER)
29 #define FLAGS3 (NC_INMEMORY|NCCLOBBER)
30 
31 #define NC_NETCDF3 0
32 #define MODIFIED 1
33 
34 #define LARGE_SPACE (1<<18)
35 
36 #define FILE3 "tst_inmemory3.nc"
37 #define FILE4 "tst_inmemory4.nc"
38 #define CREATE3 "tst_inmemory3_create.nc"
39 #define CREATE4 "tst_inmemory4_create.nc"
40 #define XFAIL "tst_xfail.nc"
41 #define MISC "tst_misc.nc"
42 
43 #define CREATEFILE3 "tst_memcreate3.nc"
44 #define CREATEFILE4 "tst_memcreate4.nc"
45 
46 /* Make no dimension larger than this */
47 #define MAXDIMLEN 100
48 #define NDIMS 2
49 #define UNLIM_LEN 2
50 #define DIM0_NAME "fun"
51 #define DIM1_NAME "money"
52 #define DIM1_LEN 8
53 #define ATT0_NAME "home"
54 #define ATT0_TEXT "earthship"
55 #define NVARS 3
56 #define VAR0_NAME "nightlife"
57 #define VAR1_NAME "time"
58 #define VAR2_NAME "taxi_distance"
59 #define VAR3_NAME "miles"
60 
61 #define FLOATVAL ((float)42.22)
62 
63 /*
64 CDL for created file:
65 netcdf tst_inmemory3 {
66 dimensions:
67 	fun = UNLIMITED ; // (2 currently)
68 	money = 8 ;
69 variables:
70 	int nightlife(fun, money) ;
71 	float time ;
72 	short taxi_distance(money) ;
73 
74 // global attributes:
75 		:home = "earthship" ;
76 data:
77 
78  nightlife =
79   0, 100, 200, 300, 400, 500, 600, 700,
80   800, 900, 1000, 1100, 1200, 1300, 1400, 1500 ;
81 
82  time = 42.22 ;
83 
84  taxi_distance = 0, 1, 2, 3, 4, 5, 6, 7 ;
85 }
86 */
87 
88 #ifdef DDBG
89 #undef ERR
90 static void
fail(int line)91 fail(int line) {
92     fflush(stdout);
93     fprintf(stderr,"\nline=%d\n",line);
94     fflush(stderr);
95     exit(1);
96 }
97 #define ERR fail(__LINE__)
98 #endif
99 
100 static int
check(int stat,const char * file,int line,int xfail)101 check(int stat, const char* file, int line, int xfail)
102 {
103 /*    int pass = ((!xfail && stat == NC_NOERR) || (xfail && stat != NC_NOERR)); */
104     fflush(stdout);
105     if(!xfail) {
106         if(stat != NC_NOERR) {
107             fprintf(stderr,"***Fail: line: %d; status=%d %s\n",
108 	            line,stat,nc_strerror(stat));
109         err++;
110         }
111     } else { /*xfail*/
112         if(stat == NC_NOERR) {
113             fprintf(stderr,"***XFail Fail: line: %d; passed instead of failed\n",
114 	            line);
115             err++;
116         } else {
117             fprintf(stderr,"\t***XFail: status=%d %s\n",
118 	            stat,nc_strerror(stat));
119         }
120 	stat = NC_NOERR; /* because xfail */
121     }
122     fflush(stderr);
123     return stat;
124 }
125 
126 #define CHECK(expr) {stat = check((expr),__FILE__,__LINE__,0); if(stat) return stat;}
127 #define XCHECK(expr) {stat = check((expr),__FILE__,__LINE__,1); if(stat) return stat;}
128 
129 #define REPORT(xfail,expr) {if((xfail)) {XCHECK((expr));} else {CHECK((expr));}}
130 
131 /**************************************************/
132 
133 static void
removefile(const char * path)134 removefile(const char* path)
135 {
136     unlink(path);
137 }
138 
139 static int
readfile(const char * path,NC_memio * memio)140 readfile(const char* path, NC_memio* memio)
141 {
142     int status = NC_NOERR;
143     FILE* f = NULL;
144     size_t filesize = 0;
145     size_t count = 0;
146     char* memory = NULL;
147     char* p = NULL;
148 
149     /* Open the file for reading */
150 #ifdef _MSC_VER
151     f = fopen(path,"rb");
152 #else
153     f = fopen(path,"r");
154 #endif
155     if(f == NULL)
156 	{status = errno; goto done;}
157     /* get current filesize */
158     if(fseek(f,0,SEEK_END) < 0)
159 	{status = errno; goto done;}
160     filesize = (size_t)ftell(f);
161     /* allocate memory */
162     memory = malloc((size_t)filesize);
163     if(memory == NULL)
164 	{status = NC_ENOMEM; goto done;}
165     /* move pointer back to beginning of file */
166     rewind(f);
167     count = filesize;
168     p = memory;
169     while(count > 0) {
170         size_t actual;
171         actual = fread(p,1,count,f);
172 	if(actual == 0 || ferror(f))
173 	    {status = NC_EIO; goto done;}
174 	count -= actual;
175 	p += actual;
176     }
177     if(memio) {
178 	memio->size = (size_t)filesize;
179 	memio->memory = memory;
180     }
181 done:
182     if(status != NC_NOERR && memory != NULL)
183 	free(memory);
184     if(f != NULL) fclose(f);
185     return status;
186 }
187 
188 
189 static int
writefile(const char * path,NC_memio * memio)190 writefile(const char* path, NC_memio* memio)
191 {
192     int status = NC_NOERR;
193     FILE* f = NULL;
194     size_t count = 0;
195     char* p = NULL;
196 
197     /* Open the file for writing */
198 #ifdef _MSC_VER
199     f = fopen(path,"wb");
200 #else
201     f = fopen(path,"w");
202 #endif
203     if(f == NULL)
204 	{status = errno; goto done;}
205     count = memio->size;
206     p = memio->memory;
207     while(count > 0) {
208         size_t actual;
209         actual = fwrite(p,1,count,f);
210 	if(actual == 0 || ferror(f))
211 	    {status = NC_EIO; goto done;}
212 	count -= actual;
213 	p += actual;
214     }
215 done:
216     if(f != NULL) fclose(f);
217     return status;
218 }
219 
220 
221 /* Duplicate an NC_memio instance; needed to avoid
222    attempting to use memory that might have been realloc'd
223    Allow the new memory to be larger than the src memory
224 */
225 int
duplicatememory(NC_memio * src,NC_memio * target,size_t alloc)226 duplicatememory(NC_memio* src, NC_memio* target, size_t alloc)
227 {
228     if(src == NULL || target == NULL || src->size == 0 || src->memory == NULL)
229 	return NC_EINVAL;
230     *target = *src;
231     if(alloc == 0) alloc = src->size;
232     target->memory = malloc(alloc);
233     if(target->memory == NULL)
234 	return NC_ENOMEM;
235     memcpy(target->memory,src->memory,src->size);
236     target->size = alloc;
237     return NC_NOERR;
238 }
239 
240 /*
241 Given an ncid of a created file, fill in the meta-data
242 and data as described by the above CDL. Do not close
243 the file.
244 */
245 
246 static int
define_metadata(int ncid)247 define_metadata(int ncid)
248 {
249     int stat = NC_NOERR;
250     int dimid[NDIMS], varid0, varid1, varid2;
251     short short_data[DIM1_LEN];
252     size_t start[1] = {0};
253     size_t count[1] = {DIM1_LEN};
254     int dimprod = (UNLIM_LEN*DIM1_LEN);
255     int i;
256     float float_data;
257     int nightdata[UNLIM_LEN*DIM1_LEN] ;
258 
259     /* Create data to write */
260     float_data = FLOATVAL;
261 
262     for (i = 0; i < DIM1_LEN; i++)
263         short_data[i] = i;
264 
265     for (i = 0; i < dimprod; i++)
266         nightdata[i] = (100*i);
267 
268     CHECK(nc_put_att_text(ncid, NC_GLOBAL, ATT0_NAME,
269 			sizeof(ATT0_TEXT), ATT0_TEXT));
270 
271     CHECK(nc_def_dim(ncid, DIM0_NAME, NC_UNLIMITED, &dimid[0]));
272     CHECK(nc_def_dim(ncid, DIM1_NAME, DIM1_LEN, &dimid[1]));
273 
274     CHECK(nc_def_var(ncid, VAR0_NAME, NC_INT, NDIMS, dimid, &varid0));
275     CHECK(nc_def_var(ncid, VAR1_NAME, NC_FLOAT, 0, NULL, &varid1));
276     CHECK(nc_def_var(ncid, VAR2_NAME, NC_SHORT, 1, &dimid[1], &varid2));
277 
278     CHECK(nc_enddef(ncid));
279 
280     CHECK(nc_put_vara_float(ncid, varid1, NULL, NULL, &float_data));
281     CHECK(nc_put_vara_short(ncid, varid2, start, count, short_data));
282 
283     {
284         size_t start[2] = {0,0};
285         size_t count[2] = {2,DIM1_LEN};
286         CHECK(nc_put_vara_int(ncid, varid0, start, count, nightdata));
287     }
288 
289     return stat;
290 }
291 
292 
293 /*
294 Create our reference file as a real on-disk file
295 and read it back into memory
296 */
297 
298 static int
create_reference_file(const char * filename,int mode,NC_memio * filedata)299 create_reference_file(const char* filename, int mode, NC_memio* filedata)
300 {
301     int stat = NC_NOERR;
302     int ncid;
303 
304     CHECK(nc_create(filename, mode|NC_CLOBBER, &ncid)); /* overwrite */
305     CHECK(define_metadata(ncid));
306     CHECK(nc_close(ncid));
307 
308     /* Read back the contents of the file into memory */
309     if(filedata != NULL) {
310 	memset(filedata,0,sizeof(NC_memio));
311 	CHECK(readfile(filename,filedata));
312     }
313     return stat;
314 }
315 
316 static int
modify_file(int ncid)317 modify_file(int ncid)
318 {
319     int stat = NC_NOERR;
320     size_t i;
321     int varid3;
322     int dimid[1];
323     size_t len;
324     int data[MAXDIMLEN];
325 
326     /* Get id of the unlimited dimension */
327     if((stat=nc_inq_dimid(ncid, DIM0_NAME, dimid))) goto done;
328     /* get current dim length */
329     if((stat=nc_inq_dimlen(ncid, dimid[0], &len))) goto done;
330     /* open file for new meta-data */
331     if((stat=nc_redef(ncid))) goto done;
332     /* Define a new variable */
333     if((stat=nc_def_var(ncid, VAR3_NAME, NC_INT, 1, dimid, &varid3))) goto done;
334     /* close metadata */
335     if((stat=nc_enddef(ncid))) goto done;
336     /* Write data to new variable */
337     for(i=0;i<len;i++)
338 	data[i] = i;
339     if((stat=nc_put_var_int(ncid,varid3,data))) goto done;
340 done:
341     return stat;
342 }
343 
344 /* Verify the content of a file */
345 static int
verify_file(int ncid,int modified)346 verify_file(int ncid, int modified)
347 {
348     int stat = NC_NOERR;
349     int i;
350     int dimid_in[NDIMS];
351     int dimid[NDIMS];
352     int ndims_in, nvars_in, natts_in, unlimdimid_in;
353     char name_in[NC_MAX_NAME + 1], att0_in[NC_MAX_NAME + 1];
354     nc_type type_in;
355     size_t len_in;
356     int varid[4];
357     int nightdata_in[UNLIM_LEN*DIM1_LEN] ;
358     float float_data_in;
359     int milesdata_in[MAXDIMLEN];
360     int dimprod = UNLIM_LEN * DIM1_LEN;
361 #ifdef USE_NETCDF4
362     int tmp;
363 #endif
364 
365     CHECK(nc_inq(ncid, &ndims_in, &nvars_in, &natts_in, &unlimdimid_in));
366     if (ndims_in != 2 || nvars_in != NVARS+modified || natts_in != 1 || unlimdimid_in != 0)
367 	CHECK(NC_EINVAL);
368 
369     /* Get all the dimids */
370 #ifdef USE_NETCDF4
371     tmp = 0;
372     CHECK((nc_inq_dimids(ncid,&tmp,dimid,1)));
373     if(tmp != NDIMS) CHECK(NC_EINVAL);
374 
375     /* Get all the varids */
376     tmp = 0;
377     CHECK((nc_inq_varids(ncid,&tmp,varid)));
378     if(tmp != (NVARS+modified)) CHECK(NC_EINVAL);
379 #else
380     { /* Simulate nc_inq_varids and nc_inq_dimids */
381 	int j;
382 	int dimcnt = 0;
383         int varcnt = 0;
384 	CHECK(nc_inq(ncid, &dimcnt, &varcnt, NULL, NULL));
385 	for(j=0;j<dimcnt;j++) dimid[j] = j;
386 	for(j=0;j<varcnt;j++) varid[j] = j;
387     }
388 #endif
389 
390     CHECK(nc_get_att_text(ncid, NC_GLOBAL, ATT0_NAME, att0_in));
391     att0_in[sizeof(ATT0_TEXT)] = '\0';
392     if (strcmp(att0_in, ATT0_TEXT)) CHECK(NC_EINVAL);
393 
394     /* CHECK dimensions. */
395     CHECK(nc_inq_dim(ncid, dimid[0], name_in, &len_in));
396     if (strcmp(name_in, DIM0_NAME)) CHECK(NC_EINVAL);
397     CHECK(nc_inq_dim(ncid, dimid[1], name_in, &len_in));
398     if (strcmp(name_in, DIM1_NAME) || len_in != DIM1_LEN) CHECK(NC_EINVAL);
399 
400     /* CHECK variables. */
401     CHECK(nc_inq_var(ncid, varid[0], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
402     if (strcmp(name_in, VAR0_NAME) || type_in != NC_INT || ndims_in != NDIMS ||
403     dimid_in[0] != 0 || dimid_in[1] != 1 || natts_in != 0) CHECK(NC_EINVAL);
404     CHECK(nc_inq_var(ncid, varid[1], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
405     if (strcmp(name_in, VAR1_NAME) || type_in != NC_FLOAT || ndims_in != 0 ||
406     natts_in != 0) CHECK(NC_EINVAL);
407     CHECK(nc_inq_var(ncid, varid[2], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
408     if (strcmp(name_in, VAR2_NAME) || type_in != NC_SHORT || ndims_in != 1 ||
409         dimid_in[0] != 1 || natts_in != 0) CHECK(NC_EINVAL);
410 
411     CHECK(nc_get_var_int(ncid, varid[0], nightdata_in));
412     for(i=0;i<dimprod;i++) {
413 	if(nightdata_in[i] != (100*i)) CHECK(NC_EINVAL);
414     }
415 
416     CHECK(nc_get_vara_float(ncid, varid[1], NULL, NULL, &float_data_in));
417     if (float_data_in != FLOATVAL) CHECK(NC_EINVAL);
418 
419     if(modified) {
420 	size_t unlimlen;
421 	CHECK(nc_inq_var(ncid, varid[3], name_in, &type_in, &ndims_in, dimid_in, &natts_in));
422         if (strcmp(name_in, VAR3_NAME) || type_in != NC_INT || ndims_in != 1 ||
423 	    dimid_in[0] != 0 || natts_in != 0) CHECK(NC_EINVAL);
424         CHECK(nc_inq_dimlen(ncid, dimid_in[0], &unlimlen));
425         CHECK(nc_get_var_int(ncid, varid[3], milesdata_in));
426 	for(i=0;i<unlimlen;i++) {
427 	    if(milesdata_in[i] != i) CHECK(NC_EINVAL);
428 	}
429     }
430 
431     return stat;
432 }
433 
434 void
memiofree(NC_memio * memio)435 memiofree(NC_memio* memio)
436 {
437     if(memio != NULL) {
438 	if(memio->memory != NULL)
439 	    free(memio->memory);
440 	memio->memory = NULL;
441     }
442 }
443 
444 static int
test_open(const char * path,NC_memio * filedata,int mode)445 test_open(const char* path, NC_memio* filedata, int mode)
446 {
447     int stat = NC_NOERR;
448     NC_memio duplicate;
449     NC_memio finaldata;
450     int ncid;
451     int xmode = mode; /* modified mode */
452 
453     finaldata.memory = NULL;
454     finaldata.size = 0;
455     finaldata.flags = 0;
456 
457     fprintf(stderr,"\n\t***Test open 1: nc_open_mem(): read-only\n");
458     CHECK(duplicatememory(filedata,&duplicate,0));
459     CHECK(nc_open_mem(path, xmode, duplicate.size, duplicate.memory, &ncid));
460     CHECK(verify_file(ncid,!MODIFIED));
461     CHECK(nc_close(ncid));
462     memiofree(&duplicate);
463 
464     fprintf(stderr,"\n\t***Test open 2: nc_open_memio(): read-only\n");
465     CHECK(duplicatememory(filedata,&duplicate,0));
466     duplicate.flags = NC_MEMIO_LOCKED;
467     CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
468     CHECK(verify_file(ncid,!MODIFIED));
469     CHECK(nc_close_memio(ncid,&finaldata));
470     /* Published returned finaldata  */
471     fprintf(stderr,"\tfinaldata: size=%lld memory=%p\n",(unsigned long long)finaldata.size,finaldata.memory);
472     /* Verify that finaldata is same */
473     if(finaldata.size != duplicate.size) CHECK(NC_EINVAL);
474     if(finaldata.memory != duplicate.memory) CHECK(NC_EINVAL);
475     memiofree(&finaldata);
476 
477     fprintf(stderr,"\n\t***Test open 3: nc_open_memio(): read-write, copy\n");
478     xmode |= NC_WRITE; /* allow file to be modified */
479     CHECK(duplicatememory(filedata,&duplicate,0));
480     CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
481     /* modify file */
482     CHECK(modify_file(ncid));
483     CHECK(verify_file(ncid,MODIFIED));
484     CHECK(nc_close_memio(ncid,&finaldata));
485     /* Published returned finaldata  */
486     fprintf(stderr,"\tfinaldata: size=%lld memory=%p\n",(unsigned long long)finaldata.size,finaldata.memory);
487     /* Verify that finaldata is same */
488     if(finaldata.size < filedata->size) CHECK(NC_EINVAL);
489     /* As a safeguard, the memory in duplicate should have been set to NULL*/
490     memiofree(&finaldata);
491 
492     fprintf(stderr,"\n\t***Test open 4: nc_open_memio(): read-write, locked, extra space\n");
493     /* Store the filedata in a memory chunk that leaves room for modification */
494     CHECK(duplicatememory(filedata,&duplicate,LARGE_SPACE));
495     /* Lock the duplicate memory */
496     duplicate.flags |= NC_MEMIO_LOCKED;
497     xmode |= NC_WRITE; /* allow file to be modified */
498     CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
499     /* modify file */
500     CHECK(modify_file(ncid));
501     CHECK(verify_file(ncid,MODIFIED));
502     CHECK(nc_close_memio(ncid,&finaldata));
503     /* Published returned finaldata  */
504     fprintf(stderr,"\tfinaldata: size=%lld memory=%p\n",(unsigned long long)finaldata.size,finaldata.memory);
505     /* Check returned finaldata:
506        should have same memory but
507        actual used final size should not exceed the original */
508     if(finaldata.size > duplicate.size) CHECK(NC_EINVAL);
509     if(finaldata.memory != duplicate.memory) CHECK(NC_EINVAL);
510     memiofree(&finaldata);
511     return stat;
512 }
513 
514 static int
test_create(const char * path,int mode)515 test_create(const char* path, int mode)
516 {
517     int stat = NC_NOERR;
518     NC_memio finaldata;
519     int ncid;
520     int xmode = mode;
521 
522     finaldata.memory = NULL;
523     finaldata.size = 0;
524     finaldata.flags = 0;
525 
526     fprintf(stderr,"\n\t***Test create 1: nc_create_memio(): no initialsize\n");
527     CHECK(nc_create_mem(path, xmode, 0, &ncid))
528     /* create file metadata */
529     CHECK(define_metadata(ncid));
530     CHECK(verify_file(ncid,!MODIFIED));
531     CHECK(nc_close_memio(ncid,&finaldata));
532     /* Published returned finaldata  */
533     fprintf(stderr,"\tfinaldata: size=%lld memory=%p\n",(unsigned long long)finaldata.size,finaldata.memory);
534     free(finaldata.memory);
535     fprintf(stderr,"\n\t***Test create 2: nc_create_memio(): initialsize; save file\n");
536     CHECK(nc_create_mem(path, xmode, LARGE_SPACE, &ncid))
537     /* create file metadata */
538     CHECK(define_metadata(ncid));
539     CHECK(verify_file(ncid,!MODIFIED));
540     CHECK(nc_close_memio(ncid,&finaldata));
541     /* Published returned finaldata */
542     fprintf(stderr,"\tfinaldata: size=%lld memory=%p\n",(unsigned long long)finaldata.size,finaldata.memory);
543     /* Write out the final data as a .nc file */
544     CHECK(writefile(path,&finaldata));
545     if(finaldata.memory != NULL)
546         free(finaldata.memory);
547     finaldata.memory = NULL;
548     return stat;
549 }
550 
551 static int
test_misc(const char * path,int mode,NC_memio * filedata)552 test_misc(const char* path, int mode, NC_memio* filedata)
553 {
554     int stat = NC_NOERR;
555     int ncid;
556     int xmode = mode;
557     NC_memio duplicate;
558 
559     fprintf(stderr,"\n\t***Test misc 1: use nc_close on created inmemory file\n");
560     CHECK(nc_create_mem(MISC, xmode, 0, &ncid))
561     CHECK(nc_close(ncid));
562     removefile(MISC);
563 
564     fprintf(stderr,"\n\t***Test misc 2: use nc_close on opened inmemory file\n");
565     CHECK(duplicatememory(filedata,&duplicate,0));
566     CHECK(nc_open_memio(path, xmode, &duplicate, &ncid))
567     CHECK(verify_file(ncid,!MODIFIED));
568     CHECK(nc_close(ncid));
569     /* Do not free: nc_close will have done it memiofree(&duplicate); */
570     removefile(MISC);
571 
572     return stat;
573 }
574 
575 /* Test various edge conditions to ensure they fail correctly */
576 static int
test_xfail(const char * path,int mode,NC_memio * filedata)577 test_xfail(const char* path, int mode, NC_memio* filedata)
578 {
579     int stat = NC_NOERR;
580     NC_memio duplicate = {0,NULL,0};
581     int ncid;
582     int xmode = mode; /* modified mode */
583 
584     fprintf(stderr,"\n\t***Test xfail 1: nc_open_mem(): write to read-only\n");
585     CHECK(duplicatememory(filedata,&duplicate,0));
586     CHECK(nc_open_mem(XFAIL, xmode, duplicate.size, duplicate.memory, &ncid));
587     XCHECK(nc_redef(ncid));
588     CHECK(nc_abort(ncid));
589     memiofree(&duplicate);
590 
591     fprintf(stderr,"\n\t***Test xfail 2: nc_open_memio(): modify without overallocating\n");
592     if((mode & NC_NETCDF4)) {
593         fprintf(stderr,"\tSuppressed because of HDF5 library bug\n");
594     } else {
595       /* With HDF5 1.8.20, and possibly other versions,
596          this tests causes a seg fault in the HDF5 Library.
597          So until it is fixed, just leave well enough alone */
598 	NC_memio finaldata;
599 	memset(&finaldata,0,sizeof(finaldata));
600 	CHECK(duplicatememory(filedata,&duplicate,0));
601 	duplicate.flags = NC_MEMIO_LOCKED;
602 	xmode |= NC_WRITE;
603 	CHECK(nc_open_memio(XFAIL, xmode, &duplicate, &ncid))
604 	XCHECK(modify_file(ncid));
605 	CHECK(nc_abort(ncid));
606 	memiofree(&finaldata);
607 	memiofree(&duplicate);
608     }
609 
610     return stat;
611 }
612 
613 int
main(int argc,char ** argv)614 main(int argc, char **argv)
615 {
616     int stat = NC_NOERR;
617     NC_memio filedata3;
618 #ifdef USE_NETCDF4
619     NC_memio filedata4;
620 #endif
621 
622     fprintf(stderr,"\n*** Testing the inmemory API: netcdf-3.\n");
623     CHECK(create_reference_file(FILE3,NC_NETCDF3,&filedata3)); /* netcdf-3 */
624     CHECK(test_open(FILE3,&filedata3,NC_NETCDF3));
625     CHECK(test_create(CREATE3,NC_NETCDF3));
626     CHECK(test_misc(FILE3, NC_NETCDF3, &filedata3));
627     CHECK(test_xfail(FILE3, NC_NETCDF3, &filedata3));
628     memiofree(&filedata3);
629 
630 #ifdef USE_NETCDF4
631     fprintf(stderr,"\n*** Testing the inmemory API: netcdf-4.\n");
632     CHECK(create_reference_file(FILE4,NC_NETCDF4,&filedata4));
633     CHECK(test_open(FILE4,&filedata4,NC_NETCDF4));
634     CHECK(test_create(CREATE4,NC_NETCDF4));
635     CHECK(test_misc(FILE4,NC_NETCDF4, &filedata4));
636     CHECK(test_xfail(FILE4, NC_NETCDF4, &filedata4));
637     memiofree(&filedata4);
638 #endif
639 
640     SUMMARIZE_ERR;
641 
642     FINAL_RESULTS;
643 
644     return 0;
645 }
646