mirror of https://gitee.com/openkylin/gdcm.git
1121 lines
27 KiB
C++
1121 lines
27 KiB
C++
/*=========================================================================
|
|
|
|
Program: GDCM (Grassroots DICOM). A DICOM library
|
|
|
|
Copyright (c) 2006-2016 Mathieu Malaterre
|
|
All rights reserved.
|
|
See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
#include "gdcmSystem.h"
|
|
#include "gdcmTrace.h"
|
|
#include "gdcmFilename.h"
|
|
#include "gdcmDirectory.h"
|
|
#include "gdcmException.h"
|
|
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h> // strspn
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <limits.h> // PATH_MAX
|
|
|
|
// gettimeofday
|
|
#ifdef GDCM_HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif
|
|
#include <time.h>
|
|
#ifdef GDCM_HAVE_WINSOCK_H
|
|
#include <winsock.h>
|
|
#endif
|
|
#include <cstdio> // snprintf
|
|
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
|
#define snprintf _snprintf
|
|
#endif
|
|
#ifdef GDCM_USE_COREFOUNDATION_LIBRARY
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
#endif
|
|
|
|
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__WATCOMC__) ||defined(__BORLANDC__) || defined(__MINGW32__))
|
|
#include <io.h>
|
|
#include <direct.h>
|
|
#define _unlink unlink
|
|
#else
|
|
//#include <features.h> // we want GNU extensions
|
|
#include <dlfcn.h>
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h> /* gethostname */
|
|
#include <strings.h> // strncasecmp
|
|
#endif
|
|
|
|
#if defined(GDCM_HAVE_LANGINFO_H)
|
|
#include <langinfo.h> // nl_langinfo
|
|
#endif
|
|
|
|
// TODO: WIN32 replacement for C99 stuff:
|
|
// #if defined(_WIN32) || defined(_WIN64)
|
|
// #define vsnprintf _vsnprintf
|
|
// #define strcasecmp _stricmp
|
|
// #define strncasecmp _strnicmp
|
|
// #endif
|
|
|
|
namespace gdcm
|
|
{
|
|
|
|
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) || defined(__MINGW32__))
|
|
inline int Mkdir(const char* dir)
|
|
{
|
|
return _mkdir(dir);
|
|
}
|
|
inline int Rmdir(const char* dir)
|
|
{
|
|
return _rmdir(dir);
|
|
}
|
|
inline const char* Getcwd(char* buf, unsigned int len)
|
|
{
|
|
const char* ret = _getcwd(buf, len);
|
|
return ret;
|
|
}
|
|
|
|
#else
|
|
inline int Mkdir(const char* dir)
|
|
{
|
|
return mkdir(dir, 00777);
|
|
}
|
|
inline int Rmdir(const char* dir)
|
|
{
|
|
return rmdir(dir);
|
|
}
|
|
inline const char* Getcwd(char* buf, unsigned int len)
|
|
{
|
|
const char* ret = getcwd(buf, len);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
// 1.14 How can I find a process' executable file?
|
|
// http://www.faqs.org/faqs/unix-faq/programmer/faq/
|
|
static std::string Argv0;
|
|
|
|
void System::SetArgv0(const char *argv0)
|
|
{
|
|
Argv0 = argv0;
|
|
//std::cout << "Set:" << Argv0 << std::endl;
|
|
}
|
|
|
|
const char* System::GetArgv0()
|
|
{
|
|
//std::cout << "Get:" << Argv0 << std::endl;
|
|
return Argv0.c_str();
|
|
}
|
|
*/
|
|
|
|
const char * System::GetCWD()
|
|
{
|
|
static char buf[2048];
|
|
const char* cwd = Getcwd(buf, 2048);
|
|
return cwd;
|
|
/*
|
|
std::string path;
|
|
if ( cwd )
|
|
{
|
|
path = cwd;
|
|
}
|
|
return path;
|
|
*/
|
|
}
|
|
|
|
static inline int Mkdir2(const char *utf8)
|
|
{
|
|
#ifdef _MSC_VER
|
|
const std::wstring unc = System::ConvertToUNC(utf8);
|
|
return _wmkdir(unc.c_str());
|
|
#else
|
|
return Mkdir(utf8);
|
|
#endif
|
|
}
|
|
|
|
bool System::MakeDirectory(const char *path)
|
|
{
|
|
if( !path || !*path )
|
|
return false;
|
|
if(System::FileExists(path))
|
|
{
|
|
return true;
|
|
}
|
|
Filename fn(path);
|
|
std::string dir = fn.ToUnixSlashes();
|
|
|
|
std::string::size_type pos = dir.find(':');
|
|
if(pos == std::string::npos)
|
|
{
|
|
pos = 0;
|
|
}
|
|
std::string topdir;
|
|
bool ok = true;
|
|
while(ok && (pos = dir.find('/', pos)) != std::string::npos)
|
|
{
|
|
topdir = dir.substr(0, pos+1);
|
|
ok = ok && (System::FileIsDirectory(topdir.c_str()) || 0 == Mkdir2(topdir.c_str()));
|
|
pos++;
|
|
}
|
|
if( !ok ) return false;
|
|
if(dir[dir.size()-1] == '/')
|
|
{
|
|
topdir = dir.substr(0, dir.size());
|
|
}
|
|
else
|
|
{
|
|
topdir = dir;
|
|
}
|
|
if(Mkdir2(topdir.c_str()) != 0)
|
|
{
|
|
// There is a bug in the Borland Run time library which makes MKDIR
|
|
// return EACCES when it should return EEXISTS
|
|
// if it is some other error besides directory exists
|
|
// then return false
|
|
if( (errno != EEXIST)
|
|
#ifdef __BORLANDC__
|
|
&& (errno != EACCES)
|
|
#endif
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// return true if the file exists
|
|
bool System::FileExists(const char* filename)
|
|
{
|
|
#ifndef R_OK
|
|
# define R_OK 04
|
|
#endif
|
|
#ifdef _MSC_VER
|
|
const std::wstring unc = System::ConvertToUNC(filename);
|
|
if (_waccess(unc.c_str(), R_OK) != 0)
|
|
#else
|
|
if ( access(filename, R_OK) != 0 )
|
|
#endif
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
//assert( !FileIsDirectory(filename) );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool System::FileIsDirectory(const char* name)
|
|
{
|
|
#ifdef _MSC_VER
|
|
struct _stat64i32 fs;
|
|
const std::wstring wname = System::ConvertToUNC(name);
|
|
if (_wstat(wname.c_str(), &fs) == 0)
|
|
#else
|
|
struct stat fs;
|
|
if(stat(name, &fs) == 0)
|
|
#endif
|
|
{
|
|
#if _WIN32
|
|
return ((fs.st_mode & _S_IFDIR) != 0);
|
|
#else
|
|
return S_ISDIR(fs.st_mode);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool System::FileIsSymlink(const char* name)
|
|
{
|
|
#if defined( _WIN32 )
|
|
(void)name;
|
|
#else
|
|
struct stat fs;
|
|
if(lstat(name, &fs) == 0)
|
|
{
|
|
return S_ISLNK(fs.st_mode);
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
// TODO st_mtimensec
|
|
time_t System::FileTime(const char* filename)
|
|
{
|
|
struct stat fs;
|
|
if(stat(filename, &fs) == 0)
|
|
{
|
|
// man 2 stat
|
|
// time_t st_atime; /* time of last access */
|
|
// time_t st_mtime; /* time of last modification */
|
|
// time_t st_ctime; /* time of last status change */
|
|
return fs.st_mtime;
|
|
|
|
// Since kernel 2.5.48, the stat structure supports nanosecond resolution
|
|
// for the three file timestamp fields. Glibc exposes the nanosecond com-
|
|
// ponent of each field using names either of the form st_atim.tv_nsec, if
|
|
// the _BSD_SOURCE or _SVID_SOURCE feature test macro is defined, or of
|
|
// the form st_atimensec, if neither of these macros is defined. On file
|
|
// systems that do not support sub-second timestamps, these nanosecond
|
|
// fields are returned with the value 0.
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char *System::GetLastSystemError()
|
|
{
|
|
int e = errno;
|
|
return strerror(e);
|
|
}
|
|
|
|
bool System::GetPermissions(const char* file, unsigned short& mode)
|
|
{
|
|
if ( !file )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
struct stat st;
|
|
if ( stat(file, &st) < 0 )
|
|
{
|
|
return false;
|
|
}
|
|
mode = (short)st.st_mode;
|
|
return true;
|
|
}
|
|
|
|
bool System::SetPermissions(const char* file, unsigned short mode)
|
|
{
|
|
if ( !file )
|
|
{
|
|
return false;
|
|
}
|
|
if ( !System::FileExists(file) )
|
|
{
|
|
return false;
|
|
}
|
|
if ( chmod(file, mode) < 0 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool System::RemoveFile(const char* source)
|
|
{
|
|
#ifdef _WIN32
|
|
unsigned short mode;
|
|
if ( !System::GetPermissions(source, mode) )
|
|
{
|
|
return false;
|
|
}
|
|
/* Win32 unlink is stupid --- it fails if the file is read-only */
|
|
System::SetPermissions(source, S_IWRITE);
|
|
#endif
|
|
bool res = unlink(source) != 0 ? false : true;
|
|
#ifdef _WIN32
|
|
if ( !res )
|
|
{
|
|
System::SetPermissions(source, mode);
|
|
}
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
// RemoveDirectory is a WIN32 function, use different name
|
|
bool System::DeleteDirectory(const char *source)
|
|
{
|
|
unsigned short mode;
|
|
if(System::GetPermissions(source, mode))
|
|
{
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
mode |= S_IWRITE;
|
|
#else
|
|
mode |= S_IWUSR;
|
|
#endif
|
|
System::SetPermissions(source, mode);
|
|
}
|
|
|
|
Directory dir;
|
|
unsigned int numfiles = dir.Load(source, false);
|
|
(void)numfiles;
|
|
Directory::FilenamesType const & files = dir.GetFilenames();
|
|
for ( Directory::FilenamesType::const_iterator it = files.begin();
|
|
it != files.end(); ++it )
|
|
{
|
|
const char *filename = it->c_str();
|
|
if( System::FileIsDirectory(filename) &&
|
|
!System::FileIsSymlink(filename) )
|
|
{
|
|
if (!System::DeleteDirectory(filename))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!System::RemoveFile(filename))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return Rmdir(source) == 0;
|
|
}
|
|
|
|
#ifndef PATH_MAX
|
|
#define PATH_MAX 4096
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
namespace {
|
|
static inline std::wstring ToUtf16(std::string const &str) {
|
|
std::wstring ret;
|
|
int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.length(),
|
|
nullptr, 0);
|
|
if (len > 0) {
|
|
ret.resize(len);
|
|
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.length(), &ret[0],
|
|
len);
|
|
}
|
|
return ret;
|
|
}
|
|
// http://arsenmk.blogspot.com/2015/12/handling-long-paths-on-windows.html
|
|
static inline bool ComputeFullPath(std::wstring const &in,
|
|
std::wstring &out) {
|
|
// consider an input fileName of type PCWSTR (const wchar_t*)
|
|
const wchar_t *fileName = in.c_str();
|
|
DWORD requiredBufferLength =
|
|
GetFullPathNameW(fileName, 0, nullptr, nullptr);
|
|
|
|
if (0 == requiredBufferLength) // means failure
|
|
{
|
|
return false;
|
|
}
|
|
|
|
out.resize(requiredBufferLength);
|
|
wchar_t *buffer = &out[0];
|
|
|
|
DWORD result =
|
|
GetFullPathNameW(fileName, requiredBufferLength, buffer, nullptr);
|
|
|
|
if (0 == result) {
|
|
return false;
|
|
}
|
|
|
|
// buffer now contains the full path name of fileName, use it.
|
|
return true;
|
|
}
|
|
|
|
static inline std::wstring HandleMaxPath(std::wstring const &in) {
|
|
if (in.size() >= MAX_PATH) {
|
|
std::wstring out;
|
|
bool ret = ComputeFullPath(in, out);
|
|
if (!ret) return in;
|
|
if (out.size() < 4) return in;
|
|
if (out[0] == '\\' && out[1] == '\\' && out[2] == '?') {
|
|
// nothing to do
|
|
} else if (out[0] == '\\' && out[1] == '\\' && out[2] != '?') {
|
|
// server path
|
|
const std::wstring prefix = LR"(\\?\UNC\)";
|
|
out = prefix + (out.c_str() + 2);
|
|
} else {
|
|
// regular C:\ style path:
|
|
assert(out[1] == ':');
|
|
const std::wstring prefix = LR"(\\?\)";
|
|
out = prefix + out.c_str();
|
|
}
|
|
return out;
|
|
}
|
|
return in;
|
|
}
|
|
} // namespace
|
|
#endif
|
|
|
|
std::wstring System::ConvertToUNC(const char *utf8path)
|
|
{
|
|
#ifdef _MSC_VER
|
|
const std::wstring uft16path = ToUtf16(utf8path);
|
|
const std::wstring uncpath = HandleMaxPath(uft16path);
|
|
return uncpath;
|
|
#else
|
|
(void)utf8path;
|
|
return std::wstring();
|
|
#endif
|
|
}
|
|
|
|
// return size of file; also returns zero if no file exists
|
|
size_t System::FileSize(const char* filename)
|
|
{
|
|
#if 0
|
|
All of these system calls return a stat structure, which contains the
|
|
following fields:
|
|
|
|
struct stat {
|
|
dev_t st_dev; /* ID of device containing file */
|
|
ino_t st_ino; /* inode number */
|
|
mode_t st_mode; /* protection */
|
|
nlink_t st_nlink; /* number of hard links */
|
|
uid_t st_uid; /* user ID of owner */
|
|
gid_t st_gid; /* group ID of owner */
|
|
dev_t st_rdev; /* device ID (if special file) */
|
|
off_t st_size; /* total size, in bytes */
|
|
blksize_t st_blksize; /* blocksize for filesystem I/O */
|
|
blkcnt_t st_blocks; /* number of blocks allocated */
|
|
time_t st_atime; /* time of last access */
|
|
time_t st_mtime; /* time of last modification */
|
|
time_t st_ctime; /* time of last status change */
|
|
};
|
|
#endif
|
|
struct stat fs;
|
|
if (stat(filename, &fs) != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
off_t size = fs.st_size;
|
|
size_t size2 = size;
|
|
// off_t can be larger than size_t
|
|
if( size != (off_t)size2 ) return 0;
|
|
return size2;
|
|
}
|
|
|
|
/*
|
|
* TODO:
|
|
* check cygwin
|
|
* check beos : get_next_image_info
|
|
* check solaris
|
|
* check hpux
|
|
* check os2: DosGetInfoBlocks / DosQueryModuleName
|
|
* ...
|
|
*/
|
|
const char *System::GetCurrentProcessFileName()
|
|
{
|
|
#ifdef _WIN32
|
|
static char buf[MAX_PATH];
|
|
if ( ::GetModuleFileName(0, buf, sizeof(buf)) )
|
|
{
|
|
return buf;
|
|
}
|
|
#elif defined(GDCM_USE_COREFOUNDATION_LIBRARY)
|
|
static char buf[PATH_MAX];
|
|
Boolean success = false;
|
|
CFURLRef pathURL = CFBundleCopyExecutableURL(CFBundleGetMainBundle());
|
|
if ( pathURL)
|
|
{
|
|
success = CFURLGetFileSystemRepresentation(pathURL, true /*resolveAgainstBase*/, (unsigned char*) buf, PATH_MAX);
|
|
CFRelease(pathURL);
|
|
}
|
|
if (success)
|
|
{
|
|
return buf;
|
|
}
|
|
#elif defined (__SVR4) && defined (__sun)
|
|
// solaris
|
|
const char *ret = getexecname();
|
|
if( ret ) return ret;
|
|
//#elif defined(__NetBSD__)
|
|
// static char path[PATH_MAX];
|
|
// if ( readlink ("/proc/curproc/exe", path, sizeof(path)) > 0)
|
|
// {
|
|
// return path;
|
|
// }
|
|
#elif defined(__DragonFly__) || defined(__OpenBSD__) || defined(__FreeBSD__)
|
|
static char path[PATH_MAX];
|
|
if ( readlink ("/proc/curproc/file", path, sizeof(path)) > 0)
|
|
{
|
|
return path;
|
|
}
|
|
#elif defined(__linux__)
|
|
static char path[PATH_MAX];
|
|
if ( readlink ("/proc/self/exe", path, sizeof(path)) > 0) // Technically 0 is not an error, but that would mean
|
|
// 0 byte were copied ... thus considered it as an error
|
|
{
|
|
return path;
|
|
}
|
|
#else
|
|
gdcmErrorMacro( "missing implementation" );
|
|
#endif
|
|
return nullptr;
|
|
}
|
|
|
|
#ifdef __USE_GNU
|
|
static void where_am_i() {}
|
|
#endif
|
|
|
|
const char *System::GetCurrentModuleFileName()
|
|
{
|
|
#ifdef __USE_GNU
|
|
static char path[PATH_MAX];
|
|
Dl_info info;
|
|
if (dladdr( (void*)&where_am_i, &info ) == 0)
|
|
{
|
|
size_t len = strlen(info.dli_fname);
|
|
if( len >= PATH_MAX ) return nullptr; // throw error ?
|
|
// else
|
|
strcpy(path,info.dli_fname);
|
|
return path;
|
|
}
|
|
#elif defined(_WIN32)
|
|
// GetModuleFileName works the same on Win32 for library AFAIK
|
|
return System::GetCurrentProcessFileName();
|
|
#endif
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const char *System::GetCurrentResourcesDirectory()
|
|
{
|
|
#ifdef GDCM_USE_COREFOUNDATION_LIBRARY
|
|
static char path[PATH_MAX];
|
|
Boolean success = false;
|
|
CFURLRef pathURL = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
|
|
if (pathURL != nullptr)
|
|
{
|
|
success = CFURLGetFileSystemRepresentation(pathURL, true /*resolveAgainstBase*/, (unsigned char*) path, PATH_MAX);
|
|
CFRelease(pathURL);
|
|
}
|
|
if (success)
|
|
{
|
|
strlcat(path, "/" GDCM_INSTALL_DATA_DIR, PATH_MAX);
|
|
return path;
|
|
}
|
|
#endif
|
|
// Is there such beast on *any* other system but APPLE ?
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* \brief Encode the mac address on a fixed length string of 15 characters.
|
|
* we save space this way.
|
|
*/
|
|
inline int getlastdigit(unsigned char *data, unsigned long size)
|
|
{
|
|
int extended, carry = 0;
|
|
for(unsigned int i=0;i<size;i++)
|
|
{
|
|
extended = (carry << 8) + data[i];
|
|
data[i] = (unsigned char)(extended / 10);
|
|
carry = extended % 10;
|
|
}
|
|
assert( carry >= 0 && carry < 10 );
|
|
return carry;
|
|
}
|
|
|
|
size_t System::EncodeBytes(char *out, const unsigned char *data, int size)
|
|
{
|
|
bool zero = false;
|
|
int res;
|
|
std::string sres;
|
|
unsigned char buffer[32];
|
|
unsigned char *addr = buffer;
|
|
memcpy(addr, data, size);
|
|
while(!zero)
|
|
{
|
|
res = getlastdigit(addr, size);
|
|
const char v = (char)('0' + res);
|
|
sres.insert(sres.begin(), v);
|
|
zero = true;
|
|
for(int i = 0; i < size; ++i)
|
|
{
|
|
zero = zero && (addr[i] == 0);
|
|
}
|
|
}
|
|
|
|
//return sres;
|
|
strcpy(out, sres.c_str()); //, sres.size() );
|
|
return sres.size();
|
|
}
|
|
|
|
#if defined(_WIN32) && !defined(GDCM_HAVE_GETTIMEOFDAY)
|
|
#include <stdio.h>
|
|
|
|
// http://www.openasthra.com/c-tidbits/gettimeofday-function-for-windows/
|
|
// http://www.sisvia.com/blog/?p=24
|
|
// -> srand + gettimeofday
|
|
// http://csl.sublevel3.org/c++/
|
|
static int gettimeofday2(struct timeval *tv, struct timezone *tz)
|
|
{
|
|
FILETIME ft;
|
|
const uint64_t c1 = 27111902;
|
|
const uint64_t c2 = 3577643008UL;
|
|
const uint64_t OFFSET = (c1 << 32) + c2;
|
|
uint64_t filetime = 0;
|
|
GetSystemTimeAsFileTime(&ft);
|
|
|
|
filetime |= ft.dwHighDateTime;
|
|
filetime <<= 32;
|
|
filetime |= ft.dwLowDateTime;
|
|
filetime -= OFFSET;
|
|
|
|
tv->tv_sec = (long)(filetime / 10000000); /* seconds since epoch */
|
|
tv->tv_usec = (uint32_t)((filetime % 10000000) / 10);
|
|
|
|
return 0;
|
|
}
|
|
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
|
|
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
|
|
#else
|
|
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
|
|
#endif
|
|
|
|
//struct timezone
|
|
//{
|
|
// int tz_minuteswest; /* minutes W of Greenwich */
|
|
// int tz_dsttime; /* type of dst correction */
|
|
//};
|
|
|
|
int gettimeofday(struct timeval *tv, struct timezone *tz)
|
|
{
|
|
/*
|
|
The use of the timezone structure is obsolete; the tz argument should
|
|
normally be specified as NULL. The tz_dsttime field has never been
|
|
used under Linux; it has not been and will not be supported by libc or
|
|
glibc. Each and every occurrence of this field in the kernel source
|
|
(other than the declaration) is a bug. Thus, the following is purely of
|
|
historic interest.
|
|
*/
|
|
assert( tz == 0 );
|
|
FILETIME ft;
|
|
unsigned __int64 tmpres = 0;
|
|
//static int tzflag;
|
|
|
|
if (NULL != tv)
|
|
{
|
|
GetSystemTimeAsFileTime(&ft);
|
|
|
|
tmpres |= ft.dwHighDateTime;
|
|
tmpres <<= 32;
|
|
tmpres |= ft.dwLowDateTime;
|
|
|
|
/*converting file time to unix epoch*/
|
|
tmpres /= 10; /*convert into microseconds*/
|
|
tmpres -= DELTA_EPOCH_IN_MICROSECS;
|
|
tv->tv_sec = (long)(tmpres / 1000000UL);
|
|
tv->tv_usec = (long)(tmpres % 1000000UL);
|
|
}
|
|
|
|
// if (NULL != tz)
|
|
// {
|
|
// if (!tzflag)
|
|
// {
|
|
// _tzset();
|
|
// tzflag++;
|
|
// }
|
|
// tz->tz_minuteswest = _timezone / 60;
|
|
// tz->tz_dsttime = _daylight;
|
|
// }
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
Implementation note. We internally use mktime which seems to be quite relaxed when it
|
|
comes to invalid date. It handles :
|
|
"17890714172557";
|
|
"19891714172557";
|
|
"19890014172557";
|
|
While the DICOM PS 3.5-2008 would prohibit them.
|
|
I leave it this way so that we correctly read in /almost/ valid date. What we write out is
|
|
always valid anyway which is what is important.
|
|
*/
|
|
bool System::ParseDateTime(time_t &timep, const char date[22])
|
|
{
|
|
long milliseconds;
|
|
return ParseDateTime(timep, milliseconds, date);
|
|
}
|
|
|
|
bool System::ParseDateTime(time_t &timep, long &milliseconds, const char date[22])
|
|
{
|
|
if(!date) return false;
|
|
size_t len = strlen(date);
|
|
if( len < 4 ) return false; // need at least the full year
|
|
if( len > 21 ) return false;
|
|
|
|
struct tm ptm;
|
|
// No such thing as strptime on some st*$^% platform
|
|
#if defined(GDCM_HAVE_STRPTIME) && 0
|
|
char *ptr1 = strptime(date, "%Y%m%d%H%M%S", &ptm);
|
|
if( ptr1 != date + 14 )
|
|
{
|
|
// We stopped parsing the string at some point, assume this is an error
|
|
return false;
|
|
}
|
|
#else
|
|
// instead write our own:
|
|
int year, mon, day, hour, min, sec, n;
|
|
if ((n = sscanf(date, "%4d%2d%2d%2d%2d%2d",
|
|
&year, &mon, &day, &hour, &min, &sec)) >= 1)
|
|
{
|
|
switch (n)
|
|
{
|
|
case 1: mon = 1; /* Falls through. */
|
|
case 2: day = 1; /* Falls through. */
|
|
case 3: hour = 0; /* Falls through. */
|
|
case 4: min = 0; /* Falls through. */
|
|
case 5: sec = 0; /* Falls through. */
|
|
break; // http://security.coverity.com/blog/2013/Sep/gimme-a-break.html
|
|
}
|
|
ptm.tm_year = year - 1900;
|
|
if( mon < 1 || mon > 12 ) return false;
|
|
ptm.tm_mon = mon - 1;
|
|
if( day < 1 || day > 31 ) return false;
|
|
ptm.tm_mday = day;
|
|
if( hour > 24 ) return false;
|
|
ptm.tm_hour = hour;
|
|
if( min > 60 ) return false;
|
|
ptm.tm_min = min;
|
|
if( sec > 60 ) return false;
|
|
ptm.tm_sec = sec;
|
|
ptm.tm_wday = -1;
|
|
ptm.tm_yday = -1;
|
|
ptm.tm_isdst = -1;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
timep = mktime(&ptm);
|
|
if( timep == (time_t)-1) return false;
|
|
|
|
milliseconds = 0;
|
|
if( len > 14 ) // more data to process
|
|
{
|
|
const char *ptr = date + 14;
|
|
if( *ptr != '.' ) return false;
|
|
++ptr;
|
|
if( !*ptr || sscanf( ptr, "%06ld", &milliseconds ) != 1 )
|
|
{
|
|
// Could not parse milliseconds but date looks ok, should I return false anyway ?
|
|
// -> yes this is an error !
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const char *System::GetTimezoneOffsetFromUTC()
|
|
{
|
|
static std::string buffer;
|
|
char outstr[10];
|
|
time_t t = time(nullptr);
|
|
struct tm *tmp = localtime(&t);
|
|
size_t l = strftime(outstr, sizeof(outstr), "%z", tmp);
|
|
assert( l == 5 ); (void)l;
|
|
buffer = outstr;
|
|
return buffer.c_str();
|
|
}
|
|
|
|
bool System::FormatDateTime(char date[22], time_t timep, long milliseconds)
|
|
{
|
|
// \precondition
|
|
if( !(milliseconds >= 0 && milliseconds < 1000000) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// YYYYMMDDHHMMSS.FFFFFF&ZZXX
|
|
if(!date)
|
|
{
|
|
return false;
|
|
}
|
|
const size_t maxsize = 40;
|
|
char tmp[maxsize];
|
|
// Obtain the time of day, and convert it to a tm struct.
|
|
struct tm *ptm = localtime (&timep);
|
|
if(!ptm)
|
|
{
|
|
return false;
|
|
}
|
|
// Format the date and time, down to a single second.
|
|
size_t ret = strftime (tmp, sizeof (tmp), "%Y%m%d%H%M%S", ptm);
|
|
assert( ret == 14 );
|
|
if( ret == 0 || ret >= maxsize )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Add milliseconds
|
|
const size_t maxsizall = 22;
|
|
const int ret2 = snprintf(date,maxsizall,"%s.%06ld",tmp,milliseconds);
|
|
if( ret2 < 0 ) return false;
|
|
if( (size_t)ret2 >= maxsizall )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Ok !
|
|
return true;
|
|
}
|
|
|
|
bool System::GetCurrentDateTime(char date[22])
|
|
{
|
|
long milliseconds;
|
|
time_t timep;
|
|
|
|
#if 0
|
|
The functions gettimeofday() and settimeofday() can get and set the
|
|
time as well as a timezone. The tv argument is a struct timeval (as
|
|
specified in <sys/time.h>):
|
|
|
|
struct timeval {
|
|
time_t tv_sec; /* seconds */
|
|
suseconds_t tv_usec; /* microseconds */
|
|
};
|
|
|
|
and gives the number of seconds and microseconds since the Epoch (see
|
|
time(2)). The tz argument is a struct timezone:
|
|
|
|
struct timezone {
|
|
int tz_minuteswest; /* minutes west of Greenwich */
|
|
int tz_dsttime; /* type of DST correction */
|
|
};
|
|
|
|
If either tv or tz is NULL, the corresponding structure is not set or
|
|
returned.
|
|
|
|
The use of the timezone structure is obsolete; the tz argument should
|
|
normally be specified as NULL. The tz_dsttime field has never been
|
|
used under Linux; it has not been and will not be supported by libc or
|
|
glibc. Each and every occurrence of this field in the kernel source
|
|
(other than the declaration) is a bug. Thus, the following is purely of
|
|
historic interest.
|
|
#endif
|
|
|
|
// Apparently suseconds_t is defined as long on linux system... why would this be signed ?
|
|
|
|
struct timeval tv;
|
|
gettimeofday (&tv, nullptr);
|
|
timep = tv.tv_sec;
|
|
// A concatenated date-time character string in the format:
|
|
// YYYYMMDDHHMMSS.FFFFFF&ZZXX
|
|
// The components of this string, from left to right, are YYYY = Year, MM =
|
|
// Month, DD = Day, HH = Hour (range "00" - "23"), MM = Minute (range "00" -
|
|
// "59"), SS = Second (range "00" - "60").
|
|
// FFFFFF = Fractional Second contains a fractional part of a second as small
|
|
// as 1 millionth of a second (range 000000 - 999999).
|
|
assert( tv.tv_usec >= 0 && tv.tv_usec < 1000000 );
|
|
milliseconds = tv.tv_usec;
|
|
|
|
return FormatDateTime(date, timep, milliseconds);
|
|
}
|
|
|
|
int System::StrNCaseCmp(const char *s1, const char *s2, size_t n)
|
|
{
|
|
#if defined(GDCM_HAVE_STRNCASECMP)
|
|
return strncasecmp(s1,s2,n);
|
|
#elif defined(GDCM_HAVE__STRNICMP)
|
|
return _strnicmp(s1,s2,n);
|
|
#else // default implementation
|
|
#error
|
|
assert( n ); // TODO
|
|
while (--n && *s1 && (tolower(*s1) == tolower(*s2)))
|
|
{
|
|
s1++;
|
|
s2++;
|
|
}
|
|
|
|
return tolower(*s1) - tolower(*s2);
|
|
#endif
|
|
}
|
|
|
|
int System::StrCaseCmp(const char *s1, const char *s2)
|
|
{
|
|
#if defined(GDCM_HAVE_STRCASECMP)
|
|
return strcasecmp(s1,s2);
|
|
#elif defined(GDCM_HAVE__STRNICMP)
|
|
return _stricmp(s1,s2);
|
|
#else // default implementation
|
|
#error
|
|
while (*s1 && (tolower(*s1) == tolower(*s2)))
|
|
{
|
|
s1++;
|
|
s2++;
|
|
}
|
|
|
|
return tolower(*s1) - tolower(*s2);
|
|
#endif
|
|
}
|
|
|
|
bool System::GetHostName(char name[255])
|
|
{
|
|
// http://msdn.microsoft.com/en-us/library/ms738527.aspx
|
|
// WSANOTINITIALISED A successful WSAStartup call must occur before using this function.
|
|
#if _WIN32
|
|
// Get the hostname
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
wVersionRequested = MAKEWORD(2,0);
|
|
|
|
if ( WSAStartup( wVersionRequested, &wsaData ) == 0 )
|
|
{
|
|
bool ret = false;
|
|
if( gethostname(name,255) == 0 )
|
|
{
|
|
ret = true;
|
|
}
|
|
else
|
|
{
|
|
*name = 0;
|
|
}
|
|
WSACleanup( );
|
|
return ret;
|
|
}
|
|
#else
|
|
if( gethostname(name, 255) == 0 )
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
// If reach here gethostname failed, uninit name just in case
|
|
*name = 0;
|
|
return false;
|
|
}
|
|
|
|
char *System::StrTokR(char *str, const char *delim, char **nextp)
|
|
{
|
|
#if 1
|
|
// http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684
|
|
// PD -> http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c
|
|
char *ret;
|
|
|
|
if (str == nullptr)
|
|
{
|
|
str = *nextp;
|
|
}
|
|
|
|
str += strspn(str, delim);
|
|
|
|
if (*str == '\0')
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
ret = str;
|
|
|
|
str += strcspn(str, delim);
|
|
|
|
if (*str)
|
|
{
|
|
*str++ = '\0';
|
|
}
|
|
|
|
*nextp = str;
|
|
|
|
return ret;
|
|
#else
|
|
return strtok_r(str,delim,nextp);
|
|
#endif
|
|
}
|
|
|
|
char *System::StrSep(char **sp, const char *sep)
|
|
{
|
|
// http://unixpapa.com/incnote/string.html
|
|
// http://stackoverflow.com/questions/8512958/is-there-a-windows-variant-of-strsep
|
|
#if 1
|
|
char *p, *s;
|
|
if (sp == nullptr || *sp == nullptr || **sp == '\0') return nullptr;
|
|
s = *sp;
|
|
p = s + strcspn(s, sep);
|
|
if (*p != '\0') *p++ = '\0';
|
|
*sp = p;
|
|
return s;
|
|
#else
|
|
return strsep(sp, sep);
|
|
#endif
|
|
}
|
|
|
|
struct CharsetAliasType
|
|
{
|
|
const char *alias;
|
|
const char *name;
|
|
};
|
|
|
|
#if defined(_WIN32)
|
|
static const char *CharsetAliasToName(const char *alias)
|
|
{
|
|
assert( alias );
|
|
//gdcmDebugMacro( alias );
|
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756(v=vs.85).aspx
|
|
// 1252 windows-1252 ANSI Latin 1; Western European (Windows)
|
|
static CharsetAliasType aliases[] = {
|
|
{ "CP1252", "ISO-8859-1" }, // mingw + debian/6.0
|
|
{ NULL, NULL },
|
|
};
|
|
for( CharsetAliasType *a = aliases; a->alias; a++)
|
|
{
|
|
if (strcmp (a->alias, alias) == 0)
|
|
{
|
|
return a->name;
|
|
}
|
|
}
|
|
// We need to tell the user...
|
|
gdcmWarningMacro( std::string("Could not find Charset from alias: ") + alias );
|
|
return NULL;
|
|
}
|
|
#endif //_WIN32
|
|
|
|
const char *System::GetLocaleCharset()
|
|
{
|
|
const char *codeset = nullptr;
|
|
#if defined(GDCM_HAVE_NL_LANGINFO)
|
|
//setlocale (LC_CTYPE, NULL);
|
|
/* According to documentation nl_langinfo needs :
|
|
setlocale(3) needs to be executed with proper arguments before.
|
|
However even if CODESET only required LC_TYPE only setting LC_TYPE is not
|
|
enough to get it working on a debian/6.0 system within c++
|
|
so instead call setlocale on LC_ALL to fix it.
|
|
*/
|
|
char *oldlocale = strdup(setlocale(LC_ALL, ""));
|
|
// TODO: what if setlocale return NULL ?
|
|
codeset = nl_langinfo (CODESET);
|
|
setlocale(LC_ALL, oldlocale);
|
|
free(oldlocale);
|
|
#endif // GDCM_HAVE_NL_LANGINFO
|
|
|
|
#if defined(_WIN32)
|
|
#if 0
|
|
char buf1[128];
|
|
char buf2[128];
|
|
const char *codeset1;
|
|
const char *codeset2;
|
|
codeset1 = buf1;
|
|
codeset2 = buf2;
|
|
snprintf(buf1, sizeof(buf1), "CP%d", GetConsoleCP());
|
|
snprintf(buf2, sizeof(buf2), "CP%d", GetConsoleOutputCP());
|
|
|
|
// BUG: both returns 'CP437' on debian + mingw32...
|
|
// instead prefer GetACP() call:
|
|
#endif
|
|
static char buf[2+10+1]; // 2 char, 10 bytes + 0
|
|
// GetACP: Retrieves the current Windows ANSI code page identifier for the
|
|
// operating system.
|
|
snprintf (buf, sizeof(buf), "CP%u", GetACP ());
|
|
codeset = CharsetAliasToName(buf);
|
|
#endif
|
|
|
|
// warning ANSI_X3.4-1968 means ASCII
|
|
return codeset;
|
|
}
|
|
|
|
} // end namespace gdcm
|