Coverage Report

Created: 2026-07-16 06:49

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