Coverage Report

Created: 2026-02-26 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/netcdf-c/libdispatch/dfile.c
Line
Count
Source
1
/**
2
 * @file
3
 *
4
 * File create and open functions
5
 *
6
 * These functions end up calling functions in one of the dispatch
7
 * layers (netCDF-4, dap server, etc).
8
 *
9
 * Copyright 2018 University Corporation for Atmospheric
10
 * Research/Unidata. See COPYRIGHT file for more info.
11
 */
12
13
#include "config.h"
14
#include <stdlib.h>
15
#ifdef HAVE_STRING_H
16
#include <string.h>
17
#endif
18
#ifdef HAVE_SYS_RESOURCE_H
19
#include <sys/resource.h>
20
#endif
21
#ifdef HAVE_SYS_TYPES_H
22
#include <sys/types.h>
23
#endif
24
#ifdef HAVE_SYS_STAT_H
25
#include <sys/stat.h>
26
#endif
27
28
#ifdef HAVE_UNISTD_H
29
#include <unistd.h> /* lseek() */
30
#endif
31
32
#ifdef HAVE_STDIO_H
33
#include <stdio.h>
34
#endif
35
36
#include "ncdispatch.h"
37
#include "netcdf_mem.h"
38
#include "ncpathmgr.h"
39
#include "fbits.h"
40
41
#undef DEBUG
42
43
#ifndef nulldup
44
 #define nulldup(s) ((s)?strdup(s):NULL)
45
#endif
46
47
48
/* User-defined formats - expanded from 2 to 10 slots.
49
 * These arrays store dispatch tables and magic numbers for up to 10 custom formats.
50
 * Array-based design replaces the previous individual UDF0/UDF1 global variables. */
51
NC_Dispatch *UDF_dispatch_tables[NC_MAX_UDF_FORMATS] = {NULL};
52
char UDF_magic_numbers[NC_MAX_UDF_FORMATS][NC_MAX_MAGIC_NUMBER_LEN + 1] = {{0}};
53
54
/** Helper function to convert NC_UDFn mode flag to array index (0-9).
55
 * @param mode_flag The mode flag (NC_UDF0 through NC_UDF9)
56
 * @return Array index 0-9, or -1 if not a valid UDF flag or multiple UDF flags set
57
 * @note UDF flags are not sequential in bit positions due to conflicts with
58
 *       NC_NOATTCREORD (0x20000) and NC_NODIMSCALE_ATTACH (0x40000) */
59
0
static int udf_mode_to_index(int mode_flag) {
60
0
    int count = 0;
61
0
    int index = -1;
62
    
63
    /* Count how many UDF flags are set and remember the index */
64
0
    if (fIsSet(mode_flag, NC_UDF0)) { count++; index = 0; }
65
0
    if (fIsSet(mode_flag, NC_UDF1)) { count++; index = 1; }
66
0
    if (fIsSet(mode_flag, NC_UDF2)) { count++; index = 2; }
67
0
    if (fIsSet(mode_flag, NC_UDF3)) { count++; index = 3; }
68
0
    if (fIsSet(mode_flag, NC_UDF4)) { count++; index = 4; }
69
0
    if (fIsSet(mode_flag, NC_UDF5)) { count++; index = 5; }
70
0
    if (fIsSet(mode_flag, NC_UDF6)) { count++; index = 6; }
71
0
    if (fIsSet(mode_flag, NC_UDF7)) { count++; index = 7; }
72
0
    if (fIsSet(mode_flag, NC_UDF8)) { count++; index = 8; }
73
0
    if (fIsSet(mode_flag, NC_UDF9)) { count++; index = 9; }
74
    
75
    /* Only one UDF flag should be set at a time */
76
0
    if (count != 1)
77
0
        return -1;
78
    
79
0
    return index;
80
0
}
81
82
/** Helper function to convert NC_FORMATX_UDFn to array index (0-9).
83
 * @param formatx The format constant (NC_FORMATX_UDF0 through NC_FORMATX_UDF9)
84
 * @return Array index 0-9, or -1 if not a valid UDF format constant
85
 * @note Handles the gap in format numbering: UDF0=8, UDF1=9, UDF2=11, UDF3=12, etc.
86
 *       (NC_FORMATX_NCZARR=10 occupies the slot between UDF1 and UDF2) */
87
0
static int udf_formatx_to_index(int formatx) {
88
    /* UDF0 and UDF1 are sequential (8, 9) */
89
0
    if (formatx >= NC_FORMATX_UDF0 && formatx <= NC_FORMATX_UDF1)
90
0
        return formatx - NC_FORMATX_UDF0;
91
    /* UDF2-UDF9 start at 11 (skipping NCZARR at 10) */
92
0
    if (formatx >= NC_FORMATX_UDF2 && formatx <= NC_FORMATX_UDF9)
93
0
        return formatx - NC_FORMATX_UDF2 + 2;
94
0
    return -1;
95
0
}
96
97
/**************************************************/
98
99
100
/** \defgroup datasets NetCDF File and Data I/O
101
102
    NetCDF opens datasets as files or remote access URLs.
103
104
    A netCDF dataset that has not yet been opened can only be referred to
105
    by its dataset name. Once a netCDF dataset is opened, it is referred
106
    to by a netCDF ID, which is a small non-negative integer returned when
107
    you create or open the dataset. A netCDF ID is much like a file
108
    descriptor in C or a logical unit number in FORTRAN. In any single
109
    program, the netCDF IDs of distinct open netCDF datasets are
110
    distinct. A single netCDF dataset may be opened multiple times and
111
    will then have multiple distinct netCDF IDs; however at most one of
112
    the open instances of a single netCDF dataset should permit
113
    writing. When an open netCDF dataset is closed, the ID is no longer
114
    associated with a netCDF dataset.
115
116
    Functions that deal with the netCDF library include:
117
    - Get version of library.
118
    - Get error message corresponding to a returned error code.
119
120
    The operations supported on a netCDF dataset as a single object are:
121
    - Create, given dataset name and whether to overwrite or not.
122
    - Open for access, given dataset name and read or write intent.
123
    - Put into define mode, to add dimensions, variables, or attributes.
124
    - Take out of define mode, checking consistency of additions.
125
    - Close, writing to disk if required.
126
    - Inquire about the number of dimensions, number of variables,
127
    number of global attributes, and ID of the unlimited dimension, if
128
    any.
129
    - Synchronize to disk to make sure it is current.
130
    - Set and unset nofill mode for optimized sequential writes.
131
    - After a summary of conventions used in describing the netCDF
132
    interfaces, the rest of this chapter presents a detailed description
133
    of the interfaces for these operations.
134
*/
135
136
/**
137
 * Add handling of user-defined format.
138
 *
139
 * User-defined formats allow users to write a library which can read
140
 * their own proprietary format as if it were netCDF. This allows
141
 * existing netCDF codes to work on non-netCDF data formats.
142
 *
143
 * User-defined formats work by specifying a netCDF dispatch
144
 * table. The dispatch table is a struct of (mostly) C function
145
 * pointers. It contains pointers to the key functions of the netCDF
146
 * API. Once these functions are provided, and the dispatch table is
147
 * specified, the netcdf-c library can read any format.
148
 *
149
 * @note Unlike the public netCDF API, the dispatch table may not be
150
 * backward compatible between netCDF releases. Instead, it contains a
151
 * dispatch version number. If this number is not correct (i.e. does
152
 * not match the current dispatch table version), then ::NC_EINVAL
153
 * will be returned.
154
 *
155
 * @param mode_flag NC_UDF0 or NC_UDF1
156
 * @param dispatch_table Pointer to dispatch table to use for this user format.
157
 * @param magic_number Magic number used to identify file. Ignored if
158
 * NULL.
159
 *
160
 * @return ::NC_NOERR No error.
161
 * @return ::NC_EINVAL Invalid input.
162
 * @author Ed Hartnett
163
 * @ingroup datasets
164
 */
165
int
166
nc_def_user_format(int mode_flag, NC_Dispatch *dispatch_table, char *magic_number)
167
0
{
168
0
    int udf_index;
169
170
    /* Check inputs. */
171
0
    if (!dispatch_table)
172
0
        return NC_EINVAL;
173
0
    if (magic_number && strlen(magic_number) > NC_MAX_MAGIC_NUMBER_LEN)
174
0
        return NC_EINVAL;
175
176
    /* Check the version of the dispatch table provided. */
177
0
    if (dispatch_table->dispatch_version != NC_DISPATCH_VERSION)
178
0
        return NC_EINVAL;
179
    /* user defined magic numbers not allowed with netcdf3 modes */ 
180
0
    if (magic_number && (fIsSet(mode_flag, NC_64BIT_OFFSET) ||
181
0
                         fIsSet(mode_flag, NC_64BIT_DATA) ||
182
0
                        (fIsSet(mode_flag, NC_CLASSIC_MODEL) &&
183
0
                        !fIsSet(mode_flag, NC_NETCDF4))))
184
0
        return NC_EINVAL;
185
186
    /* Get the UDF index from the mode flag. */
187
0
    udf_index = udf_mode_to_index(mode_flag);
188
0
    if (udf_index < 0 || udf_index >= NC_MAX_UDF_FORMATS)
189
0
        return NC_EINVAL;
190
191
    /* Retain a pointer to the dispatch_table and a copy of the magic
192
     * number, if one was provided. */
193
0
    UDF_dispatch_tables[udf_index] = dispatch_table;
194
0
    if (magic_number) {
195
0
        strncpy(UDF_magic_numbers[udf_index], magic_number, NC_MAX_MAGIC_NUMBER_LEN);
196
        /* Ensure null-termination since strncpy doesn't guarantee it if source >= max length */
197
0
        UDF_magic_numbers[udf_index][NC_MAX_MAGIC_NUMBER_LEN] = '\0';
198
0
    }
199
200
0
    return NC_NOERR;
201
0
}
202
203
/**
204
 * Inquire about user-defined format.
205
 *
206
 * @param mode_flag NC_UDF0 or NC_UDF1
207
 * @param dispatch_table Pointer that gets pointer to dispatch table
208
 * to use for this user format, or NULL if this user-defined format is
209
 * not defined. Ignored if NULL.
210
 * @param magic_number Pointer that gets magic number used to identify
211
 * file, if one has been set. Magic number will be of max size
212
 * NC_MAX_MAGIC_NUMBER_LEN. Ignored if NULL.
213
 *
214
 * @return ::NC_NOERR No error.
215
 * @return ::NC_EINVAL Invalid input.
216
 * @author Ed Hartnett
217
 * @ingroup datasets
218
 */
