Newer
Older
// Gmsh - Copyright (C) 1997-2015 C. Geuzaine, J.-F. Remacle
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to the public mailing list <gmsh@geuz.org>.
// This file contains a bunch of functions that depend on OS-dependent
// features and/or system calls
// these are available on all OSes
#include <stdlib.h>

Christophe Geuzaine
committed
#include "GmshConfig.h"

Christophe Geuzaine
committed
#include "StringUtils.h"
#include "Context.h"
#if defined(__APPLE__)
#include <sys/sysctl.h>
#include <mach-o/dyld.h>
#endif
#if defined(__linux__) && !defined(BUILD_ANDROID)
#include <sys/sysinfo.h>
#endif
#include <sys/time.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <iostream>
#include <fstream>
#endif

Christophe Geuzaine
committed
#include "GmshMessage.h"
#if defined(WIN32) && !defined(__CYGWIN__)
static unsigned utf8decode(const char* p, const char* end, int* len)
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
static unsigned short cp1252[32] = {
0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178
};
unsigned char c = *(unsigned char*)p;
if (c < 0x80) {
if (len) *len = 1;
return c;
} else if (c < 0xa0) {
if (len) *len = 1;
return cp1252[c-0x80];
} else if (c < 0xc2) {
goto FAIL;
}
if ( (end && p+1 >= end) || (p[1]&0xc0) != 0x80) goto FAIL;
if (c < 0xe0) {
if (len) *len = 2;
return
((p[0] & 0x1f) << 6) +
((p[1] & 0x3f));
} else if (c == 0xe0) {
if (((unsigned char*)p)[1] < 0xa0) goto FAIL;
goto UTF8_3;
} else if (c < 0xf0) {
UTF8_3:
if ( (end && p+2 >= end) || (p[2]&0xc0) != 0x80) goto FAIL;
if (len) *len = 3;
return
((p[0] & 0x0f) << 12) +
((p[1] & 0x3f) << 6) +
((p[2] & 0x3f));
} else if (c == 0xf0) {
if (((unsigned char*)p)[1] < 0x90) goto FAIL;
goto UTF8_4;
} else if (c < 0xf4) {
UTF8_4:
if ( (end && p+3 >= end) || (p[2]&0xc0) != 0x80 || (p[3]&0xc0) != 0x80) goto FAIL;
if (len) *len = 4;
return
((p[0] & 0x07) << 18) +
((p[1] & 0x3f) << 12) +
((p[2] & 0x3f) << 6) +
((p[3] & 0x3f));
} else if (c == 0xf4) {
if (((unsigned char*)p)[1] > 0x8f) goto FAIL; // after 0x10ffff
goto UTF8_4;
} else {
FAIL:
if (len) *len = 1;
return c;
}
}
static unsigned utf8toUtf16(const char* src, unsigned srclen,
unsigned short* dst, unsigned dstlen)
{
const char* p = src;
const char* e = src+srclen;
unsigned count = 0;
if (dstlen) for (;;) {
if (p >= e) {dst[count] = 0; return count;}
dst[count] = *p++;
} else {
int len; unsigned ucs = utf8decode(p,e,&len);
p += len;
if (ucs < 0x10000) {
dst[count] = ucs;
} else {
if (count+2 >= dstlen) {dst[count] = 0; count += 2; break;}
dst[count] = (((ucs-0x10000u)>>10)&0x3ff) | 0xd800;
dst[++count] = (ucs&0x3ff) | 0xdc00;
}
}
if (++count == dstlen) {dst[count-1] = 0; break;}
}
while (p < e) {
if (!(*p & 0x80)) p++;
else {
int len; unsigned ucs = utf8decode(p,e,&len);
p += len;
if (ucs >= 0x10000) ++count;
}
++count;
}
return count;
}
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
static unsigned utf8FromUtf16(char* dst, unsigned dstlen,
const wchar_t* src, unsigned srclen)
{
unsigned i = 0;
unsigned count = 0;
if (dstlen) {
for (;;) {
unsigned ucs;
if (i >= srclen) {dst[count] = 0; return count;}
ucs = src[i++];
if (ucs < 0x80U) {
dst[count++] = ucs;
if (count >= dstlen) {dst[count-1] = 0; break;}
}
else if (ucs < 0x800U) { /* 2 bytes */
if (count+2 >= dstlen) {dst[count] = 0; count += 2; break;}
dst[count++] = 0xc0 | (ucs >> 6);
dst[count++] = 0x80 | (ucs & 0x3F);
}
else if (ucs >= 0xd800 && ucs <= 0xdbff && i < srclen &&
src[i] >= 0xdc00 && src[i] <= 0xdfff) {
/* surrogate pair */
unsigned ucs2 = src[i++];
ucs = 0x10000U + ((ucs&0x3ff)<<10) + (ucs2&0x3ff);
/* all surrogate pairs turn into 4-byte utf8 */
if (count+4 >= dstlen) {dst[count] = 0; count += 4; break;}
dst[count++] = 0xf0 | (ucs >> 18);
dst[count++] = 0x80 | ((ucs >> 12) & 0x3F);
dst[count++] = 0x80 | ((ucs >> 6) & 0x3F);
dst[count++] = 0x80 | (ucs & 0x3F);
}
else {
/* all others are 3 bytes: */
if (count+3 >= dstlen) {dst[count] = 0; count += 3; break;}
dst[count++] = 0xe0 | (ucs >> 12);
dst[count++] = 0x80 | ((ucs >> 6) & 0x3F);
dst[count++] = 0x80 | (ucs & 0x3F);
}
}
}
/* we filled dst, measure the rest: */
while (i < srclen) {
unsigned ucs = src[i++];
if (ucs < 0x80U) {
count++;
}
else if (ucs < 0x800U) { /* 2 bytes */
count += 2;
}
else if (ucs >= 0xd800 && ucs <= 0xdbff && i < srclen-1 &&
src[i+1] >= 0xdc00 && src[i+1] <= 0xdfff) {
/* surrogate pair */
++i;
count += 4;
}
else {
count += 3;
}
}
return count;
}
static wchar_t *wbuf[3] = {NULL, NULL, NULL};
// all strings in Gmsh are supposed to be UTF8-encoded, which is natively
// supported by Mac and Linux. Windows does not support UTF-8, but UTF-16
// (through wchar_t), so we need to convert.
if(i < 0 || i > 2) return;
size_t l = strlen(f);
unsigned wn = utf8toUtf16(f, (unsigned) l, NULL, 0) + 1;
wbuf[i] = (wchar_t*)realloc(wbuf[i], sizeof(wchar_t)*wn);
wn = utf8toUtf16(f, (unsigned) l, (unsigned short *)wbuf[i], wn);
wbuf[i][wn] = 0;
}
{
#if defined (WIN32) && !defined(__CYGWIN__)
setwbuf(0, f);
setwbuf(1, mode);
return _wfopen(wbuf[0], wbuf[1]);
return fopen(f, mode);
#endif
}
const char *GetEnvironmentVar(const char *var)
{
#if defined(WIN32) && !defined(__CYGWIN__)
// Should probably use the Unicode version
const char *tmp = getenv(var);
// Don't accept top dir or anything partially expanded like
// c:\Documents and Settings\%USERPROFILE%, etc.
if(!tmp || !strcmp(tmp, "/") || strstr(tmp, "%") || strstr(tmp, "$"))
return 0;
else
return tmp;
#else
return getenv(var);
void SetEnvironmentVar(const char *var, const char *val)
#if defined(WIN32) && !defined(__CYGWIN__)
// should probably use Unicode version here
_putenv((std::string(var) + "=" + std::string(val)).c_str());
#else
setenv(var, val, 1);
#endif
}
#if defined(WIN32) && !defined(__CYGWIN__)
double t = 1.e-7 * 4294967296. * (double)ft.dwHighDateTime +
1.e-7 * (double)ft.dwLowDateTime;
#else
struct timeval tp;
gettimeofday(&tp, (struct timezone *)0);
double t = (double)tp.tv_sec + 1.e-6 * (double)tp.tv_usec;
return t;
#endif
#if defined(WIN32) && !defined(__CYGWIN__)
#else
usleep((long)(1.e6 * s));
#endif
#if defined(WIN32) && !defined(__CYGWIN__)
FILETIME creation, exit, kernel, user;
if(GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user)){
*s = 1.e-7 * 4294967296. * (double)user.dwHighDateTime +
1.e-7 * (double)user.dwLowDateTime;
}
PROCESS_MEMORY_COUNTERS info;
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
*mem = (long)info.PeakWorkingSetSize;
#else
static struct rusage r;
getrusage(RUSAGE_SELF, &r);
*s = (double)r.ru_utime.tv_sec + 1.e-6 * (double)r.ru_utime.tv_usec;
#if defined(__APPLE__)
*mem = (long)r.ru_maxrss;
#else
*mem = (long)(r.ru_maxrss * 1024L);
#endif
}
void CheckResources()
{
#if !defined (WIN32) || defined(__CYGWIN__)
static struct rlimit r;
getrlimit(RLIMIT_STACK, &r);
// Try to get at least 16 MB of stack. Running with too small a stack
// can cause crashes in the recursive calls (e.g. for tet
// classification in 3D Delaunay)
if(r.rlim_cur < 16 * 1024 * 1024){
Msg::Info("Increasing process stack size (%d kB < 16 MB)", r.rlim_cur / 1024);
r.rlim_cur = r.rlim_max;
setrlimit(RLIMIT_STACK, &r);
}
#endif
}
double Cpu()
{
long mem = 0;
double s = 0.;
GetResources(&s, &mem);
return s;
}
double TotalRam()
{
double ram = 0;
#if defined(__APPLE__)
int name[] = {CTL_HW, HW_MEMSIZE};
int64_t value;
size_t len = sizeof(value);
if(sysctl(name, 2, &value, &len, NULL, 0) != -1)
ram = value / (1024 * 1024);
#elif defined (WIN32)
MEMORYSTATUSEX status;
status.dwLength = sizeof(status);
GlobalMemoryStatusEx(&status);
ram = status.ullTotalPhys / ((double)1024 * 1024);
#elif defined(BUILD_ANDROID)
ram = 1024;
#elif defined(__linux__)
struct sysinfo infos;
if(sysinfo(&infos) != -1)
ram = infos.totalram * (unsigned long)infos.mem_unit / ((double)1024 * 1024);
#endif
return ram;
}
long GetMemoryUsage()
{
long mem = 0;
double s = 0.;
GetResources(&s, &mem);
return mem;
}
#if defined(WIN32) && !defined(__CYGWIN__)
#else
return getpid();
std::string GetExecutableName(const std::string &argv0)
{
std::string name = "";
#if defined(WIN32) && !defined(__CYGWIN__)
WCHAR src[MAX_PATH];
if(size){
char dst[MAX_PATH];
utf8FromUtf16(dst, MAX_PATH, src, size);
name = std::string(dst);
}
#elif defined(__APPLE__)
char path[PATH_MAX];
uint32_t size = sizeof(path);
char real[PATH_MAX];
if(realpath(path, real)){
name = std::string(real);
}
}
#elif defined(__linux__)
char path[4096];
if(readlink("/proc/self/exe", path, 4096) > 0){
name = std::string(path);
}
#endif
if(name.empty()){
name = argv0;
}
return name;
}
std::string GetHostName()
{
char host[256];
gethostname(host, sizeof(host));
return std::string(host);
}
int UnlinkFile(const std::string &fileName)
#if defined(WIN32) && !defined(__CYGWIN__)
setwbuf(0, fileName.c_str());
return _wunlink(wbuf[0]);
return unlink(fileName.c_str());
int StatFile(const std::string &fileName)
#if defined(WIN32) && !defined(__CYGWIN__)
struct _stat buf;
setwbuf(0, fileName.c_str());
int ret = _wstat(wbuf[0], &buf);

