1/*********************************************************************
2 *   Copyright 2010, University Corporation for Atmospheric Research
3 *   See netcdf/README file for copying and redistribution conditions.
4 *   Thanks to Philippe Poilbarbe and Antonio S. CofiƱo for
5 *   compression additions.
6 *   $Id: nccopy.c 400 2010-08-27 21:02:52Z russ $
7 *********************************************************************/
8
9#include "config.h" /* for USE_NETCDF4 macro */
10#include <stdlib.h>
11#ifdef HAVE_GETOPT_H
12#include <getopt.h>
13#endif
14#ifdef HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17#include <string.h>
18#include <netcdf.h>
19#include "nciter.h"
20#include "utils.h"
21#include "chunkspec.h"
22#include "dimmap.h"
23#include "nccomps.h"
24
25#ifdef _MSC_VER
26#include "XGetopt.h"
27#define snprintf _snprintf
28int opterr;
29int optind;
30#endif
31
32/* default bytes of memory we are willing to allocate for variable
33 * values during copy */
34#define COPY_BUFFER_SIZE (5000000)
35#define COPY_CHUNKCACHE_PREEMPTION (1.0f) /* for copying, can eject fully read chunks */
36#define SAME_AS_INPUT (-1) /* default, if kind not specified */
37#define CHUNK_THRESHOLD (8192) /* non-record variables with fewer bytes don't get chunked */
38
39#ifndef USE_NETCDF4
40#define NC_CLASSIC_MODEL 0x0100 /* Enforce classic model if netCDF-4 not available. */
41#endif
42
43/* Global variables for command-line requests */
44char *progname;        /* for error messages */
45static int option_kind = SAME_AS_INPUT;
46static int option_deflate_level = -1; /* default, compress output only if input compressed */
47static int option_shuffle_vars = NC_NOSHUFFLE; /* default, no shuffling on compression */
48static int option_fix_unlimdims = 0; /* default, preserve unlimited dimensions */
49static char* option_chunkspec = 0;   /* default, no chunk specification */
50static size_t option_copy_buffer_size = COPY_BUFFER_SIZE;
51static size_t option_chunk_cache_size = CHUNK_CACHE_SIZE; /* default from config.h */
52static size_t option_chunk_cache_nelems = CHUNK_CACHE_NELEMS; /* default from config.h */
53static int option_read_diskless = 0; /* default, don't read input into memory on open */
54static int option_write_diskless = 0; /* default, don't write output to diskless file */
55static int option_min_chunk_bytes = CHUNK_THRESHOLD; /* default, don't chunk variable if prod of
56       * chunksizes of its dimensions is smaller
57       * than this */
58static int option_nlgrps = 0;     /* Number of groups specified with -g
59      * option on command line */
60static char** option_lgrps = 0;     /* list of group names specified with -g
61      * option on command line */
62static idnode_toption_grpids = 0; /* list of grpids matching list specified with -g option */
63static bool_t option_grpstruct = false; /* if -g set, copy structure for non-selected groups */
64static int option_nlvars = 0; /* Number of variables specified with -v * option on command line */
65static char** option_lvars = 0;         /* list of variable names specified with -v
66                                         * option on command line */
67static bool_t option_varstruct = false;   /* if -v set, copy structure for non-selected vars */
68static int option_compute_chunkcaches = 0; /* default, don't try still flaky estimate of
69     * chunk cache for each variable */
70
71/* get group id in output corresponding to group igrp in input,
72 * given parent group id (or root group id) parid in output. */
73static int
74get_grpid(int igrp, int parid, int *ogrpp) {
75    int stat = NC_NOERR;
76    int ogid = parid; /* like igrp but in output file */
77#ifdef USE_NETCDF4
78    int inparid;
79
80    /* if not root group, get corresponding output groupid from group name */
81    stat = nc_inq_grp_parent(igrp, &inparid);
82    if(stat == NC_NOERR) { /* not root group */
83 char grpname[NC_MAX_NAME + 1];
84 NC_CHECK(nc_inq_grpname(igrpgrpname));
85 NC_CHECK(nc_inq_grp_ncid(paridgrpname, &ogid));
86    } else if(stat == NC_ENOGRP) { /* root group */
87 stat = NC_NOERR;
88    } else {
89 NC_CHECK(stat);
90    }
91#endif /* USE_NETCDF4 */
92    *ogrpp = ogid;
93    return stat;
94}
95
96/* Return size in bytes of a variable value */
97static size_t
98val_size(int grpid, int varid) {
99    nc_type vartype;
100    size_t value_size;
101    NC_CHECK(nc_inq_vartype(grpidvarid, &vartype));
102    NC_CHECK(nc_inq_type(grpidvartypeNULL, &value_size));
103    return value_size;
104}
105
106#ifdef USE_NETCDF4
107/* Get parent id needed to define a new group from its full name in an
108 * open file identified by ncid.  Assumes all intermediate groups are
109 * already defined.  */
110static int
111nc_inq_parid(int ncid, const char *fullname, int *locidp) {
112    char *parent = strdup(fullname);
113    char *slash = "/"; /* groupname separator */
114    char *last_slash;
115    if(parent == NULL) {
116 return NC_ENOMEM; /* exits */
117    }
118    last_slash = strrchr(parent, '/');
119    if(last_slash == parent || last_slash == NULL) { /* parent is root */
120 free(parent);
121 parent = strdup(slash);
122    } else {
123 *last_slash = '\0'; /* truncate to get parent name */
124    }
125    NC_CHECK(nc_inq_grp_full_ncid(ncidparentlocidp));
126       free(parent);
127    return NC_NOERR;
128}
129
130/* Return size of chunk in bytes for a variable varid in a group igrp, or 0 if
131 * layout is contiguous */
132static int
133inq_var_chunksize(int igrp, int varid, size_t* chunksizep) {
134    int stat = NC_NOERR;
135    int ndims;
136    size_t *chunksizes;
137    int dim;
138    int contig = 1;
139    nc_type vartype;
140    size_t value_size;
141    size_t prod;
142
143    NC_CHECK(nc_inq_vartype(igrpvarid, &vartype));
144    /* from type, get size in memory needed for each value */
145    NC_CHECK(nc_inq_type(igrpvartypeNULL, &value_size));
146    prod = value_size;
147    NC_CHECK(nc_inq_varndims(igrpvarid, &ndims));
148    chunksizes = (size_t *) emalloc((ndims + 1) * sizeof(size_t));
149    if(ndims > 0) {
150 NC_CHECK(nc_inq_var_chunking(igrpvarid, &contigNULL));
151    }
152    if(contig == 1) {
153 *chunksizep = 0;
154    } else {
155 NC_CHECK(nc_inq_var_chunking(igrpvarid, &contigchunksizes));
156 for(dim = 0; dim < ndimsdim++) {
157     prod *= chunksizes[dim];
158 }
159 *chunksizep = prod;
160    }
161    free(chunksizes);
162    return stat;
163}
164
165/* Return estimated number of elems required in chunk cache and
166 * estimated size of chunk cache adequate to efficiently copy input
167 * variable ivarid to output variable ovarid, which may have different
168 * chunk size and shape */
169static int
170inq_var_chunking_params(int igrp, int ivarid, int ogrp, int ovarid,
171 size_t* chunkcache_sizep,
172 size_t *chunkcache_nelemsp,
173                        float * chunkcache_preemptionp)
174{
175    int stat = NC_NOERR;
176    int ndims;
177    size_t *ichunksizes, *ochunksizes;
178    int dim;
179    int icontig = 1, ocontig = 1;
180    nc_type vartype;
181    size_t value_size;
182    size_t prodiprodoprod;
183    size_t nelems;
184    *chunkcache_nelemsp = CHUNK_CACHE_NELEMS;
185    *chunkcache_sizep = CHUNK_CACHE_SIZE;
186    *chunkcache_preemptionp = COPY_CHUNKCACHE_PREEMPTION;
187
188    NC_CHECK(nc_inq_varndims(igrpivarid, &ndims));
189    if(ndims > 0) {
190 NC_CHECK(nc_inq_var_chunking(igrpivarid, &icontigNULL));
191 NC_CHECK(nc_inq_var_chunking(ogrpovarid, &ocontigNULL));
192    }
193    if(icontig == 1 && ocontig == 1) { /* no chunking in input or output */
194 *chunkcache_nelemsp = 0;
195 *chunkcache_sizep = 0;
196 *chunkcache_preemptionp = 0;
197 return stat;
198    }
199
200    NC_CHECK(nc_inq_vartype(igrpivarid, &vartype));
201    NC_CHECK(nc_inq_type(igrpvartypeNULL, &value_size));
202    iprod = value_size;
203
204    if(icontig == 0 && ocontig == 1) { /* chunking only in input */
205 *chunkcache_nelemsp = 1;       /* read one input chunk at a time */
206 *chunkcache_sizep = iprod;
207 *chunkcache_preemptionp = 1.0f;
208 return stat;
209    }
210
211    ichunksizes = (size_t *) emalloc((ndims + 1) * sizeof(size_t));
212    if(icontig == 1) { /* if input contiguous, treat as if chunked on
213 * first dimension */
214 ichunksizes[0] = 1;
215 for(dim = 1; dim < ndimsdim++) {
216     ichunksizes[dim] = dim;
217 }
218    } else {
219 NC_CHECK(nc_inq_var_chunking(igrpivarid, &icontigichunksizes));
220    }
221
222    /* now can assume chunking in both input and output */
223    ochunksizes = (size_t *) emalloc((ndims + 1) * sizeof(size_t));
224    NC_CHECK(nc_inq_var_chunking(ogrpovarid, &ocontigochunksizes));
225
226    nelems = 1;
227    oprod = value_size;
228    for(dim = 0; dim < ndimsdim++) {
229 nelems += 1 + (ichunksizes[dim] - 1) / ochunksizes[dim];
230 iprod *= ichunksizes[dim];
231 oprod *= ochunksizes[dim];
232    }
233    prod = iprod + oprod * (nelems - 1);
234    *chunkcache_nelemsp = nelems;
235    *chunkcache_sizep = prod;
236    free(ichunksizes);
237    free(ochunksizes);
238    return stat;
239}
240
241/* Forward declaration, because copy_type, copy_vlen_type call each other */
242static int copy_type(int igrpnc_type typeid, int ogrp);
243
244/*
245 * copy a user-defined variable length type in the group igrp to the
246 * group ogrp
247 */
248static int
249copy_vlen_type(int igrpnc_type itype, int ogrp)
250{
251    int stat = NC_NOERR;
252    nc_type ibasetype;
253    nc_type obasetype; /* base type in target group */
254    char name[NC_MAX_NAME];
255    size_t size;
256    char basename[NC_MAX_NAME];
257    size_t basesize;
258    nc_type vlen_type;
259
260    NC_CHECK(nc_inq_vlen(igrpitypename, &size, &ibasetype));
261    /* to get base type id in target group, use name of base type in
262     * source group */
263    NC_CHECK(nc_inq_type(igrpibasetypebasename, &basesize));
264    stat = nc_inq_typeid(ogrpbasename, &obasetype);
265    /* if no such type, create it now */
266    if(stat == NC_EBADTYPE) {
267 NC_CHECK(copy_type(igrpibasetypeogrp));
268 stat = nc_inq_typeid(ogrpbasename, &obasetype);
269    }
270    NC_CHECK(stat);
271
272    /* Now we know base type exists in output and we know its type id */
273    NC_CHECK(nc_def_vlen(ogrpnameobasetype, &vlen_type));
274
275    return stat;
276}
277
278/*
279 * copy a user-defined opaque type in the group igrp to the group ogrp
280 */
281static int
282copy_opaque_type(int igrpnc_type itype, int ogrp)
283{
284    int stat = NC_NOERR;
285    nc_type otype;
286    char name[NC_MAX_NAME];
287    size_t size;
288
289    NC_CHECK(nc_inq_opaque(igrpitypename, &size));
290    NC_CHECK(nc_def_opaque(ogrpsizename, &otype));
291
292    return stat;
293}
294
295/*
296 * copy a user-defined enum type in the group igrp to the group ogrp
297 */
298static int
299copy_enum_type(int igrpnc_type itype, int ogrp)
300{
301    int stat = NC_NOERR;
302    nc_type otype;
303    nc_type basetype;
304    size_t basesize;
305    size_t nmembers;
306    char name[NC_MAX_NAME];
307    int i;
308
309    NC_CHECK(nc_inq_enum(igrpitypename, &basetype, &basesize, &nmembers));
310    NC_CHECK(nc_def_enum(ogrpbasetypename, &otype));
311    for(i = 0; i < nmembersi++) { /* insert enum members */
312 char ename[NC_MAX_NAME];
313 long long val; /* large enough to hold any integer type */
314 NC_CHECK(nc_inq_enum_member(igrpitypeiename, &val));
315 NC_CHECK(nc_insert_enum(ogrpotypeename, &val));
316    }
317    return stat;
318}
319
320/*
321 * copy a user-defined compound type in the group igrp to the group ogrp
322 */
323static int
324copy_compound_type(int igrpnc_type itype, int ogrp)
325{
326    int stat = NC_NOERR;
327    char name[NC_MAX_NAME];
328    size_t size;
329    size_t nfields;
330    nc_type otype;
331    int fid;
332
333    NC_CHECK(nc_inq_compound(igrpitypename, &size, &nfields));
334    NC_CHECK(nc_def_compound(ogrpsizename, &otype));
335
336    for (fid = 0; fid < nfieldsfid++) {
337 char fname[NC_MAX_NAME];
338 char ftypename[NC_MAX_NAME];
339 size_t foff;
340 nc_type iftypeoftype;
341 int fndims;
342
343 NC_CHECK(nc_inq_compound_field(igrpitypefidfname, &foff, &iftype, &fndimsNULL));
344 /* type ids in source don't necessarily correspond to same
345  * typeids in destination, so look up destination typeid by using
346  * field type name */
347 NC_CHECK(nc_inq_type(igrpiftypeftypenameNULL));
348 NC_CHECK(nc_inq_typeid(ogrpftypename, &oftype));
349 if(fndims == 0) {
350     NC_CHECK(nc_insert_compound(ogrpotypefnamefoffoftype));
351 } else { /* field is array type */
352     int *fdimsizes;
353     fdimsizes = (int *) emalloc((fndims + 1) * sizeof(int));
354     stat = nc_inq_compound_field(igrpitypefidNULLNULLNULL,
355  NULLfdimsizes);
356     NC_CHECK(nc_insert_array_compound(ogrpotypefnamefoffoftypefndimsfdimsizes));
357     free(fdimsizes);
358 }
359    }
360    return stat;
361}
362
363
364/*
365 * copy a user-defined type in the group igrp to the group ogrp
366 */
367static int
368copy_type(int igrpnc_type typeid, int ogrp)
369{
370    int stat = NC_NOERR;
371    nc_type type_class;
372
373    NC_CHECK(nc_inq_user_type(igrptypeidNULLNULLNULLNULL, &type_class));
374
375    switch(type_class) {
376    case NC_VLEN:
377 NC_CHECK(copy_vlen_type(igrptypeidogrp));
378 break;
379    case NC_OPAQUE:
380 NC_CHECK(copy_opaque_type(igrptypeidogrp));
381 break;
382    case NC_ENUM:
383 NC_CHECK(copy_enum_type(igrptypeidogrp));
384 break;
385    case NC_COMPOUND:
386 NC_CHECK(copy_compound_type(igrptypeidogrp));
387 break;
388    default:
389 NC_CHECK(NC_EBADTYPE);
390    }
391    return stat;
392}
393
394/* Copy a group and all its subgroups, recursively, from iroot to
395 * oroot, the ncids of input file and output file.  This just creates
396 * all the groups in the destination, but doesn't copy anything that's
397 * in the groups yet. */
398static int
399copy_groups(int iroot, int oroot)
400{
401    int stat = NC_NOERR;
402    int numgrps;
403    int *grpids;
404    int i;
405
406    /* get total number of groups and their ids, including all descendants */
407    NC_CHECK(nc_inq_grps_full(iroot, &numgrpsNULL));
408    if(numgrps > 1) { /* there's always 1 root group */
409 grpids = emalloc(numgrps * sizeof(int));
410 NC_CHECK(nc_inq_grps_full(irootNULLgrpids));
411 /* create corresponding new groups in ogrp, except for root group */
412 for(i = 1; i < numgrpsi++) {
413     char *grpname_full;
414     char grpname[NC_MAX_NAME];
415     size_t len_name;
416     int ogid = 0, oparid = 0, iparid = 0;
417     /* get full group name of input group */
418     NC_CHECK(nc_inq_grpname(grpids[i], grpname));
419     if (option_grpstruct || group_wanted(grpids[i], option_nlgrpsoption_grpids)) {
420         NC_CHECK(nc_inq_grpname_full(grpids[i], &len_nameNULL));
421 grpname_full = emalloc(len_name + 1);
422 NC_CHECK(nc_inq_grpname_full(grpids[i], &len_namegrpname_full));
423 /* Make sure, the parent group is also wanted (root group is always wanted) */
424 NC_CHECK(nc_inq_parid(irootgrpname_full, &iparid));
425 if (!option_grpstruct && !group_wanted(iparidoption_nlgrpsoption_grpids)
426     && iparid != iroot) {
427     error("ERROR: trying to copy a group but not the parent: %s", grpname_full);
428 }
429 /* get id of parent group of corresponding group in output.
430  * Note that this exists, because nc_inq_groups returned
431  * grpids in preorder, so parents are always copied before
432  * their subgroups */
433 NC_CHECK(nc_inq_parid(orootgrpname_full, &oparid));
434 NC_CHECK(nc_inq_grpname(grpids[i], grpname));
435 /* define corresponding group in output */
436 NC_CHECK(nc_def_grp(oparidgrpname, &ogid));
437 free(grpname_full);
438     }
439 }
440 free(grpids);
441    }
442    return stat;
443}
444
445/*
446 * Copy the user-defined types in this group (igrp) and all its
447 * subgroups, recursively, to corresponding group in output (ogrp)
448 */
449static int
450copy_types(int igrp, int ogrp)
451{
452    int stat = NC_NOERR;
453    int ntypes;
454    nc_type *types = NULL;
455    int numgrps;
456    int *grpids = NULL;
457    int i;
458
459    NC_CHECK(nc_inq_typeids(igrp, &ntypesNULL));
460
461    if(ntypes > 0) {
462 types = (nc_type *) emalloc(ntypes * sizeof(nc_type));
463 NC_CHECK(nc_inq_typeids(igrp, &ntypestypes));
464 for (i = 0; i < ntypesi++) {
465     NC_CHECK(copy_type(igrptypes[i], ogrp));
466 }
467 free(types);
468    }
469
470    /* Copy types from subgroups */
471    NC_CHECK(nc_inq_grps(igrp, &numgrpsNULL));
472    if(numgrps > 0) {
473 grpids = (int *)emalloc(sizeof(int) * numgrps);
474 NC_CHECK(nc_inq_grps(igrp, &numgrpsgrpids));
475 for(i = 0; i < numgrpsi++) {
476     if (option_grpstruct || group_wanted(grpids[i], option_nlgrpsoption_grpids)) {
477 int ogid;
478 /* get groupid in output corresponding to grpids[i] in
479  * input, given parent group (or root group) ogrp in
480  * output */
481 NC_CHECK(get_grpid(grpids[i], ogrp, &ogid));
482 NC_CHECK(copy_types(grpids[i], ogid));
483     }
484 }
485 free(grpids);
486    }
487    return stat;
488}
489
490/* Copy all netCDF-4 specific variable properties such as chunking,
491 * endianness, deflation, checksumming, fill, etc. */
492static int
493copy_var_specials(int igrp, int varid, int ogrp, int o_varid)
494{
495    int stat = NC_NOERR;
496    { /* handle chunking parameters */
497 int ndims;
498 NC_CHECK(nc_inq_varndims(igrpvarid, &ndims));
499 if (ndims > 0) { /* no chunking for scalar variables */
500     int contig = 0;
501     size_t *chunkp = (size_t *) emalloc(ndims * sizeof(size_t));
502     int *dimids = (int *) emalloc(ndims * sizeof(int));
503     int idim;
504      /* size of a chunk: product of dimension chunksizes and size of value */
505     size_t csprod = val_size(ogrpo_varid);
506     int is_unlimited = 0;
507     NC_CHECK(nc_inq_var_chunking(igrpvarid, &contigchunkp));
508     NC_CHECK(nc_inq_vardimid(igrpvariddimids));
509
510     for(idim = 0; idim < ndimsidim++) {
511 int idimid = dimids[idim];
512 int odimid = dimmap_odimid(idimid);
513 size_t chunksize = chunkspec_size(idimid);
514 if(chunksize > 0) { /* found in chunkspec */
515     chunkp[idim] = chunksize;
516 }
517 csprod *= chunkp[idim];
518 if(dimmap_ounlim(odimid))
519     is_unlimited = 1;
520     }
521     /* Explicitly set chunking, even if default */
522     /* If product of chunksizes is too small and no unlimited
523      * dimensions used, don't chunk.  Also if chunking
524      * explicitly turned off with chunk spec, don't chunk. */
525     if ((csprod < option_min_chunk_bytes && !is_unlimited) || contig == 1
526 || chunkspec_omit() == true) {
527 NC_CHECK(nc_def_var_chunking(ogrpo_varidNC_CONTIGUOUSNULL));
528     } else {
529 NC_CHECK(nc_def_var_chunking(ogrpo_varidNC_CHUNKEDchunkp));
530     }
531     free(dimids);
532     free(chunkp);
533 }
534    }
535    { /* handle compression parameters, copying from input, overriding
536       * with command-line options */
537 int shuffle_in=0, deflate_in=0, deflate_level_in=0;
538 int shuffle_out=0, deflate_out=0, deflate_level_out=0;
539 if(option_deflate_level != 0) {
540     NC_CHECK(nc_inq_var_deflate(igrpvarid, &shuffle_in, &deflate_in, &deflate_level_in));
541     if(option_deflate_level == -1) { /* not specified, copy input compression and shuffling */
542 shuffle_out = shuffle_in;
543 deflate_out = deflate_in;
544 deflate_level_out = deflate_level_in;
545     } else if(option_deflate_level > 0) { /* change to specified compression, shuffling */
546 shuffle_out = option_shuffle_vars;
547 deflate_out=1;
548 deflate_level_out = option_deflate_level;
549     }
550     NC_CHECK(nc_def_var_deflate(ogrpo_varidshuffle_outdeflate_outdeflate_level_out));
551 }
552    }
553    { /* handle checksum parameters */
554 int fletcher32 = 0;
555 NC_CHECK(nc_inq_var_fletcher32(igrpvarid, &fletcher32));
556 if(fletcher32 != 0) {
557     NC_CHECK(nc_def_var_fletcher32(ogrpo_varidfletcher32));
558 }
559    }
560    { /* handle endianness */
561 int endianness = 0;
562 NC_CHECK(nc_inq_var_endian(igrpvarid, &endianness));
563 if(endianness != NC_ENDIAN_NATIVE) { /* native is the default */
564     NC_CHECK(nc_def_var_endian(ogrpo_varidendianness));
565 }
566    }
567    return stat;
568}
569
570/* Set output variable o_varid (in group ogrp) to use chunking
571 * specified on command line, only called for classic format input and
572 * netCDF-4 format output, so no existing chunk lengths to override. */
573static int
574set_var_chunked(int ogrp, int o_varid)
575{
576    int stat = NC_NOERR;
577    int ndims;
578    int odim;
579    size_t chunk_threshold = CHUNK_THRESHOLD;
580
581    if(chunkspec_ndims() == 0)  /* no chunking specified on command line */
582 return stat;
583    NC_CHECK(nc_inq_varndims(ogrpo_varid, &ndims));
584
585    if (ndims > 0) { /* no chunking for scalar variables */
586 int chunked = 0;
587 int *dimids = (int *) emalloc(ndims * sizeof(int));
588 size_t varsize;
589 nc_type vartype;
590 size_t value_size;
591 int is_unlimited = 0;
592
593 NC_CHECK(nc_inq_vardimid (ogrpo_variddimids));
594 NC_CHECK(nc_inq_vartype(ogrpo_varid, &vartype));
595 /* from type, get size in memory needed for each value */
596 NC_CHECK(nc_inq_type(ogrpvartypeNULL, &value_size));
597 varsize = value_size;
598
599 /* Determine if this variable should be chunked.  A variable
600  * should be chunked if any of its dims are in command-line
601  * chunk spec. It will also be chunked if any of its
602  * dims are unlimited. */
603 for(odim = 0; odim < ndimsodim++) {
604     int odimid = dimids[odim];
605     int idimid = dimmap_idimid(odimid); /* corresponding dimid in input file */
606     if(dimmap_ounlim(odimid))
607 is_unlimited = 1; /* whether vriable is unlimited */
608     if(idimid != -1) {
609 size_t chunksize = chunkspec_size(idimid); /* from chunkspec */
610 size_t dimlen;
611 NC_CHECK(nc_inq_dimlen(ogrpodimid, &dimlen));
612 if( (chunksize > 0) || dimmap_ounlim(odimid)) {
613     chunked = 1;
614 }
615 if(dimlen > 0) { /* dimlen for unlimited dims is still 0 before copying data */
616     varsize *= dimlen;
617 }
618     }
619 }
620 /* Don't chunk small variables that don't use an unlimited
621  * dimension. */
622 if(varsize < chunk_threshold && !is_unlimited)
623     chunked = 0;
624
625 if(chunked) {
626     /* Allocate chunksizes and set defaults to dimsize for any
627      * dimensions not mentioned in chunkspec, except use 1 for unlimited dims. */
628     size_t *chunkp = (size_t *) emalloc(ndims * sizeof(size_t));
629     for(odim = 0; odim < ndimsodim++) {
630 int odimid = dimids[odim];
631 int idimid = dimmap_idimid(odimid);
632 size_t chunksize = chunkspec_size(idimid);
633 if(chunksize > 0) {
634     chunkp[odim] = chunksize;
635 } else {
636     if(dimmap_ounlim(odimid)){
637 chunkp[odim] = 1;
638     } else {
639 NC_CHECK(nc_inq_dimlen(ogrpodimid, &chunkp[odim]));
640     }
641 }
642     }
643     NC_CHECK(nc_def_var_chunking(ogrpo_varidNC_CHUNKEDchunkp));
644     free(chunkp);
645 }
646 free(dimids);
647    }
648    return stat;
649}
650
651/* Set variable to compression specified on command line */
652static int
653set_var_compressed(int ogrp, int o_varid)
654{
655    int stat = NC_NOERR;
656    if (option_deflate_level > 0) {
657 int deflate = 1;
658 NC_CHECK(nc_def_var_deflate(ogrpo_varidoption_shuffle_varsdeflateoption_deflate_level));
659    }
660    return stat;
661}
662
663/* Release the variable chunk cache allocated for variable varid in
664 * group grp.  This is not necessary, but will save some memory when
665 * processing one variable at a time.  */
666#ifdef UNUSED
667static int
668free_var_chunk_cache(int grp, int varid)
669{
670    int stat = NC_NOERR;
671    size_t chunk_cache_size = 1;
672    size_t cache_nelems = 1;
673    float cache_preemp = 0;
674    int kind;
675    NC_CHECK(nc_inq_format(grp, &kind));
676    if(kind == NC_FORMAT_NETCDF4 || kind == NC_FORMAT_NETCDF4_CLASSIC) {
677 int contig = 1;
678 NC_CHECK(nc_inq_var_chunking(grpvarid, &contigNULL));
679 if(contig == 0) { /* chunked */
680     NC_CHECK(nc_set_var_chunk_cache(grpvaridchunk_cache_sizecache_nelemscache_preemp));
681 }
682    }
683    return stat;
684}
685#endif
686
687#endif /* USE_NETCDF4 */
688
689/* Copy dimensions from group igrp to group ogrp, also associate input
690 * dimids with output dimids (they need not match, because the input
691 * dimensions may have been defined in a different order than we define
692 * the output dimensions here. */
693static int
694copy_dims(int igrp, int ogrp)
695{
696    int stat = NC_NOERR;
697    int ndims;
698    int dgrp;
699#ifdef USE_NETCDF4
700    int nunlims;
701    int *dimids;
702    int *unlimids;
703#else
704    int unlimid;
705#endif /* USE_NETCDF4 */
706
707    NC_CHECK(nc_inq_ndims(igrp, &ndims));
708
709#ifdef USE_NETCDF4
710   /* In netCDF-4 files, dimids may not be sequential because they
711    * may be defined in various groups, and we are only looking at one
712    * group at a time. */
713    /* Find the dimension ids in this group, don't include parents. */
714    dimids = (int *) emalloc((ndims + 1) * sizeof(int));
715    NC_CHECK(nc_inq_dimids(igrpNULLdimids, 0));
716    /* Find the number of unlimited dimensions and get their IDs */
717    NC_CHECK(nc_inq_unlimdims(igrp, &nunlimsNULL));
718    unlimids = (int *) emalloc((nunlims + 1) * sizeof(int));
719    NC_CHECK(nc_inq_unlimdims(igrpNULLunlimids));
720#else
721    NC_CHECK(nc_inq_unlimdim(igrp, &unlimid));
722#endif /* USE_NETCDF4 */
723
724    /* Copy each dimension to output, including unlimited dimension(s) */
725    for (dgrp = 0; dgrp < ndimsdgrp++) {
726 char name[NC_MAX_NAME];
727 size_t length;
728 int i_is_unlim;
729 int o_is_unlim;
730 int idimidodimid;
731#ifdef USE_NETCDF4
732 int uld;
733#endif
734
735 i_is_unlim = 0;
736#ifdef USE_NETCDF4
737 idimid = dimids[dgrp];
738 for (uld = 0; uld < nunlimsuld++) {
739     if(idimid == unlimids[uld]) {
740 i_is_unlim = 1;
741 break;
742     }
743 }
744#else
745 idimid = dgrp;
746 if(unlimid != -1 && (idimid == unlimid)) {
747     i_is_unlim = 1;
748 }
749#endif /* USE_NETCDF4 */
750
751 stat = nc_inq_dim(igrpidimidname, &length);
752 if (stat == NC_EDIMSIZE && sizeof(size_t) < 8) {
753     error("dimension \"%s\" requires 64-bit platform", name);
754 }
755 NC_CHECK(stat);
756 o_is_unlim = i_is_unlim;
757 if(i_is_unlim && !option_fix_unlimdims) {
758     NC_CHECK(nc_def_dim(ogrpnameNC_UNLIMITED, &odimid));
759 } else {
760     NC_CHECK(nc_def_dim(ogrpnamelength, &odimid));
761     o_is_unlim = 0;
762 }
763 /* Store (idimid, odimid) mapping for later use, also whether unlimited */
764 dimmap_store(idimidodimidi_is_unlimo_is_unlim);
765    }
766#ifdef USE_NETCDF4
767    free(dimids);
768    free(unlimids);
769#endif /* USE_NETCDF4 */
770    return stat;
771}
772
773/* Copy the attributes for variable ivar in group igrp to variable
774 * ovar in group ogrp.  Global (group) attributes are specified by
775 * using the varid NC_GLOBAL */
776static int
777copy_atts(int igrp, int ivar, int ogrp, int ovar)
778{
779    int natts;
780    int iatt;
781    int stat = NC_NOERR;
782
783    NC_CHECK(nc_inq_varnatts(igrpivar, &natts));
784
785    for(iatt = 0; iatt < nattsiatt++) {
786 char name[NC_MAX_NAME];
787 NC_CHECK(nc_inq_attname(igrpivariattname));
788 NC_CHECK(nc_copy_att(igrpivarnameogrpovar));
789    }
790    return stat;
791}
792
793/* copy the schema for a single variable in group igrp to group ogrp */
794static int
795copy_var(int igrp, int varid, int ogrp)
796{
797    int stat = NC_NOERR;
798    int ndims;
799    int *idimids; /* ids of dims for input variable */
800    int *odimids; /* ids of dims for output variable */
801    char name[NC_MAX_NAME];
802    nc_type typeido_typeid;
803    int natts;
804    int i;
805    int o_varid;
806
807    NC_CHECK(nc_inq_varndims(igrpvarid, &ndims));
808    idimids = (int *) emalloc((ndims + 1) * sizeof(int));
809    NC_CHECK(nc_inq_var(igrpvaridname, &typeidNULLidimids, &natts));
810    o_typeid = typeid;
811#ifdef USE_NETCDF4
812    if (typeid > NC_STRING) { /* user-defined type */
813 /* type ids in source don't necessarily correspond to same
814  * typeids in destination, so look up destination typeid by
815  * using type name */
816 char type_name[NC_MAX_NAME];
817 NC_CHECK(nc_inq_type(igrptypeidtype_nameNULL));
818 NC_CHECK(nc_inq_typeid(ogrptype_name, &o_typeid));
819    }
820#endif /* USE_NETCDF4 */
821
822    /* get the corresponding dimids in the output file */
823    odimids = (int *) emalloc((ndims + 1) * sizeof(int));
824    for(i = 0; i < ndimsi++) {
825 odimids[i] = dimmap_odimid(idimids[i]);
826 if(odimids[i] == -1) {
827     error("Oops, no dimension in output associated with input dimid %d", idimids[i]);
828 }
829    }
830
831    /* define the output variable */
832    NC_CHECK(nc_def_var(ogrpnameo_typeidndimsodimids, &o_varid));
833    /* attach the variable attributes to the output variable */
834    NC_CHECK(copy_atts(igrpvaridogrpo_varid));
835#ifdef USE_NETCDF4
836    {
837 int inkind;
838 int outkind;
839 NC_CHECK(nc_inq_format(igrp, &inkind));
840 NC_CHECK(nc_inq_format(ogrp, &outkind));
841 if(outkind == NC_FORMAT_NETCDF4 || outkind == NC_FORMAT_NETCDF4_CLASSIC) {
842     if((inkind == NC_FORMAT_NETCDF4 || inkind == NC_FORMAT_NETCDF4_CLASSIC)) {
843 /* Copy all netCDF-4 specific variable properties such as
844  * chunking, endianness, deflation, checksumming, fill, etc. */
845 NC_CHECK(copy_var_specials(igrpvaridogrpo_varid));
846     } else {
847 /* Set chunking if specified in command line option */
848 NC_CHECK(set_var_chunked(ogrpo_varid));
849 /* Set compression if specified in command line option */
850 NC_CHECK(set_var_compressed(ogrpo_varid));
851     }
852 }
853    }
854#endif /* USE_NETCDF4 */
855    free(idimids);
856    free(odimids);
857    return stat;
858}
859
860/* copy the schema for all the variables in group igrp to group ogrp */
861static int
862copy_vars(int igrp, int ogrp)
863{
864    int stat = NC_NOERR;
865    int nvars;
866    int varid;
867
868    int iv; /* variable number */
869    idnode_tvlist = 0; /* list for vars specified with -v option */
870
871    /*
872     * If any vars were specified with -v option, get list of
873     * associated variable ids relative to this group.  Assume vars
874     * specified with syntax like "grp1/grp2/varname" or
875     * "/grp1/grp2/varname" if they are in groups.
876     */
877    vlist = newidlist(); /* list for vars specified with -v option */
878    for (iv=0; iv < option_nlvarsiv++) {
879        if(nc_inq_gvarid(igrpoption_lvars[iv], &varid) == NC_NOERR)
880            idadd(vlistvarid);
881    }
882
883    NC_CHECK(nc_inq_nvars(igrp, &nvars));
884    for (varid = 0; varid < nvarsvarid++) {
885 if (!option_varstruct && option_nlvars > 0 && ! idmember(vlistvarid))
886            continue;
887 NC_CHECK(copy_var(igrpvaridogrp));
888    }
889    freeidlist(vlist);
890    return stat;
891}
892
893/* Copy the schema in a group and all its subgroups, recursively, from
894 * group igrp in input to parent group ogrp in destination.  Use
895 * dimmap array to map input dimids to output dimids. */
896static int
897copy_schema(int igrp, int ogrp)
898{
899    int stat = NC_NOERR;
900    int ogid; /* like igrp but in output file */
901
902    /* get groupid in output corresponding to group igrp in input,
903     * given parent group (or root group) ogrp in output */
904    NC_CHECK(get_grpid(igrpogrp, &ogid));
905
906    NC_CHECK(copy_dims(igrpogid));
907    NC_CHECK(copy_atts(igrpNC_GLOBALogidNC_GLOBAL));
908    NC_CHECK(copy_vars(igrpogid));
909#ifdef USE_NETCDF4
910    {
911 int numgrps;
912 int *grpids;
913 int i;
914 /* Copy schema from subgroups */
915 stat = nc_inq_grps(igrp, &numgrpsNULL);
916 grpids = (int *)emalloc((numgrps + 1) * sizeof(int));
917 NC_CHECK(nc_inq_grps(igrp, &numgrpsgrpids));
918
919 for(i = 0; i < numgrpsi++) {
920     if (option_grpstruct || group_wanted(grpids[i], option_nlgrpsoption_grpids)) {
921         NC_CHECK(copy_schema(grpids[i], ogid));
922     }
923 }
924 free(grpids);
925    }
926#endif /* USE_NETCDF4 */
927    return stat;
928}
929
930/* Return number of values for a variable varid in a group igrp */
931static int
932inq_nvals(int igrp, int varid, long long *nvalsp) {
933    int stat = NC_NOERR;
934    int ndims;
935    int *dimids;
936    int dim;
937    long long nvals = 1;
938
939    NC_CHECK(nc_inq_varndims(igrpvarid, &ndims));
940    dimids = (int *) emalloc((ndims + 1) * sizeof(int));
941    NC_CHECK(nc_inq_vardimid (igrpvariddimids));
942    for(dim = 0; dim < ndimsdim++) {
943 size_t len;
944 NC_CHECK(nc_inq_dimlen(igrpdimids[dim], &len));
945 nvals *= len;
946    }
947    if(nvalsp)
948 *nvalsp = nvals;
949    free(dimids);
950    return stat;
951}
952
953/* Copy data from variable varid in group igrp to corresponding group
954 * ogrp. */
955static int
956copy_var_data(int igrp, int varid, int ogrp) {
957    int stat = NC_NOERR;
958    nc_type vartype;
959    long long nvalues; /* number of values for this variable */
960    size_t ntoget; /* number of values to access this iteration */
961    size_t value_size; /* size of a single value of this variable */
962    static void *buf = 0; /* buffer for the variable values */
963    char varname[NC_MAX_NAME];
964    int ovarid;
965    size_t *start;
966    size_t *count;
967    nciter_t *iterp; /* opaque structure for iteration status */
968    int do_realloc = 0;
969#ifdef USE_NETCDF4
970    int okind;
971    size_t chunksize;
972#endif
973
974    NC_CHECK(inq_nvals(igrpvarid, &nvalues));
975    if(nvalues == 0)
976 return stat;
977    /* get corresponding output variable */
978    NC_CHECK(nc_inq_varname(igrpvaridvarname));
979    NC_CHECK(nc_inq_varid(ogrpvarname, &ovarid));
980    NC_CHECK(nc_inq_vartype(igrpvarid, &vartype));
981    value_size = val_size(igrpvarid);
982    if(value_size > option_copy_buffer_size) {
983 option_copy_buffer_size = value_size;
984 do_realloc = 1;
985    }
986#ifdef USE_NETCDF4
987    NC_CHECK(nc_inq_format(ogrp, &okind));
988    if(okind == NC_FORMAT_NETCDF4 || okind == NC_FORMAT_NETCDF4_CLASSIC) {
989 /* if this variable chunked, set variable chunk cache size */
990 int contig = 1;
991 NC_CHECK(nc_inq_var_chunking(ogrpovarid, &contigNULL));
992 if(contig == 0) { /* chunked */
993     if(option_compute_chunkcaches) {
994 /* Try to estimate variable-specific chunk cache,
995  * depending on specific size and shape of this
996  * variable's chunks.  This doesn't work yet. */
997 size_t chunkcache_sizechunkcache_nelems;
998 float chunkcache_preemption;
999 NC_CHECK(inq_var_chunking_params(igrpvaridogrpovarid,
1000  &chunkcache_size,
1001  &chunkcache_nelems,
1002  &chunkcache_preemption));
1003 NC_CHECK(nc_set_var_chunk_cache(ogrpovarid,
1004 chunkcache_size,
1005 chunkcache_nelems,
1006 chunkcache_preemption));
1007     } else {
1008 /* by default, use same chunk cache for all chunked variables */
1009 NC_CHECK(nc_set_var_chunk_cache(ogrpovarid,
1010 option_chunk_cache_size,
1011 option_chunk_cache_nelems,
1012 COPY_CHUNKCACHE_PREEMPTION));
1013     }
1014 }
1015    }
1016    /* For chunked variables, option_copy_buffer_size must also be at least as large as
1017     * size of a chunk in input, otherwise resize it. */
1018    {
1019 NC_CHECK(inq_var_chunksize(igrpvarid, &chunksize));
1020 if(chunksize > option_copy_buffer_size) {
1021     option_copy_buffer_size = chunksize;
1022     do_realloc = 1;
1023 }
1024    }
1025#endif /* USE_NETCDF4 */
1026    if(buf && do_realloc) {
1027 free(buf);
1028 buf = 0;
1029    }
1030    if(buf == 0) { /* first time or needs to grow */
1031 buf = emalloc(option_copy_buffer_size);
1032 memset((void*)buf,0,option_copy_buffer_size);
1033    }
1034
1035    /* initialize variable iteration */
1036    NC_CHECK(nc_get_iter(igrpvaridoption_copy_buffer_size, &iterp));
1037
1038    start = (size_t *) emalloc((iterp->rank + 1) * sizeof(size_t));
1039    count = (size_t *) emalloc((iterp->rank + 1) * sizeof(size_t));
1040    /* nc_next_iter() initializes start and count on first call,
1041     * changes start and count to iterate through whole variable on
1042     * subsequent calls. */
1043    while((ntoget = nc_next_iter(iterpstartcount)) > 0) {
1044 NC_CHECK(nc_get_vara(igrpvaridstartcountbuf));
1045 NC_CHECK(nc_put_vara(ogrpovaridstartcountbuf));
1046#ifdef USE_NETCDF4
1047 /* we have to explicitly free values for strings and vlens */
1048 if(vartype == NC_STRING) {
1049     NC_CHECK(nc_free_string(ntoget, (char **)buf));
1050 } else if(vartype > NC_STRING) { /* user-defined type */
1051     nc_type vclass;
1052     NC_CHECK(nc_inq_user_type(igrpvartypeNULLNULLNULLNULL, &vclass));
1053     if(vclass == NC_VLEN) {
1054 NC_CHECK(nc_free_vlens(ntoget, (nc_vlen_t *)buf));
1055     }
1056 }
1057#endif /* USE_NETCDF4 */
1058    } /* end main iteration loop */
1059#ifdef USE_NETCDF4
1060    /* We're all done with this input and output variable, so if
1061     * either variable is chunked, free up its variable chunk cache */
1062    /* NC_CHECK(free_var_chunk_cache(igrp, varid)); */
1063    /* NC_CHECK(free_var_chunk_cache(ogrp, ovarid)); */
1064#endif /* USE_NETCDF4 */
1065    free(start);
1066    free(count);
1067    NC_CHECK(nc_free_iter(iterp));
1068    return stat;
1069}
1070
1071/* Copy data from variables in group igrp to variables in
1072 * corresponding group with parent ogrp, and all subgroups
1073 * recursively  */
1074static int
1075copy_data(int igrp, int ogrp)
1076{
1077    int stat = NC_NOERR;
1078    int ogid;
1079    int nvars;
1080    int varid;
1081#ifdef USE_NETCDF4
1082    int numgrps;
1083    int *grpids;
1084    int i;
1085#endif
1086
1087    int iv; /* variable number */
1088    idnode_tvlist = NULL; /* list for vars specified with -v option */
1089
1090    /*
1091     * If any vars were specified with -v option, get list of
1092     * associated variable ids relative to this group.  Assume vars
1093     * specified with syntax like "grp1/grp2/varname" or
1094     * "/grp1/grp2/varname" if they are in groups.
1095     */
1096    vlist = newidlist(); /* list for vars specified with -v option */
1097    for (iv=0; iv < option_nlvarsiv++) {
1098        if(nc_inq_gvarid(igrpoption_lvars[iv], &varid) == NC_NOERR)
1099            idadd(vlistvarid);
1100    }
1101
1102    /* get groupid in output corresponding to group igrp in input,
1103     * given parent group (or root group) ogrp in output */
1104    NC_CHECK(get_grpid(igrpogrp, &ogid));
1105
1106    /* Copy data from this group */
1107    NC_CHECK(nc_inq_nvars(igrp, &nvars));
1108
1109    for (varid = 0; varid < nvarsvarid++) {
1110 if (option_nlvars > 0 && ! idmember(vlistvarid))
1111            continue;
1112        if (!group_wanted(igrpoption_nlgrpsoption_grpids))
1113            continue;
1114 NC_CHECK(copy_var_data(igrpvaridogid));
1115    }
1116#ifdef USE_NETCDF4
1117    /* Copy data from subgroups */
1118    stat = nc_inq_grps(igrp, &numgrpsNULL);
1119    grpids = (int *)emalloc((numgrps + 1) * sizeof(int));
1120    NC_CHECK(nc_inq_grps(igrp, &numgrpsgrpids));
1121
1122    for(i = 0; i < numgrpsi++) {
1123        if (!option_grpstruct && !group_wanted(grpids[i], option_nlgrpsoption_grpids))
1124            continue;
1125 NC_CHECK(copy_data(grpids[i], ogid));
1126    }
1127    free(grpids);
1128#endif /* USE_NETCDF4 */
1129    freeidlist(vlist);
1130    return stat;
1131}
1132
1133/* Count total number of dimensions in ncid and all its descendant subgroups */
1134int
1135count_dims(int ncid) {
1136    int numgrps;
1137    int ndims;
1138    NC_CHECK(nc_inq_ndims(ncid, &ndims));
1139#ifdef USE_NETCDF4
1140    NC_CHECK(nc_inq_grps(ncid, &numgrpsNULL));
1141    if(numgrps > 0) {
1142 int igrp;
1143 int *grpids = emalloc(numgrps * sizeof(int));
1144 NC_CHECK(nc_inq_grps(ncid, &numgrpsgrpids));
1145 for(igrp = 0; igrp < numgrpsigrp++) {
1146     ndims += count_dims(grpids[igrp]);
1147 }
1148 free(grpids);
1149    }
1150#endif /* USE_NETCDF4 */
1151    return ndims;
1152}
1153
1154/* Test if special case: netCDF-3 file with more than one record
1155 * variable.  Performance can be very slow for this case when the disk
1156 * block size is large, there are many record variables, and a
1157 * record's worth of data for some variables is smaller than the disk
1158 * block size.  In this case, copying the record variables a variable
1159 * at a time causes much rereading of record data, so instead we want
1160 * to copy data a record at a time. */
1161static int
1162nc3_special_case(int ncid, int kind) {
1163    if (kind == NC_FORMAT_CLASSIC ||  kind == NC_FORMAT_64BIT_OFFSET
1164        || kind == NC_FORMAT_CDF5) {
1165 int recdimid = 0;
1166 NC_CHECK(nc_inq_unlimdim(ncid, &recdimid));
1167 if (recdimid != -1) { /* we have a record dimension */
1168     int nvars;
1169     int varid;
1170     NC_CHECK(nc_inq_nvars(ncid, &nvars));
1171     for (varid = 0; varid < nvarsvarid++) {
1172 int *dimids = 0;
1173 int ndims;
1174 NC_CHECKnc_inq_varndims(ncidvarid, &ndims) );
1175 if (ndims > 0) {
1176     int dimids0;
1177     dimids = (int *) emalloc((ndims + 1) * sizeof(int));
1178     NC_CHECKnc_inq_vardimid(ncidvariddimids) );
1179     dimids0 = dimids[0];
1180     free(dimids);
1181     if(dimids0 == recdimid) {
1182 return 1; /* found a record variable */
1183     }
1184 }
1185     }
1186 }
1187    }
1188    return 0;
1189}
1190
1191/* Classify variables in ncid as either fixed-size variables (with no
1192 * unlimited dimension) or as record variables (with an unlimited
1193 * dimension) */
1194static int
1195classify_vars(
1196    int ncid, /* netCDF ID */
1197    size_t *nf, /* for returning number of fixed-size variables */
1198    int **fvars, /* the array of fixed_size variable IDS, caller should free */
1199    size_t *nr, /* for returning number of record variables */
1200    int **rvars) /* the array of record variable IDs, caller should free */
1201{
1202    int varid;
1203    int nvars;
1204    NC_CHECK(nc_inq_nvars(ncid, &nvars));
1205    *nf = 0;
1206    *fvars = (int *) emalloc(nvars * sizeof(int));
1207    *nr = 0;
1208    *rvars = (int *) emalloc(nvars * sizeof(int));
1209    for (varid = 0; varid < nvarsvarid++) {
1210 if (isrecvar(ncidvarid)) {
1211     (*rvars)[*nr] = varid;
1212     (*nr)++;
1213 } else {
1214     (*fvars)[*nf] = varid;
1215     (*nf)++;
1216 }
1217    }
1218    return NC_NOERR;
1219}
1220
1221/* Only called for classic format or 64-bit offset format files, to speed up special case */
1222static int
1223copy_fixed_size_data(int igrp, int ogrp, size_t nfixed_vars, int *fixed_varids) {
1224    size_t ivar;
1225    /* for each fixed-size variable, copy data */
1226    for (ivar = 0; ivar < nfixed_varsivar++) {
1227 int varid = fixed_varids[ivar];
1228 NC_CHECK(copy_var_data(igrpvaridogrp));
1229    }
1230    if (fixed_varids)
1231 free(fixed_varids);
1232    return NC_NOERR;
1233}
1234
1235/* copy a record's worth of data for a variable from input to output */
1236static int
1237copy_rec_var_data(int ncid,  /* input */
1238   int ogrp,  /* output */
1239   int irec,  /* record number */
1240   int varid,  /* input variable id */
1241   int ovarid,  /* output variable id */
1242   size_t *start,   /* start indices for record data */
1243   size_t *count,   /* edge lengths for record data */
1244   void *buf    /* buffer large enough to hold data */
1245    )
1246{
1247    NC_CHECK(nc_get_vara(ncidvaridstartcountbuf));
1248    NC_CHECK(nc_put_vara(ogrpovaridstartcountbuf));
1249    return NC_NOERR;
1250}
1251
1252/* Only called for classic format or 64-bit offset format files, to speed up special case */
1253static int
1254copy_record_data(int ncid, int ogrp, size_t nrec_vars, int *rec_varids) {
1255    int unlimid;
1256    size_t nrecs = 0; /* how many records? */
1257    size_t irec;
1258    size_t ivar;
1259    void **buf; /* space for reading in data for each variable */
1260    int *rec_ovarids; /* corresponding varids in output */
1261    size_t **start;
1262    size_t **count;
1263    NC_CHECK(nc_inq_unlimdim(ncid, &unlimid));
1264    NC_CHECK(nc_inq_dimlen(ncidunlimid, &nrecs));
1265    buf = (void **) emalloc(nrec_vars * sizeof(void *));
1266    rec_ovarids = (int *) emalloc(nrec_vars * sizeof(int));
1267    start = (size_t **) emalloc(nrec_vars * sizeof(size_t*));
1268    count = (size_t **) emalloc(nrec_vars * sizeof(size_t*));
1269    /* get space to hold one record's worth of data for each record variable */
1270    for (ivar = 0; ivar < nrec_varsivar++) {
1271 int varid;
1272 int ndims;
1273 int *dimids;
1274 size_t value_size;
1275 int dimid;
1276 int ii;
1277 size_t nvals;
1278 char varname[NC_MAX_NAME];
1279 varid = rec_varids[ivar];
1280 NC_CHECK(nc_inq_varndims(ncidvarid, &ndims));
1281 dimids = (int *) emalloc((1 + ndims) * sizeof(int));
1282 start[ivar] = (size_t *) emalloc(ndims * sizeof(size_t));
1283 count[ivar] = (size_t *) emalloc(ndims * sizeof(size_t));
1284 NC_CHECK(nc_inq_vardimid (ncidvariddimids));
1285 value_size = val_size(ncidvarid);
1286 nvals = 1;
1287 for(ii = 1; ii < ndimsii++) { /* for rec size, don't include first record dimension */
1288     size_t dimlen;
1289     dimid = dimids[ii];
1290     NC_CHECK(nc_inq_dimlen(nciddimid, &dimlen));
1291     nvals *= dimlen;
1292     start[ivar][ii] = 0;
1293     count[ivar][ii] = dimlen;
1294 }
1295 start[ivar][0] = 0;
1296 count[ivar][0] = 1; /* 1 record */
1297 buf[ivar] = (void *) emalloc(nvals * value_size);
1298 NC_CHECK(nc_inq_varname(ncidvaridvarname));
1299 NC_CHECK(nc_inq_varid(ogrpvarname, &rec_ovarids[ivar]));
1300 if(dimids)
1301     free(dimids);
1302    }
1303
1304    /* for each record, copy all variable data */
1305    for(irec = 0; irec < nrecsirec++) {
1306 for (ivar = 0; ivar < nrec_varsivar++) {
1307     int varidovarid;
1308     varid = rec_varids[ivar];
1309     ovarid = rec_ovarids[ivar];
1310     start[ivar][0] = irec;
1311     NC_CHECK(copy_rec_var_data(ncidogrpirecvaridovarid,
1312        start[ivar], count[ivar], buf[ivar]));
1313 }
1314    }
1315    for (ivar = 0; ivar < nrec_varsivar++) {
1316 if(start[ivar])
1317     free(start[ivar]);
1318 if(count[ivar])
1319     free(count[ivar]);
1320    }
1321    if(start)
1322 free(start);
1323    if(count)
1324 free(count);
1325    for (ivar = 0; ivar < nrec_varsivar++) {
1326 if(buf[ivar]) {
1327     free(buf[ivar]);
1328 }
1329    }
1330    if (rec_varids)
1331 free(rec_varids);
1332    if(buf)
1333 free(buf);
1334    if(rec_ovarids)
1335 free(rec_ovarids);
1336    return NC_NOERR;
1337}
1338
1339/* copy infile to outfile using netCDF API
1340 */
1341static int
1342copy(char* infile, char* outfile)
1343{
1344    int stat = NC_NOERR;
1345    int igrpogrp;
1346    int inkindoutkind;
1347    int open_mode = NC_NOWRITE;
1348    int create_mode = NC_CLOBBER;
1349    size_t ndims;
1350
1351    if(option_read_diskless) {
1352 open_mode |= NC_DISKLESS;
1353    }
1354
1355    NC_CHECK(nc_open(infileopen_mode, &igrp));
1356
1357    NC_CHECK(nc_inq_format(igrp, &inkind));
1358
1359/* option_kind specifies which netCDF format for output, one of
1360 *
1361 *     SAME_AS_INPUT, NC_FORMAT_CLASSIC, NC_FORMAT_64BIT,
1362 *     NC_FORMAT_NETCDF4, NC_FORMAT_NETCDF4_CLASSIC
1363 *
1364 * However, if compression or shuffling was specified and kind was SAME_AS_INPUT,
1365 * option_kind is changed to NC_FORMAT_NETCDF4_CLASSIC, if input format is
1366 * NC_FORMAT_CLASSIC or NC_FORMAT_64BIT .
1367 */
1368    outkind = option_kind;
1369    if (option_kind == SAME_AS_INPUT) { /* default, kind not specified */
1370 outkind = inkind;
1371 /* Deduce output kind if netCDF-4 features requested */
1372 if (inkind == NC_FORMAT_CLASSIC || inkind == NC_FORMAT_64BIT_OFFSET
1373     || inkind == NC_FORMAT_CDF5) {
1374     if (option_deflate_level > 0 ||
1375 option_shuffle_vars == NC_SHUFFLE ||
1376 option_chunkspec)
1377     {
1378 outkind = NC_FORMAT_NETCDF4_CLASSIC;
1379     }
1380 }
1381    }
1382
1383#ifdef USE_NETCDF4
1384    if(option_chunkspec) {
1385 /* Now that input is open, can parse option_chunkspec into binary
1386  * structure. */
1387 NC_CHECK(chunkspec_parse(igrpoption_chunkspec));
1388    }
1389#endif /* USE_NETCDF4 */
1390
1391 /* Check if any vars in -v don't exist */
1392    if(missing_vars(igrpoption_nlvarsoption_lvars))
1393 exit(EXIT_FAILURE);
1394
1395    if(option_nlgrps > 0) {
1396 if(inkind != NC_FORMAT_NETCDF4) {
1397     error("Group list (-g ...) only permitted for netCDF-4 file");
1398     exit(EXIT_FAILURE);
1399 }
1400 /* Check if any grps in -g don't exist */
1401 if(grp_matches(igrpoption_nlgrpsoption_lgrpsoption_grpids) == 0)
1402     exit(EXIT_FAILURE);
1403    }
1404
1405    if(option_write_diskless)
1406 create_mode |= NC_WRITE | NC_DISKLESS; /* NC_WRITE persists diskless file on close */
1407    switch(outkind) {
1408    case NC_FORMAT_CLASSIC:
1409 /* nothing to do */
1410 break;
1411    case NC_FORMAT_64BIT_OFFSET:
1412 create_mode |= NC_64BIT_OFFSET;
1413 break;
1414    case NC_FORMAT_CDF5:
1415 create_mode |= NC_64BIT_DATA;
1416 break;
1417#ifdef USE_NETCDF4
1418    case NC_FORMAT_NETCDF4:
1419 create_mode |= NC_NETCDF4;
1420 break;
1421    case NC_FORMAT_NETCDF4_CLASSIC:
1422 create_mode |= NC_NETCDF4 | NC_CLASSIC_MODEL;
1423 break;
1424#else
1425    case NC_FORMAT_NETCDF4:
1426    case NC_FORMAT_NETCDF4_CLASSIC:
1427 error("netCDF library built with --disable-netcdf4, can't create netCDF-4 files");
1428 break;
1429#endif /* USE_NETCDF4 */
1430    default:
1431 error("bad value for option specifying desired output format, see usage\n");
1432 break;
1433    }
1434    NC_CHECK(nc_create(outfilecreate_mode, &ogrp));
1435    NC_CHECK(nc_set_fill(ogrpNC_NOFILLNULL));
1436
1437#ifdef USE_NETCDF4
1438    /* Because types in one group may depend on types in a different
1439     * group, need to create all groups before defining types */
1440    if(inkind == NC_FORMAT_NETCDF4) {
1441 NC_CHECK(copy_groups(igrpogrp));
1442 NC_CHECK(copy_types(igrpogrp));
1443    }
1444#endif /* USE_NETCDF4 */
1445
1446    ndims = count_dims(igrp);
1447    NC_CHECK(dimmap_init(ndims));
1448    NC_CHECK(copy_schema(igrpogrp));
1449    NC_CHECK(nc_enddef(ogrp));
1450
1451    /* For performance, special case netCDF-3 input or output file with record
1452     * variables, to copy a record-at-a-time instead of a
1453     * variable-at-a-time. */
1454    /* TODO: check that these special cases work with -v option */
1455    if(nc3_special_case(igrpinkind)) {
1456 size_t nfixed_varsnrec_vars;
1457 int *fixed_varids;
1458 int *rec_varids;
1459 NC_CHECK(classify_vars(igrp, &nfixed_vars, &fixed_varids, &nrec_vars, &rec_varids));
1460 NC_CHECK(copy_fixed_size_data(igrpogrpnfixed_varsfixed_varids));
1461 NC_CHECK(copy_record_data(igrpogrpnrec_varsrec_varids));
1462    } else if (nc3_special_case(ogrpoutkind)) {
1463 size_t nfixed_varsnrec_vars;
1464 int *fixed_varids;
1465 int *rec_varids;
1466 /* classifies output vars, but returns input varids */
1467 NC_CHECK(classify_vars(ogrp, &nfixed_vars, &fixed_varids, &nrec_vars, &rec_varids));
1468 NC_CHECK(copy_fixed_size_data(igrpogrpnfixed_varsfixed_varids));
1469 NC_CHECK(copy_record_data(igrpogrpnrec_varsrec_varids));
1470    } else {
1471 NC_CHECK(copy_data(igrpogrp)); /* recursive, to handle nested groups */
1472    }
1473
1474    NC_CHECK(nc_close(igrp));
1475    NC_CHECK(nc_close(ogrp));
1476    return stat;
1477}
1478
1479/*
1480 * For non-negative numeric string with multiplier suffix K, M, G, T,
1481 * or P (or lower-case equivalent), return corresponding value
1482 * incorporating multiplier 1000, 1000000, 1.0d9, ... 1.0d15, or -1.0
1483 * for error.
1484 */
1485static double
1486double_with_suffix(char *str) {
1487    double dval;
1488    char *suffix = 0;
1489    errno = 0;
1490    dval = strtod(str, &suffix);
1491    if(dval < 0 || errno != 0)
1492 return -1.0;
1493    if(*suffix) {
1494 switch (*suffix) {
1495 case 'k': case 'K':
1496     dval *= 1000;
1497     break;
1498 case 'm': case 'M':
1499     dval *= 1000000;
1500     break;
1501 case 'g': case 'G':
1502     dval *= 1000000000;
1503     break;
1504 case 't': case 'T':
1505     dval *= 1.0e12;
1506     break;
1507 case 'p': case 'P':
1508     dval *= 1.0e15;
1509     break;
1510 default:
1511     dval = -1.0; /* error, suffix multiplier must be K, M, G, or T */
1512 }
1513    }
1514    return dval;
1515}
1516
1517static void
1518usage(void)
1519{
1520#define USAGE   "\
1521  [-k kind] specify kind of netCDF format for output file, default same as input\n\
1522     kind strings: 'classic', '64-bit offset', 'cdf5',\n\
1523                          'netCDF-4', 'netCDF-4 classic model'\n\
1524  [-3]      netCDF classic output (same as -k 'classic')\n\
1525  [-6]      64-bit-offset output (same as -k '64-bit offset')\n\
1526  [-4]      netCDF-4 output (same as -k 'netCDF-4')\n\
1527  [-7]      netCDF-4-classic output (same as -k 'netCDF-4 classic model')\n\
1528  [-5]      CDF5 output (same as -k 'cdf5)\n\
1529  [-d n]    set output deflation compression level, default same as input (0=none 9=max)\n\
1530  [-s]      add shuffle option to deflation compression\n\
1531  [-c chunkspec] specify chunking for dimensions, e.g. \"dim1/N1,dim2/N2,...\"\n\
1532  [-u]      convert unlimited dimensions to fixed-size dimensions in output copy\n\
1533  [-w]      write whole output file from diskless netCDF on close\n\
1534  [-v var1,...] include data for only listed variables, but definitions for all variables\n\
1535  [-V var1,...] include definitions and data for only listed variables\n\
1536  [-g grp1,...] include data for only variables in listed groups, but all definitions\n\
1537  [-G grp1,...] include definitions and data only for variables in listed groups\n\
1538  [-m n]    set size in bytes of copy buffer, default is 5000000 bytes\n\
1539  [-h n]    set size in bytes of chunk_cache for chunked variables\n\
1540  [-e n]    set number of elements that chunk_cache can hold\n\
1541  [-r]      read whole input file into diskless file on open (classic or 64-bit offset or cdf5 formats only)\n\
1542  infile    name of netCDF input file\n\
1543  outfile   name for netCDF output file\n"
1544
1545    /* Don't document this flaky option until it works better */
1546    /* [-x]      use experimental computed estimates for variable-specific chunk caches\n\ */
1547
1548    error("%s [-k kind] [-[3|4|6|7]] [-d n] [-s] [-c chunkspec] [-u] [-w] [-[v|V] varlist] [-[g|G] grplist] [-m n] [-h n] [-e n] [-r] infile outfile\n%s\nnetCDF library version %s",
1549   prognameUSAGEnc_inq_libvers());
1550}
1551
1552int
1553main(int argc, char**argv)
1554{
1555    char* inputfile = NULL;
1556    char* outputfile = NULL;
1557    int c;
1558
1559/* table of formats for legal -k values */
1560    struct Kvalues {
1561 char* name;
1562 int kind;
1563    } legalkinds[] = {
1564 /* NetCDF-3 classic format (32-bit offsets) */
1565 {"classic", NC_FORMAT_CLASSIC}, /* canonical format name */
1566 {"nc3", NC_FORMAT_CLASSIC}, /* short format name */
1567 {"1", NC_FORMAT_CLASSIC}, /* deprecated, use "-3" or "-k nc3" instead */
1568
1569 /* NetCDF-3 64-bit offset format */
1570 {"64-bit offset", NC_FORMAT_64BIT_OFFSET}, /* canonical format name */
1571 {"nc6", NC_FORMAT_64BIT_OFFSET},     /* short format name */
1572 {"2", NC_FORMAT_64BIT_OFFSET}, /* deprecated, use "-6" or "-k nc6" instead */
1573 {"64-bit-offset", NC_FORMAT_64BIT_OFFSET}, /* deprecated alias */
1574
1575 /* NetCDF-4 HDF5-based format */
1576 {"netCDF-4", NC_FORMAT_NETCDF4}, /* canonical format name */
1577 {"nc4", NC_FORMAT_NETCDF4},  /* short format name */
1578 {"3", NC_FORMAT_NETCDF4}, /* deprecated, use "-4" or "-k nc4" instead */
1579 {"netCDF4", NC_FORMAT_NETCDF4}, /* deprecated aliases */
1580 {"hdf5", NC_FORMAT_NETCDF4},
1581 {"enhanced", NC_FORMAT_NETCDF4},
1582
1583 /* NetCDF-4 HDF5-based format, restricted to classic data model */
1584 {"netCDF-4 classic model", NC_FORMAT_NETCDF4_CLASSIC}, /* canonical format name */
1585 {"nc7", NC_FORMAT_NETCDF4_CLASSIC}, /* short format name */
1586 {"4", NC_FORMAT_NETCDF4_CLASSIC}, /* deprecated, use "-7" or -k nc7" */
1587 {"netCDF-4-classic", NC_FORMAT_NETCDF4_CLASSIC}, /* deprecated aliases */
1588 {"netCDF-4_classic", NC_FORMAT_NETCDF4_CLASSIC},
1589 {"netCDF4_classic", NC_FORMAT_NETCDF4_CLASSIC},
1590 {"hdf5-nc3", NC_FORMAT_NETCDF4_CLASSIC},
1591 {"enhanced-nc3", NC_FORMAT_NETCDF4_CLASSIC},
1592
1593 /* The 64-bit data (CDF5) kind (5) */
1594 {"5", NC_FORMAT_CDF5},
1595 {"64-bit-data", NC_FORMAT_CDF5},
1596 {"64-bit data", NC_FORMAT_CDF5},
1597 {"nc5", NC_FORMAT_CDF5},
1598 {"cdf5", NC_FORMAT_CDF5},
1599
1600 /* null terminate*/
1601 {NULL,0}
1602    };
1603
1604    opterr = 1;
1605    progname = argv[0];
1606
1607    if (argc <= 1)
1608    {
1609       usage();
1610    }
1611
1612    while ((c = getopt(argcargv, "k:3467d:sum:c:h:e:rwxg:G:v:V:")) != -1) {
1613 switch(c) {
1614        case 'k': /* for specifying variant of netCDF format to be generated
1615                     Format names:
1616                       "classic" or "nc3"
1617                       "64-bit offset" or "nc6"
1618        "netCDF-4" or "nc4"
1619        "netCDF-4 classic model" or "nc7"
1620                       "64-bit-data" | "64-bit data" | "cdf5" | "nc5"
1621                     Format version numbers (deprecated):
1622                       1 (=> classic)
1623                       2 (=> 64-bit offset)
1624                       3 (=> netCDF-4)
1625                       4 (=> netCDF-4 classic model)
1626                       5 (=> classic 64 bit data, CDF-5)
1627    */
1628     {
1629 struct Kvalueskvalue;
1630 char *kind_name = (char *) emalloc(strlen(optarg)+1);
1631 (void)strcpy(kind_nameoptarg);
1632         for(kvalue=legalkinds;kvalue->name;kvalue++) {
1633     if(strcmp(kind_name,kvalue->name) == 0) {
1634         option_kind = kvalue->kind;
1635 break;
1636     }
1637 }
1638 if(kvalue->name == NULL) {
1639     error("invalid output format: %s", kind_name);
1640 }
1641     }
1642     break;
1643 case '3': /* output format is classic (netCDF-3) */
1644     option_kind = NC_FORMAT_CLASSIC;
1645     break;
1646 case '5': /* output format is cdf5 */
1647     option_kind = NC_FORMAT_CDF5;
1648     break;
1649 case '6': /* output format is 64-bit-offset (netCDF-3 version 2) */
1650     option_kind = NC_FORMAT_64BIT_OFFSET;
1651     break;
1652 case '4': /* output format is netCDF-4 (variant of HDF5) */
1653     option_kind = NC_FORMAT_NETCDF4;
1654     break;
1655 case '7': /* output format is netCDF-4 (restricted to classic model)*/
1656     option_kind = NC_FORMAT_NETCDF4_CLASSIC;
1657     break;
1658 case 'd': /* non-default compression level specified */
1659     option_deflate_level = strtol(optargNULL, 10);
1660     if(option_deflate_level < 0 || option_deflate_level > 9) {
1661 error("invalid deflation level: %d", option_deflate_level);
1662     }
1663     break;
1664 case 's': /* shuffling, may improve compression */
1665     option_shuffle_vars = NC_SHUFFLE;
1666     break;
1667 case 'u': /* convert unlimited dimensions to fixed size */
1668     option_fix_unlimdims = 1;
1669     break;
1670 case 'm': /* non-default size of data copy buffer */
1671 {
1672     double dval = double_with_suffix(optarg); /* "K" for kilobytes. "M" for megabytes, ... */
1673     if(dval < 0)
1674 error("Suffix used for '-m' option value must be K, M, G, T, or P");
1675     option_copy_buffer_size = dval;
1676     break;
1677 }
1678 case 'h': /* non-default size of chunk cache */
1679 {
1680     double dval = double_with_suffix(optarg); /* "K" for kilobytes. "M" for megabytes, ... */
1681     if(dval < 0)
1682 error("Suffix used for '-h' option value must be K, M, G, T, or P");
1683     option_chunk_cache_size = dval;
1684     break;
1685 }
1686 case 'e': /* number of elements chunk cache can hold */
1687 {
1688     double dval = double_with_suffix(optarg); /* "K" for kilobytes. "M" for megabytes, ... */
1689     if(dval < 0 )
1690 error("Suffix used for '-e' option value must be K, M, G, T, or P");
1691     option_chunk_cache_nelems = (long)dval;
1692     break;
1693 }
1694 case 'r':
1695     option_read_diskless = 1; /* read into memory on open */
1696     break;
1697 case 'w':
1698     option_write_diskless = 1; /* write to memory, persist on close */
1699     break;
1700 case 'x': /* use experimental variable-specific chunk caches */
1701     option_compute_chunkcaches = 1;
1702     break;
1703 case 'c':               /* optional chunking spec for each dimension in list */
1704     /* save chunkspec string for parsing later, once we know input ncid */
1705     option_chunkspec = strdup(optarg);
1706     break;
1707 case 'g': /* group names */
1708     /* make list of names of groups specified */
1709     make_lgrps (optarg, &option_nlgrps, &option_lgrps, &option_grpids);
1710     option_grpstruct = true;
1711     break;
1712 case 'G': /* group names */
1713     /* make list of names of groups specified */
1714     make_lgrps (optarg, &option_nlgrps, &option_lgrps, &option_grpids);
1715     option_grpstruct = false;
1716     break;
1717 case 'v': /* variable names */
1718     /* make list of names of variables specified */
1719     make_lvars (optarg, &option_nlvars, &option_lvars);
1720     option_varstruct = true;
1721     break;
1722 case 'V': /* variable names */
1723     /* make list of names of variables specified */
1724     make_lvars (optarg, &option_nlvars, &option_lvars);
1725     option_varstruct = false;
1726     break;
1727 default:
1728     usage();
1729        }
1730    }
1731    argc -= optind;
1732    argv += optind;
1733
1734    if (argc != 2) {
1735 error("one input file and one output file required");
1736    }
1737    inputfile = argv[0];
1738    outputfile = argv[1];
1739
1740    if(strcmp(inputfileoutputfile) == 0) {
1741 error("output would overwrite input");
1742    }
1743
1744    if(copy(inputfileoutputfile) != NC_NOERR)
1745        exit(EXIT_FAILURE);
1746    exit(EXIT_SUCCESS);
1747}
1748END_OF_MAIN()


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