219
int
220
nc_inq_user_format(int mode_flag, NC_Dispatch **dispatch_table, char *magic_number)
221
0
{
222
0
    int udf_index;
223
224
    /* Get the UDF index from the mode flag. */
225
0
    udf_index = udf_mode_to_index(mode_flag);
226
0
    if (udf_index < 0 || udf_index >= NC_MAX_UDF_FORMATS)
227
0
        return NC_EINVAL;
228
229
    /* Return the dispatch table and magic number. */
230
0
    if (dispatch_table)
231
0
        *dispatch_table = UDF_dispatch_tables[udf_index];
232
0
    if (magic_number)
233
0
        strncpy(magic_number, UDF_magic_numbers[udf_index], NC_MAX_MAGIC_NUMBER_LEN);
234
235
0
    return NC_NOERR;
236
0
}
237
238
/**  \ingroup datasets
239
     Create a new netCDF file.
240
241
     This function creates a new netCDF dataset, returning a netCDF ID that
242
     can subsequently be used to refer to the netCDF dataset in other
243
     netCDF function calls. The new netCDF dataset opened for write access
244
     and placed in define mode, ready for you to add dimensions, variables,
245
     and attributes.
246
247
     \param path The file name of the new netCDF dataset.
248
249
     \param cmode The creation mode flag. The following flags are available:
250
     NC_CLOBBER (overwrite existing file),
251
     NC_NOCLOBBER (do not overwrite existing file),
252
     NC_SHARE (limit write caching - netcdf classic files only),
253
     NC_64BIT_OFFSET (create 64-bit offset file),
254
     NC_64BIT_DATA (alias NC_CDF5) (create CDF-5 file),
255
     NC_NETCDF4 (create netCDF-4/HDF5 file),
256
     NC_CLASSIC_MODEL (enforce netCDF classic mode on netCDF-4/HDF5 files),
257
     NC_DISKLESS (store data in memory), and
258
     NC_PERSIST (force the NC_DISKLESS data from memory to a file),
259
     NC_MMAP (use MMAP for NC_DISKLESS instead of NC_INMEMORY -- deprecated).
260
     See discussion below.
261
262
     \param ncidp Pointer to location where returned netCDF ID is to be
263
     stored.
264
265
     <h2>The cmode Flag</h2>
266
267
     The cmode flag is used to control the type of file created, and some
268
     aspects of how it may be used.
269
270
     Setting NC_NOCLOBBER means you do not want to clobber (overwrite) an
271
     existing dataset; an error (NC_EEXIST) is returned if the specified
272
     dataset already exists.
273
274
     The NC_SHARE flag is appropriate when one process may be writing the
275
     dataset and one or more other processes reading the dataset
276
     concurrently; it means that dataset accesses are not buffered and
277
     caching is limited. Since the buffering scheme is optimized for
278
     sequential access, programs that do not access data sequentially may
279
     see some performance improvement by setting the NC_SHARE flag. This
280
     flag is ignored for netCDF-4 files.
281
282
     Setting NC_64BIT_OFFSET causes netCDF to create a 64-bit offset format
283
     file, instead of a netCDF classic format file. The 64-bit offset
284
     format imposes far fewer restrictions on very large (i.e. over 2 GB)
285
     data files. See Large File Support.
286
287
     Setting NC_64BIT_DATA (alias NC_CDF5) causes netCDF to create a CDF-5
288
     file format that supports large files (i.e. over 2GB) and large
289
     variables (over 2B array elements.). See Large File Support.
290
291
     A zero value (defined for convenience as NC_CLOBBER) specifies the
292
     default behavior: overwrite any existing dataset with the same file
293
     name and buffer and cache accesses for efficiency. The dataset will be
294
     in netCDF classic format. See NetCDF Classic Format Limitations.
295
296
     Setting NC_NETCDF4 causes netCDF to create a HDF5/NetCDF-4 file.
297
298
     Setting NC_CLASSIC_MODEL causes netCDF to enforce the classic data
299
     model in this file. (This only has effect for netCDF-4/HDF5 files, as
300
     CDF-1, 2 and 5 files always use the classic model.) When
301
     used with NC_NETCDF4, this flag ensures that the resulting
302
     netCDF-4/HDF5 file may never contain any new constructs from the
303
     enhanced data model. That is, it cannot contain groups, user defined
304
     types, multiple unlimited dimensions, or new atomic types. The
305
     advantage of this restriction is that such files are guaranteed to
306
     work with existing netCDF software.
307
308
     Setting NC_DISKLESS causes netCDF to create the file only in
309
     memory and to optionally write the final contents to the
310
     correspondingly named disk file. This allows for the use of
311
     files that have no long term purpose. Operating on an existing file
312
     in memory may also be faster. The decision on whether
313
     or not to "persist" the memory contents to a disk file is
314
     described in detail in the file docs/inmemory.md, which is
315
     definitive.  By default, closing a diskless fill will cause it's
316
     contents to be lost.
317
318
     If NC_DISKLESS is going to be used for creating a large classic
319
     file, it behooves one to use nc__create and specify an
320
     appropriately large value of the initialsz parameter to avoid to
321
     many extensions to the in-memory space for the file.  This flag
322
     applies to files in classic format and to file in extended
323
     format (netcdf-4).
324
325
     Note that nc_create(path,cmode,ncidp) is equivalent to the invocation of
326
     nc__create(path,cmode,NC_SIZEHINT_DEFAULT,NULL,ncidp).
327
328
     \returns ::NC_NOERR No error.
329
     \returns ::NC_EEXIST Specifying a file name of a file that exists and also specifying NC_NOCLOBBER.
330
     \returns ::NC_EPERM Attempting to create a netCDF file in a directory where you do not have permission to create files.
331
     \returns ::NC_ENOMEM System out of memory.
332
     \returns ::NC_ENFILE Too many files open.
333
     \returns ::NC_EHDFERR HDF5 error (netCDF-4 files only).
334
     \returns ::NC_EFILEMETA Error writing netCDF-4 file-level metadata in
335
     HDF5 file. (netCDF-4 files only).
336
     \returns ::NC_EDISKLESS if there was an error in creating the
337
     in-memory file.
338
339
     \note When creating a netCDF-4 file HDF5 error reporting is turned
340
     off, if it is on. This doesn't stop the HDF5 error stack from
341
     recording the errors, it simply stops their display to the user
342
     through stderr.
343
344
     <h1>Examples</h1>
345
346
     In this example we create a netCDF dataset named foo.nc; we want the
347
     dataset to be created in the current directory only if a dataset with
348
     that name does not already exist:
349
350
     @code
351
     #include <netcdf.h>
352
     ...
353
     int status = NC_NOERR;
354
     int ncid;
355
     ...
356
     status = nc_create("foo.nc", NC_NOCLOBBER, &ncid);
357
     if (status != NC_NOERR) handle_error(status);
358
     @endcode
359
360
     In this example we create a netCDF dataset named foo_large.nc. It will
361
     be in the 64-bit offset format.
362
363
     @code
364
     #include <netcdf.h>
365
     ...
366
     int status = NC_NOERR;
367
     int ncid;
368
     ...
369
     status = nc_create("foo_large.nc", NC_NOCLOBBER|NC_64BIT_OFFSET, &ncid);
370
     if (status != NC_NOERR) handle_error(status);
371
     @endcode
372
373
     In this example we create a netCDF dataset named foo_HDF5.nc. It will
374
     be in the HDF5 format.
375
376
     @code
377
     #include <netcdf.h>
378
     ...
379
     int status = NC_NOERR;
380
     int ncid;
381
     ...
382
     status = nc_create("foo_HDF5.nc", NC_NOCLOBBER|NC_NETCDF4, &ncid);
383
     if (status != NC_NOERR) handle_error(status);
384
     @endcode
385
386
     In this example we create a netCDF dataset named
387
     foo_HDF5_classic.nc. It will be in the HDF5 format, but will not allow
388
     the use of any netCDF-4 advanced features. That is, it will conform to
389
     the classic netCDF-3 data model.
390
391
     @code
392
     #include <netcdf.h>
393
     ...
394
     int status = NC_NOERR;
395
     int ncid;
396
     ...
397
     status = nc_create("foo_HDF5_classic.nc", NC_NOCLOBBER|NC_NETCDF4|NC_CLASSIC_MODEL, &ncid);
398
     if (status != NC_NOERR) handle_error(status);
399
     @endcode
400
401
     In this example we create an in-memory netCDF classic dataset named
402
     diskless.nc whose content will be lost when nc_close() is called.
403
404
     @code
405
     #include <netcdf.h>
406
     ...
407
     int status = NC_NOERR;
408
     int ncid;
409
     ...
410
     status = nc_create("diskless.nc", NC_DISKLESS, &ncid);
411
     if (status != NC_NOERR) handle_error(status);
412
     @endcode
413
414
     In this example we create a in-memory netCDF classic dataset named
415
     diskless.nc and specify that it should be made persistent
416
     in a file named diskless.nc when nc_close() is called.
417
418
     @code
419
     #include <netcdf.h>
420
     ...
421
     int status = NC_NOERR;
422
     int ncid;
423
     ...
424
     status = nc_create("diskless.nc", NC_DISKLESS|NC_PERSIST, &ncid);
425
     if (status != NC_NOERR) handle_error(status);
426
     @endcode
427
428
     A variant of nc_create(), nc__create() (note the double underscore) allows
429
     users to specify two tuning parameters for the file that it is
430
     creating.  */
431
int
432
nc_create(const char *path, int cmode, int *ncidp)
433
0
{
434
0
    return nc__create(path,cmode,NC_SIZEHINT_DEFAULT,NULL,ncidp);
435
0
}
436
437
/**
438
 * Create a netCDF file with some extra parameters controlling classic
439
 * file caching.
440
 *
441
 * Like nc_create(), this function creates a netCDF file.
442
 *
443
 * @param path The file name of the new netCDF dataset.
444
 * @param cmode The creation mode flag, the same as in nc_create().
445
 * @param initialsz On some systems, and with custom I/O layers, it
446
 * may be advantageous to set the size of the output file at creation
447
 * time. This parameter sets the initial size of the file at creation
448
 * time. This only applies to classic CDF-1, 2, and 5 files.  The
449
 * special value NC_SIZEHINT_DEFAULT (which is the value 0), lets the
450
 * netcdf library choose a suitable initial size.
451
 * @param chunksizehintp A pointer to the chunk size hint, which
452
 * controls a space versus time tradeoff, memory allocated in the
453
 * netcdf library versus number of system calls. Because of internal
454
 * requirements, the value may not be set to exactly the value
455
 * requested. The actual value chosen is returned by reference. Using
456
 * a NULL pointer or having the pointer point to the value
457
 * NC_SIZEHINT_DEFAULT causes the library to choose a default. How the
458
 * system chooses the default depends on the system. On many systems,
459
 * the "preferred I/O block size" is available from the stat() system
460
 * call, struct stat member st_blksize. If this is available it is
461
 * used. Lacking that, twice the system pagesize is used. Lacking a
462
 * call to discover the system pagesize, we just set default bufrsize
463
 * to 8192. The bufrsize is a property of a given open netcdf
464
 * descriptor ncid, it is not a persistent property of the netcdf
465
 * dataset. This only applies to classic files.
466
 * @param ncidp Pointer to location where returned netCDF ID is to be
467
 * stored.
468
 *
469
 * @note This function uses the same return codes as the nc_create()
470
 * function.
471
 *
472
 * @returns ::NC_NOERR No error.
473
 * @returns ::NC_ENOMEM System out of memory.
474
 * @returns ::NC_EHDFERR HDF5 error (netCDF-4 files only).
475
 * @returns ::NC_EFILEMETA Error writing netCDF-4 file-level metadata in
476
 * HDF5 file. (netCDF-4 files only).
477
 * @returns ::NC_EDISKLESS if there was an error in creating the
478
 * in-memory file.
479
 *
480
 * <h1>Examples</h1>
481
 *
482
 * In this example we create a netCDF dataset named foo_large.nc; we
483
 * want the dataset to be created in the current directory only if a
484
 * dataset with that name does not already exist. We also specify that
485
 * bufrsize and initial size for the file.
486
 *
487
 * @code
488
 #include <netcdf.h>
489
 ...
490
 int status = NC_NOERR;
491
 int ncid;
492
 int intialsz = 2048;
493
 int *bufrsize;
494
 ...
495
 *bufrsize = 1024;
496
 status = nc__create("foo.nc", NC_NOCLOBBER, initialsz, bufrsize, &ncid);
497
 if (status != NC_NOERR) handle_error(status);
498
 @endcode
499
 *
500
 * @ingroup datasets
501
 * @author Glenn Davis
502
 */
503
int
504
nc__create(const char *path, int cmode, size_t initialsz,
505
           size_t *chunksizehintp, int *ncidp)
506
0
{
507
0
    return NC_create(path, cmode, initialsz, 0,
508
0
                     chunksizehintp, 0, NULL, ncidp);
509
0
}
510
511
/** \ingroup datasets
512
    Create a netCDF file with the contents stored in memory.
513
514
    \param path Must be non-null, but otherwise only used to set the dataset name.
515
516
    \param mode the mode flags; Note that this procedure uses a limited set of flags because it forcibly sets NC_INMEMORY.
517
518
    \param initialsize (advisory) size to allocate for the created file
519
520
    \param ncidp Pointer to location where returned netCDF ID is to be
521
    stored.
522
523
    \returns ::NC_NOERR No error.
524
525
    \returns ::NC_ENOMEM Out of memory.
526
527
    \returns ::NC_EDISKLESS diskless io is not enabled for fails.
528
529
    \returns ::NC_EINVAL, etc. other errors also returned by nc_open.
530
531
    <h1>Examples</h1>
532
533
    In this example we use nc_create_mem() to create a classic netCDF dataset
534
    named foo.nc. The initial size is set to 4096.
535
536
    @code
537
    #include <netcdf.h>
538
    ...
539
    int status = NC_NOERR;
540
    int ncid;
541
    int mode = 0;
542
    size_t initialsize = 4096;
543
    ...
544
    status = nc_create_mem("foo.nc", mode, initialsize, &ncid);
545
    if (status != NC_NOERR) handle_error(status);
546
    @endcode
547
*/
548
549
int
550
nc_create_mem(const char* path, int mode, size_t initialsize, int* ncidp)
551
0
{
552
0
    if(mode & NC_MMAP) return NC_EINVAL;
553
0
    mode |= NC_INMEMORY; /* Specifically, do not set NC_DISKLESS */
554
0
    return NC_create(path, mode, initialsize, 0, NULL, 0, NULL, ncidp);
555
0
}
556
557
/**
558
 * @internal Create a file with special (deprecated) Cray settings.
559
 *
560
 * @deprecated This function was used in the old days with the Cray at
561
 * NCAR. The Cray is long gone, and this call is supported only for
562
 * backward compatibility. Use nc_create() instead.
563
 *
564
 * @param path File name.
565
 * @param cmode Create mode.
566
 * @param initialsz Initial size of metadata region for classic files,
567
 * ignored for other files.
568
 * @param basepe Deprecated parameter from the Cray days.
569
 * @param chunksizehintp A pointer to the chunk size hint. This only
570
 * applies to classic files.
571
 * @param ncidp Pointer that gets ncid.
572
 *
573
 * @return ::NC_NOERR No error.
574
 * @author Glenn Davis
575
 */
576
int
577
nc__create_mp(const char *path, int cmode, size_t initialsz,
578
              int basepe, size_t *chunksizehintp, int *ncidp)
579
0
{
580
0
    return NC_create(path, cmode, initialsz, basepe,
581
0
                     chunksizehintp, 0, NULL, ncidp);
582
0
}
583
584
/**
585
 * Open an existing netCDF file.
586
 *
587
 * This function opens an existing netCDF dataset for access. It
588
 * determines the underlying file format automatically. Use the same
589
 * call to open a netCDF classic or netCDF-4 file.
590
 *
591
 * @param path File name for netCDF dataset to be opened. When the dataset
592
 * is located on some remote server, then the path may be an OPeNDAP URL
593
 * rather than a file path.
594
 * @param omode The open mode flag may include NC_WRITE (for read/write
595
 * access) and NC_SHARE (see below) and NC_DISKLESS (see below).
596
 * @param ncidp Pointer to location where returned netCDF ID is to be
597
 * stored.
598
 *
599
 * <h2>Open Mode</h2>
600
 *
601
 * A zero value (or ::NC_NOWRITE) specifies the default behavior: open
602
 * the dataset with read-only access, buffering and caching accesses
603
 * for efficiency.
604
 *
605
 * Otherwise, the open mode is ::NC_WRITE, ::NC_SHARE, or
606
 * ::NC_WRITE|::NC_SHARE. Setting the ::NC_WRITE flag opens the
607
 * dataset with read-write access. ("Writing" means any kind of change
608
 * to the dataset, including appending or changing data, adding or
609
 * renaming dimensions, variables, and attributes, or deleting
610
 * attributes.)
611
 *
612
 * The NC_SHARE flag is only used for netCDF classic
613
 * files. It is appropriate when one process may be writing the
614
 * dataset and one or more other processes reading the dataset
615
 * concurrently; it means that dataset accesses are not buffered and
616
 * caching is limited. Since the buffering scheme is optimized for
617
 * sequential access, programs that do not access data sequentially
618
 * may see some performance improvement by setting the NC_SHARE flag.
619
 *
620
 * This procedure may also be invoked with the NC_DISKLESS flag set in
621
 * the omode argument if the file to be opened is a classic format
622
 * file.  For nc_open(), this flag applies only to files in classic
623
 * format.  If the file is of type NC_NETCDF4, then the NC_DISKLESS
624
 * flag will be ignored.
625
 *
626
 * If NC_DISKLESS is specified, then the whole file is read completely
627
 * into memory. In effect this creates an in-memory cache of the file.
628
 * If the omode flag also specifies NC_PERSIST, then the in-memory cache
629
 * will be re-written to the disk file when nc_close() is called.  For
630
 * some kinds of manipulations, having the in-memory cache can speed
631
 * up file processing. But in simple cases, non-cached processing may
632
 * actually be faster than using cached processing.  You will need to
633
 * experiment to determine if the in-memory caching is worthwhile for
634
 * your application.
635
 *
636
 * Normally, NC_DISKLESS allocates space in the heap for storing the
637
 * in-memory file. If, however, the ./configure flags --enable-mmap is
638
 * used, and the additional omode flag NC_MMAP is specified, then the
639
 * file will be opened using the operating system MMAP facility.  This
640
 * flag only applies to files in classic format. Extended format
641
 * (netcdf-4) files will ignore the NC_MMAP flag.
642
 *
643
 * In most cases, using MMAP provides no advantage for just
644
 * NC_DISKLESS. The one case where using MMAP is an advantage is when
645
 * a file is to be opened and only a small portion of its data is to
646
 * be read and/or written.  In this scenario, MMAP will cause only the
647
 * accessed data to be retrieved from disk. Without MMAP, NC_DISKLESS
648
 * will read the whole file into memory on nc_open. Thus, MMAP will
649
 * provide some performance improvement in this case.
650
 *
651
 * It is not necessary to pass any information about the format of the
652
 * file being opened. The file type will be detected automatically by
653
 * the netCDF library.
654
 *
655
 * If a the path is a DAP URL, then the open mode is read-only.
656
 * Setting NC_WRITE will be ignored.
657
 *
658
 * As of version 4.3.1.2, multiple calls to nc_open with the same
659
 * path will return the same ncid value.
660
 *
661
 * @note When opening a netCDF-4 file HDF5 error reporting is turned
662
 * off, if it is on. This doesn't stop the HDF5 error stack from
663
 * recording the errors, it simply stops their display to the user
664
 * through stderr.
665
 *
666
 * nc_open()returns the value NC_NOERR if no errors
667
 * occurred. Otherwise, the returned status indicates an
668
 * error. Possible causes of errors include:
669
 *
670
 * Note that nc_open(path,omode,ncidp) is equivalent to the invocation
671
 * of nc__open(path,omode,NC_SIZEHINT_DEFAULT,NULL,ncidp).
672
 *
673
 * @returns ::NC_NOERR No error.
674
 * @returns ::NC_EPERM Attempting to create a netCDF file in a directory where you do not have permission to open files.
675
 * @returns ::NC_ENFILE Too many files open
676
 * @returns ::NC_ENOMEM Out of memory.
677
 * @returns ::NC_EHDFERR HDF5 error. (NetCDF-4 files only.)
678
 * @returns ::NC_EDIMMETA Error in netCDF-4 dimension metadata. (NetCDF-4 files only.)
679
 *
680
 * <h1>Examples</h1>
681
 *
682
 * Here is an example using nc_open()to open an existing netCDF dataset
683
 * named foo.nc for read-only, non-shared access:
684
 *
685
 * @code
686
 * #include <netcdf.h>
687
 *   ...
688
 * int status = NC_NOERR;
689
 * int ncid;
690
 *   ...
691
 * status = nc_open("foo.nc", 0, &ncid);
692
 * if (status != NC_NOERR) handle_error(status);
693
 * @endcode
694
 * @ingroup datasets
695
 * @author Glenn Davis, Ed Hartnett, Dennis Heimbigner
696
 */
697
int
698
nc_open(const char *path, int omode, int *ncidp)
699
0
{
700
0
    return NC_open(path, omode, 0, NULL, 0, NULL, ncidp);
701
0
}
702
703
/** \ingroup datasets
704
    Open a netCDF file with extra performance parameters for the classic
705
    library.
706
707
    \param path File name for netCDF dataset to be opened. When DAP
708
    support is enabled, then the path may be an OPeNDAP URL rather than a
709
    file path.
710
711
    \param omode The open mode flag may include NC_WRITE (for read/write
712
    access) and NC_SHARE as in nc_open().
713
714
    \param chunksizehintp A size hint for the classic library. Only
715
    applies to classic files. See below for more
716
    information.
717
718
    \param ncidp Pointer to location where returned netCDF ID is to be
719
    stored.
720
721
    <h1>The chunksizehintp Parameter</h1>
722
723
    The argument referenced by bufrsizehintp controls a space versus time
724
    tradeoff, memory allocated in the netcdf library versus number of
725
    system calls.
726
727
    Because of internal requirements, the value may not be set to exactly
728
    the value requested. The actual value chosen is returned by reference.
729
730
    Using a NULL pointer or having the pointer point to the value
731
    NC_SIZEHINT_DEFAULT causes the library to choose a default.
732
    How the system chooses the default depends on the system. On
733
    many systems, the "preferred I/O block size" is available from the
734
    stat() system call, struct stat member st_blksize. If this is
735
    available it is used. Lacking that, twice the system pagesize is used.
736
737
    Lacking a call to discover the system pagesize, we just set default
738
    bufrsize to 8192.
739
740
    The bufrsize is a property of a given open netcdf descriptor ncid, it
741
    is not a persistent property of the netcdf dataset.
742
743
744
    \returns ::NC_NOERR No error.
745
746
    \returns ::NC_ENOMEM Out of memory.
747
748
    \returns ::NC_EHDFERR HDF5 error. (NetCDF-4 files only.)
749
750
    \returns ::NC_EDIMMETA Error in netCDF-4 dimension metadata. (NetCDF-4
751
    files only.)
752
753
*/
754
int
755
nc__open(const char *path, int omode,
756
         size_t *chunksizehintp, int *ncidp)
757
0
{
758
    /* this API is for non-parallel access.
759
     * Note nc_open_par() also calls NC_open().
760
     */
761
0
    return NC_open(path, omode, 0, chunksizehintp, 0, NULL, ncidp);
762
0
}
763
764
/** \ingroup datasets
765
    Open a netCDF file with the contents taken from a block of memory.
766
767
    \param path Must be non-null, but otherwise only used to set the dataset name.
768
769
    \param omode the open mode flags; Note that this procedure uses a limited set of flags because it forcibly sets NC_INMEMORY.
770
771
    \param size The length of the block of memory being passed.
772
773
    \param memory Pointer to the block of memory containing the contents
774
    of a netcdf file.
775
776
    \param ncidp Pointer to location where returned netCDF ID is to be
777
    stored.
778
779
    \returns ::NC_NOERR No error.
780
781
    \returns ::NC_ENOMEM Out of memory.
782
783
    \returns ::NC_EDISKLESS diskless io is not enabled for fails.
784
785
    \returns ::NC_EINVAL, etc. other errors also returned by nc_open.
786
787
    <h1>Examples</h1>
788
789
    Here is an example using nc_open_mem() to open an existing netCDF dataset
790
    named foo.nc for read-only, non-shared access. It differs from the nc_open()
791
    example in that it assumes the contents of foo.nc have been read into memory.
792
793
    @code
794
    #include <netcdf.h>
795
    #include <netcdf_mem.h>
796
    ...
797
    int status = NC_NOERR;
798
    int ncid;
799
    size_t size;
800
    void* memory;
801
    ...
802
    size = <compute file size of foo.nc in bytes>;
803
    memory = malloc(size);
804
    ...
805
    status = nc_open_mem("foo.nc", 0, size, memory, &ncid);
806
    if (status != NC_NOERR) handle_error(status);
807
    @endcode
808
*/
809
int
810
nc_open_mem(const char* path, int omode, size_t size, void* memory, int* ncidp)
811
112
{
812
112
    NC_memio meminfo;
813
814
    /* Sanity checks */
815
112
    if(memory == NULL || size < MAGIC_NUMBER_LEN || path == NULL)
816
0
        return NC_EINVAL;
817
112
    if(omode & (NC_WRITE|NC_MMAP))
818
0
        return NC_EINVAL;
819
112
    omode |= (NC_INMEMORY); /* Note: NC_INMEMORY and NC_DISKLESS are mutually exclusive*/
820
112
    meminfo.size = size;
821
112
    meminfo.memory = memory;
822
112
    meminfo.flags = NC_MEMIO_LOCKED;
823
112
    return NC_open(path, omode, 0, NULL, 0, &meminfo, ncidp);
824
112
}
825
826
/** \ingroup datasets
827
    Open a netCDF file with the contents taken from a block of memory.
828
    Similar to nc_open_mem, but with parameters. Warning: if you do
829
    specify that the provided memory is locked, then <b>never</b>
830
    pass in non-heap allocated memory. Additionally, if not locked,
831
    then do not assume that the memory returned by nc_close_mem
832
    is the same as passed to nc_open_memio. You <b>must</b> check
833
    before attempting to free the original memory.
834
835
    \param path Must be non-null, but otherwise only used to set the dataset name.
836
837
    \param omode the open mode flags; Note that this procedure uses a limited set of flags because it forcibly sets NC_INMEMORY.
838
839
    \param params controlling parameters
840
841
    \param ncidp Pointer to location where returned netCDF ID is to be
842
    stored.
843
844
    \returns ::NC_NOERR No error.
845
846
    \returns ::NC_ENOMEM Out of memory.
847
848
    \returns ::NC_EDISKLESS diskless io is not enabled for fails.
849
850
    \returns ::NC_EINVAL, etc. other errors also returned by nc_open.
851
852
    <h1>Examples</h1>
853
854
    Here is an example using nc_open_memio() to open an existing netCDF dataset
855
    named foo.nc for read-only, non-shared access. It differs from the nc_open_mem()
856
    example in that it uses a parameter block.
857
858
    @code
859
    #include <netcdf.h>
860
    #include <netcdf_mem.h>
861
    ...
862
    int status = NC_NOERR;
863
    int ncid;
864
    NC_memio params;
865
    ...
866
    params.size = <compute file size of foo.nc in bytes>;
867
    params.memory = malloc(size);
868
    params.flags = <see netcdf_mem.h>
869
    ...
870
    status = nc_open_memio("foo.nc", 0, &params, &ncid);
871
    if (status != NC_NOERR) handle_error(status);
872
    @endcode
873
*/
874
int
875
nc_open_memio(const char* path, int omode, NC_memio* params, int* ncidp)
876
0
{
877
    /* Sanity checks */
878
0
    if(path == NULL || params == NULL)
879
0
        return NC_EINVAL;
880
0
    if(params->memory == NULL || params->size < MAGIC_NUMBER_LEN)
881
0
        return NC_EINVAL;
882
883
0
    if(omode & NC_MMAP)
884
0
        return NC_EINVAL;
885
0
    omode |= (NC_INMEMORY);
886
0
    return NC_open(path, omode, 0, NULL, 0, params, ncidp);
887
0
}
888
889
/**
890
 * @internal Open a netCDF file with extra parameters for Cray.
891
 *
892
 * @deprecated This function was used in the old days with the Cray at
893
 * NCAR. The Cray is long gone, and this call is supported only for
894
 * backward compatibility. Use nc_open() instead.
895
 *
896
 * @param path The file name of the new netCDF dataset.
897
 * @param omode Open mode.
898
 * @param basepe Deprecated parameter from the Cray days.
899
 * @param chunksizehintp A pointer to the chunk size hint. This only
900
 * applies to classic files.
901
 * @param ncidp Pointer to location where returned netCDF ID is to be
902
 * stored.
903
 *
904
 * @return ::NC_NOERR
905
 * @author Glenn Davis
906
 */
