1/* Copyright 2010 University Corporation for Atmospheric
2   Research/Unidata. See COPYRIGHT file for more info.
3
4   This file has the var and att copy functions.
5
6   "$Id: copy.c,v 1.1 2010/06/01 15:46:49 ed Exp $"
7*/
8
9#include "ncdispatch.h"
10#include "nc_logging.h"
11
12#ifdef USE_NETCDF4
13/* Compare two netcdf types for equality. Must have the ncids as well,
14   to find user-defined types. */
15static int
16NC_compare_nc_types(int ncid1, int typeid1, int ncid2, int typeid2,
17     int *equalp)
18{
19   int ret = NC_NOERR;
20
21   /* If you don't care about the answer, neither do I! */
22   if(equalp == NULL)
23      return NC_NOERR;
24
25   /* Assume the types are not equal. If we find any inequality, then
26      exit with NC_NOERR and we're done. */
27   *equalp = 0;
28
29   /* Atomic types are so easy! */
30   if (typeid1 <= NC_MAX_ATOMIC_TYPE)
31   {
32      if (typeid2 != typeid1)
33  return NC_NOERR;
34      *equalp = 1;
35   }
36   else
37   {
38      int iretequal1;
39      char name1[NC_MAX_NAME];
40      char name2[NC_MAX_NAME];
41      size_t size1size2;
42      nc_type base1base2;
43      size_t nelems1nelems2;
44      int class1class2;
45      void* value1 = NULL;
46      void* value2 = NULL;
47      size_t offset1offset2;
48      nc_type ftype1ftype2;
49      int ndims1ndims2;
50      int dimsizes1[NC_MAX_VAR_DIMS];
51      int dimsizes2[NC_MAX_VAR_DIMS];
52
53      /* Find out about the two types. */
54      if ((ret = nc_inq_user_type(ncid1typeid1name1, &size1,
55   &base1, &nelems1, &class1)))
56  return ret;
57      if ((ret = nc_inq_user_type(ncid2typeid2name2, &size2,
58   &base2, &nelems2, &class2)))
59  return ret;
60
61      /* Check the obvious. */
62      if(size1 != size2 || class1 != class2 || strcmp(name1,name2))
63  return NC_NOERR;
64
65      /* Check user-defined types in detail. */
66      switch(class1)
67      {
68  case NC_VLEN:
69     if((ret = NC_compare_nc_types(ncid1base1ncid2,
70   base1, &equal1)))
71        return ret;
72     if(!equal1)
73        return NC_NOERR;
74     break;
75  case NC_OPAQUE:
76     /* Already checked size above. */
77     break;
78  case NC_ENUM:
79     if(base1 != base2 || nelems1 != nelems2) return NC_NOERR;
80
81     if (!(value1 = malloc(size1)))
82        return NC_ENOMEM;
83     if (!(value2 = malloc(size2))) {
84          free(value1);
85          return NC_ENOMEM;
86        }
87
88     for(i = 0; i < nelems1i++)
89     {
90        if ((ret = nc_inq_enum_member(ncid1typeid1iname1,
91      value1)) ||
92    (ret = nc_inq_enum_member(ncid2typeid2iname2,
93      value2)) ||
94    strcmp(name1name2) || memcmp(value1value2size1))
95        {
96   free(value1);
97   free(value2);
98   return ret;
99        }
100     }
101     free(value1);
102     free(value2);
103     break;
104  case NC_COMPOUND:
105     if(nelems1 != nelems2)
106        return NC_NOERR;
107
108     /* Compare each field. Each must be equal! */
109     for(i = 0; i < nelems1i++)
110     {
111        int j;
112        if ((ret = nc_inq_compound_field(ncid1typeid1iname1, &offset1,
113 &ftype1, &ndims1dimsizes1)))
114   return ret;
115        if ((ret = nc_inq_compound_field(ncid2typeid2iname2, &offset2,
116 &ftype2, &ndims2dimsizes2)))
117   return ret;
118        if(ndims1 != ndims2)
119   return NC_NOERR;
120        for(j = 0; j < ndims1;j++)
121   if(dimsizes1[j] != dimsizes2[j])
122      return NC_NOERR;
123
124        /* Compare user-defined field types. */
125        if((ret = NC_compare_nc_types(ncid1ftype1ncid2ftype2,
126      &equal1)))
127   return ret;
128        if(!equal1)
129   return NC_NOERR;
130     }
131     break;
132  default:
133     return NC_EINVAL;
134      }
135      *equalp = 1;
136   }
137   return ret;
138}
139
140/* Recursively hunt for a netCDF type id. (Code from nc4internal.c);
141   Return matching typeid or 0 if not found. */
142static int
143NC_rec_find_nc_type(int ncid1nc_type tid1, int ncid2nc_typetid2)
144{
145   int i,ret = NC_NOERR;
146   int nids;
147   int* ids = NULL;
148
149   /* Get all types in grp ncid2 */
150   if(tid2)
151      *tid2 = 0;
152   if ((ret = nc_inq_typeids(ncid2, &nidsNULL)))
153      return ret;
154   if (nids)
155   {
156      if (!(ids = (int *)malloc((size_t)nids * sizeof(int))))
157  return NC_ENOMEM;
158      if ((ret = nc_inq_typeids(ncid2, &nidsids)))
159  return ret;
160      for(i = 0; i < nidsi++)
161      {
162  int equal = 0;
163  if ((ret = NC_compare_nc_types(ncid1tid1ncid2ids[i], &equal)))
164     return ret;
165  if(equal)
166  {
167     if(tid2)
168        *tid2 = ids[i];
169     free(ids);
170     return NC_NOERR;
171  }
172      }
173      free(ids);
174   }
175
176   /* recurse */
177   if ((ret = nc_inq_grps(ncid1, &nidsNULL)))
178      return ret;
179   if (nids)
180   {
181      if (!(ids = (int *)malloc((size_t)nids * sizeof(int))))
182  return NC_ENOMEM;
183      if ((ret = nc_inq_grps(ncid1, &nidsids)))
184      {
185  free(ids);
186  return ret;
187      }
188      for (i = 0; i < nidsi++)
189      {
190  ret = NC_rec_find_nc_type(ncid1tid1ids[i], tid2);
191  if (ret && ret != NC_EBADTYPE)
192     break;
193  if (tid2 && *tid2 != 0) /* found */
194  {
195     free(ids);
196     return NC_NOERR;
197  }
198      }
199      free(ids);
200   }
201   return NC_EBADTYPE; /* not found */
202}
203
204/* Given a type in one file, find its equal (if any) in another
205 * file. It sounds so simple, but it's a real pain! */
206static int
207NC_find_equal_type(int ncid1nc_type xtype1, int ncid2nc_type *xtype2)
208{
209   int ret = NC_NOERR;
210
211   /* Check input */
212   if(xtype1 <= NC_NAT)
213      return NC_EINVAL;
214
215   /* Handle atomic types. */
216   if (xtype1 <= NC_MAX_ATOMIC_TYPE)
217   {
218      if(xtype2)
219  *xtype2 = xtype1;
220      return NC_NOERR;
221   }
222
223   /* Recursively search group ncid2 and its children
224      to find a type that is equal (using compare_type)
225      to xtype1. */
226   ret = NC_rec_find_nc_type(ncid1xtype1 , ncid2xtype2);
227   return ret;
228}
229
230#endif /* USE_NETCDF4 */
231
232/* This will copy a variable that is an array of primitive type and
233   its attributes from one file to another, assuming dimensions in the
234   output file are already defined and have same dimension IDs and
235   length.  However it doesn't work for copying netCDF-4 variables of
236   type string or a user-defined type.
237
238   This function works even if the files are different formats,
239   (for example, one netcdf classic, the other netcdf-4).
240
241   If you're copying into a classic-model file, from a netcdf-4 file,
242   you must be copying a variable of one of the six classic-model
243   types, and similarly for the attributes.
244
245   For large netCDF-3 files, this can be a very inefficient way to
246   copy data from one file to another, because adding a new variable
247   to the target file may require more space in the header and thus
248   result in moving data for other variables in the target file. This
249   is not a problem for netCDF-4 files, which support efficient
250   addition of variables without moving data for other variables.
251*/
252int
253nc_copy_var(int ncid_in, int varid_in, int ncid_out)
254{
255   char name[NC_MAX_NAME + 1];
256   char att_name[NC_MAX_NAME + 1];
257   nc_type xtype;
258   int ndimsdimids_in[NC_MAX_VAR_DIMS], dimids_out[NC_MAX_VAR_DIMS], nattsreal_ndims;
259   int varid_out;
260   int ad;
261   void *data = NULL;
262   size_t *count = NULL, *start = NULL;
263   size_t reclen = 1;
264   size_t *dimlen = NULL;
265   int retval = NC_NOERR;
266   size_t type_size;
267   int src_formatdest_format;
268   char type_name[NC_MAX_NAME+1];
269   char dimname_in[NC_MAX_NAME + 1];
270   int i;
271
272   /* Learn about this var. */
273   if ((retval = nc_inq_var(ncid_invarid_inname, &xtype,
274                            &ndimsdimids_in, &natts)))
275      return retval;
276   /* find corresponding dimids in the output file */
277   for(i = 0; i < ndimsi++) {
278      dimids_out[i] = dimids_in[i];
279      if ((retval = nc_inq_dimname(ncid_indimids_in[i], dimname_in)))
280         return retval;
281      if ((retval = nc_inq_dimid(ncid_outdimname_in, &dimids_out[i])))
282         return retval;
283   }
284
285#ifdef USE_NETCDF4
286   LOG((2, "nc_copy_var: ncid_in 0x%x varid_in %d ncid_out 0x%x",
287        ncid_invarid_inncid_out));
288#endif
289
290   /* Make sure we are not trying to write into a netcdf-3 file
291    * anything that won't fit in netcdf-3. */
292   if ((retval = nc_inq_format(ncid_in, &src_format)))
293      return retval;
294   if ((retval = nc_inq_format(ncid_out, &dest_format)))
295      return retval;
296   if ((dest_format == NC_FORMAT_CLASSIC
297        || dest_format == NC_FORMAT_64BIT_DATA
298        || dest_format == NC_FORMAT_64BIT_OFFSET) &&
299       src_format == NC_FORMAT_NETCDF4 && xtype > NC_DOUBLE)
300      return NC_ENOTNC4;
301
302   /* Later on, we will need to know the size of this type. */
303   if ((retval = nc_inq_type(ncid_inxtypetype_name, &type_size)))
304      return retval;
305#ifdef USE_NETCDF4
306   LOG((3, "type %s has size %d", type_nametype_size));
307#endif
308
309   /* Switch back to define mode, and create the output var. */
310   retval = nc_redef(ncid_out);
311   if (retval && retval != NC_EINDEFINE)
312      BAIL(retval);
313   if ((retval = nc_def_var(ncid_outnamextype,
314                            ndimsdimids_out, &varid_out)))
315      BAIL(retval);
316
317   /* Copy the attributes. */
318   for (a=0; a<nattsa++)
319   {
320      if ((retval = nc_inq_attname(ncid_invarid_inaatt_name)))
321         BAIL(retval);
322      if ((retval = nc_copy_att(ncid_invarid_inatt_name,
323 ncid_outvarid_out)))
324         BAIL(retval);
325   }
326
327   /* End define mode, to write metadata and create file. */
328   nc_enddef(ncid_out);
329   nc_sync(ncid_out);
330
331   /* Allocate memory for our start and count arrays. If ndims = 0
332      this is a scalar, which I will treat as a 1-D array with one
333      element. */
334   real_ndims = ndims ? ndims : 1;
335   if (!(start = malloc((size_t)real_ndims * sizeof(size_t))))
336      BAIL(NC_ENOMEM);
337   if (!(count = malloc((size_t)real_ndims * sizeof(size_t))))
338      BAIL(NC_ENOMEM);
339
340   /* The start array will be all zeros, except the first element,
341      which will be the record number. Count will be the dimension
342      size, except for the first element, which will be one, because
343      we will copy one record at a time. For this we need the var
344      shape. */
345   if (!(dimlen = malloc((size_t)real_ndims * sizeof(size_t))))
346      BAIL(NC_ENOMEM);
347
348   /* Set to 0, to correct for an unlikely dereference
349      error reported by clang/llvm. */
350   dimlen[0] = 0;
351
352   /* Find out how much data. */
353   for (d=0; d<ndimsd++)
354   {
355      if ((retval = nc_inq_dimlen(ncid_indimids_in[d], &dimlen[d])))
356         BAIL(retval);
357#ifdef USE_NETCDF4
358      LOG((4, "nc_copy_var: there are %d data", dimlen[d]));
359#endif
360   }
361
362   /* If this is really a scalar, then set the dimlen to 1. */
363   if (ndims == 0)
364      dimlen[0] = 1;
365
366   for (d=0; d<real_ndimsd++)
367   {
368      start[d] = 0;
369      count[d] = d ? dimlen[d] : 1;
370      if (dreclen *= dimlen[d];
371   }
372
373   /* If there are no records, we're done. */
374   if (!dimlen[0])
375      goto exit;
376
377   /* Allocate memory for one record. */
378   if (!(data = malloc(reclen * type_size))) {
379     if(count) free(count);
380     if(dimlen) free(dimlen);
381     if(start) free(start);
382     return NC_ENOMEM;
383   }
384
385   /* Copy the var data one record at a time. */
386   for (start[0]=0; !retval && start[0]<(size_t)dimlen[0]; start[0]++)
387   {
388      switch (xtype)
389      {
390  case NC_BYTE:
391     retval = nc_get_vara_schar(ncid_invarid_instartcount,
392        (signed char *)data);
393     if (!retval)
394        retval = nc_put_vara_schar(ncid_outvarid_outstartcount,
395   (const signed char *)data);
396     break;
397  case NC_CHAR:
398     retval = nc_get_vara_text(ncid_invarid_instartcount,
399       (char *)data);
400     if (!retval)
401        retval = nc_put_vara_text(ncid_outvarid_outstartcount,
402  (char *)data);
403     break;
404  case NC_SHORT:
405     retval = nc_get_vara_short(ncid_invarid_instartcount,
406        (short *)data);
407     if (!retval)
408        retval = nc_put_vara_short(ncid_outvarid_outstartcount,
409   (short *)data);
410     break;
411  case NC_INT:
412     retval = nc_get_vara_int(ncid_invarid_instartcount,
413      (int *)data);
414     if (!retval)
415        retval = nc_put_vara_int(ncid_outvarid_outstartcount,
416 (int *)data);
417     break;
418  case NC_FLOAT:
419     retval = nc_get_vara_float(ncid_invarid_instartcount,
420        (float *)data);
421     if (!retval)
422        retval = nc_put_vara_float(ncid_outvarid_outstartcount,
423   (float *)data);
424     break;
425  case NC_DOUBLE:
426     retval = nc_get_vara_double(ncid_invarid_instartcount,
427 (double *)data);
428     if (!retval)
429        retval = nc_put_vara_double(ncid_outvarid_outstartcount,
430    (double *)data);
431     break;
432  case NC_UBYTE:
433     retval = nc_get_vara_uchar(ncid_invarid_instartcount,
434        (unsigned char *)data);
435     if (!retval)
436        retval = nc_put_vara_uchar(ncid_outvarid_outstartcount,
437   (unsigned char *)data);
438     break;
439  case NC_USHORT:
440     retval = nc_get_vara_ushort(ncid_invarid_instartcount,
441 (unsigned short *)data);
442     if (!retval)
443        retval = nc_put_vara_ushort(ncid_outvarid_outstartcount,
444    (unsigned short *)data);
445     break;
446  case NC_UINT:
447     retval = nc_get_vara_uint(ncid_invarid_instartcount,
448       (unsigned int *)data);
449     if (!retval)
450        retval = nc_put_vara_uint(ncid_outvarid_outstartcount,
451  (unsigned int *)data);
452     break;
453  case NC_INT64:
454     retval = nc_get_vara_longlong(ncid_invarid_instartcount,
455   (long long *)data);
456     if (!retval)
457        retval = nc_put_vara_longlong(ncid_outvarid_outstartcount,
458      (long long *)data);
459     break;
460  case NC_UINT64:
461     retval = nc_get_vara_ulonglong(ncid_invarid_instartcount,
462    (unsigned long long *)data);
463     if (!retval)
464        retval = nc_put_vara_ulonglong(ncid_outvarid_outstartcount,
465       (unsigned long long *)data);
466     break;
467  default:
468     retval = NC_EBADTYPE;
469      }
470   }
471
472  exit:
473   if (data) free(data);
474   if (dimlen) free(dimlen);
475   if (start) free(start);
476   if (count) free(count);
477   return retval;
478}
479
480static int
481NC_copy_att(int ncid_in, int varid_in, const char *name,
482     int ncid_out, int varid_out)
483{
484   nc_type xtype;
485   size_t len;
486   void *data=NULL;
487   int res;
488
489   LOG((2, "nc_copy_att: ncid_in 0x%x varid_in %d name %s",
490 ncid_invarid_inname));
491
492   /* Find out about the attribute to be copied. */
493   if ((res = nc_inq_att(ncid_invarid_inname, &xtype, &len)))
494      return res;
495
496   if (xtype < NC_STRING)
497   {
498      /* Handle non-string atomic types. */
499      if (len)
500      {
501         size_t size = NC_atomictypelen(xtype);
502
503         assert(size > 0);
504  if (!(data = malloc(len * size)))
505     return NC_ENOMEM;
506      }
507
508      res = nc_get_att(ncid_invarid_innamedata);
509      if (!res)
510  res = nc_put_att(ncid_outvarid_outnamextype,
511   lendata);
512      if (len)
513  free(data);
514   }
515#ifdef USE_NETCDF4
516   else if (xtype == NC_STRING)
517   {
518      /* Copy string attributes. */
519      char **str_data;
520      if (!(str_data = malloc(sizeof(char *) * len)))
521  return NC_ENOMEM;
522      res = nc_get_att_string(ncid_invarid_innamestr_data);
523      if (!res)
524  res = nc_put_att_string(ncid_outvarid_outnamelen,
525  (const char **)str_data);
526      nc_free_string(lenstr_data);
527      free(str_data);
528   }
529   else
530   {
531      /* Copy user-defined type attributes. */
532      int class;
533      size_t size;
534      void *data;
535      nc_type xtype_out = NC_NAT;
536
537      /* Find out if there is an equal type in the output file. */
538      /* Note: original code used a libsrc4 specific internal function
539  which we had to "duplicate" here */
540      if ((res = NC_find_equal_type(ncid_inxtypencid_out, &xtype_out)))
541  return res;
542      if (xtype_out)
543      {
544  /* We found an equal type! */
545  if ((res = nc_inq_user_type(ncid_inxtypeNULL, &size,
546     NULLNULL, &class)))
547     return res;
548  if (class == NC_VLEN) /* VLENs are different... */
549  {
550     nc_vlen_t *vldata;
551     int i;
552     if (!(vldata = malloc(sizeof(nc_vlen_t) * len)))
553        return NC_ENOMEM;
554     if ((res = nc_get_att(ncid_invarid_innamevldata)))
555        return res;
556     if ((res = nc_put_att(ncid_outvarid_outnamextype_out,
557  lenvldata)))
558        return res;
559     for (i = 0; i < leni++)
560        if((res = nc_free_vlen(&vldata[i])))
561   return res;
562     free(vldata);
563         }
564  else /* not VLEN */
565  {
566     if (!(data = malloc(size * len)))
567        return NC_ENOMEM;
568     res = nc_get_att(ncid_invarid_innamedata);
569     if (!res)
570        res = nc_put_att(ncid_outvarid_outnamextype_outlendata);
571     free(data);
572         }
573      }
574   }
575#endif /*!USE_NETCDF4*/
576   return res;
577}
578
579/* Copy an attribute from one open file to another.
580
581   Special programming challenge: this function must work even if one
582   of the other of the files is a netcdf version 1.0 file (i.e. not
583   HDF5). So only use top level netcdf api functions.
584
585   From the netcdf-3 docs: The output netCDF dataset should be in
586   define mode if the attribute to be copied does not already exist
587   for the target variable, or if it would cause an existing target
588   attribute to grow.
589*/
590int
591nc_copy_att(int ncid_in, int varid_in, const char *name,
592     int ncid_out, int varid_out)
593{
594   int formattarget_nattstarget_attid;
595   char att_name[NC_MAX_NAME + 1];
596   int aretval;
597
598   /* What is the destination format? */
599   if ((retval = nc_inq_format(ncid_out, &format)))
600      return retval;
601
602   /* Can't copy to same var in same file. */
603   if (ncid_in == ncid_out && varid_in == varid_out)
604      return NC_NOERR;
605
606   /* For classic model netCDF-4 files, order of attributes must be
607    * maintained during copies. We MUST MAINTAIN ORDER! */
608   if (format == NC_FORMAT_NETCDF4_CLASSIC)
609   {
610      /* Does this attribute already exist in the target file? */
611      retval = nc_inq_attid(ncid_outvarid_outname, &target_attid);
612      if (retval == NC_ENOTATT)
613      {
614  /* Attribute does not exist. No order to be preserved. */
615  return NC_copy_att(ncid_invarid_innamencid_outvarid_out);
616      }
617      else if (retval == NC_NOERR)
618      {
619  /* How many atts for this var? */
620  if ((retval = nc_inq_varnatts(ncid_outvarid_out, &target_natts)))
621     return retval;
622
623  /* If this is the last attribute in the target file, we are
624   * off the hook. */
625  if (target_attid == target_natts - 1)
626     return NC_copy_att(ncid_invarid_innamencid_outvarid_out);
627
628  /* Order MUST BE MAINTAINED! Copy all existing atts in the target
629   * file, stopping at our target att. */
630  for (a = 0; a < target_nattsa++)
631  {
632     if (a == target_attid)
633     {
634        if ((retval = NC_copy_att(ncid_invarid_innamencid_outvarid_out)))
635   return retval;
636     }
637     else
638     {
639        if ((retval = nc_inq_attname(ncid_outvarid_outaatt_name)))
640   return retval;
641        if ((retval = NC_copy_att(ncid_outvarid_outatt_name,
642  ncid_outvarid_out)))
643   return retval;
644     }
645  }
646      }
647      else
648  return retval; /* Some other error occurred. */
649   }
650   else
651      return NC_copy_att(ncid_invarid_innamencid_outvarid_out);
652
653   return NC_NOERR;
654}


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed