Coverage Report

Created: 2025-10-28 07:06

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