907
int
908
nc__open_mp(const char *path, int omode, int basepe,
909
            size_t *chunksizehintp, int *ncidp)
910
0
{
911
0
    return NC_open(path, omode, basepe, chunksizehintp, 0, NULL, ncidp);
912
0
}
913
914
/** \ingroup datasets
915
    Get the file pathname (or the opendap URL) which was used to
916
    open/create the ncid's file.
917
918
    \param ncid NetCDF ID, from a previous call to nc_open() or
919
    nc_create().
920
921
    \param pathlen Pointer where length of path will be returned. Ignored
922
    if NULL.
923
924
    \param path Pointer where path name will be copied. Space must already
925
    be allocated. Ignored if NULL.
926
927
    \returns ::NC_NOERR No error.
928
929
    \returns ::NC_EBADID Invalid ncid passed.
930
*/
931
int
932
nc_inq_path(int ncid, size_t *pathlen, char *path)
933
0
{
934
0
    NC* ncp;
935
0
    int stat = NC_NOERR;
936
0
    if ((stat = NC_check_id(ncid, &ncp)))
937
0
        return stat;
938
0
    if(ncp->path == NULL) {
939
0
        if(pathlen) *pathlen = 0;
940
0
        if(path) path[0] = '\0';
941
0
    } else {
942
0
        if (pathlen) *pathlen = strlen(ncp->path);
943
0
        if (path) strcpy(path, ncp->path);
944
0
    }
945
0
    return stat;
946
0
}
947
948
/** \ingroup datasets
949
    Put open netcdf dataset into define mode
950
951
    The function nc_redef puts an open netCDF dataset into define mode, so
952
    dimensions, variables, and attributes can be added or renamed and
953
    attributes can be deleted.
954
955
    For netCDF-4 files (i.e. files created with NC_NETCDF4 in the cmode in
956
    their call to nc_create()), it is not necessary to call nc_redef()
957
    unless the file was also created with NC_STRICT_NC3. For straight-up
958
    netCDF-4 files, nc_redef() is called automatically, as needed.
959
960
    For all netCDF-4 files, the root ncid must be used. This is the ncid
961
    returned by nc_open() and nc_create(), and points to the root of the
962
    hierarchy tree for netCDF-4 files.
963
964
    \param ncid NetCDF ID, from a previous call to nc_open() or
965
    nc_create().
966
967
    \returns ::NC_NOERR No error.
968
969
    \returns ::NC_EBADID Bad ncid.
970
971
    \returns ::NC_EBADGRPID The ncid must refer to the root group of the
972
    file, that is, the group returned by nc_open() or nc_create().
973
974
    \returns ::NC_EINDEFINE Already in define mode.
975
976
    \returns ::NC_EPERM File is read-only.
977
978
    <h1>Example</h1>
979
980
    Here is an example using nc_redef to open an existing netCDF dataset
981
    named foo.nc and put it into define mode:
982
983
    \code
984
    #include <netcdf.h>
985
    ...
986
    int status = NC_NOERR;
987
    int ncid;
988
    ...
989
    status = nc_open("foo.nc", NC_WRITE, &ncid);
990
    if (status != NC_NOERR) handle_error(status);
991
    ...
992
    status = nc_redef(ncid);
993
    if (status != NC_NOERR) handle_error(status);
994
    \endcode
995
*/
996
int
997
nc_redef(int ncid)
998
0
{
999
0
    NC* ncp;
1000
0
    int stat = NC_check_id(ncid, &ncp);
1001
0
    if(stat != NC_NOERR) return stat;
1002
0
    return ncp->dispatch->redef(ncid);
1003
0
}
1004
1005
/** \ingroup datasets
1006
    Leave define mode
1007
1008
    The function nc_enddef() takes an open netCDF dataset out of define
1009
    mode. The changes made to the netCDF dataset while it was in define
1010
    mode are checked and committed to disk if no problems
1011
    occurred. Non-record variables may be initialized to a "fill value" as
1012
    well with nc_set_fill(). The netCDF dataset is then placed in data
1013
    mode, so variable data can be read or written.
1014
1015
    It's not necessary to call nc_enddef() for netCDF-4 files. With netCDF-4
1016
    files, nc_enddef() is called when needed by the netcdf-4 library. User
1017
    calls to nc_enddef() for netCDF-4 files still flush the metadata to
1018
    disk.
1019
1020
    This call may involve copying data under some circumstances. For a
1021
    more extensive discussion see File Structure and Performance.
1022
1023
    For netCDF-4/HDF5 format files there are some variable settings (the
1024
    compression, endianness, fletcher32 error correction, and fill value)
1025
    which must be set (if they are going to be set at all) between the
1026
    nc_def_var() and the next nc_enddef(). Once the nc_enddef() is called,
1027
    these settings can no longer be changed for a variable.
1028
1029
    \param ncid NetCDF ID, from a previous call to nc_open() or
1030
    nc_create().
1031
1032
    If you use a group id (in a netCDF-4/HDF5 file), the enddef
1033
    will apply to the entire file. That means the enddef will not just end
1034
    define mode in one group, but in the entire file.
1035
1036
    \returns ::NC_NOERR no error
1037
1038
    \returns ::NC_EBADID Invalid ncid passed.
1039
1040
    <h1>Example</h1>
1041
1042
    Here is an example using nc_enddef() to finish the definitions of a new
1043
    netCDF dataset named foo.nc and put it into data mode:
1044
1045
    \code
1046
    #include <netcdf.h>
1047
    ...
1048
    int status = NC_NOERR;
1049
    int ncid;
1050
    ...
1051
    status = nc_create("foo.nc", NC_NOCLOBBER, &ncid);
1052
    if (status != NC_NOERR) handle_error(status);
1053
1054
    ...  create dimensions, variables, attributes
1055
1056
    status = nc_enddef(ncid);
1057
    if (status != NC_NOERR) handle_error(status);
1058
    \endcode
1059
*/
1060
int
1061
nc_enddef(int ncid)
1062
0
{
1063
0
    int status = NC_NOERR;
1064
0
    NC *ncp;
1065
0
    status = NC_check_id(ncid, &ncp);
1066
0
    if(status != NC_NOERR) return status;
1067
0
    return ncp->dispatch->_enddef(ncid,0,1,0,1);
1068
0
}
1069
1070
/** \ingroup datasets
1071
    Leave define mode with performance tuning
1072
1073
    The function nc__enddef takes an open netCDF dataset out of define
1074
    mode. The changes made to the netCDF dataset while it was in define
1075
    mode are checked and committed to disk if no problems
1076
    occurred. Non-record variables may be initialized to a "fill value" as
1077
    well with nc_set_fill(). The netCDF dataset is then placed in data mode,
1078
    so variable data can be read or written.
1079
1080
    This call may involve copying data under some circumstances. For a
1081
    more extensive discussion see File Structure and Performance.
1082
1083
    \warning This function exposes internals of the netcdf version 1 file
1084
    format. Users should use nc_enddef() in most circumstances. This
1085
    function may not be available on future netcdf implementations.
1086
1087
    The classic netcdf file format has three sections, the "header"
1088
    section, the data section for fixed size variables, and the data
1089
    section for variables which have an unlimited dimension (record
1090
    variables).
1091
1092
    The header begins at the beginning of the file. The index (offset) of
1093
    the beginning of the other two sections is contained in the
1094
    header. Typically, there is no space between the sections. This causes
1095
    copying overhead to accrue if one wishes to change the size of the
1096
    sections, as may happen when changing names of things, text attribute
1097
    values, adding attributes or adding variables. Also, for buffered i/o,
1098
    there may be advantages to aligning sections in certain ways.
1099
1100
    The minfree parameters allow one to control costs of future calls to
1101
    nc_redef, nc_enddef() by requesting that minfree bytes be available at
1102
    the end of the section.
1103
1104
    The align parameters allow one to set the alignment of the beginning
1105
    of the corresponding sections. The beginning of the section is rounded
1106
    up to an index which is a multiple of the align parameter. The flag
1107
    value ALIGN_CHUNK tells the library to use the bufrsize (see above) as
1108
    the align parameter. It has nothing to do with the chunking
1109
    (multidimensional tiling) features of netCDF-4.
1110
1111
    The file format requires mod 4 alignment, so the align parameters are
1112
    silently rounded up to multiples of 4. The usual call,
1113
1114
    \code
1115
    nc_enddef(ncid);
1116
    \endcode
1117
1118
    is equivalent to
1119
1120
    \code
1121
    nc__enddef(ncid, 0, 4, 0, 4);
1122
    \endcode
1123
1124
    The file format does not contain a "record size" value, this is
1125
    calculated from the sizes of the record variables. This unfortunate
1126
    fact prevents us from providing minfree and alignment control of the
1127
    "records" in a netcdf file. If you add a variable which has an
1128
    unlimited dimension, the third section will always be copied with the
1129
    new variable added.
1130
1131
    \param ncid NetCDF ID, from a previous call to nc_open() or
1132
    nc_create().
1133
1134
    \param h_minfree Sets the pad at the end of the "header" section.
1135
1136
    \param v_align Controls the alignment of the beginning of the data
1137
    section for fixed size variables.
1138
1139
    \param v_minfree Sets the pad at the end of the data section for fixed
1140
    size variables.
1141
1142
    \param r_align Controls the alignment of the beginning of the data
1143
    section for variables which have an unlimited dimension (record
1144
    variables).
1145
1146
    \returns ::NC_NOERR No error.
1147
1148
    \returns ::NC_EBADID Invalid ncid passed.
1149
1150
*/
1151
int
1152
nc__enddef(int ncid, size_t h_minfree, size_t v_align, size_t v_minfree,
1153
           size_t r_align)
