mirror of
https://github.com/Unidata/netcdf-c.git
synced 2025-02-05 16:20:10 +08:00
re: Issue https://github.com/Unidata/netcdf-c/issues/2190 The primary purpose of this PR is to improve the utf8 support for windows. This is persuant to a change in Windows that supports utf8 natively (almost). The almost means that it is still utf16 internally and the set of characters representable by utf8 is larger than those representable by utf16. This leaves open the question in the Issue about handling the Windows 1252 character set. This required the following changes: 1. Test the Windows build and major version in order to see if native utf8 is supported. 2. If native utf8 is supported, Modify dpathmgr.c to call the 8-bit version of the windows fopen() and open() functions. 3. In support of this, programs that use XGetOpt (Windows versions) need to get the command line as utf8 and then parse to arc+argv as utf8. This requires using a homegrown command line parser named XCommandLineToArgvA. 4. Add a utility program called "acpget" that prints out the current Windows code page and locale. Additionally, some technical debt was cleaned up as follows: 1. Unify all the places which attempt to read all or a part of a file into the dutil.c#NC_readfile code. 2. Similary unify all the code that creates temp files into dutil.c#NC_mktmp code. 3. Convert almost all remaining calls to fopen() and open() to NCfopen() and NCopen3(). This is to ensure that path management is used consistently. This touches a number of files. 4. extern->EXTERNL as needed to get it to work under Windows.
243 lines
5.7 KiB
C
243 lines
5.7 KiB
C
/*! \file
|
|
|
|
Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
|
2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014,
|
|
2015, 2016, 2017, 2018
|
|
University Corporation for Atmospheric Research/Unidata.
|
|
|
|
See \ref copyright file for more info.
|
|
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#ifdef HAVE_TIME_H
|
|
#include <time.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include "unistd.h"
|
|
#endif
|
|
#ifdef _WIN32
|
|
#include <Windows.h>
|
|
#include <io.h>
|
|
#endif
|
|
|
|
#include "netcdf.h"
|
|
#include "ncpathmgr.h"
|
|
|
|
#define CLEANUP
|
|
|
|
#ifndef NC_NETCDF3
|
|
#define NC_NETCDF3 0
|
|
#endif
|
|
|
|
#define FILE3D "file3d.nc"
|
|
#define FILE3DP "file3dp.nc"
|
|
#define FILE4D "file4d.nc"
|
|
#define FILE4DP "file4dp.nc"
|
|
|
|
/* Mnemonics */
|
|
#define RDONLY 1
|
|
#define RDWRITE 0
|
|
|
|
#ifdef _WIN32
|
|
#define RDONLYMODE (_S_IREAD)
|
|
#define RDWRMODE (_S_IREAD|_S_IWRITE)
|
|
#define CHMOD _chmod
|
|
#define SLEEP(x) Sleep((x)*1000)
|
|
#else
|
|
#define RDONLYMODE (S_IRUSR|S_IRGRP|S_IROTH)
|
|
#define RDWRMODE (RDONLY | S_IWUSR)
|
|
#define CHMOD chmod
|
|
#define SLEEP(x) sleep(x)
|
|
#endif
|
|
|
|
typedef enum OC { OPEN, CLOSE} OC;
|
|
|
|
static int lineno = 0;
|
|
|
|
static char*
|
|
smode(int mode)
|
|
{
|
|
static char ms[8192];
|
|
ms[0] = '\0';
|
|
if(mode & NC_NETCDF4)
|
|
strcat(ms,"NC_NETCDF4");
|
|
else
|
|
strcat(ms,"NC_NETCDF3");
|
|
if(mode & NC_DISKLESS)
|
|
strcat(ms,"|NC_DISKLESS");
|
|
if(mode & NC_WRITE)
|
|
strcat(ms,"|NC_WRITE");
|
|
if(mode & NC_NOCLOBBER)
|
|
strcat(ms,"|NC_NOCLOBBER");
|
|
if(mode & NC_INMEMORY)
|
|
strcat(ms,"|NC_INMEMORY");
|
|
if(mode & NC_PERSIST)
|
|
strcat(ms,"|NC_PERSIST");
|
|
if(mode & NC_MMAP)
|
|
strcat(ms,"|NC_MMAP");
|
|
return ms;
|
|
}
|
|
|
|
/* Return 1 if file was changed else 0 */
|
|
static time_t
|
|
getmodified(const char* path)
|
|
{
|
|
struct stat attr;
|
|
stat(path, &attr);
|
|
return attr.st_mtime;
|
|
}
|
|
|
|
static void
|
|
changeaccess(int rdonly)
|
|
{
|
|
const char* p3 = FILE3DP; /* Keep Visual Studio happy */
|
|
const char* p4 = FILE4DP;
|
|
int mode = (rdonly?RDONLYMODE:RDWRMODE);
|
|
(void)CHMOD(p3,mode);
|
|
(void)CHMOD(p4,mode);
|
|
}
|
|
|
|
static void
|
|
cleanup()
|
|
{
|
|
changeaccess(RDWRITE);
|
|
/* cleanup */
|
|
(void)unlink(FILE3DP);
|
|
(void)unlink(FILE4DP);
|
|
}
|
|
|
|
static void
|
|
fail(int ret)
|
|
{
|
|
if(ret != NC_NOERR) {
|
|
fprintf(stderr,"*** Fail: line: %d: (%d) %s\n", lineno, ret, nc_strerror(ret));
|
|
fflush(stderr);
|
|
#ifdef CLEANUP
|
|
cleanup();
|
|
#endif
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void
|
|
exists(const char* file)
|
|
{
|
|
int stat = 0;
|
|
FILE* f = NCfopen(file, "r");
|
|
if(f == NULL) fail(NC_EPERM);
|
|
fclose(f);
|
|
}
|
|
|
|
void
|
|
notexists(const char* file)
|
|
{
|
|
FILE* f = NCfopen(file,"r");
|
|
if(f != NULL) {fclose(f); fail(NC_EEXIST);}
|
|
}
|
|
|
|
#define TESTCREATE(file,mode,exist) {lineno=__LINE__; testcreate(file,mode,exist);}
|
|
#define TESTOPEN(file,mode,rw) {lineno=__LINE__; testopen(file,mode,rw);}
|
|
|
|
static void
|
|
testcreate(const char* file, int mode, int mustexist)
|
|
{
|
|
int ret = NC_NOERR;
|
|
int ncid, dimid;
|
|
|
|
printf("test: file=%s mode=%s\n",file,smode(mode)); fflush(stdout);
|
|
|
|
if((ret = nc_create(file,mode,&ncid))) fail(ret);
|
|
if((ret = nc_def_dim(ncid,"dim",5,&dimid))) fail(ret);
|
|
if((ret = nc_close(ncid))) fail(ret);
|
|
if(mustexist)
|
|
exists(file);
|
|
else
|
|
notexists(file);
|
|
}
|
|
|
|
static void
|
|
testopen(const char* file, int mode, int rdwrite)
|
|
{
|
|
int ret = NC_NOERR;
|
|
int ncid, dimid;
|
|
size_t len;
|
|
time_t time1, time2;
|
|
|
|
printf("test: file=%s mode=%s\n",file,smode(mode)); fflush(stdout);
|
|
|
|
time1 = getmodified(file);
|
|
SLEEP(1); /* Ensure that if modified, it will be reported */
|
|
if((ret = nc_open(file,mode,&ncid))) fail(ret);
|
|
if(rdwrite) {
|
|
/* Modify */
|
|
if((ret=nc_redef(ncid))) fail(ret);
|
|
/* See if dim2 is already defined */
|
|
ret = nc_inq_dimid(ncid,"dim2",&dimid);
|
|
if(ret == NC_NOERR) {/* defined */
|
|
if((ret = nc_def_dim(ncid,"dim3",3,&dimid))) fail(ret);
|
|
} else if(ret == NC_EBADDIM) { /* not defined */
|
|
if((ret = nc_def_dim(ncid,"dim2",2,&dimid))) fail(ret);
|
|
} else
|
|
fail(ret);
|
|
if((ret=nc_enddef(ncid))) fail(ret);
|
|
}
|
|
if((ret = nc_inq_dimid(ncid,"dim",&dimid))) fail(ret);
|
|
if((ret = nc_inq_dimlen(ncid,dimid,&len))) fail(ret);
|
|
ret = nc_close(ncid);
|
|
if(ret != NC_NOERR && rdwrite) fail(ret);
|
|
else if(ret == NC_NOERR && !rdwrite) fail(NC_EINVAL);
|
|
time2 = getmodified(file);
|
|
if(!rdwrite) {
|
|
if(time2 != time1) {
|
|
fprintf(stderr,"file modified: %s\n",file);
|
|
fail(NC_EACCESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
main()
|
|
{
|
|
changeaccess(RDWRITE);
|
|
cleanup();
|
|
|
|
/* Test various mode combinations */
|
|
|
|
/* Create and persist some files using diskless */
|
|
printf("*** Test create\n"); fflush(stdout);
|
|
TESTCREATE(FILE3D,NC_NETCDF3|NC_DISKLESS,0); /* Not persisted */
|
|
TESTCREATE(FILE3DP,NC_NETCDF3|NC_DISKLESS|NC_PERSIST,1); /* persisted */
|
|
TESTCREATE(FILE4D,NC_NETCDF4|NC_DISKLESS,0); /* Not persisted */
|
|
TESTCREATE(FILE4DP,NC_NETCDF4|NC_DISKLESS|NC_PERSIST,1); /* persisted */
|
|
|
|
/* Of the above persisted files, re-read and modify and re-persist */
|
|
printf("*** Test open + modify + rdwrite\n"); fflush(stdout);
|
|
TESTOPEN(FILE3DP,NC_NETCDF3|NC_DISKLESS|NC_PERSIST|NC_WRITE,1); /* re-persist */
|
|
TESTOPEN(FILE4DP,NC_NETCDF4|NC_DISKLESS|NC_PERSIST|NC_WRITE,1); /* re-persist */
|
|
|
|
/* Of the above modified files, re-read and modify but do not re-persist */
|
|
/* Test open + modify + rdonly; requires NC_DISKLESS*/
|
|
#if 0
|
|
/* Fails with hdf5, file must be writeable even if not persisted */
|
|
changeaccess(RDONLY); /* prevent re-persist */
|
|
#endif
|
|
printf("*** Testopen modify + rdonly\n"); fflush(stdout);
|
|
TESTOPEN(FILE3DP,NC_NETCDF3|NC_DISKLESS|NC_WRITE,1);
|
|
TESTOPEN(FILE4DP,NC_NETCDF4|NC_DISKLESS|NC_WRITE,1);
|
|
|
|
#ifdef CLEANUP
|
|
cleanup();
|
|
#endif
|
|
return 0;
|
|
}
|