/** * @file * Copyright 2018 University Corporation for Atmospheric * Research/Unidata. See COPYRIGHT file for more info. * * This file has the var and att copy functions. * * @author Dennis Heimbigner */ #include "config.h" #include "ncdispatch.h" #include "nc_logging.h" #include "nclist.h" #ifdef USE_NETCDF4 static int searchgroup(int ncid1, int tid1, int grp, int* tid2); static int searchgrouptree(int ncid1, int tid1, int grp, int* tid2); /** * @internal Compare two netcdf types for equality. Must have the * ncids as well, to find user-defined types. * * @param ncid1 File ID. * @param typeid1 Type ID. * @param ncid2 File ID. * @param typeid2 Type ID. * @param equalp Pointer that gets 1 of the types are equal, 0 * otherwise. * * @return ::NC_NOERR No error. * @author Ed Hartnett */ static int NC_compare_nc_types(int ncid1, int typeid1, int ncid2, int typeid2, int *equalp) { int ret = NC_NOERR; /* If you don't care about the answer, neither do I! */ if(equalp == NULL) return NC_NOERR; /* Assume the types are not equal. If we find any inequality, then exit with NC_NOERR and we're done. */ *equalp = 0; /* Atomic types are so easy! */ if (typeid1 <= NC_MAX_ATOMIC_TYPE) { if (typeid2 != typeid1) return NC_NOERR; *equalp = 1; } else { int i, ret, equal1; char name1[NC_MAX_NAME]; char name2[NC_MAX_NAME]; size_t size1, size2; nc_type base1, base2; size_t nelems1, nelems2; int class1, class2; void* value1 = NULL; void* value2 = NULL; size_t offset1, offset2; nc_type ftype1, ftype2; int ndims1, ndims2; int dimsizes1[NC_MAX_VAR_DIMS]; int dimsizes2[NC_MAX_VAR_DIMS]; /* Find out about the two types. */ if ((ret = nc_inq_user_type(ncid1, typeid1, name1, &size1, &base1, &nelems1, &class1))) return ret; if ((ret = nc_inq_user_type(ncid2, typeid2, name2, &size2, &base2, &nelems2, &class2))) return ret; /* Check the obvious. */ if(size1 != size2 || class1 != class2 || strcmp(name1,name2)) return NC_NOERR; /* Check user-defined types in detail. */ switch(class1) { case NC_VLEN: if((ret = NC_compare_nc_types(ncid1, base1, ncid2, base1, &equal1))) return ret; if(!equal1) return NC_NOERR; break; case NC_OPAQUE: /* Already checked size above. */ break; case NC_ENUM: if(base1 != base2 || nelems1 != nelems2) return NC_NOERR; if (!(value1 = malloc(size1))) return NC_ENOMEM; if (!(value2 = malloc(size2))) { free(value1); return NC_ENOMEM; } for(i = 0; i < nelems1; i++) { if ((ret = nc_inq_enum_member(ncid1, typeid1, i, name1, value1)) || (ret = nc_inq_enum_member(ncid2, typeid2, i, name2, value2)) || strcmp(name1, name2) || memcmp(value1, value2, size1)) { free(value1); free(value2); return ret; } } free(value1); free(value2); break; case NC_COMPOUND: if(nelems1 != nelems2) return NC_NOERR; /* Compare each field. Each must be equal! */ for(i = 0; i < nelems1; i++) { int j; if ((ret = nc_inq_compound_field(ncid1, typeid1, i, name1, &offset1, &ftype1, &ndims1, dimsizes1))) return ret; if ((ret = nc_inq_compound_field(ncid2, typeid2, i, name2, &offset2, &ftype2, &ndims2, dimsizes2))) return ret; if(ndims1 != ndims2) return NC_NOERR; for(j = 0; j < ndims1;j++) if(dimsizes1[j] != dimsizes2[j]) return NC_NOERR; /* Compare user-defined field types. */ if((ret = NC_compare_nc_types(ncid1, ftype1, ncid2, ftype2, &equal1))) return ret; if(!equal1) return NC_NOERR; } break; default: return NC_EINVAL; } *equalp = 1; } return ret; } /** * @internal Recursively hunt for a netCDF type id, tid2, that is "equal" to tid1. * Question is: what search order do we use? Ncgen uses root group tree in pre-order. * But NC4_inq_typeid uses these rules: * 1. ncid2 * 2. parents of ncid2 (up the tree to root) * 3. root group tree in pre-order. * We will leave ncgen for another day and use the nc_inq_typeid rule. * * Return matching typeid or 0 if not found. * * @param ncid1 File ID. * @param tid1 Type ID. * @param ncid2 File ID. * @param tid2 Pointer that gets type ID of equal type. * * @return ::NC_NOERR No error. * @author Ed Hartnett, Dennis Heimbigner */ static int NC_rec_find_nc_type(int ncid1, nc_type tid1, int ncid2, nc_type* tid2) { int ret = NC_NOERR; int parent; if((ret = searchgroup(ncid1,tid1,ncid2,tid2))) goto done; if(*tid2 != 0) goto done; /* found */ /* Look in the parents of ncid2 upto the root */ switch (ret = nc_inq_grp_parent(ncid2,&parent)) { case NC_NOERR: /* Recurse up using parent grp */ ret = NC_rec_find_nc_type(ncid1, tid1, parent, tid2); break; case NC_ENOGRP: /* do the breadth-first pre-order search of the whole tree */ /* ncid2 should be root group */ ret = searchgrouptree(ncid1,tid1,ncid2,tid2); break; default: break; } done: return ret; } /** * @internal Given a type in one file, find its equal (if any) in * another file. It sounds so simple, but it's a real pain! * * @param ncid1 File ID. * @param xtype1 Type ID. * @param ncid2 File ID. * @param xtype2 Pointer that gets type ID of equal type. * * @return ::NC_NOERR No error. * @author Ed Hartnett */ static int NC_find_equal_type(int ncid1, nc_type xtype1, int ncid2, nc_type *xtype2) { int ret = NC_NOERR; /* Check input */ if(xtype1 <= NC_NAT) return NC_EINVAL; /* Handle atomic types. */ if (xtype1 <= NC_MAX_ATOMIC_TYPE) { if(xtype2) *xtype2 = xtype1; return NC_NOERR; } /* Recursively search group ncid2 and its children to find a type that is equal (using compare_type) to xtype1. */ ret = NC_rec_find_nc_type(ncid1, xtype1 , ncid2, xtype2); return ret; } #endif /* USE_NETCDF4 */ /** * This will copy a variable that is an array of primitive type and * its attributes from one file to another, assuming dimensions in the * output file are already defined and have same dimension IDs and * length. However it doesn't work for copying netCDF-4 variables of * type string or a user-defined type. * * This function works even if the files are different formats, * (for example, one netcdf classic, the other netcdf-4). * * If you're copying into a classic-model file, from a netcdf-4 file, * you must be copying a variable of one of the six classic-model * types, and similarly for the attributes. * * For large netCDF-3 files, this can be a very inefficient way to * copy data from one file to another, because adding a new variable * to the target file may require more space in the header and thus * result in moving data for other variables in the target file. This * is not a problem for netCDF-4 files, which support efficient * addition of variables without moving data for other variables. * * @param ncid_in File ID to copy from. * @param varid_in Variable ID to copy. * @param ncid_out File ID to copy to. * * @return ::NC_NOERR No error. * @author Glenn Davis, Ed Hartnett, Dennis Heimbigner */ int nc_copy_var(int ncid_in, int varid_in, int ncid_out) { char name[NC_MAX_NAME + 1]; char att_name[NC_MAX_NAME + 1]; nc_type xtype; int ndims, dimids_in[NC_MAX_VAR_DIMS], dimids_out[NC_MAX_VAR_DIMS], natts, real_ndims; int varid_out; int a, d; void *data = NULL; size_t *count = NULL, *start = NULL; size_t reclen = 1; size_t *dimlen = NULL; int retval = NC_NOERR; size_t type_size; int src_format, dest_format; char type_name[NC_MAX_NAME+1]; char dimname_in[NC_MAX_NAME + 1]; int i; /* Learn about this var. */ if ((retval = nc_inq_var(ncid_in, varid_in, name, &xtype, &ndims, dimids_in, &natts))) return retval; /* find corresponding dimids in the output file */ for(i = 0; i < ndims; i++) { dimids_out[i] = dimids_in[i]; if ((retval = nc_inq_dimname(ncid_in, dimids_in[i], dimname_in))) return retval; if ((retval = nc_inq_dimid(ncid_out, dimname_in, &dimids_out[i]))) return retval; } LOG((2, "nc_copy_var: ncid_in 0x%x varid_in %d ncid_out 0x%x", ncid_in, varid_in, ncid_out)); /* Make sure we are not trying to write into a netcdf-3 file * anything that won't fit in netcdf-3. */ if ((retval = nc_inq_format(ncid_in, &src_format))) return retval; if ((retval = nc_inq_format(ncid_out, &dest_format))) return retval; if ((dest_format == NC_FORMAT_CLASSIC || dest_format == NC_FORMAT_64BIT_DATA || dest_format == NC_FORMAT_64BIT_OFFSET) && src_format == NC_FORMAT_NETCDF4 && xtype > NC_DOUBLE) return NC_ENOTNC4; /* Later on, we will need to know the size of this type. */ if ((retval = nc_inq_type(ncid_in, xtype, type_name, &type_size))) return retval; LOG((3, "type %s has size %d", type_name, type_size)); /* Switch back to define mode, and create the output var. */ retval = nc_redef(ncid_out); if (retval && retval != NC_EINDEFINE) BAIL(retval); if ((retval = nc_def_var(ncid_out, name, xtype, ndims, dimids_out, &varid_out))) BAIL(retval); /* Copy the attributes. */ for (a=0; a 0) { id = (uintptr_t)nclistremove(queue,0); gid = (int)id; if((ret = searchgroup(ncid1,tid1,gid,tid2))) goto done; if(*tid2 != 0) goto done; /*we found it*/ /* Get subgroups of gid and push onto front of the queue (for breadth first) */ if((ret = nc_inq_grps(gid,&nids,NULL))) goto done; if (!(ids = (int *)malloc((size_t)nids * sizeof(int)))) {ret = NC_ENOMEM; goto done;} if ((ret = nc_inq_grps(gid, &nids, ids))) goto done; /* push onto the end of the queue */ for(i=0;i