1154
0
{
1155
0
    NC* ncp;
1156
0
    int stat = NC_check_id(ncid, &ncp);
1157
0
    if(stat != NC_NOERR) return stat;
1158
0
    return ncp->dispatch->_enddef(ncid,h_minfree,v_align,v_minfree,r_align);
1159
0
}
1160
1161
/** \ingroup datasets
1162
    Synchronize an open netcdf dataset to disk
1163
1164
    The function nc_sync() offers a way to synchronize the disk copy of a
1165
    netCDF dataset with in-memory buffers. There are two reasons you might
1166
    want to synchronize after writes:
1167
    - To minimize data loss in case of abnormal termination, or
1168
    - To make data available to other processes for reading immediately
1169
    after it is written. But note that a process that already had the
1170
    dataset open for reading would not see the number of records
1171
    increase when the writing process calls nc_sync(); to accomplish this,
1172
    the reading process must call nc_sync.
1173
1174
    This function is backward-compatible with previous versions of the
1175
    netCDF library. The intent was to allow sharing of a netCDF dataset
1176
    among multiple readers and one writer, by having the writer call
1177
    nc_sync() after writing and the readers call nc_sync() before each
1178
    read. For a writer, this flushes buffers to disk. For a reader, it
1179
    makes sure that the next read will be from disk rather than from
1180
    previously cached buffers, so that the reader will see changes made by
1181
    the writing process (e.g., the number of records written) without
1182
    having to close and reopen the dataset. If you are only accessing a
1183
    small amount of data, it can be expensive in computer resources to
1184
    always synchronize to disk after every write, since you are giving up
1185
    the benefits of buffering.
1186
1187
    An easier way to accomplish sharing (and what is now recommended) is
1188
    to have the writer and readers open the dataset with the NC_SHARE
1189
    flag, and then it will not be necessary to call nc_sync() at
1190
    all. However, the nc_sync() function still provides finer granularity
1191
    than the NC_SHARE flag, if only a few netCDF accesses need to be
1192
    synchronized among processes.
1193
1194
    It is important to note that changes to the ancillary data, such as
1195
    attribute values, are not propagated automatically by use of the
1196
    NC_SHARE flag. Use of the nc_sync() function is still required for this
1197
    purpose.
1198
1199
    Sharing datasets when the writer enters define mode to change the data
1200
    schema requires extra care. In previous releases, after the writer
1201
    left define mode, the readers were left looking at an old copy of the
1202
    dataset, since the changes were made to a new copy. The only way
1203
    readers could see the changes was by closing and reopening the
1204
    dataset. Now the changes are made in place, but readers have no
1205
    knowledge that their internal tables are now inconsistent with the new
1206
    dataset schema. If netCDF datasets are shared across redefinition,
1207
    some mechanism external to the netCDF library must be provided that
1208
    prevents access by readers during redefinition and causes the readers
1209
    to call nc_sync before any subsequent access.
1210
1211
    When calling nc_sync(), the netCDF dataset must be in data mode. A
1212
    netCDF dataset in define mode is synchronized to disk only when
1213
    nc_enddef() is called. A process that is reading a netCDF dataset that
1214
    another process is writing may call nc_sync to get updated with the
1215
    changes made to the data by the writing process (e.g., the number of
1216
    records written), without having to close and reopen the dataset.
1217
1218
    Data is automatically synchronized to disk when a netCDF dataset is
1219
    closed, or whenever you leave define mode.
1220
1221
    \param ncid NetCDF ID, from a previous call to nc_open() or
1222
    nc_create().
1223
1224
    \returns ::NC_NOERR No error.
1225
1226
    \returns ::NC_EBADID Invalid ncid passed.
1227
*/
1228
int
1229
nc_sync(int ncid)
1230
0
{
1231
0
    NC* ncp;
1232
0
    int stat = NC_check_id(ncid, &ncp);
1233
0
    if(stat != NC_NOERR) return stat;
1234
0
    return ncp->dispatch->sync(ncid);
1235
0
}
1236
1237
/** \ingroup datasets
1238
    No longer necessary for user to invoke manually.
1239
1240
1241
    \warning Users no longer need to call this function since it is called
1242
    automatically by nc_close() in case the dataset is in define mode and
1243
    something goes wrong with committing the changes. The function
1244
    nc_abort() just closes the netCDF dataset, if not in define mode. If
1245
    the dataset is being created and is still in define mode, the dataset
1246
    is deleted. If define mode was entered by a call to nc_redef(), the
1247
    netCDF dataset is restored to its state before definition mode was
1248
    entered and the dataset is closed.
1249
1250
    \param ncid NetCDF ID, from a previous call to nc_open() or
1251
    nc_create().
1252
1253
    \returns ::NC_NOERR No error.
1254
1255
    <h1>Example</h1>
1256
1257
    Here is an example using nc_abort to back out of redefinitions of a
1258
    dataset named foo.nc:
1259
1260
    \code
1261
    #include <netcdf.h>
1262
    ...
1263
    int ncid, status, latid;
1264
    ...
1265
    status = nc_open("foo.nc", NC_WRITE, &ncid);
1266
    if (status != NC_NOERR) handle_error(status);
1267
    ...
1268
    status = nc_redef(ncid);
1269
    if (status != NC_NOERR) handle_error(status);
1270
    ...
1271
    status = nc_def_dim(ncid, "lat", 18L, &latid);
1272
    if (status != NC_NOERR) {
1273
    handle_error(status);
1274
    status = nc_abort(ncid);
1275
    if (status != NC_NOERR) handle_error(status);
1276
    }
1277
    \endcode
1278
1279
*/
1280
int
1281
nc_abort(int ncid)
1282
0
{
1283
0
    NC* ncp;
1284
0
    int stat = NC_check_id(ncid, &ncp);
1285
0
    if(stat != NC_NOERR) return stat;
1286
1287
0
    stat = ncp->dispatch->abort(ncid);
1288
0
    del_from_NCList(ncp);
1289
0
    free_NC(ncp);
1290
0
    return stat;
1291
0
}
1292
1293
/** \ingroup datasets
1294
    Close an open netCDF dataset
1295
1296
    If the dataset in define mode, nc_enddef() will be called before
1297
    closing. (In this case, if nc_enddef() returns an error, nc_abort() will
1298
    automatically be called to restore the dataset to the consistent state
1299
    before define mode was last entered.) After an open netCDF dataset is
1300
    closed, its netCDF ID may be reassigned to the next netCDF dataset
1301
    that is opened or created.
1302
1303
    \param ncid NetCDF ID, from a previous call to nc_open() or nc_create().
1304
1305
    \returns ::NC_NOERR No error.
1306
1307
    \returns ::NC_EBADID Invalid id passed.
1308
1309
    \returns ::NC_EBADGRPID ncid did not contain the root group id of this
1310
    file. (NetCDF-4 only).
1311
1312
    <h1>Example</h1>
1313
1314
    Here is an example using nc_close to finish the definitions of a new
1315
    netCDF dataset named foo.nc and release its netCDF ID:
1316
1317
    \code
1318
    #include <netcdf.h>
1319
    ...
1320
    int status = NC_NOERR;
1321
    int ncid;
1322
    ...
1323
    status = nc_create("foo.nc", NC_NOCLOBBER, &ncid);
1324
    if (status != NC_NOERR) handle_error(status);
1325
1326
    ...   create dimensions, variables, attributes
1327
1328
    status = nc_close(ncid);
1329
    if (status != NC_NOERR) handle_error(status);
1330
    \endcode
1331
1332
*/
1333
int
1334
nc_close(int ncid)
1335
8
{
1336
8
    NC* ncp;
1337
8
    int stat = NC_check_id(ncid, &ncp);
1338
8
    if(stat != NC_NOERR) return stat;
1339
1340
8
    stat = ncp->dispatch->close(ncid,NULL);
1341
    /* Remove from the nc list */
1342
8
    if (!stat)
1343
8
    {
1344
8
        del_from_NCList(ncp);
1345
8
        free_NC(ncp);
1346
8
    }
1347
8
    return stat;
1348
8
}
1349
1350
/** \ingroup datasets
1351
    Do a normal close (see nc_close()) on an in-memory dataset,
1352
    then return a copy of the final memory contents of the dataset.
1353
1354
    \param ncid NetCDF ID, from a previous call to nc_open() or nc_create().
1355
1356
    \param memio a pointer to an NC_memio object into which the final valid memory
1357
    size and memory will be returned.
1358
1359
    \returns ::NC_NOERR No error.
1360
1361
    \returns ::NC_EBADID Invalid id passed.
1362
1363
    \returns ::NC_ENOMEM Out of memory.
1364
1365
    \returns ::NC_EDISKLESS if the file was not created as an inmemory file.
1366
1367
    \returns ::NC_EBADGRPID ncid did not contain the root group id of this
1368
    file. (NetCDF-4 only).
1369
1370
    <h1>Example</h1>
1371
1372
    Here is an example using nc_close_mem to finish the definitions of a new
1373
    netCDF dataset named foo.nc, return the final memory,
1374
    and release its netCDF ID:
1375
1376
    \code
1377
    #include <netcdf.h>
1378
    ...
1379
    int status = NC_NOERR;
1380
    int ncid;
1381
    NC_memio finalmem;
1382
    size_t initialsize = 65000;
1383
    ...
1384
    status = nc_create_mem("foo.nc", NC_NOCLOBBER, initialsize, &ncid);
1385
    if (status != NC_NOERR) handle_error(status);
1386
    ...   create dimensions, variables, attributes
1387
    status = nc_close_memio(ncid,&finalmem);
1388
    if (status != NC_NOERR) handle_error(status);
1389
    \endcode
1390
1391
*/
1392
int
1393
nc_close_memio(int ncid, NC_memio* memio)
1394
0
{
1395
0
    NC* ncp;
1396
0
    int stat = NC_check_id(ncid, &ncp);
1397
0
    if(stat != NC_NOERR) return stat;
1398
1399
0
    stat = ncp->dispatch->close(ncid,memio);
1400
    /* Remove from the nc list */
1401
0
    if (!stat)
1402
0
    {
1403
0
        del_from_NCList(ncp);
1404
0
        free_NC(ncp);
1405
0
    }
1406
0
    return stat;
1407
0
}
1408
1409
/** \ingroup datasets
1410
    Change the fill-value mode to improve write performance.
1411
1412
    This function is intended for advanced usage, to optimize writes under
1413
    some circumstances described below. The function nc_set_fill() sets the
1414
    fill mode for a netCDF dataset open for writing and returns the
1415
    current fill mode in a return parameter. The fill mode can be
1416
    specified as either ::NC_FILL or ::NC_NOFILL. The default behavior
1417
    corresponding to ::NC_FILL is that data is pre-filled with fill values,
1418
    that is fill values are written when you create non-record variables
1419
    or when you write a value beyond data that has not yet been
1420
    written. This makes it possible to detect attempts to read data before
1421
    it was written. For more information on the use of fill values see
1422
    Fill Values. For information about how to define your own fill values
1423
    see Attribute Conventions.
1424
1425
    The behavior corresponding to ::NC_NOFILL overrides the default behavior
1426
    of prefilling data with fill values. This can be used to enhance
1427
    performance, because it avoids the duplicate writes that occur when
1428
    the netCDF library writes fill values that are later overwritten with
1429
    data.
1430
1431
    A value indicating which mode the netCDF dataset was already in is
1432
    returned. You can use this value to temporarily change the fill mode
1433
    of an open netCDF dataset and then restore it to the previous mode.
1434
1435
    After you turn on ::NC_NOFILL mode for an open netCDF dataset, you must
1436
    be certain to write valid data in all the positions that will later be
1437
    read. Note that nofill mode is only a transient property of a netCDF
1438
    dataset open for writing: if you close and reopen the dataset, it will
1439
    revert to the default behavior. You can also revert to the default
1440
    behavior by calling nc_set_fill() again to explicitly set the fill mode
1441
    to ::NC_FILL.
1442
1443
    There are three situations where it is advantageous to set nofill
1444
    mode:
1445
    - Creating and initializing a netCDF dataset. In this case, you should
1446
    set nofill mode before calling nc_enddef() and then write completely
1447
    all non-record variables and the initial records of all the record
1448
    variables you want to initialize.
1449
    - Extending an existing record-oriented netCDF dataset. Set nofill
1450
    mode after opening the dataset for writing, then append the
1451
    additional records to the dataset completely, leaving no intervening
1452
    unwritten records.
1453
    - Adding new variables that you are going to initialize to an existing
1454
    netCDF dataset. Set nofill mode before calling nc_enddef() then write
1455
    all the new variables completely.
1456
1457
    If the netCDF dataset has an unlimited dimension and the last record
1458
    was written while in nofill mode, then the dataset may be shorter than
1459
    if nofill mode was not set, but this will be completely transparent if
1460
    you access the data only through the netCDF interfaces.
1461
1462
    The use of this feature may not be available (or even needed) in
1463
    future releases. Programmers are cautioned against heavy reliance upon
1464
    this feature.
1465
1466
    \param ncid NetCDF ID, from a previous call to nc_open() or
1467
    nc_create().
1468
1469
    \param fillmode Desired fill mode for the dataset, either ::NC_NOFILL or
1470
    ::NC_FILL.
1471
1472
    \param old_modep Pointer to location for returned current fill mode of
1473
    the dataset before this call, either ::NC_NOFILL or ::NC_FILL.
1474
1475
    \returns ::NC_NOERR No error.
1476
1477
    \returns ::NC_EBADID The specified netCDF ID does not refer to an open
1478
    netCDF dataset.
1479
1480
    \returns ::NC_EPERM The specified netCDF ID refers to a dataset open for
1481
    read-only access.
1482
1483
    \returns ::NC_EINVAL The fill mode argument is neither ::NC_NOFILL nor
1484
    ::NC_FILL.
1485
1486
    <h1>Example</h1>
1487
1488
    Here is an example using nc_set_fill() to set nofill mode for subsequent
1489
    writes of a netCDF dataset named foo.nc:
1490
1491
    \code
1492
    #include <netcdf.h>
1493
    ...
1494
    int ncid, status, old_fill_mode;
1495
    ...
1496
    status = nc_open("foo.nc", NC_WRITE, &ncid);
1497
    if (status != NC_NOERR) handle_error(status);
1498
1499
    ...     write data with default prefilling behavior
1500
1501
    status = nc_set_fill(ncid, ::NC_NOFILL, &old_fill_mode);
1502
    if (status != NC_NOERR) handle_error(status);
1503
1504
    ...    write data with no prefilling
1505
    \endcode
1506
*/
1507
int
1508
nc_set_fill(int ncid, int fillmode, int *old_modep)
1509
0
{
1510
0
    NC* ncp;
1511
0
    int stat = NC_check_id(ncid, &ncp);
1512
0
    if(stat != NC_NOERR) return stat;
1513
0
    return ncp->dispatch->set_fill(ncid,fillmode,old_modep);
1514
0
}
1515
1516
/**
1517
 * @internal Learn base PE.
1518
 *
1519
 * @deprecated This function was used in the old days with the Cray at
1520
 * NCAR. The Cray is long gone, and this call is now meaningless. The
1521
 * value returned for pe is always 0.
1522
 *
1523
 * @param ncid File and group ID.
1524
 * @param pe Pointer for base PE.
1525
 *
1526
 * @return ::NC_NOERR No error.
1527
 * @return ::NC_EBADID Invalid ncid passed.
1528
 * @author Glenn Davis
1529
 */