Christophe Geuzaine
committed
int ret = stat(fileName.c_str(), &buf);

Christophe Geuzaine
committed
return ret;
int CreateSingleDir(const std::string &dirName)

Christophe Geuzaine
committed
{
#if defined(WIN32) && !defined(__CYGWIN__)
setwbuf(0, dirName.c_str());
if(_wmkdir(wbuf[0])) return 0;

Christophe Geuzaine
committed
#else
if(mkdir(dirName.c_str(), 0777)) return 0;

Christophe Geuzaine
committed
#endif
return 1;
}
void CreatePath(const std::string &fullPath)
{
size_t lastp = fullPath.find_last_of('/'); // TODO: handle backslash for win?
if(lastp == std::string::npos) return;
std::string dirname = std::string(fullPath, 0, lastp);
size_t cur = 0;
while(cur != std::string::npos) {
cur = dirname.find("/", cur + 1);
CreateSingleDir(dirname.substr(0, cur));
}
#if defined(WIN32) && !defined(__CYGWIN__)
HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if(!TerminateProcess(hProc, 0)){
CloseHandle(hProc);
return 0;
}
#else
if(kill(pid, 9))
return 0;

Christophe Geuzaine
committed
int SystemCallExe(const std::string &exe, const std::string &argsOrCommand,
bool blocking)
// do we try to run a .py script, .m script or an .exe?
std::vector<std::string> split = SplitFileName(exe);
bool isPython = (split[2] == ".py" || split[2] == ".PY");
bool isOctave = (split[2] == ".m" || split[2] == ".M");
bool isExe = (split[2] == ".exe" || split[2] == ".EXE");
if(isPython || isOctave || isExe){
if(StatFile(exe)){
Msg::Error("Unable to open file '%s'", exe.c_str());
return 1;
}
std::string command;
if(exe.size()){
command.append("\"" + exe + "\""); // allows exe with white space

Christophe Geuzaine
committed
if(argsOrCommand.size()) command.append(" ");

Christophe Geuzaine
committed
command.append(argsOrCommand);
#if defined(WIN32) && !defined(__CYGWIN__)
if(isPython || isOctave){
Msg::Info("Shell opening '%s' with arguments '%s'", exe.c_str(),

Christophe Geuzaine
committed
argsOrCommand.c_str());
setwbuf(0, "open");
setwbuf(1, exe.c_str());

Christophe Geuzaine
committed
setwbuf(2, argsOrCommand.c_str());
ShellExecuteW(NULL, wbuf[0], wbuf[1], wbuf[2], NULL, 0);
PROCESS_INFORMATION prInfo;
memset(&suInfo, 0, sizeof(suInfo));
suInfo.cb = sizeof(suInfo);
Msg::Info("Calling '%s'", command.c_str());
setwbuf(0, command.c_str());
if(blocking){
CreateProcessW(NULL, wbuf[0], NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS, NULL, NULL,
&suInfo, &prInfo);
// wait until child process exits.
WaitForSingleObject(prInfo.hProcess, INFINITE);
// close process and thread handles.
CloseHandle(prInfo.hProcess);
CloseHandle(prInfo.hThread);
}
else{
// DETACHED_PROCESS removes the console (useful if the program to launch
// is a console-mode exe)
CreateProcessW(NULL, wbuf[0], NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS|DETACHED_PROCESS, NULL, NULL,
&suInfo, &prInfo);
}

Christophe Geuzaine
committed
std::string cmd(command);
if(isPython || isOctave || isExe){

Christophe Geuzaine
committed
if(access(exe.c_str(), X_OK)){
Msg::Info("Script '%s' is not executable: running with `%s'",
exe.c_str(), CTX::instance()->solver.pythonInterpreter.c_str());
cmd = CTX::instance()->solver.pythonInterpreter + " " + cmd;
}
else if(isOctave){
Msg::Info("Script '%s' is not executable: running with `%s'",
exe.c_str(), CTX::instance()->solver.octaveInterpreter.c_str());
cmd = CTX::instance()->solver.octaveInterpreter + " " + cmd;
}
else
Msg::Warning("File '%s' is not executable", exe.c_str());

Christophe Geuzaine
committed
}
else if(split[0].empty()){
// workaround if pwd is not in PATH
cmd = "./" + cmd;
}

Christophe Geuzaine
committed
}
Msg::Error("Could not find /bin/sh: aborting system call");
int SystemCall(const std::string &command, bool blocking)
{
return SystemCallExe("", command, blocking);
}
#if defined(WIN32) && !defined(__CYGWIN__)
// should use Unicode version
if(!_getcwd(path, sizeof(path))) return "";
#else
if(!getcwd(path, sizeof(path))) return "";
// match the convention of SplitFileName that delivers directory path
// ending with a directory separator
#if defined(WIN32)
str.append("\\");
#else
str.append("/");
#endif
return str;
}
void RedirectIOToConsole()
{
#if defined(WIN32) && !defined(__CYGWIN__)
// Win32 GUI apps do not write to the DOS console; make it work again by
// attaching to parent console, which allows to use the DOS shell to work with
// Gmsh on the command line (without this hack, you need to either use a
// better shell (e.g. bash), or compile a /subsystem:console version
if(!AttachConsole(ATTACH_PARENT_PROCESS)) return;
// redirect unbuffered stdout, stdin and stderr to the console
{
intptr_t lStdHandle = (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE);
int hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
if(hConHandle >= 0){
FILE *fp = _fdopen(hConHandle, "w");
if(fp){
*stdout = *fp;
setvbuf(stdout, NULL, _IONBF, 0);
}
}
}
}
{
intptr_t lStdHandle = (intptr_t)GetStdHandle(STD_INPUT_HANDLE);
int hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
if(hConHandle >= 0){
FILE *fp = _fdopen(hConHandle, "r");
if(fp){
*stdin = *fp;
setvbuf(stdin, NULL, _IONBF, 0);
}
}
}
}
{
intptr_t lStdHandle = (intptr_t)GetStdHandle(STD_ERROR_HANDLE);
int hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
if(hConHandle >= 0){
FILE *fp = _fdopen(hConHandle, "w");
if(fp){
*stderr = *fp;
setvbuf(stderr, NULL, _IONBF, 0);
}
}
}
}
// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog point to console
// as well
std::ios::sync_with_stdio();
#endif
}