netcdf-c/ncgen/main.c
Dennis Heimbigner 751300ec59 Fix more memory leaks in netcdf-c library
This is a follow up to PR https://github.com/Unidata/netcdf-c/pull/1173

Sorry that it is so big, but leak suppression can be complex.

This PR fixes all remaining memory leaks -- as determined by
-fsanitize=address, and with the exceptions noted below.

Unfortunately. there remains a significant leak that I cannot
solve. It involves vlens, and it is unclear if the leak is
occurring in the netcdf-c library or the HDF5 library.

I have added a check_PROGRAM to the ncdump directory to show the
problem.  The program is called tst_vlen_demo.c To exercise it,
build the netcdf library with -fsanitize=address enabled. Then
go into ncdump and do a "make clean check".  This should build
tst_vlen_demo without actually executing it.  Then do the
command "./tst_vlen_demo" to see the output of the memory
checker.  Note the the lost malloc is deep in the HDF5 library
(in H5Tvlen.c).

I am temporarily working around this error in the following way.
1. I modified several test scripts to not execute known vlen tests
   that fail as described above.
2. Added an environment variable called NC_VLEN_NOTEST.
   If set, then those specific tests are suppressed.

This should mean that the --disable-utilities option to
./configure should not need to be set to get a memory leak clean
build.  This should allow for detection of any new leaks.

Note: I used an environment variable rather than a ./configure
option to control the vlen tests. This is because it is
temporary (I hope) and because it is a bit tricky for shell
scripts to access ./configure options.

Finally, as before, this only been tested with netcdf-4 and hdf5 support.
2018-11-15 10:00:38 -07:00

613 lines
16 KiB
C

/*********************************************************************
* Copyright 1993, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*********************************************************************/
/* $Id: main.c,v 1.33 2010/05/26 21:43:36 dmh Exp $ */
/* $Header: /upc/share/CVS/netcdf-3/ncgen/main.c,v 1.33 2010/05/26 21:43:36 dmh Exp $ */
#include "includes.h"
#include "ncoffsets.h"
#include "ncwinpath.h"
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifdef _MSC_VER
#include "XGetopt.h"
#define snprintf _snprintf
int opterr;
int optind;
#endif
/* Default is netcdf-3 mode 1 */
#define DFALTCMODE 0
/* For error messages */
char* progname; /* Global: not reclaimed */
char* cdlname; /* Global: not reclaimed */
/* option flags */
int nofill_flag;
char* mainname; /* name to use for main function; defaults to "main"*/
Language l_flag;
int syntax_only;
int header_only;
/* flags for tracking what output format to use */
int k_flag; /* > 0 => -k was specified on command line*/
int format_attribute; /* 1=>format came from format attribute */
int enhanced_flag; /* 1 => netcdf-4 */
int cdf5_flag; /* 1 => cdf5 | maybe netcdf-4 */
int specials_flag; /* 1=> special attributes are present */
int usingclassic;
int cmode_modifier;
int diskless;
int ncloglevel;
GlobalSpecialData globalspecials;
char* binary_ext = ".nc";
size_t nciterbuffersize;
struct Vlendata* vlendata;
char *netcdf_name = NULL; /* command line -o file name */
char *datasetname = NULL; /* name from the netcdf <name> {} || from -N */
extern FILE *ncgin;
/* Forward */
static char* ubasename(char*);
void usage( void );
int main( int argc, char** argv );
/* Define tables vs modes for legal -k values*/
struct Kvalues legalkinds[] = {
/* NetCDF-3 classic format (32-bit offsets) */
{"classic", NC_FORMAT_CLASSIC}, /* canonical format name */
{"nc3", NC_FORMAT_CLASSIC}, /* short format name */
{"1", NC_FORMAT_CLASSIC}, /* deprecated, use "-3" or "-k nc3" instead */
/* NetCDF-3 64-bit offset format */
{"64-bit offset", NC_FORMAT_64BIT_OFFSET}, /* canonical format name */
{"nc6", NC_FORMAT_64BIT_OFFSET}, /* short format name */
{"2", NC_FORMAT_64BIT_OFFSET}, /* deprecated, use "-6" or "-k nc6" instead */
{"64-bit-offset", NC_FORMAT_64BIT_OFFSET}, /* aliases */
/* NetCDF-4 HDF5-based format */
{"netCDF-4", NC_FORMAT_NETCDF4}, /* canonical format name */
{"nc4", NC_FORMAT_NETCDF4}, /* short format name */
{"3", NC_FORMAT_NETCDF4}, /* deprecated, use "-4" or "-k nc4" instead */
{"netCDF4", NC_FORMAT_NETCDF4}, /* aliases */
{"hdf5", NC_FORMAT_NETCDF4},
{"enhanced", NC_FORMAT_NETCDF4},
{"netcdf-4", NC_FORMAT_NETCDF4},
{"netcdf4", NC_FORMAT_NETCDF4},
/* NetCDF-4 HDF5-based format, restricted to classic data model */
{"netCDF-4 classic model", NC_FORMAT_NETCDF4_CLASSIC}, /* canonical format name */
{"nc7", NC_FORMAT_NETCDF4_CLASSIC}, /* short format name */
{"4", NC_FORMAT_NETCDF4_CLASSIC}, /* deprecated, use "-7" or -k nc7" instead */
{"netCDF-4-classic", NC_FORMAT_NETCDF4_CLASSIC}, /* aliases */
{"netCDF-4_classic", NC_FORMAT_NETCDF4_CLASSIC},
{"netCDF4_classic", NC_FORMAT_NETCDF4_CLASSIC},
{"hdf5-nc3", NC_FORMAT_NETCDF4_CLASSIC},
{"enhanced-nc3", NC_FORMAT_NETCDF4_CLASSIC},
/* CDF-5 format */
{"5", NC_FORMAT_64BIT_DATA},
{"64-bit-data", NC_FORMAT_64BIT_DATA},
{"64-bit data", NC_FORMAT_64BIT_DATA},
{"nc5", NC_FORMAT_64BIT_DATA},
{"cdf5", NC_FORMAT_64BIT_DATA},
{"cdf-5", NC_FORMAT_64BIT_DATA},
/* null terminate*/
{NULL,0}
};
#ifndef _MSC_VER
struct Languages {
char* name;
Language flag;
} legallanguages[] = {
{"b", L_BINARY},
{"c", L_C},
{"C", L_C},
{"f77", L_F77},
{"fortran77", L_F77},
{"Fortran77", L_F77},
{"j", L_JAVA},
{"java", L_JAVA},
{NULL,L_UNDEFINED}
};
#else
typedef struct Languages {
char* name;
Language flag;
} Languages;
struct Languages legallanguages[] = {
{"b", L_BINARY},
{"c", L_C},
{"C", L_C},
{"f77", L_F77},
{"fortran77", L_F77},
{"Fortran77", L_F77},
{"j", L_JAVA},
{"java", L_JAVA},
{NULL,L_UNDEFINED}
};
#endif
#if 0 /*not used*/
/* BOM Sequences */
static char* U8 = "\xEF\xBB\xBF"; /* UTF-8 */
static char* BE32 = "\x00\x00\xFE\xFF"; /* UTF-32; big-endian */
static char* LE32 = "\xFF\xFE"; /* UTF-32; little-endian */
static char* BE16 = "\xFE\xFF"; /* UTF-16; big-endian */
static char* LE16 = "\xFF\xFE"; /* UTF-16; little-endian */
#endif
/* The default minimum iterator size depends
on whether we are doing binary or language
based output.
*/
#define DFALTBINNCITERBUFFERSIZE 0x40000 /* about 250k bytes */
#define DFALTLANGNCITERBUFFERSIZE 0x4000 /* about 15k bytes */
/* strip off leading path */
/* result is malloc'd */
static char *
ubasename(char *logident)
{
char* sep;
sep = strrchr(logident,'/');
#ifdef MSDOS
if(sep == NULL) sep = strrchr(logident,'\\');
#endif
if(sep == NULL) return logident;
sep++; /* skip past the separator */
return sep;
}
void
usage(void)
{
derror("Usage: %s"
" [-1]"
" [-3]"
" [-4]"
" [-5]"
" [-6]"
" [-7]"
" [-b]"
" [-B buffersize]"
" [-d]"
" [-D debuglevel]"
" [-h]"
" [-k kind ]"
" [-l language=b|c|f77|java]"
" [-M <name>]"
" [-n]"
" [-o outfile]"
" [-P]"
" [-x]"
" [-N datasetname]"
" [-L loglevel]"
" [-H]"
" [file ... ]",
progname);
derror("netcdf library version %s", nc_inq_libvers());
}
int
main(
int argc,
char *argv[])
{
int code = 0;
int c;
FILE *fp;
struct Languages* langs;
#ifdef __hpux
setlocale(LC_CTYPE,"");
#endif
init_netcdf();
opterr = 1; /* print error message if bad option */
progname = nulldup(ubasename(argv[0]));
cdlname = NULL;
netcdf_name = NULL;
datasetname = NULL;
l_flag = 0;
nofill_flag = 0;
syntax_only = 0;
header_only = 0;
mainname = "main";
nciterbuffersize = 0;
k_flag = 0;
format_attribute = 0;
enhanced_flag = 0;
cdf5_flag = 0;
specials_flag = 0;
diskless = 0;
#ifdef LOGGING
ncloglevel = NC_TURN_OFF_LOGGING;
#else
ncloglevel = -1;
#endif
memset(&globalspecials,0,sizeof(GlobalSpecialData));
#if _CRAYMPP && 0
/* initialize CRAY MPP parallel-I/O library */
(void) par_io_init(32, 32);
#endif
while ((c = getopt(argc, argv, "134567bB:cdD:fhHk:l:M:no:Pv:xL:N:")) != EOF)
switch(c) {
case 'd':
debug = 1;
break;
case 'D':
debug = atoi(optarg);
break;
case 'c': /* for c output, old version of "-lc" */
if(l_flag != 0) {
fprintf(stderr,"Please specify only one language\n");
return 1;
}
l_flag = L_C;
fprintf(stderr,"-c is deprecated: please use -lc\n");
break;
case 'f': /* for f77 output, old version of "-lf" */
if(l_flag != 0) {
fprintf(stderr,"Please specify only one language\n");
return 1;
}
l_flag = L_F77;
fprintf(stderr,"-f is deprecated: please use -lf77\n");
break;
case 'b': /* for binary netcdf output, ".nc" extension */
if(l_flag != 0) {
fprintf(stderr,"Please specify only one language\n");
return 1;
}
l_flag = L_BINARY;
break;
case 'H':
header_only = 1;
break;
case 'h':
usage();
goto done;
case 'l': /* specify language, instead of using -c or -f or -b */
{
char* lang_name = NULL;
if(l_flag != 0) {
fprintf(stderr,"Please specify only one language\n");
return 1;
}
if(!optarg) {
derror("%s: output language is null", progname);
return(1);
}
//lang_name = estrdup(optarg);
lang_name = (char*) emalloc(strlen(optarg)+1);
(void)strcpy(lang_name, optarg);
for(langs=legallanguages;langs->name != NULL;langs++) {
if(strcmp(lang_name,langs->name)==0) {
l_flag = langs->flag;
break;
}
}
if(langs->name == NULL) {
derror("%s: output language %s not implemented",progname, lang_name);
nullfree(lang_name);
return(1);
}
nullfree(lang_name);
}; break;
case 'L':
ncloglevel = atoi(optarg);
break;
case 'n': /* old version of -b, uses ".cdf" extension */
if(l_flag != 0) {
fprintf(stderr,"Please specify only one language\n");
return 1;
}
l_flag = L_BINARY;
binary_ext = ".cdf";
break;
case 'o': /* to explicitly specify output name */
if(netcdf_name) efree(netcdf_name);
netcdf_name = nulldup(optarg);
break;
case 'N': /* to explicitly specify dataset name */
if(datasetname) efree(datasetname);
datasetname = nulldup(optarg);
break;
case 'x': /* set nofill mode to speed up creation of large files */
nofill_flag = 1;
break;
case 'v': /* a deprecated alias for "kind" option */
/*FALLTHRU*/
case 'k': { /* for specifying variant of netCDF format to be generated
Possible values are:
Format names:
"classic" or "nc3"
"64-bit offset" or "nc6"
"64-bit data" or "nc5" or "cdf-5"
"netCDF-4" or "nc4"
"netCDF-4 classic model" or "nc7"
"netCDF-5" or "nc5" or "cdf5"
Format version numbers (deprecated):
1 (=> classic)
2 (=> 64-bit offset)
3 (=> netCDF-4)
4 (=> netCDF-4 classic model)
5 (=> classic 64 bit data aka CDF-5)
*/
struct Kvalues* kvalue;
if(optarg == NULL) {
derror("-k flag has no value");
return 2;
}
for(kvalue=legalkinds;kvalue->name;kvalue++) {
if(strcmp(optarg,kvalue->name) == 0) {
k_flag = kvalue->k_flag;
break;
}
}
if(kvalue->name == NULL) {
derror("Invalid format: %s",optarg);
return 2;
}
} break;
case '3': /* output format is classic (netCDF-3) */
k_flag = NC_FORMAT_CLASSIC;
break;
case '6': /* output format is 64-bit-offset (netCDF-3 version 2) */
k_flag = NC_FORMAT_64BIT_OFFSET;
break;
case '4': /* output format is netCDF-4 (variant of HDF5) */
k_flag = NC_FORMAT_NETCDF4;
break;
case '5': /* output format is CDF5 */
k_flag = NC_FORMAT_CDF5;
break;
case '7': /* output format is netCDF-4 (restricted to classic model)*/
k_flag = NC_FORMAT_NETCDF4_CLASSIC;
break;
case 'M': /* Determine the name for the main function */
mainname = nulldup(optarg);
break;
case 'B':
nciterbuffersize = atoi(optarg);
break;
case 'P': /* diskless with persistence */
diskless = 1;
break;
case '?':
usage();
return(8);
}
if(l_flag == 0) {
l_flag = L_BINARY; /* default */
/* Treat -k or -o as an implicit -lb assuming no other -l flags */
if(k_flag == 0 && netcdf_name == NULL)
syntax_only = 1;
}
/* Compute/default the iterator buffer size */
if(l_flag == L_BINARY) {
if(nciterbuffersize == 0 )
nciterbuffersize = DFALTBINNCITERBUFFERSIZE;
} else {
if(nciterbuffersize == 0)
nciterbuffersize = DFALTLANGNCITERBUFFERSIZE;
}
#ifndef ENABLE_C
if(l_flag == L_C) {
fprintf(stderr,"C not currently supported\n");
code=1; goto done;
}
#endif
#ifndef ENABLE_BINARY
if(l_flag == L_BINARY) {
fprintf(stderr,"Binary netcdf not currently supported\n");
code=1; goto done;
}
#endif
#ifndef ENABLE_JAVA
if(l_flag == L_JAVA) {
fprintf(stderr,"Java not currently supported\n");
code=1; goto done;
}
#else
if(l_flag == L_JAVA && mainname != NULL && strcmp(mainname,"main")==0)
mainname = "Main";
#endif
#ifndef ENABLE_F77
if(l_flag == L_F77) {
fprintf(stderr,"F77 not currently supported\n");
code=1; goto done;
}
#endif
if(l_flag != L_BINARY)
diskless = 0;
argc -= optind;
argv += optind;
if (argc > 1) {
derror ("%s: only one input file argument permitted",progname);
return(6);
}
fp = stdin;
if (argc > 0 && strcmp(argv[0], "-") != 0) {
char bom[4];
size_t count;
if ((fp = NCfopen(argv[0], "r")) == NULL) {
derror ("can't open file %s for reading: ", argv[0]);
perror("");
return(7);
}
/* Check the leading bytes for an occurrence of a BOM */
/* re: http://www.unicode.org/faq/utf_bom.html#BOM */
/* Attempt to read the first four bytes */
memset(bom,0,sizeof(bom));
count = fread(bom,1,2,fp);
if(count == 2) {
switch (bom[0]) {
case '\x00':
case '\xFF':
case '\xFE':
/* Only UTF-* is allowed; complain and exit */
fprintf(stderr,"Input file contains a BOM indicating a non-UTF8 encoding\n");
return 1;
case '\xEF':
/* skip the BOM */
(void)fread(bom,1,1,fp);
break;
default: /* legal printable char, presumably; rewind */
rewind(fp);
break;
}
}
}
cdlname = nulldup(argv[0]);
if(cdlname != NULL) {
if(strlen(cdlname) > NC_MAX_NAME)
cdlname[NC_MAX_NAME] = '\0';
}
parse_init();
ncgin = fp;
if(debug >= 2) {ncgdebug=1;}
if(ncgparse() != 0)
return 1;
/* Compute the k_flag (1st pass) using rules in the man page (ncgen.1).*/
#ifndef ENABLE_CDF5
if(k_flag == NC_FORMAT_CDF5) {
derror("Output format CDF5 requested, but netcdf was built without cdf5 support.");
return 0;
}
#endif
#ifndef USE_NETCDF4
if(enhanced_flag) {
derror("CDL input is enhanced mode, but --disable-netcdf4 was specified during build");
return 0;
}
#endif
if(l_flag == L_JAVA || l_flag == L_F77) {
k_flag = NC_FORMAT_CLASSIC;
if(enhanced_flag) {
derror("Java or Fortran requires classic model CDL input");
return 0;
}
}
if(k_flag == 0)
k_flag = globalspecials._Format;
if(cdf5_flag && !enhanced_flag && k_flag == 0)
k_flag = NC_FORMAT_64BIT_DATA;
if(enhanced_flag && k_flag == 0)
k_flag = NC_FORMAT_NETCDF4;
if(enhanced_flag && k_flag != NC_FORMAT_NETCDF4) {
if(enhanced_flag && k_flag != NC_FORMAT_NETCDF4 && k_flag != NC_FORMAT_64BIT_DATA) {
derror("-k or _Format conflicts with enhanced CDL input");
return 0;
}
}
if(specials_flag > 0 && k_flag == 0)
#ifdef USE_NETCDF4
k_flag = NC_FORMAT_NETCDF4;
#else
k_flag = NC_FORMAT_CLASSIC;
#endif
if(k_flag == 0)
k_flag = NC_FORMAT_CLASSIC;
/* Figure out usingclassic */
switch (k_flag) {
case NC_FORMAT_64BIT_DATA:
case NC_FORMAT_CLASSIC:
case NC_FORMAT_64BIT_OFFSET:
case NC_FORMAT_NETCDF4_CLASSIC:
usingclassic = 1;
break;
case NC_FORMAT_NETCDF4:
default:
usingclassic = 0;
break;
}
/* compute cmode_modifier */
switch (k_flag) {
case NC_FORMAT_CLASSIC:
cmode_modifier = 0; break;
case NC_FORMAT_64BIT_OFFSET:
cmode_modifier = NC_64BIT_OFFSET; break;
case NC_FORMAT_NETCDF4:
cmode_modifier = NC_NETCDF4; break;
case NC_FORMAT_NETCDF4_CLASSIC:
cmode_modifier = NC_NETCDF4 | NC_CLASSIC_MODEL; break;
case NC_FORMAT_64BIT_DATA:
cmode_modifier = NC_CDF5; break;
default: ASSERT(0); /* cannot happen */
}
if(diskless)
cmode_modifier |= (NC_DISKLESS|NC_NOCLOBBER);
processsemantics();
if(!syntax_only && error_count == 0)
define_netcdf();
done:
nullfree(netcdf_name);
nullfree(datasetname);
finalize_netcdf(code);
return code;
}
void
init_netcdf(void) /* initialize global counts, flags */
{
memset((void*)&nullconstant,0,sizeof(NCConstant));
fillconstant = nullconstant;
fillconstant.nctype = NC_FILLVALUE;
codebuffer = bbNew();
stmt = bbNew();
error_count = 0; /* Track # of errors */
}
void
finalize_netcdf(int retcode)
{
nc_finalize();
exit(retcode);
}