1530
int
1531
nc_inq_base_pe(int ncid, int *pe)
1532
0
{
1533
0
    NC* ncp;
1534
0
    int stat = NC_check_id(ncid, &ncp);
1535
0
    if(stat != NC_NOERR) return stat;
1536
0
    if (pe) *pe = 0;
1537
0
    return NC_NOERR;
1538
0
}
1539
1540
/**
1541
 * @internal Sets base processing element (ignored).
1542
 *
1543
 * @deprecated This function was used in the old days with the Cray at
1544
 * NCAR. The Cray is long gone, and this call is supported only for
1545
 * backward compatibility.
1546
 *
1547
 * @param ncid File ID.
1548
 * @param pe Base PE.
1549
 *
1550
 * @return ::NC_NOERR No error.
1551
 * @return ::NC_EBADID Invalid ncid passed.
1552
 * @author Glenn Davis
1553
 */
1554
int
1555
nc_set_base_pe(int ncid, int pe)
1556
0
{
1557
0
    NC* ncp;
1558
0
    int stat = NC_check_id(ncid, &ncp);
1559
0
    if(stat != NC_NOERR) return stat;
1560
0
    return NC_NOERR;
1561
0
}
1562
1563
/**
1564
 * @ingroup datasets
1565
 * Inquire about the binary format of a netCDF file
1566
 * as presented by the API.
1567
 *
1568
 * This function returns the (rarely needed) format version.
1569
 *
1570
 * @param ncid NetCDF ID, from a previous call to nc_open() or
1571
 * nc_create().
1572
 * @param formatp Pointer to location for returned format version, one
1573
 * of NC_FORMAT_CLASSIC, NC_FORMAT_64BIT_OFFSET, NC_FORMAT_CDF5,
1574
 * NC_FORMAT_NETCDF4, NC_FORMAT_NETCDF4_CLASSIC.
1575
 *
1576
 * @returns ::NC_NOERR No error.
1577
 * @returns ::NC_EBADID Invalid ncid passed.
1578
 * @author Dennis Heimbigner
1579
 */
1580
int
1581
nc_inq_format(int ncid, int *formatp)
1582
0
{
1583
0
    NC* ncp;
1584
0
    int stat = NC_check_id(ncid, &ncp);
1585
0
    if(stat != NC_NOERR) return stat;
1586
0
    return ncp->dispatch->inq_format(ncid,formatp);
1587
0
}
1588
1589
/** \ingroup datasets
1590
    Obtain more detailed (vis-a-vis nc_inq_format)
1591
    format information about an open dataset.
1592
1593
    Note that the netcdf API will present the file
1594
    as if it had the format specified by nc_inq_format.
1595
    The true file format, however, may not even be
1596
    a netcdf file; it might be DAP, HDF4, or PNETCDF,
1597
    for example. This function returns that true file type.
1598
    It also returns the effective mode for the file.
1599
1600
    \param ncid NetCDF ID, from a previous call to nc_open() or
1601
    nc_create().
1602
1603
    \param formatp Pointer to location for returned true format.
1604
1605
    \param modep Pointer to location for returned mode flags.
1606
1607
    Refer to the actual list in the file netcdf.h to see the
1608
    currently defined set.
1609
1610
    \returns ::NC_NOERR No error.
1611
1612
    \returns ::NC_EBADID Invalid ncid passed.
1613
1614
*/
1615
int
1616
nc_inq_format_extended(int ncid, int *formatp, int *modep)
1617
0
{
1618
0
    NC* ncp;
1619
0
    int stat = NC_check_id(ncid, &ncp);
1620
0
    if(stat != NC_NOERR) return stat;
1621
0
    return ncp->dispatch->inq_format_extended(ncid,formatp,modep);
1622
0
}
1623
1624
/**\ingroup datasets
1625
   Inquire about a file or group.
1626
1627
   \param ncid NetCDF or group ID, from a previous call to nc_open(),
1628
   nc_create(), nc_def_grp(), or associated inquiry functions such as
1629
   nc_inq_ncid().
1630
1631
   \param ndimsp Pointer to location for returned number of dimensions
1632
   defined for this netCDF dataset. Ignored if NULL.
1633
1634
   \param nvarsp Pointer to location for returned number of variables
1635
   defined for this netCDF dataset. Ignored if NULL.
1636
1637
   \param nattsp Pointer to location for returned number of global
1638
   attributes defined for this netCDF dataset. Ignored if NULL.
1639
1640
   \param unlimdimidp Pointer to location for returned ID of the
1641
   unlimited dimension, if there is one for this netCDF dataset. If no
1642
   unlimited length dimension has been defined, -1 is returned. Ignored
1643
   if NULL.  If there are multiple unlimited dimensions (possible only
1644
   for netCDF-4 files), only a pointer to the first is returned, for
1645
   backward compatibility.  If you want them all, use nc_inq_unlimids().
1646
1647
   \returns ::NC_NOERR No error.
1648
1649
   \returns ::NC_EBADID Invalid ncid passed.
1650
1651
   <h1>Example</h1>
1652
1653
   Here is an example using nc_inq to find out about a netCDF dataset
1654
   named foo.nc:
1655
1656
   \code
1657
   #include <netcdf.h>
1658
   ...
1659
   int status, ncid, ndims, nvars, ngatts, unlimdimid;
1660
   ...
1661
   status = nc_open("foo.nc", NC_NOWRITE, &ncid);
1662
   if (status != NC_NOERR) handle_error(status);
1663
   ...
1664
   status = nc_inq(ncid, &ndims, &nvars, &ngatts, &unlimdimid);
1665
   if (status != NC_NOERR) handle_error(status);
1666
   \endcode
1667
*/
1668
int
1669
nc_inq(int ncid, int *ndimsp, int *nvarsp, int *nattsp, int *unlimdimidp)
1670
0
{
1671
0
    NC* ncp;
1672
0
    int stat = NC_check_id(ncid, &ncp);
1673
0
    if(stat != NC_NOERR) return stat;
1674
0
    return ncp->dispatch->inq(ncid,ndimsp,nvarsp,nattsp,unlimdimidp);
1675
0
}
1676
1677
/**
1678
 * Learn the number of variables in a file or group.
1679
 *
1680
 * @param ncid File and group ID.
1681
 * @param nvarsp Pointer that gets number of variables. Ignored if NULL.
1682
 *
1683
 * @return ::NC_NOERR No error.
1684
 * @return ::NC_EBADID Bad ncid.
1685
 * @author Glenn Davis, Ed Hartnett, Dennis Heimbigner
1686
 */
1687
int
1688
nc_inq_nvars(int ncid, int *nvarsp)
1689
0
{
1690
0
    NC* ncp;
1691
0
    int stat = NC_check_id(ncid, &ncp);
1692
0
    if(stat != NC_NOERR) return stat;
1693
0
    return ncp->dispatch->inq(ncid, NULL, nvarsp, NULL, NULL);
1694
0
}
1695
1696
/**\ingroup datasets
1697
   Inquire about a type.
1698
1699
   Given an ncid and a typeid, get the information about a type. This
1700
   function will work on any type, including atomic and any user defined
1701
   type, whether compound, opaque, enumeration, or variable length array.
1702
1703
   For even more information about a user defined type nc_inq_user_type().
1704
1705
   \param ncid The ncid for the group containing the type (ignored for
1706
   atomic types).
1707
1708
   \param xtype The typeid for this type, as returned by nc_def_compound,
1709
   nc_def_opaque, nc_def_enum, nc_def_vlen, or nc_inq_var, or as found in
1710
   netcdf.h in the list of atomic types (NC_CHAR, NC_INT, etc.).
1711
1712
   \param name If non-NULL, the name of the user defined type will be
1713
   copied here. It will be NC_MAX_NAME bytes or less. For atomic types,
1714
   the type name from CDL will be given.
1715
1716
   \param size If non-NULL, the (in-memory) size of the type in bytes
1717
   will be copied here. VLEN type size is the size of nc_vlen_t. String
1718
   size is returned as the size of a character pointer. The size may be
1719
   used to malloc space for the data, no matter what the type.
1720
1721
   \returns ::NC_NOERR No error.
1722
1723
   \returns ::NC_EBADTYPE Bad typeid.
1724
1725
   \returns ::NC_ENOTNC4 Seeking a user-defined type in a netCDF-3 file.
1726
1727
   \returns ::NC_ESTRICTNC3 Seeking a user-defined type in a netCDF-4 file
1728
   for which classic model has been turned on.
1729
1730
   \returns ::NC_EBADGRPID Bad group ID in ncid.
1731
1732
   \returns ::NC_EBADID Type ID not found.
1733
1734
   \returns ::NC_EHDFERR An error was reported by the HDF5 layer.
1735
1736
   <h1>Example</h1>
1737
1738
   This example is from the test program tst_enums.c, and it uses all the
1739
   possible inquiry functions on an enum type.
1740
1741
   \code
1742
   if (nc_inq_user_type(ncid, typeids[0], name_in, &base_size_in, &base_nc_type_in,
1743
   &nfields_in, &class_in)) ERR;
1744
   if (strcmp(name_in, TYPE_NAME) || base_size_in != sizeof(int) ||
1745
   base_nc_type_in != NC_INT || nfields_in != NUM_MEMBERS || class_in != NC_ENUM) ERR;
1746
   if (nc_inq_type(ncid, typeids[0], name_in, &base_size_in)) ERR;
1747
   if (strcmp(name_in, TYPE_NAME) || base_size_in != sizeof(int)) ERR;
1748
   if (nc_inq_enum(ncid, typeids[0], name_in, &base_nc_type, &base_size_in, &num_members)) ERR;
1749
   if (strcmp(name_in, TYPE_NAME) || base_nc_type != NC_INT || num_members != NUM_MEMBERS) ERR;
1750
   for (i = 0; i < NUM_MEMBERS; i++)
1751
   {
1752
   if (nc_inq_enum_member(ncid, typeid, i, name_in, &value_in)) ERR;
1753
   if (strcmp(name_in, member_name[i]) || value_in != member_value[i]) ERR;
1754
   if (nc_inq_enum_ident(ncid, typeid, member_value[i], name_in)) ERR;
1755
   if (strcmp(name_in, member_name[i])) ERR;
1756
   }
1757
1758
   if (nc_close(ncid)) ERR;
1759
   \endcode
1760
*/
1761
int
1762
nc_inq_type(int ncid, nc_type xtype, char *name, size_t *size)
1763
0
{
1764
0
    NC* ncp;
1765
0
    int stat;
1766
1767
    /* Do a quick triage on xtype */
1768
0
    if(xtype <= NC_NAT) return NC_EBADTYPE;
1769
    /* For compatibility, we need to allow inq about
1770
       atomic types, even if ncid is ill-defined */
1771
0
    if(xtype <= ATOMICTYPEMAX4) {
1772
0
        if(name) strncpy(name,NC_atomictypename(xtype),NC_MAX_NAME);
1773
0
        if(size) *size = NC_atomictypelen(xtype);
1774
0
        return NC_NOERR;
1775
0
    }
1776
    /* Apparently asking about a user defined type, so we need
1777
       a valid ncid */
1778
0
    stat = NC_check_id(ncid, &ncp);
1779
0
    if(stat != NC_NOERR) /* bad ncid */
1780
0
        return NC_EBADTYPE;
1781
    /* have good ncid */
1782
0
    return ncp->dispatch->inq_type(ncid,xtype,name,size);
1783
0
}
1784
1785
/** \defgroup dispatch dispatch functions. */
1786
/** \{
1787
1788
\ingroup dispatch
1789
*/
1790
1791
1792
/**
1793
   Check the create mode parameter for sanity.
1794
1795
   Some create flags cannot be used if corresponding library features are
1796
   enabled during the build. This function does a pre-check of the mode
1797
   flag before calling the dispatch layer nc_create functions.
1798
1799
   \param mode The creation mode flag.
1800
1801
   \returns ::NC_NOERR No error.
1802
   \returns ::NC_ENOTBUILT Requested feature not built into library
1803
   \returns ::NC_EINVAL Invalid combination of modes.
1804
   \internal
1805
   \ingroup dispatch
1806
   \author Ed Hartnett
1807
*/
1808
static int
1809
check_create_mode(int mode)
1810
0
{
1811
0
    int mode_format;
1812
0
    int use_mmap = 0;
1813
0
    int inmemory = 0;
1814
0
    int diskless = 0;
1815
1816
    /* This is a clever check to see if more than one format bit is
1817
     * set. */
1818
0
    mode_format = (mode & NC_NETCDF4) | (mode & NC_64BIT_OFFSET) |
1819
0
        (mode & NC_CDF5);
1820
0
    if (mode_format && (mode_format & (mode_format - 1)))
1821
0
        return NC_EINVAL;
1822
1823
0
    use_mmap = ((mode & NC_MMAP) == NC_MMAP);
1824
0
    inmemory = ((mode & NC_INMEMORY) == NC_INMEMORY);
1825
0
    diskless = ((mode & NC_DISKLESS) == NC_DISKLESS);
1826
1827
    /* NC_INMEMORY and NC_DISKLESS and NC_MMAP are all mutually exclusive */
1828
0
    if(diskless && inmemory) return NC_EDISKLESS;
1829
0
    if(diskless && use_mmap) return NC_EDISKLESS;
1830
0
    if(inmemory && use_mmap) return NC_EINMEMORY;
1831
1832
    /* mmap is not allowed for netcdf-4 */
1833
0
    if(use_mmap && (mode & NC_NETCDF4)) return NC_EINVAL;
1834
1835
#ifndef USE_NETCDF4
1836
    /* If the user asks for a netCDF-4 file, and the library was built
1837
     * without netCDF-4, then return an error.*/
1838
    if (mode & NC_NETCDF4)
1839
        return NC_ENOTBUILT;
1840
#endif /* USE_NETCDF4 undefined */
1841
1842
    /* Well I guess there is some sanity in the world after all. */
1843
0
    return NC_NOERR;
1844
0
}
1845
1846
/**
1847
 * @internal Create a file, calling the appropriate dispatch create
1848
 * call.
1849
 *
1850
 * For create, we have the following pieces of information to use to
1851
 * determine the dispatch table:
1852
 * - path
1853
 * - cmode
1854
 *
1855
 * @param path0 The file name of the new netCDF dataset.
1856
 * @param cmode The creation mode flag, the same as in nc_create().
1857
 * @param initialsz This parameter sets the initial size of the file
1858
 * at creation time. This only applies to classic
1859
 * files.
1860
 * @param basepe Deprecated parameter from the Cray days.
1861
 * @param chunksizehintp A pointer to the chunk size hint. This only
1862
 * applies to classic files.
1863
 * @param useparallel Non-zero if parallel I/O is to be used on this
1864
 * file.
1865
 * @param parameters Pointer to MPI comm and info.
1866
 * @param ncidp Pointer to location where returned netCDF ID is to be
1867
 * stored.
1868
 *
1869
 * @returns ::NC_NOERR No error.
1870
 * @ingroup dispatch
1871
 * @author Dennis Heimbigner, Ed Hartnett, Ward Fisher
1872
 */
1873
int
1874
NC_create(const char *path0, int cmode, size_t initialsz,
1875
          int basepe, size_t *chunksizehintp, int useparallel,
1876
          void* parameters, int *ncidp)
1877
0
{
1878
0
    int stat = NC_NOERR;
1879
0
    NC* ncp = NULL;
1880
0
    const NC_Dispatch* dispatcher = NULL;
1881
0
    char* path = NULL;
1882
0
    NCmodel model;
1883
0
    char* newpath = NULL;
1884
1885
0
    TRACE(nc_create);
1886
0
    if(path0 == NULL)
1887
0
        {stat = NC_EINVAL; goto done;}
1888
1889
    /* Check mode flag for sanity. */
1890
0
    if ((stat = check_create_mode(cmode))) goto done;
1891
1892
    /* Initialize the library. The available dispatch tables
1893
     * will depend on how netCDF was built
1894
     * (with/without netCDF-4, DAP, CDMREMOTE). */
1895
0
    if(!NC_initialized) {
1896
0
        if ((stat = nc_initialize())) goto done;
1897
0
    }
1898
1899
0
    {
1900
        /* Skip past any leading whitespace in path */
1901
0
        const unsigned char* p;
1902
0
        for(p=(const unsigned char*)path0;*p;p++) {if(*p > ' ') break;}
1903
0
        path = nulldup((const char*)p);
1904
0
    }
1905
1906
0
    memset(&model,0,sizeof(model));
1907
0
    newpath = NULL;
1908
0
    if((stat = NC_infermodel(path,&cmode,1,useparallel,NULL,&model,&newpath))) goto done;
1909
0
    if(newpath) {
1910
0
        nullfree(path);
1911
0
        path = newpath;
1912
0
        newpath = NULL;
1913
0
    }
1914
1915
0
    assert(model.format != 0 && model.impl != 0);
1916
1917
    /* Now, check for NC_ENOTBUILT cases limited to create (so e.g. HDF4 is not listed) */
1918
0
#ifndef USE_HDF5
1919
0
    if (model.impl == NC_FORMATX_NC4)
1920
0
    {stat = NC_ENOTBUILT; goto done;}
1921
0
#endif
1922
0
#ifndef USE_PNETCDF
1923
0
    if (model.impl == NC_FORMATX_PNETCDF)
1924
0
    {stat = NC_ENOTBUILT; goto done;}
1925
0
#endif
1926
#ifndef NETCDF_ENABLE_CDF5
1927
    if (model.impl == NC_FORMATX_NC3 && (cmode & NC_64BIT_DATA))
1928
    {stat = NC_ENOTBUILT; goto done;}
1929
#endif
1930
1931
    /* Figure out what dispatcher to use */
1932
0
    switch (model.impl) {
1933
#ifdef USE_HDF5
1934
    case NC_FORMATX_NC4:
1935
        dispatcher = HDF5_dispatch_table;
1936
        break;
1937
#endif
1938
#ifdef USE_PNETCDF
1939
    case NC_FORMATX_PNETCDF:
1940
        dispatcher = NCP_dispatch_table;
1941
        break;
1942
#endif
1943
0
#ifdef USE_NETCDF4
1944
0
    case NC_FORMATX_UDF0:
1945
0
    case NC_FORMATX_UDF1:
1946
0
    case NC_FORMATX_UDF2:
1947
0
    case NC_FORMATX_UDF3:
1948
0
    case NC_FORMATX_UDF4:
1949
0
    case NC_FORMATX_UDF5:
1950
0
    case NC_FORMATX_UDF6:
1951
0
    case NC_FORMATX_UDF7:
1952
0
    case NC_FORMATX_UDF8:
1953
0
    case NC_FORMATX_UDF9:
1954
0
        {
1955
            /* Convert format constant to array index and validate */
1956
0
            int udf_index = udf_formatx_to_index(model.impl);
1957
0
            if (udf_index < 0 || udf_index >= NC_MAX_UDF_FORMATS) {
1958
0
                stat = NC_EINVAL;
1959
0
                goto done;
1960
0
            }
1961
            /* Get the dispatch table for this UDF slot */
1962
0
            dispatcher = UDF_dispatch_tables[udf_index];
1963
            /* Ensure the UDF format has been registered via nc_def_user_format() */
1964
0
            if (!dispatcher) {
1965
0
                stat = NC_ENOTBUILT;
1966
0
                goto done;
1967
0
            }
1968
0
        }
1969
0
        break;
1970
0
#endif /* USE_NETCDF4 */
1971
0
#ifdef NETCDF_ENABLE_NCZARR
1972
0
    case NC_FORMATX_NCZARR:
1973
0
        dispatcher = NCZ_dispatch_table;
1974
0
  break;
1975
0
#endif
1976
0
    case NC_FORMATX_NC3:
1977
0
        dispatcher = NC3_dispatch_table;
1978
0
        break;
1979
0
    default:
1980
0
        {stat = NC_ENOTNC; goto done;}
1981
0
    }
1982
1983
    /* Create the NC* instance and insert its dispatcher and model */
1984
0
    if((stat = new_NC(dispatcher,path,cmode,&ncp))) goto done;
1985
1986
    /* Add to list of known open files and define ext_ncid */
1987
0
    add_to_NCList(ncp);
1988
1989
    /* Assume create will fill in remaining ncp fields */
1990
0
    if ((stat = dispatcher->create(ncp->path, cmode, initialsz, basepe, chunksizehintp,
1991
0
                                   parameters, dispatcher, ncp->ext_ncid))) {
1992
0
        del_from_NCList(ncp); /* oh well */
1993
0
        free_NC(ncp);
1994
0
    } else {
1995
0
        if(ncidp)*ncidp = ncp->ext_ncid;
1996
0
    }
1997
0
done:
1998
0
    nullfree(path);
1999
0
    nullfree(newpath);
2000
0
    return stat;
2001
0
}
2002
2003
/**
2004
 * @internal Open a netCDF file (or remote dataset) calling the
2005
 * appropriate dispatch function.
2006
 *
2007
 * For open, we have the following pieces of information to use to
2008
 * determine the dispatch table.
2009
 * - table specified by override
2010
 * - path
2011
 * - omode
2012
 * - the contents of the file (if it exists), basically checking its magic number.
2013
 *
2014
 * @param path0 Path to the file to open.
2015
 * @param omode Open mode.
2016
 * @param basepe Base processing element (ignored).
2017
 * @param chunksizehintp Size hint for classic files.
2018
 * @param useparallel If true use parallel I/O.
2019
 * @param parameters Extra parameters for the open.
2020
 * @param ncidp Pointer that gets ncid.
2021
 *
2022
 * @returns ::NC_NOERR No error.
2023
 * @ingroup dispatch
2024
 * @author Dennis Heimbigner
2025
 */
2026
int
2027
NC_open(const char *path0, int omode, int basepe, size_t *chunksizehintp,
2028
        int useparallel, void* parameters, int *ncidp)
2029
112
{
2030
112
    int stat = NC_NOERR;
2031
112
    NC* ncp = NULL;
2032
112
    const NC_Dispatch* dispatcher = NULL;
2033
112
    int inmemory = 0;
2034
112
    int diskless = 0;
2035
112
    int use_mmap = 0;
2036
112
    char* path = NULL;
2037
112
    NCmodel model;
2038
112
    char* newpath = NULL;
2039
2040
112
    TRACE(nc_open);
2041
112
    if(!NC_initialized) {
2042
1
        stat = nc_initialize();
2043
1
        if(stat) goto done;
2044
1
    }
2045
2046
    /* Check inputs. */
2047
112
    if (!path0)
2048
0
        {stat = NC_EINVAL; goto done;}
2049
2050
    /* Capture the inmemory related flags */
2051
112
    use_mmap = ((omode & NC_MMAP) == NC_MMAP);
2052
112
    diskless = ((omode & NC_DISKLESS) == NC_DISKLESS);
2053
112
    inmemory = ((omode & NC_INMEMORY) == NC_INMEMORY);
2054
2055
    /* NC_INMEMORY and NC_DISKLESS and NC_MMAP are all mutually exclusive */
2056
112
    if(diskless && inmemory) {stat = NC_EDISKLESS; goto done;}
2057
112
    if(diskless && use_mmap) {stat = NC_EDISKLESS; goto done;}
2058
112
    if(inmemory && use_mmap) {stat = NC_EINMEMORY; goto done;}
2059
2060
    /* mmap is not allowed for netcdf-4 */
2061
112
    if(use_mmap && (omode & NC_NETCDF4)) {stat = NC_EINVAL; goto done;}
2062
2063
    /* Attempt to do file path conversion: note that this will do
2064
       nothing if path is a 'file:...' url, so it will need to be
2065
       repeated in protocol code (e.g. libdap2, libdap4, etc).
2066
    */
2067
2068
112
    {
2069
        /* Skip past any leading whitespace in path */
2070
112
        const char* p;
2071
112
        for(p=(const char*)path0;*p;p++) {if(*p < 0 || *p > ' ') break;}
2072
112
        path = nulldup(p);
2073
112
    }
2074
2075
112
    memset(&model,0,sizeof(model));
2076
    /* Infer model implementation and format, possibly by reading the file */
2077
112
    if((stat = NC_infermodel(path,&omode,0,useparallel,parameters,&model,&newpath)))
2078
15
        goto done;
2079
97
    if(newpath) {
2080
0
        nullfree(path);
2081
0
        path = newpath;
2082
0
  newpath = NULL;
2083
0
    }
2084
2085
    /* Still no implementation, give up */
2086
97
    if(model.impl == 0) {
2087
#ifdef DEBUG
2088
        fprintf(stderr,"implementation == 0\n");
2089
#endif
2090
0
        {stat = NC_ENOTNC; goto done;}
2091
0
    }
2092
2093
    /* Suppress unsupported formats */
2094
#if 0
2095
    /* (should be more compact, table-driven, way to do this) */
2096
    {
2097
  int hdf5built = 0;
2098
  int hdf4built = 0;
2099
  int cdf5built = 0;
2100
  int udf0built = 0;
2101
  int udf1built = 0;
2102
  int nczarrbuilt = 0;
2103
#ifdef USE_NETCDF4
2104
        hdf5built = 1;
2105
#endif
2106
#ifdef USE_HDF4
2107
        hdf4built = 1;
2108
#endif
2109
#ifdef NETCDF_ENABLE_CDF5
2110
        cdf5built = 1;
2111
#endif
2112
#ifdef NETCDF_ENABLE_NCZARR
2113
  nczarrbuilt = 1;
2114
#endif
2115
        /* Check which UDF formats are built (i.e., have been registered).
2116
         * A UDF format is considered "built" if its dispatch table is non-NULL. */
2117
        int udfbuilt[NC_MAX_UDF_FORMATS] = {0};
2118
        for(int i = 0; i < NC_MAX_UDF_FORMATS; i++) {
2119
            if(UDF_dispatch_tables[i] != NULL)
2120
                udfbuilt[i] = 1;
2121
        }
2122
2123
        if(!hdf5built && model.impl == NC_FORMATX_NC4)
2124
        {stat = NC_ENOTBUILT; goto done;}
2125
        if(!hdf4built && model.impl == NC_FORMATX_NC_HDF4)
2126
        {stat = NC_ENOTBUILT; goto done;}
2127
        if(!cdf5built && model.impl == NC_FORMATX_NC3 && model.format == NC_FORMAT_CDF5)
2128
        {stat = NC_ENOTBUILT; goto done;}
2129
  if(!nczarrbuilt && model.impl == NC_FORMATX_NCZARR)
2130
        {stat = NC_ENOTBUILT; goto done;}
2131
        /* Check all UDF formats - ensure the requested format has been registered */
2132
        for(int i = 0; i < NC_MAX_UDF_FORMATS; i++) {
2133
            /* Convert array index to format constant (handles gap: UDF0=8, UDF1=9, UDF2=11...) */
2134
            int formatx = (i <= 1) ? (NC_FORMATX_UDF0 + i) : (NC_FORMATX_UDF2 + i - 2);
2135
            if(!udfbuilt[i] && model.impl == formatx)
2136
                {stat = NC_ENOTBUILT; goto done;}
2137
        }
2138
    }
2139
#else
2140
97
    {
2141
97
  unsigned built = 0 /* leave off the trailing semicolon so we can build constant */
2142
97
    | (1<<NC_FORMATX_NC3) /* NC3 always supported */
2143
#ifdef USE_HDF5
2144
    | (1<<NC_FORMATX_NC_HDF5)
2145
#endif
2146
#ifdef USE_HDF4
2147
    | (1<<NC_FORMATX_NC_HDF4)
2148
#endif
2149
97
#ifdef NETCDF_ENABLE_NCZARR
2150
97
    | (1<<NC_FORMATX_NCZARR)
2151
97
#endif
2152
#ifdef NETCDF_ENABLE_DAP
2153
    | (1<<NC_FORMATX_DAP2)
2154
#endif
2155
#ifdef NETCDF_ENABLE_DAP4
2156
    | (1<<NC_FORMATX_DAP4)
2157
#endif
2158
#ifdef USE_PNETCDF
2159
    | (1<<NC_FORMATX_PNETCDF)
2160
#endif
2161
97
    ; /* end of the built flags */
2162
        /* Add UDF formats to built flags - set bit for each registered UDF format.
2163
         * Use unsigned literal (1U) to avoid integer overflow for higher bit positions. */
2164
1.06k
        for(int i = 0; i < NC_MAX_UDF_FORMATS; i++) {
2165
970
            if(UDF_dispatch_tables[i] != NULL) {
2166
                /* Convert array index to format constant */
2167
0
                int formatx = (i <= 1) ? (NC_FORMATX_UDF0 + i) : (NC_FORMATX_UDF2 + i - 2);
2168
0
                built |= (1U << formatx);
2169
0
            }
2170
970
        }
2171
  /* Verify the requested format is available */
2172
97
  if((built & (1U << model.impl)) == 0)
2173
0
            {stat = NC_ENOTBUILT; goto done;}
2174
#ifndef NETCDF_ENABLE_CDF5
2175
  /* Special case because there is no separate CDF5 dispatcher */
2176
        if(model.impl == NC_FORMATX_NC3 && (omode & NC_64BIT_DATA))
2177
            {stat = NC_ENOTBUILT; goto done;}
2178
#endif
2179
97
    }
2180
97
#endif
2181
    /* Figure out what dispatcher to use */
2182
97
    if (!dispatcher) {
2183
97
        switch (model.impl) {
2184
#ifdef NETCDF_ENABLE_DAP
2185
        case NC_FORMATX_DAP2:
2186
            dispatcher = NCD2_dispatch_table;
2187
            break;
2188
#endif
2189
#ifdef NETCDF_ENABLE_DAP4
2190
        case NC_FORMATX_DAP4:
2191
            dispatcher = NCD4_dispatch_table;
2192
            break;
2193
#endif
2194
0
#ifdef NETCDF_ENABLE_NCZARR
2195
0
  case NC_FORMATX_NCZARR:
2196
0
      dispatcher = NCZ_dispatch_table;
2197
0
      break;
2198
0
#endif
2199
#ifdef USE_PNETCDF
2200
        case NC_FORMATX_PNETCDF:
2201
            dispatcher = NCP_dispatch_table;
2202
            break;
2203
#endif
2204
#ifdef USE_HDF5
2205
        case NC_FORMATX_NC4:
2206
            dispatcher = HDF5_dispatch_table;
2207
            break;
2208
#endif
2209
#ifdef USE_HDF4
2210
        case NC_FORMATX_NC_HDF4:
2211
            dispatcher = HDF4_dispatch_table;
2212
            break;
2213
#endif
2214
0
#ifdef USE_NETCDF4
2215
0
        case NC_FORMATX_UDF0:
2216
0
        case NC_FORMATX_UDF1:
2217
0
        case NC_FORMATX_UDF2:
2218
0
        case NC_FORMATX_UDF3:
2219
0
        case NC_FORMATX_UDF4:
2220
0
        case NC_FORMATX_UDF5:
2221
0
        case NC_FORMATX_UDF6:
2222
0
        case NC_FORMATX_UDF7:
2223
0
        case NC_FORMATX_UDF8:
2224
0
        case NC_FORMATX_UDF9:
2225
0
            {
2226
                /* Convert format constant to array index and validate */
2227
0
                int udf_index = udf_formatx_to_index(model.impl);
2228
0
                if (udf_index < 0 || udf_index >= NC_MAX_UDF_FORMATS) {
2229
0
                    stat = NC_EINVAL;
2230
0
                    goto done;
2231
0
                }
2232
                /* Get the dispatch table for this UDF slot */
2233
0
                dispatcher = UDF_dispatch_tables[udf_index];
2234
                /* Ensure the UDF format has been registered via nc_def_user_format() */
2235
0
                if (!dispatcher) {
2236
0
                    stat = NC_ENOTBUILT;
2237
0
                    goto done;
2238
0
                }
2239
0
            }
2240
0
            break;
2241
0
#endif /* USE_NETCDF4 */
2242
97
        case NC_FORMATX_NC3:
2243
97
            dispatcher = NC3_dispatch_table;
2244
97
            break;
2245
0
        default:
2246
0
            stat = NC_ENOTNC;
2247
0
      goto done;
2248
97
        }
2249
97
    }
2250
2251
2252
    /* If we can't figure out what dispatch table to use, give up. */
2253
97
    if (!dispatcher) {stat = NC_ENOTNC; goto done;}
2254
2255
    /* Create the NC* instance and insert its dispatcher */
2256
97
    if((stat = new_NC(dispatcher,path,omode,&ncp))) goto done;
2257
2258
    /* Add to list of known open files. This assigns an ext_ncid. */
2259
97
    add_to_NCList(ncp);
2260
2261
    /* Assume open will fill in remaining ncp fields */
2262
97
    stat = dispatcher->open(ncp->path, omode, basepe, chunksizehintp,
2263
97
                            parameters, dispatcher, ncp->ext_ncid);
2264
97
    if(stat == NC_NOERR) {
2265
8
        if(ncidp) *ncidp = ncp->ext_ncid;
2266
89
    } else {
2267
89
        del_from_NCList(ncp);
2268
89
        free_NC(ncp);
2269
89
    }
2270
2271
112
done:
2272
112
    nullfree(path);
2273
112
    nullfree(newpath);
2274
112
    return stat;
2275
97
}
2276
2277
/*Provide an internal function for generating pseudo file descriptors
2278
  for systems that are not file based (e.g. dap, memio).
2279
*/
2280
2281
/** @internal Static counter for pseudo file descriptors (incremented) */
2282
static int pseudofd = 0;
2283
2284
/**
2285
 * @internal Create a pseudo file descriptor that does not
2286
 * overlap real file descriptors
2287
 *
2288
 * @return pseudo file number
2289
 * @author Dennis Heimbigner
2290
 */
2291
int
2292
nc__pseudofd(void)
2293
97
{
2294
97
    if(pseudofd == 0)  {
2295
1
#ifdef HAVE_GETRLIMIT
2296
1
        int maxfd = 32767; /* default */
2297
1
        struct rlimit rl;
2298
1
        if(getrlimit(RLIMIT_NOFILE,&rl) == 0) {
2299
1
            if(rl.rlim_max != RLIM_INFINITY)
2300
1
                maxfd = (int)rl.rlim_max;
2301
1
            if(rl.rlim_cur != RLIM_INFINITY)
2302
1
                maxfd = (int)rl.rlim_cur;
2303
1
        }
2304
1
        pseudofd = maxfd+1;
2305
1
#endif
2306
1
    }
2307
97
    return pseudofd++;
2308
97
}
2309
/** \} */