1 // Gmsh - Copyright (C) 1997-2021 C. Geuzaine, J.-F. Remacle
2 //
3 // See the LICENSE.txt file in the Gmsh root directory for license information.
4 // Please report all issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
5 
6 #include "GmshConfig.h"
7 
8 #if defined(HAVE_MPI)
9 #include <mpi.h>
10 #endif
11 
12 #if !defined(WIN32) || defined(__CYGWIN__)
13 #include <unistd.h>
14 #endif
15 
16 #include <clocale>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <math.h>
20 #include <string.h>
21 #include <time.h>
22 #include <sys/stat.h>
23 #include "GmshMessage.h"
24 #include "GmshSocket.h"
25 #include "GmshGlobal.h"
26 #include "GModel.h"
27 #include "Options.h"
28 #include "Context.h"
29 #include "OpenFile.h"
30 #include "StringUtils.h"
31 #include "OS.h"
32 
33 #if defined(HAVE_ONELAB)
34 #include "onelab.h"
35 #include "onelabUtils.h"
36 #endif
37 
38 #include "gmshLocalNetworkClient.h"
39 
40 #if defined(HAVE_PETSC)
41 #include <petsc.h>
42 #endif
43 
44 #if defined(HAVE_SLEPC)
45 #include <slepc.h>
46 #endif
47 
48 #if defined(HAVE_FLTK)
49 #include <FL/fl_ask.H>
50 #include "FlGui.h"
51 #include "extraDialogs.h"
52 #endif
53 
54 #if defined(_OPENMP)
55 #include <omp.h>
56 #endif
57 
58 int Msg::_commRank = 0;
59 int Msg::_commSize = 1;
60 int Msg::_verbosity = 5;
61 int Msg::_progressMeterStep = 10;
62 std::atomic<int> Msg::_progressMeterCurrent(-1);
63 int Msg::_progressMeterTotal = 0;
64 std::map<std::string, double> Msg::_timers;
65 bool Msg::_infoCpu = false;
66 bool Msg::_infoMem = false;
67 double Msg::_startTime = 0.;
68 int Msg::_startMaxThreads = 0;
69 int Msg::_warningCount = 0;
70 int Msg::_errorCount = 0;
71 int Msg::_atLeastOneErrorInRun = 0;
72 std::string Msg::_firstWarning;
73 std::string Msg::_firstError;
74 std::string Msg::_lastError;
75 GmshMessage *Msg::_callback = nullptr;
76 std::vector<std::string> Msg::_commandLineArgs;
77 std::string Msg::_launchDate;
78 std::map<std::string, std::vector<double> > Msg::_commandLineNumbers;
79 std::map<std::string, std::string> Msg::_commandLineStrings;
80 GmshClient *Msg::_client = nullptr;
81 std::string Msg::_execName;
82 #if defined(HAVE_ONELAB)
83 onelab::client *Msg::_onelabClient = nullptr;
84 onelab::server *onelab::server::_server = nullptr;
85 #endif
86 std::string Msg::_logFileName;
87 FILE *Msg::_logFile = nullptr;
88 
89 #if defined(_MSC_VER) && (_MSC_VER >= 1310) //NET 2003
90 #define vsnprintf _vsnprintf
91 #else
92 #if defined(HAVE_NO_VSNPRINTF)
vsnprintf(char * str,size_t size,const char * fmt,va_list ap)93 static int vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
94 {
95   if (strlen(fmt) > size - 1) { // just copy the format
96     strncpy(str, fmt, size - 1);
97     str[size - 1] = '\0';
98     return size;
99   }
100   return vsprintf(str, fmt, ap);
101 }
102 #endif
103 #endif
104 
addGmshPathToEnvironmentVar(const std::string & name)105 static void addGmshPathToEnvironmentVar(const std::string &name)
106 {
107   std::string gmshPath = SplitFileName(CTX::instance()->exeFileName)[0];
108   if(gmshPath.size()){
109     std::string tmp = GetEnvironmentVar(name);
110     if(tmp.find(gmshPath) != std::string::npos) return; // already there
111     std::string path;
112     if(tmp.empty()) {
113       path = gmshPath;
114     }
115     else {
116 #if defined(WIN32)
117       path = tmp + ";" + gmshPath;
118 #else
119       path = tmp + ":" + gmshPath;
120 #endif
121     }
122     SetEnvironmentVar(name, path);
123   }
124 }
125 
Initialize(int argc,char ** argv)126 void Msg::Initialize(int argc, char **argv)
127 {
128   _startTime = TimeOfDay();
129   _startMaxThreads = GetMaxThreads();
130 #if defined(HAVE_MPI)
131   int flag;
132   MPI_Initialized(&flag);
133   if(!flag) MPI_Init(&argc, &argv);
134   MPI_Comm_rank(MPI_COMM_WORLD, &_commRank);
135   MPI_Comm_size(MPI_COMM_WORLD, &_commSize);
136   MPI_Comm_set_errhandler(MPI_COMM_WORLD, MPI_ERRORS_RETURN);
137 #endif
138 #if defined(HAVE_PETSC)
139   int sargc = 0;
140   char **sargv = new char*[argc + 1];
141   // prune argv from gmsh-specific options that make PETSc verbose
142   for(int i = 0; i < argc; i++){
143     std::string val(argv[i]);
144     if(val != "-info" && val != "-help" && val != "-version" && val != "-v")
145       sargv[sargc++] = argv[i];
146   }
147   sargv[sargc] = nullptr;
148   PetscInitialize(&sargc, &sargv, PETSC_NULL, PETSC_NULL);
149   PetscPopSignalHandler();
150 #if defined(HAVE_SLEPC)
151   SlepcInitialize(&sargc, &sargv, PETSC_NULL, PETSC_NULL);
152 #endif
153   delete [] sargv;
154 #endif
155   time_t now;
156   time(&now);
157   _launchDate = ctime(&now);
158   _launchDate.resize(_launchDate.size() - 1);
159 
160   bool _env = true, _locale = true;
161   _commandLineArgs.resize(argc);
162   for(int i = 0; i < argc; i++) {
163     _commandLineArgs[i] = argv[i];
164     if(_commandLineArgs[i] == "-noenv")
165       _env = false;
166     else if(_commandLineArgs[i] == "-nolocale")
167       _locale = false;
168   }
169 
170   CTX::instance()->exeFileName = GetExecutableFileName();
171   if(CTX::instance()->exeFileName.empty() && _commandLineArgs.size())
172     CTX::instance()->exeFileName = _commandLineArgs[0];
173 
174   if(_env) {
175     // add the directory where the binary is installed to the path where Python
176     // looks for modules, and to the path for executables (this allows us to
177     // find the onelab.py module or subclients automatically)
178     addGmshPathToEnvironmentVar("PYTHONPATH");
179     addGmshPathToEnvironmentVar("PATH");
180   }
181 
182   if(_locale) {
183     // make sure to use the "C" locale; in particular this ensures that we will
184     // use a dot for for the decimal separator when writing ASCII mesh files
185     std::setlocale(LC_ALL, "C.UTF-8");
186     std::setlocale(LC_NUMERIC, "C");
187   }
188 
189   InitializeOnelab("Gmsh");
190 
191 #if defined(_OPENMP)
192   // this is deprecated
193   //omp_set_nested(true);
194 #endif
195 }
196 
Finalize()197 void Msg::Finalize()
198 {
199   // close log file
200   if(_logFile){
201     fclose(_logFile);
202     _logFile = nullptr;
203   }
204 
205 #if defined(HAVE_SLEPC)
206   SlepcFinalize();
207 #endif
208 #if defined(HAVE_PETSC)
209   // this often crashes when called multiple times
210   //PetscFinalize();
211 #endif
212 #if defined(HAVE_MPI)
213   int finalized; // Some PETSc versions call MPI_FINALIZE
214   MPI_Finalized(&finalized);
215   if (!finalized)
216     MPI_Finalize();
217 #endif
218   FinalizeOnelab();
219   if(GetMaxThreads() != _startMaxThreads)
220     SetNumThreads(_startMaxThreads);
221 }
222 
GetCommRank()223 int Msg::GetCommRank()
224 {
225   return _commRank;
226 }
227 
GetCommSize()228 int Msg::GetCommSize()
229 {
230   return _commSize;
231 }
232 
SetCommRank(int val)233 void Msg::SetCommRank(int val)
234 {
235   _commRank = val;
236 }
237 
SetCommSize(int val)238 void Msg::SetCommSize(int val)
239 {
240   _commSize = val;
241 }
242 
SetCallback(GmshMessage * callback)243 void Msg::SetCallback(GmshMessage *callback)
244 {
245   _callback = callback;
246 }
247 
GetCallback()248 GmshMessage *Msg::GetCallback()
249 {
250   return _callback;
251 }
252 
SetVerbosity(int val)253 void Msg::SetVerbosity(int val)
254 {
255   _verbosity = val;
256 }
257 
GetVerbosity()258 int Msg::GetVerbosity()
259 {
260   return _verbosity;
261 }
262 
SetLogFile(const std::string & name)263 void Msg::SetLogFile(const std::string &name)
264 {
265   _logFileName = name;
266   if(_logFile) fclose(_logFile);
267   if(name.size()){
268     _logFile = Fopen(name.c_str(), "w");
269     if(!_logFile)
270       Msg::Error("Could not open file '%s'", name.c_str());
271   }
272   else
273     _logFile = nullptr;
274 }
275 
GetLaunchDate()276 std::string Msg::GetLaunchDate()
277 {
278   return _launchDate;
279 }
280 
GetCommandLineArgs()281 std::vector<std::string> &Msg::GetCommandLineArgs()
282 {
283   return _commandLineArgs;
284 }
285 
GetCommandLineFull()286 std::string Msg::GetCommandLineFull()
287 {
288   std::string tmp;
289   for(std::size_t i = 0; i < _commandLineArgs.size(); i++){
290     if(i) tmp += " ";
291     tmp += _commandLineArgs[i];
292   }
293   return tmp;
294 }
295 
GetCommandLineNumbers()296 std::map<std::string, std::vector<double> > &Msg::GetCommandLineNumbers()
297 {
298   return _commandLineNumbers;
299 }
300 
GetCommandLineStrings()301 std::map<std::string, std::string> &Msg::GetCommandLineStrings()
302 {
303   return _commandLineStrings;
304 }
305 
SetProgressMeterStep(int step)306 void Msg::SetProgressMeterStep(int step)
307 {
308   _progressMeterStep = step;
309 }
310 
GetProgressMeterStep()311 int Msg::GetProgressMeterStep()
312 {
313   return _progressMeterStep;
314 }
315 
StartProgressMeter(int ntotal)316 void Msg::StartProgressMeter(int ntotal)
317 {
318   _progressMeterCurrent = 0;
319   _progressMeterTotal = ntotal;
320 }
321 
StopProgressMeter()322 void Msg::StopProgressMeter()
323 {
324   _progressMeterCurrent = -1;
325   _progressMeterTotal = 0;
326 #if defined(HAVE_FLTK)
327   if(FlGui::available()){
328     FlGui::instance()->setProgress("", 0, 0, 1);
329     FlGui::check(true);
330   }
331 #endif
332 }
333 
SetInfoCpu(bool val)334 void Msg::SetInfoCpu(bool val)
335 {
336   _infoCpu = val;
337 }
338 
SetInfoMem(bool val)339 void Msg::SetInfoMem(bool val)
340 {
341   _infoMem = val;
342 }
343 
Timer(const std::string & str)344 double &Msg::Timer(const std::string &str) { return _timers[str]; }
345 
GetWarningCount()346 int Msg::GetWarningCount()
347 {
348   return _warningCount;
349 }
350 
GetErrorCount()351 int Msg::GetErrorCount()
352 {
353   return _errorCount;
354 }
355 
GetFirstWarning()356 std::string Msg::GetFirstWarning()
357 {
358   return _firstWarning;
359 }
360 
GetFirstError()361 std::string Msg::GetFirstError()
362 {
363   return _firstError;
364 }
365 
GetLastError()366 std::string Msg::GetLastError()
367 {
368   return _lastError;
369 }
370 
SetExecutableName(const std::string & name)371 void Msg::SetExecutableName(const std::string &name)
372 {
373   _execName.assign(name);
374 }
375 
GetExecutableName()376 std::string Msg::GetExecutableName()
377 {
378   return _execName;
379 }
380 
381 #if defined(HAVE_ONELAB)
GetOnelabClient()382 onelab::client *Msg::GetOnelabClient()
383 {
384   return _onelabClient;
385 }
386 #endif
387 
Exit(int level)388 void Msg::Exit(int level)
389 {
390   Finalize();
391 #if defined(HAVE_MPI)
392   if(level) MPI_Abort(MPI_COMM_WORLD, level);
393 #endif
394   exit(level ? level : _atLeastOneErrorInRun);
395 }
396 
streamIsFile(FILE * stream)397 static int streamIsFile(FILE *stream)
398 {
399   // the given stream is definitely not interactive if it is a regular file
400   struct stat stream_stat;
401   if(fstat(fileno(stream), &stream_stat) == 0){
402     if(stream_stat.st_mode & S_IFREG) return 1;
403   }
404   return 0;
405 }
406 
streamIsVT100(FILE * stream)407 static int streamIsVT100(FILE *stream)
408 {
409   // on unix directly check if the file descriptor refers to a terminal
410 #if !defined(WIN32) || defined(__CYGWIN__)
411   return isatty(fileno(stream));
412 #endif
413 
414   // otherwise try to detect some known cases:
415 
416   // if running inside emacs the terminal is not VT100
417   const char* emacs = getenv("EMACS");
418   if(emacs && *emacs == 't') return 0;
419 
420   // list of known terminal names (from cmake)
421   static const char* names[] =
422     {"Eterm", "ansi", "color-xterm", "con132x25", "con132x30", "con132x43",
423      "con132x60", "con80x25",  "con80x28", "con80x30", "con80x43", "con80x50",
424      "con80x60",  "cons25", "console", "cygwin", "dtterm", "eterm-color", "gnome",
425      "gnome-256color", "konsole", "konsole-256color", "kterm", "linux", "msys",
426      "linux-c", "mach-color", "mlterm", "putty", "rxvt", "rxvt-256color",
427      "rxvt-cygwin", "rxvt-cygwin-native", "rxvt-unicode", "rxvt-unicode-256color",
428      "screen", "screen-256color", "screen-256color-bce", "screen-bce", "screen-w",
429      "screen.linux", "vt100", "xterm", "xterm-16color", "xterm-256color",
430      "xterm-88color", "xterm-color", "xterm-debian", nullptr};
431   const char** t = nullptr;
432   const char* term = getenv("TERM");
433   if(term){
434     for(t = names; *t && strcmp(term, *t) != 0; ++t) {}
435   }
436   if(!(t && *t)) return 0;
437   return 1;
438 }
439 
PrintResources(bool printDate,bool printWallTime,bool printCpu,bool printMem)440 std::string Msg::PrintResources(bool printDate, bool printWallTime,
441                                 bool printCpu, bool printMem)
442 {
443   long mem = GetMemoryUsage();
444 
445   std::string pdate = "";
446   if(printDate){
447     time_t now;
448     time(&now);
449     pdate = ctime(&now);
450     pdate.resize(pdate.size() - 1);
451     if(printWallTime || printCpu || (printMem && mem))
452       pdate += ", ";
453   }
454 
455   std::string pwall = "";
456   if(printWallTime){
457     char tmp[128];
458     sprintf(tmp, "Wall %gs", TimeOfDay() - _startTime);
459     pwall = tmp;
460     if(printCpu || (printMem && mem))
461       pwall += ", ";
462   }
463 
464   std::string pcpu = "";
465   if(printCpu){
466     char tmp[128];
467     sprintf(tmp, "CPU %gs", Cpu());
468     pcpu = tmp;
469     if(printMem && mem)
470       pcpu += ", ";
471   }
472 
473   std::string pmem = "";
474   if(mem && printMem){
475     char tmp[128];
476     sprintf(tmp, "Mem %gMb", (double)mem / 1024. / 1024.);
477     pmem = tmp;
478   }
479 
480   std::string str;
481   if(pdate.size() || pwall.size() || pcpu.size() || pmem.size())
482     str += " (From start: " + pdate +  pwall +  pcpu +  pmem + ")";
483   return str;
484 }
485 
Error(const char * fmt,...)486 void Msg::Error(const char *fmt, ...)
487 {
488   _errorCount++;
489   _atLeastOneErrorInRun = 1;
490 
491   char str[5000];
492   va_list args;
493   va_start(args, fmt);
494   vsnprintf(str, sizeof(str), fmt, args);
495   va_end(args);
496   int l = strlen(str); if(str[l - 1] == '\n') str[l - 1] = '\0';
497 
498   if(_firstError.empty()) _firstError = str;
499   _lastError = str;
500 
501   if(GetVerbosity() >= 1) {
502     if(_logFile) fprintf(_logFile, "Error: %s\n", str);
503     if(_callback) (*_callback)("Error", str);
504     if(_client) _client->Error(str);
505 #if defined(HAVE_FLTK)
506     if(FlGui::available()){
507       std::string tmp = std::string(CTX::instance()->guiColorScheme ? "@B72@." : "@C1@.")
508         + "Error   : " + str;
509       FlGui::instance()->addMessage(tmp.c_str());
510       FlGui::instance()->setLastStatus
511         (CTX::instance()->guiColorScheme ? FL_DARK_RED : FL_RED);
512       FlGui::check(true);
513     }
514 #endif
515     if(CTX::instance()->terminal){
516       const char *c0 = "", *c1 = "";
517       if(!streamIsFile(stderr) && streamIsVT100(stderr)){
518         c0 = "\33[1m\33[31m"; c1 = "\33[0m";  // bold red
519       }
520       if(_commSize > 1)
521         fprintf(stderr, "%sError   : [rank %3d] %s%s\n", c0, GetCommRank(), str, c1);
522       else
523         fprintf(stderr, "%sError   : %s%s\n", c0, str, c1);
524       fflush(stderr);
525     }
526   }
527 
528   if(CTX::instance()->abortOnError == 2) {
529 #if defined(HAVE_FLTK)
530     if(FlGui::available()) return; // don't throw if GUI is running
531 #endif
532     throw _lastError;
533   }
534   else if(CTX::instance()->abortOnError == 3) {
535     throw _lastError;
536   }
537   else if(CTX::instance()->abortOnError == 4) {
538     Exit(1);
539   }
540 }
541 
Warning(const char * fmt,...)542 void Msg::Warning(const char *fmt, ...)
543 {
544   _warningCount++;
545 
546   if(GetVerbosity() < 2) return;
547 
548   char str[5000];
549   va_list args;
550   va_start(args, fmt);
551   vsnprintf(str, sizeof(str), fmt, args);
552   va_end(args);
553   int l = strlen(str); if(str[l - 1] == '\n') str[l - 1] = '\0';
554 
555   if(_logFile) fprintf(_logFile, "Warning: %s\n", str);
556   if(_callback) (*_callback)("Warning", str);
557   if(_client) _client->Warning(str);
558 
559 #if defined(HAVE_FLTK)
560   if(FlGui::available()){
561     std::string tmp = std::string(CTX::instance()->guiColorScheme ? "@B152@." : "@C5@.")
562       + "Warning : " + str;
563     FlGui::instance()->addMessage(tmp.c_str());
564     if(_firstWarning.empty()) _firstWarning = str;
565     FlGui::instance()->setLastStatus();
566     FlGui::check(true);
567   }
568 #endif
569 
570   if(CTX::instance()->terminal){
571     const char *c0 = "", *c1 = "";
572     if(!streamIsFile(stderr) && streamIsVT100(stderr)){
573       c0 = "\33[35m"; c1 = "\33[0m";  // magenta
574     }
575     if(_commSize > 1)
576       fprintf(stderr, "%sWarning : [rank %3d] %s%s\n", c0, GetCommRank(), str, c1);
577     else
578       fprintf(stderr, "%sWarning : %s%s\n", c0, str, c1);
579     fflush(stderr);
580   }
581 }
582 
Info(const char * fmt,...)583 void Msg::Info(const char *fmt, ...)
584 {
585   if(GetVerbosity() < 4) return;
586 
587   char str[5000];
588   va_list args;
589   va_start(args, fmt);
590   vsnprintf(str, sizeof(str), fmt, args);
591   va_end(args);
592   int l = strlen(str); if(str[l - 1] == '\n') str[l - 1] = '\0';
593 
594   if(_infoCpu || _infoMem){
595     std::string res = PrintResources(false, _infoCpu, _infoCpu, _infoMem);
596     strcat(str, res.c_str());
597   }
598 
599   if(_logFile) fprintf(_logFile, "Info: %s\n", str);
600   if(_callback) (*_callback)("Info", str);
601   if(_client) _client->Info(str);
602 
603 #if defined(HAVE_FLTK)
604   if(FlGui::available()){
605     std::string tmp = std::string("Info    : ") + str;
606     FlGui::instance()->addMessage(tmp.c_str());
607     FlGui::check(true);
608   }
609 #endif
610 
611   if(CTX::instance()->terminal){
612     if(_progressMeterCurrent >= 0 && _progressMeterTotal > 1 &&
613        _commSize == 1) {
614       int p =  _progressMeterCurrent;
615       fprintf(stdout, "Info    : [%3d%%] %s\n", p, str);
616     }
617     else if(_commSize > 1)
618       fprintf(stdout, "Info    : [rank %3d] %s\n", GetCommRank(), str);
619     else
620       fprintf(stdout, "Info    : %s\n", str);
621     fflush(stdout);
622   }
623 }
624 
RequestRender()625 void Msg::RequestRender()
626 {
627   if(_callback) (*_callback)("RequestRender", "");
628 }
629 
Direct(const char * fmt,...)630 void Msg::Direct(const char *fmt, ...)
631 {
632   if(GetVerbosity() < 3) return;
633 
634   char str[5000];
635   va_list args;
636   va_start(args, fmt);
637   vsnprintf(str, sizeof(str), fmt, args);
638   va_end(args);
639   int l = strlen(str); if(str[l - 1] == '\n') str[l - 1] = '\0';
640 
641   if(_logFile) fprintf(_logFile, "Direct: %s\n", str);
642   if(_callback) (*_callback)("Direct", str);
643   if(_client) _client->Info(str);
644 
645 #if defined(HAVE_FLTK)
646   if(FlGui::available()){
647     std::string tmp = std::string(CTX::instance()->guiColorScheme ? "@B136@." : "@C4@.")
648       + str;
649     FlGui::instance()->addMessage(tmp.c_str());
650     FlGui::check(true);
651   }
652 #endif
653 
654   if(CTX::instance()->terminal){
655     const char *c0 = "", *c1 = "";
656     if(!streamIsFile(stdout) && streamIsVT100(stdout)){
657       c0 = "\33[34m"; c1 = "\33[0m";  // blue
658     }
659     if(_commSize > 1)
660       fprintf(stdout, "%s[rank %3d] %s%s\n", c0, GetCommRank(), str, c1);
661     else
662       fprintf(stdout, "%s%s%s\n", c0, str, c1);
663     fflush(stdout);
664   }
665 }
666 
Auto(const char * fmt,...)667 void Msg::Auto(const char *fmt, ...)
668 {
669   char str[5000];
670   va_list args;
671   va_start(args, fmt);
672   vsnprintf(str, sizeof(str), fmt, args);
673   va_end(args);
674   if(strstr(str, "Error") || strstr(str, "error") || strstr(str, "ERROR"))
675     Msg::Error("%s", str);
676   else if(strstr(str, "Warning") || strstr(str, "warning") || strstr(str, "WARNING"))
677     Msg::Warning("%s", str);
678   else
679     Msg::Info("%s", str);
680 }
681 
StatusBar(bool log,const char * fmt,...)682 void Msg::StatusBar(bool log, const char *fmt, ...)
683 {
684   if(GetVerbosity() < 4) return;
685 
686   char str[5000];
687   va_list args;
688   va_start(args, fmt);
689   vsnprintf(str, sizeof(str), fmt, args);
690   va_end(args);
691   int l = strlen(str); if(str[l - 1] == '\n') str[l - 1] = '\0';
692 
693   if(_infoCpu || _infoMem){
694     std::string res = PrintResources(false, _infoCpu, _infoCpu, _infoMem);
695     strcat(str, res.c_str());
696   }
697 
698   if(_logFile) fprintf(_logFile, "Info: %s\n", str);
699   if(_callback && log) (*_callback)("Info", str);
700   if(_client && log) _client->Info(str);
701 
702 #if defined(HAVE_FLTK)
703   if(FlGui::available()){
704     if(!log || GetVerbosity() > 4)
705       FlGui::instance()->setStatus(str);
706     if(log){
707       std::string tmp = std::string("Info    : ") + str;
708       FlGui::instance()->addMessage(tmp.c_str());
709       FlGui::check(true);
710     }
711   }
712 #endif
713 
714   if(log && CTX::instance()->terminal){
715     if(_commSize > 1)
716       fprintf(stdout, "Info    : [rank %3d] %s\n", GetCommRank(), str);
717     else
718       fprintf(stdout, "Info    : %s\n", str);
719     fflush(stdout);
720   }
721 }
722 
StatusGl(const char * fmt,...)723 void Msg::StatusGl(const char *fmt, ...)
724 {
725 #if defined(HAVE_FLTK)
726   if(GetCommRank()) return;
727   char str[5000];
728   va_list args;
729   va_start(args, fmt);
730   vsnprintf(str, sizeof(str), fmt, args);
731   va_end(args);
732   int l = strlen(str); if(str[l - 1] == '\n') str[l - 1] = '\0';
733 
734   if(FlGui::available())
735     FlGui::instance()->setStatus(str, true);
736 #endif
737 }
738 
SetWindowTitle(const std::string & title)739 void Msg::SetWindowTitle(const std::string &title)
740 {
741 #if defined(HAVE_FLTK)
742   if(FlGui::available()){
743     FlGui::instance()->setGraphicTitle(title);
744   }
745 #endif
746 }
747 
Debug(const char * fmt,...)748 void Msg::Debug(const char *fmt, ...)
749 {
750   if(GetVerbosity() < 99) return;
751 
752   char str[5000];
753   va_list args;
754   va_start(args, fmt);
755   vsnprintf(str, sizeof(str), fmt, args);
756   va_end(args);
757   int l = strlen(str); if(str[l - 1] == '\n') str[l - 1] = '\0';
758 
759   if(_logFile) fprintf(_logFile, "Debug: %s\n", str);
760   if(_callback) (*_callback)("Debug", str);
761   if(_client) _client->Info(str);
762 
763 #if defined(HAVE_FLTK)
764   if(FlGui::available()){
765     std::string tmp = std::string("Debug   : ") + str;
766     FlGui::instance()->addMessage(tmp.c_str());
767   }
768 #endif
769 
770   if(CTX::instance()->terminal){
771     if(_commSize > 1)
772       fprintf(stdout, "Debug   : [rank %3d] %s\n", GetCommRank(), str);
773     else
774       fprintf(stdout, "Debug   : %s\n", str);
775     fflush(stdout);
776   }
777 }
778 
ProgressMeter(int n,bool log,const char * fmt,...)779 void Msg::ProgressMeter(int n, bool log, const char *fmt, ...)
780 {
781   if(GetCommRank() || GetVerbosity() < 4) return;
782   if(_progressMeterStep <= 0 || _progressMeterStep >= 100) return;
783   if(_progressMeterTotal <= 0) return;
784 
785   int N = _progressMeterTotal;
786   double percent = 100. * (double)n / (double)N;
787 
788   if(percent >= _progressMeterCurrent || n > N - 1){
789     int p = _progressMeterCurrent;
790     while(p < percent) p += _progressMeterStep;
791     if(p >= 100) p = 100;
792 
793     _progressMeterCurrent = p;
794 
795     // TODO With C++11 use std::string (contiguous layout) and avoid all these C
796     // problems
797     // str2 needs to have at least 5018 bytes or buffer overflow will occur
798     char str[5000], str2[5100];
799     va_list args;
800     va_start(args, fmt);
801     vsnprintf(str, sizeof(str), fmt, args);
802     va_end(args);
803     int l = strlen(str); if(str[l - 1] == '\n') str[l - 1] = '\0';
804     sprintf(str2, "Info    : [%3d%%] %s", p, str);
805 
806     if(_client) _client->Progress(str2);
807 
808 #if defined(HAVE_FLTK)
809     if(FlGui::available() && GetVerbosity() > 4){
810       FlGui::instance()->setProgress(str, (n > N - 1) ? 0 : n, 0, N);
811       FlGui::check(true);
812     }
813 #endif
814     if(_logFile) fprintf(_logFile, "Progress: %s\n", str);
815     if(_callback) (*_callback)("Progress", str);
816     if(!streamIsFile(stdout) && log && CTX::instance()->terminal){
817       fprintf(stdout, "%s                                          \r",
818               (n > N - 1) ? "" : str2);
819       fflush(stdout);
820     }
821   }
822 }
823 
PrintTimers()824 void Msg::PrintTimers()
825 {
826   // do a single stdio call!
827   std::string str;
828   for(auto it = _timers.begin();
829       it != _timers.end(); it++){
830     if(it != _timers.begin()) str += ", ";
831     char tmp[256];
832     sprintf(tmp, "%s = %gs ", it->first.c_str(), it->second);
833     str += std::string(tmp);
834   }
835   if(!str.size()) return;
836 
837   if(CTX::instance()->terminal){
838     if(_commSize > 1)
839       fprintf(stdout, "Timers  : [rank %3d] %s\n", GetCommRank(), str.c_str());
840     else
841       fprintf(stdout, "Timers  : %s\n", str.c_str());
842     fflush(stdout);
843   }
844 }
845 
ResetErrorCounter()846 void Msg::ResetErrorCounter()
847 {
848   _warningCount = 0; _errorCount = 0;
849   _firstWarning.clear(); _firstError.clear(); _lastError.clear();
850 #if defined(HAVE_FLTK)
851   if(FlGui::available()) FlGui::instance()->setLastStatus();
852 #endif
853 }
854 
PrintErrorCounter(const char * title)855 void Msg::PrintErrorCounter(const char *title)
856 {
857   if(GetCommRank() || GetVerbosity() < 1) return;
858   if(!GetWarningCount() && !GetErrorCount()) return;
859 
860   std::string prefix = GetErrorCount() ? "Error   : " : "Warning : ";
861   std::string help("Check the full log for details");
862   std::string line(std::max(strlen(title), help.size()), '-');
863   char warn[128], err[128];
864   sprintf(warn, "%5d warning%s", GetWarningCount(), GetWarningCount() == 1 ? "" : "s");
865   sprintf(err, "%5d error%s", GetErrorCount(), GetErrorCount() == 1 ? "" : "s");
866 
867 #if defined(HAVE_FLTK)
868   if(FlGui::available()){
869     std::string col = GetErrorCount() ?
870       std::string(CTX::instance()->guiColorScheme ? "@B72@." : "@C1@.") :
871       std::string(CTX::instance()->guiColorScheme ? "@B152@." : "@C5@.");
872     FlGui::instance()->addMessage((col + prefix + line).c_str());
873     FlGui::instance()->addMessage((col + prefix + title).c_str());
874     FlGui::instance()->addMessage((col + prefix + warn).c_str());
875     FlGui::instance()->addMessage((col + prefix + err).c_str());
876     FlGui::instance()->addMessage((col + prefix + help).c_str());
877     FlGui::instance()->addMessage((col + prefix + line).c_str());
878     if(GetErrorCount()) fl_beep();
879   }
880 #endif
881 
882   if(CTX::instance()->terminal){
883     const char *c0 = "", *c1 = "";
884     if(!streamIsFile(stderr) && streamIsVT100(stderr)){
885       c0 = GetErrorCount() ? "\33[1m\33[31m" : "\33[35m"; // bold red or magenta
886       c1 = "\33[0m";
887     }
888     fprintf(stderr, "%s%s\n%s\n%s\n%s\n%s\n%s%s\n", c0, (prefix + line).c_str(),
889             (prefix + title).c_str(), (prefix + warn).c_str(),
890             (prefix + err).c_str(), (prefix + help).c_str(),
891             (prefix + line).c_str(), c1);
892     fflush(stderr);
893   }
894 }
895 
GetValue(const char * text,double defaultval)896 double Msg::GetValue(const char *text, double defaultval)
897 {
898   // if a callback is given let's assume we don't want to be bothered
899   // with interactive stuff
900   if(CTX::instance()->noPopup || _callback) return defaultval;
901 
902 #if defined(HAVE_FLTK)
903   if(FlGui::available()){
904     char defaultstr[256];
905     sprintf(defaultstr, "%.16g", defaultval);
906     const char *ret = fl_input(text, defaultstr, "");
907     if(!ret)
908       return defaultval;
909     else
910       return atof(ret);
911   }
912 #endif
913 
914   printf("%s (default=%.16g): ", text, defaultval);
915   char str[256];
916   char *ret = fgets(str, sizeof(str), stdin);
917   if(!ret || !strlen(str) || !strcmp(str, "\n"))
918     return defaultval;
919   else
920     return atof(str);
921 }
922 
GetString(const char * text,const std::string & defaultval)923 std::string Msg::GetString(const char *text, const std::string &defaultval)
924 {
925   // if a callback is given let's assume we don't want to be bothered
926   // with interactive stuff
927   if(CTX::instance()->noPopup || _callback) return defaultval;
928 
929 #if defined(HAVE_FLTK)
930   if(FlGui::available()){
931     const char *ret = fl_input(text, defaultval.c_str(), "");
932     if(!ret)
933       return defaultval;
934     else
935       return std::string(ret);
936   }
937 #endif
938 
939   printf("%s (default=%s): ", text, defaultval.c_str());
940   char str[256];
941   char *ret = fgets(str, sizeof(str), stdin);
942   if(!ret || !strlen(str) || !strcmp(str, "\n"))
943     return defaultval;
944   else
945     return std::string(str);
946 }
947 
GetAnswer(const char * question,int defaultval,const char * zero,const char * one,const char * two)948 int Msg::GetAnswer(const char *question, int defaultval, const char *zero,
949                    const char *one, const char *two)
950 {
951   // if a callback is given let's assume we don't want to be bothered
952   // with interactive stuff
953   if(CTX::instance()->noPopup || _callback) return defaultval;
954 
955 #if defined(HAVE_FLTK)
956   if(FlGui::available())
957     return fl_choice(question, zero, one, two, "");
958 #endif
959 
960   if(two)
961     printf("%s\n\n0=[%s] 1=[%s] 2=[%s] (default=%d): ", question,
962            zero, one, two, defaultval);
963   else
964     printf("%s\n\n0=[%s] 1=[%s] (default=%d): ", question,
965            zero, one, defaultval);
966   char str[256];
967   char *ret = fgets(str, sizeof(str), stdin);
968   if(!ret || !strlen(str) || !strcmp(str, "\n"))
969     return defaultval;
970   else
971     return atoi(ret);
972 }
973 
UseOnelab()974 bool Msg::UseOnelab()
975 {
976 #if defined(HAVE_ONELAB)
977   return _onelabClient ? true : false;
978 #else
979   return false;
980 #endif
981 }
982 
SetOnelabNumber(const std::string & name,double val,bool visible,bool persistent,bool readOnly,int changedValue)983 void Msg::SetOnelabNumber(const std::string &name, double val, bool visible,
984                           bool persistent, bool readOnly, int changedValue)
985 {
986 #if defined(HAVE_ONELAB)
987   if(_onelabClient){
988     // get if first so we can keep its options
989     std::vector<onelab::number> numbers;
990     _onelabClient->get(numbers, name);
991     if(numbers.empty()){
992       numbers.resize(1);
993       numbers[0].setName(name);
994     }
995     numbers[0].setValue(val);
996     if(!visible) numbers[0].setVisible(false);
997     if(persistent) numbers[0].setAttribute("Persistent", "1");
998     numbers[0].setReadOnly(readOnly);
999     numbers[0].setChangedValue(changedValue);
1000     _onelabClient->set(numbers[0]);
1001   }
1002 #endif
1003 }
1004 
SetOnelabNumber(const std::string & name,const std::vector<double> & val,bool visible)1005 void Msg::SetOnelabNumber(const std::string &name, const std::vector<double> &val,
1006                           bool visible)
1007 {
1008 #if defined(HAVE_ONELAB)
1009   if(_onelabClient){
1010     onelab::number n(name, val);
1011     if(!visible) n.setVisible(false);
1012     _onelabClient->set(n);
1013   }
1014 #endif
1015 }
1016 
SetOnelabString(const std::string & name,const std::string & val,bool visible,bool persistent,bool readOnly,int changedValue,const std::string & kind)1017 void Msg::SetOnelabString(const std::string &name, const std::string &val,
1018                           bool visible, bool persistent, bool readOnly,
1019                           int changedValue, const std::string &kind)
1020 {
1021 #if defined(HAVE_ONELAB)
1022   if(_onelabClient){
1023     // get if first so we can keep its options
1024     std::vector<onelab::string> strings;
1025     _onelabClient->get(strings, name);
1026     if(strings.empty()){
1027       strings.resize(1);
1028       strings[0].setName(name);
1029     }
1030     strings[0].setValue(val);
1031     if(!visible) strings[0].setVisible(false);
1032     if(persistent) strings[0].setAttribute("Persistent", "1");
1033     strings[0].setReadOnly(readOnly);
1034     strings[0].setChangedValue(changedValue);
1035     if(kind.size()) strings[0].setKind(kind);
1036     _onelabClient->set(strings[0]);
1037   }
1038 #endif
1039 }
1040 
AddOnelabStringChoice(const std::string & name,const std::string & kind,const std::string & value,bool updateValue,bool readOnly,bool visible)1041 void Msg::AddOnelabStringChoice(const std::string &name,
1042                                 const std::string &kind,
1043                                 const std::string &value, bool updateValue,
1044                                 bool readOnly, bool visible)
1045 {
1046 #if defined(HAVE_ONELAB)
1047   if(_onelabClient){
1048     std::vector<std::string> choices;
1049     std::vector<onelab::string> ps;
1050     _onelabClient->get(ps, name);
1051     if(ps.size()){
1052       choices = ps[0].getChoices();
1053       if(std::find(choices.begin(), choices.end(), value) == choices.end())
1054         choices.push_back(value);
1055       if(updateValue)
1056         ps[0].setValue(value);
1057     }
1058     else{
1059       ps.resize(1);
1060       ps[0].setName(name);
1061       ps[0].setKind(kind);
1062       ps[0].setValue(value);
1063       choices.push_back(value);
1064     }
1065     ps[0].setChoices(choices);
1066     if(readOnly){
1067       ps[0].setReadOnly(true);
1068       ps[0].setAttribute("AutoCheck", "0");
1069     }
1070     else{
1071       ps[0].setReadOnly(false);
1072       ps[0].setAttribute("AutoCheck", "1");
1073     }
1074     ps[0].setVisible(visible);
1075     _onelabClient->set(ps[0]);
1076   }
1077 #endif
1078 }
1079 
GetOnelabNumber(const std::string & name,double defaultValue,bool errorIfMissing)1080 double Msg::GetOnelabNumber(const std::string &name, double defaultValue,
1081                             bool errorIfMissing)
1082 {
1083 #if defined(HAVE_ONELAB)
1084   if(_onelabClient){
1085     std::vector<onelab::number> numbers;
1086     _onelabClient->get(numbers, name);
1087     if(numbers.empty()){
1088       if(errorIfMissing)
1089         Msg::Error("Unknown ONELAB number parameter '%s'", name.c_str());
1090       return defaultValue;
1091     }
1092     else
1093       return numbers[0].getValue();
1094   }
1095 #endif
1096   if(errorIfMissing)
1097     Msg::Error("GetNumber requires a ONELAB client");
1098   return defaultValue;
1099 }
1100 
GetOnelabString(const std::string & name,const std::string & defaultValue,bool errorIfMissing)1101 std::string Msg::GetOnelabString(const std::string &name,
1102                                  const std::string &defaultValue,
1103                                  bool errorIfMissing)
1104 {
1105 #if defined(HAVE_ONELAB)
1106   if(_onelabClient){
1107     std::vector<onelab::string> strings;
1108     _onelabClient->get(strings, name);
1109     if(strings.empty()){
1110       if(errorIfMissing)
1111         Msg::Error("Unknown ONELAB string parameter '%s'", name.c_str());
1112       return defaultValue;
1113     }
1114     else
1115       return strings[0].getValue();
1116   }
1117 #endif
1118   if(errorIfMissing)
1119     Msg::Error("GetString requires a ONELAB client");
1120   return defaultValue;
1121 }
1122 
1123 #if defined(HAVE_ONELAB)
1124 class localGmsh : public onelab::localClient {
1125 public:
localGmsh()1126   localGmsh() : onelab::localClient("Gmsh") {}
1127   // redefinition of virtual onelab_client::sendMergeFileRequest
sendMergeFileRequest(const std::string & name)1128   void sendMergeFileRequest(const std::string &name)
1129   {
1130     if(name.find(".geo") != std::string::npos){
1131       MergePostProcessingFile(name, CTX::instance()->solver.autoShowViews,
1132                               CTX::instance()->solver.autoShowLastStep, true);
1133       GModel::current()->setFileName(name);
1134     }
1135     else if((name.find(".opt") != std::string::npos)){
1136       MergeFile(name);
1137     }
1138     else if((name.find(".macro") != std::string::npos)){
1139       MergeFile(name);
1140     }
1141     else
1142       MergePostProcessingFile(name, CTX::instance()->solver.autoShowViews,
1143                               CTX::instance()->solver.autoShowLastStep, true);
1144   }
sendInfo(const std::string & msg)1145   void sendInfo(const std::string &msg){ Msg::Info("%s", msg.c_str()); }
sendWarning(const std::string & msg)1146   void sendWarning(const std::string &msg){ Msg::Warning("%s", msg.c_str()); }
sendError(const std::string & msg)1147   void sendError(const std::string &msg){ Msg::Error("%s", msg.c_str()); }
1148 };
1149 #endif
1150 
InitializeOnelab(const std::string & name,const std::string & sockname)1151 void Msg::InitializeOnelab(const std::string &name, const std::string &sockname)
1152 {
1153 #if defined(HAVE_ONELAB)
1154   if(_onelabClient) delete _onelabClient;
1155   if(sockname.empty()){
1156     _onelabClient = new localGmsh();
1157     if(name != "Gmsh"){ // load db from file:
1158       FILE *fp = Fopen(name.c_str(), "rb");
1159       if(fp){
1160         Msg::Info("Reading ONELAB database '%s'", name.c_str());
1161         _onelabClient->fromFile(fp);
1162         fclose(fp);
1163       }
1164       else
1165         Error("Error loading onelab database '%s'", name.c_str());
1166     }
1167   }
1168   else{
1169     onelab::remoteNetworkClient *c = new onelab::remoteNetworkClient(name, sockname);
1170     _onelabClient = c;
1171     _client = c->getGmshClient();
1172 
1173     SetOnelabNumber(name + "/Use command line", 1, false);
1174     SetOnelabString(name + "/File extension", ".geo", false);
1175     SetOnelabString(name + "/9CheckCommand", "-", false);
1176     SetOnelabString(name + "/9ComputeCommand", "-3", false);
1177 
1178     std::vector<onelab::string> ps;
1179     _onelabClient->get(ps, name + "/Action");
1180     if(ps.size()){
1181       //removed message, as terminal is set to 1 when we get here
1182       //Info("Performing ONELAB '%s'", ps[0].getValue().c_str());
1183       if(ps[0].getValue() == "initialize") Exit(0);
1184     }
1185   }
1186 
1187   // default onelab button mode
1188   SetOnelabString("ONELAB/Button", "", false, true);
1189 #endif
1190 }
1191 
FinalizeOnelab()1192 void Msg::FinalizeOnelab()
1193 {
1194 #if defined(HAVE_ONELAB)
1195   // kill any running clients
1196   for(auto it = onelab::server::instance()->firstClient();
1197       it != onelab::server::instance()->lastClient(); it++){
1198     (*it)->kill();
1199   }
1200   // delete local client
1201   if(_onelabClient){
1202     delete _onelabClient;
1203     _onelabClient = nullptr;
1204     _client = nullptr;
1205   }
1206 #endif
1207 }
1208 
SetOnelabAction(const std::string & action)1209 void Msg::SetOnelabAction(const std::string &action)
1210 {
1211 #if defined(HAVE_ONELAB)
1212   if(_onelabClient){
1213     onelab::string o(_onelabClient->getName() + "/Action", action);
1214     o.setVisible(false);
1215     o.setChangedValue(0);
1216     _onelabClient->set(o);
1217   }
1218 #endif
1219 }
1220 
GetOnelabAction()1221 std::string Msg::GetOnelabAction()
1222 {
1223 #if defined(HAVE_ONELAB)
1224   if(_onelabClient){
1225     std::vector<onelab::string> ps;
1226     _onelabClient->get(ps, _onelabClient->getName() + "/Action");
1227     if(ps.size()) return ps[0].getValue();
1228   }
1229 #endif
1230   return "";
1231 }
1232 
LoadOnelabClient(const std::string & clientName,const std::string & sockName)1233 void Msg::LoadOnelabClient(const std::string &clientName, const std::string &sockName)
1234 {
1235 #if defined(HAVE_ONELAB)
1236   onelab::remoteNetworkClient *client = nullptr;
1237   client = new onelab::remoteNetworkClient(clientName, sockName);
1238   if(client){
1239     std::string action, cmd;
1240     std::vector<onelab::string> ps;
1241     client->get(ps, clientName + "/Action");
1242     if(ps.size() && ps[0].getValue().size())
1243       action.assign(ps[0].getValue());
1244     //cmd.assign("");
1245     if(!action.compare("compute")){
1246       std::vector<onelab::string> ps;
1247       client->get(ps,clientName+"/FullCmdLine");
1248       if(ps.size() && ps[0].getValue().size())
1249         cmd.assign(ps[0].getValue());
1250 
1251       if(cmd.size()){
1252         Msg::Info("Loader calls <%s>", cmd.c_str());
1253         //client->sendInfo(strcat("Loader calls",cmd.c_str()));
1254         std::cout << "Loader calls " << cmd << std::endl;
1255         SystemCall(cmd.c_str(), true); //true->blocking
1256       }
1257       else
1258         Msg::Info("No full command line found for <%s>",
1259                   clientName.c_str());
1260     }
1261     Msg::Info("Stopping client <%s>", clientName.c_str());
1262     delete client;
1263   }
1264   exit(1);
1265 #endif
1266 }
1267 
GetGmshClient()1268 GmshClient *Msg::GetGmshClient()
1269 {
1270   return _client;
1271 }
1272 
GetNumOnelabClients()1273 int Msg::GetNumOnelabClients()
1274 {
1275 #if defined(HAVE_ONELAB)
1276   return onelab::server::instance()->getNumClients();
1277 #endif
1278   return 0;
1279 }
1280 
1281 #if defined(HAVE_ONELAB)
_setStandardOptions(onelab::parameter * p,std::map<std::string,std::vector<double>> & fopt,std::map<std::string,std::vector<std::string>> & copt)1282 static void _setStandardOptions(onelab::parameter *p,
1283                                 std::map<std::string, std::vector<double> > &fopt,
1284                                 std::map<std::string, std::vector<std::string> > &copt)
1285 {
1286   // strings
1287   if(copt.count("Label")) p->setLabel(copt["Label"][0]);
1288   if(copt.count("ShortHelp")) // for backward compatibility
1289     p->setLabel(copt["ShortHelp"][0]);
1290   if(copt.count("Help")) p->setHelp(copt["Help"][0]);
1291   if(copt.count("Highlight")) p->setAttribute("Highlight", copt["Highlight"][0]);
1292   if(copt.count("Macro")) p->setAttribute("Macro", copt["Macro"][0]);
1293   if(copt.count("GmshOption")) p->setAttribute("GmshOption", copt["GmshOption"][0]);
1294   if(copt.count("ServerAction")) p->setAttribute("ServerAction", copt["ServerAction"][0]);
1295   if(copt.count("Units")) p->setAttribute("Units", copt["Units"][0]);
1296   if(copt.count("AutoCheck")) // for backward compatibility
1297     p->setAttribute("AutoCheck", copt["AutoCheck"][0]);
1298 
1299   // numbers
1300   if(fopt.count("Visible")) p->setVisible(fopt["Visible"][0] ? true : false);
1301   if(fopt.count("ReadOnly")) p->setReadOnly(fopt["ReadOnly"][0] ? true : false);
1302   if(fopt.count("NeverChanged")) p->setNeverChanged(fopt["NeverChanged"][0] ? true : false);
1303   if(fopt.count("ChangedValue")) p->setChangedValue(fopt["ChangedValue"][0]);
1304   if(fopt.count("ReadOnlyRange"))
1305     p->setAttribute("ReadOnlyRange", fopt["ReadOnlyRange"][0] ? "1" : "0");
1306   if(fopt.count("AutoCheck"))
1307     p->setAttribute("AutoCheck", fopt["AutoCheck"][0] ? "1" : "0");
1308 }
1309 
_setOtherAttributes(onelab::parameter * p,std::map<std::string,std::vector<std::string>> & copt)1310 static void _setOtherAttributes(onelab::parameter *p,
1311                                 std::map<std::string, std::vector<std::string> > &copt)
1312 {
1313   for(auto it = copt.begin(); it != copt.end(); it++)
1314     if(p->getAttribute(it->first).empty() &&
1315        it->first.compare("Name")          &&
1316        it->first.compare("Label")         &&
1317        it->first.compare("ShortHelp")     &&
1318        it->first.compare("Help")          &&
1319        it->first.compare("Visible")       &&
1320        it->first.compare("ReadOnly")      &&
1321        it->first.compare("NeverChanged")  &&
1322        it->first.compare("ChangedValue")) // Attribute 'it' was not already set
1323       p->setAttribute(it->first, it->second[0]);
1324 }
1325 
_getParameterName(const std::string & key,std::map<std::string,std::vector<std::string>> & copt)1326 static std::string _getParameterName(const std::string &key,
1327                                      std::map<std::string, std::vector<std::string> > &copt)
1328 {
1329   std::string name(key);
1330   if(copt.count("Path")){
1331     std::string path = copt["Path"][0];
1332     // if path ends with a number, assume it's for ordering purposes
1333     if(path.size() && path[path.size() - 1] >= '0' && path[path.size() - 1] <= '9')
1334       name = path + name;
1335     else if(path.size() && path[path.size() - 1] == '/')
1336       name = path + name;
1337     else
1338       name = path + "/" + name;
1339   }
1340   return name;
1341 }
1342 #endif
1343 
ExchangeOnelabParameter(const std::string & key,std::vector<double> & val,std::map<std::string,std::vector<double>> & fopt,std::map<std::string,std::vector<std::string>> & copt)1344 void Msg::ExchangeOnelabParameter(const std::string &key,
1345                                   std::vector<double> &val,
1346                                   std::map<std::string, std::vector<double> > &fopt,
1347                                   std::map<std::string, std::vector<std::string> > &copt)
1348 {
1349 #if defined(HAVE_ONELAB)
1350   if(!_onelabClient) return;
1351 
1352   std::string name;
1353   if(copt.count("Name"))
1354     name = copt["Name"][0];
1355 
1356   if(name.empty()){
1357     if(copt.size() || fopt.size())
1358       Msg::Error("From now on you need to use the `Name' attribute to create a "
1359                  "ONELAB parameter: `Name \"%s\"'",
1360                  _getParameterName(key, copt).c_str());
1361     return;
1362   }
1363 
1364   std::vector<onelab::number> ps;
1365   _onelabClient->get(ps, name);
1366   bool noRange = true, noChoices = true, noLoop = true;
1367   bool noGraph = true, noClosed = true;
1368   if(ps.size()){
1369     bool useLocalValue = ps[0].getReadOnly();
1370     if(fopt.count("ReadOnly")) useLocalValue = fopt["ReadOnly"][0];
1371     if(useLocalValue)
1372       ps[0].setValues(val);
1373     else
1374       val = ps[0].getValues(); // use value from server
1375     // keep track of these attributes, which can be changed server-side (unless
1376     // they are not visible or, for the range/choices, when explicitly setting
1377     // these attributes as ReadOnly)
1378     if(ps[0].getVisible()){
1379       if(!(fopt.count("ReadOnlyRange") && fopt["ReadOnlyRange"][0])){
1380         if(ps[0].getMin() != -onelab::parameter::maxNumber() ||
1381            ps[0].getMax() != onelab::parameter::maxNumber() ||
1382            ps[0].getStep() != 0.) noRange = false;
1383         if(ps[0].getChoices().size()) noChoices = false;
1384       }
1385       if(ps[0].getAttribute("Loop").size()) noLoop = false;
1386       if(ps[0].getAttribute("Graph").size()) noGraph = false;
1387       if(ps[0].getAttribute("Closed").size()) noClosed = false;
1388     }
1389   }
1390   else{
1391     ps.resize(1);
1392     ps[0].setName(name);
1393     ps[0].setValues(val);
1394   }
1395   // send updated parameter to server
1396   if(noRange && fopt.count("Range") && fopt["Range"].size() == 2){
1397     ps[0].setMin(fopt["Range"][0]); ps[0].setMax(fopt["Range"][1]);
1398   }
1399   else if(noRange && fopt.count("Min") && fopt.count("Max")){
1400     ps[0].setMin(fopt["Min"][0]); ps[0].setMax(fopt["Max"][0]);
1401   }
1402   else if(noRange && fopt.count("Min")){
1403     ps[0].setMin(fopt["Min"][0]); ps[0].setMax(onelab::parameter::maxNumber());
1404   }
1405   else if(noRange && fopt.count("Max")){
1406     ps[0].setMax(fopt["Max"][0]); ps[0].setMin(-onelab::parameter::maxNumber());
1407   }
1408   if(noRange && fopt.count("Step")) ps[0].setStep(fopt["Step"][0]);
1409   // if no range/min/max/step info is provided, try to compute a reasonnable
1410   // range and step (this makes the gui much nicer to use)
1411   if(val.size() && noRange && !fopt.count("Range") && !fopt.count("Step") &&
1412      !fopt.count("Min") && !fopt.count("Max")){
1413     bool isInteger = (floor(val[0]) == val[0]);
1414     double fact = isInteger ? 5. : 20.;
1415     if(val[0] > 0){
1416       ps[0].setMin(val[0] / fact);
1417       ps[0].setMax(val[0] * fact);
1418       ps[0].setStep((ps[0].getMax() - ps[0].getMin()) / 100.);
1419     }
1420     else if(val[0] < 0){
1421       ps[0].setMin(val[0] * fact);
1422       ps[0].setMax(val[0] / fact);
1423       ps[0].setStep((ps[0].getMax() - ps[0].getMin()) / 100.);
1424     }
1425     if(val[0] && isInteger){
1426       ps[0].setMin((int)ps[0].getMin());
1427       ps[0].setMax((int)ps[0].getMax());
1428       ps[0].setStep((int)ps[0].getStep());
1429     }
1430   }
1431   if(noChoices && fopt.count("Choices")){
1432     ps[0].setChoices(fopt["Choices"]);
1433     if(copt.count("Choices")) ps[0].setChoiceLabels(copt["Choices"]);
1434   }
1435   if(noLoop && copt.count("Loop")) // for backward compatibity
1436     ps[0].setAttribute("Loop", copt["Loop"][0]);
1437   if(noLoop && fopt.count("Loop"))
1438     ps[0].setAttribute("Loop", (fopt["Loop"][0] == 3.) ? "3" :
1439                        (fopt["Loop"][0] == 2.) ? "2" :
1440                        (fopt["Loop"][0] == 1.) ? "1" : "");
1441   if(noGraph && copt.count("Graph")) ps[0].setAttribute("Graph", copt["Graph"][0]);
1442   if(noClosed && copt.count("Closed")) // for backward compatibility
1443     ps[0].setAttribute("Closed", copt["Closed"][0]);
1444   if(noClosed && fopt.count("Closed"))
1445     ps[0].setAttribute("Closed", fopt["Closed"][0] ? "1" : "0");
1446   if(copt.count("NumberFormat"))
1447     ps[0].setAttribute("NumberFormat", copt["NumberFormat"][0]);
1448   _setStandardOptions(&ps[0], fopt, copt);
1449   _setOtherAttributes(&ps[0], copt);
1450   _onelabClient->set(ps[0]);
1451 #endif
1452 }
1453 
ExchangeOnelabParameter(const std::string & key,std::string & val,std::map<std::string,std::vector<double>> & fopt,std::map<std::string,std::vector<std::string>> & copt)1454 void Msg::ExchangeOnelabParameter(const std::string &key,
1455                                   std::string &val,
1456                                   std::map<std::string, std::vector<double> > &fopt,
1457                                   std::map<std::string, std::vector<std::string> > &copt)
1458 {
1459 #if defined(HAVE_ONELAB)
1460   if(!_onelabClient) return;
1461 
1462   std::string name;
1463   if(copt.count("Name"))
1464     name = copt["Name"][0];
1465 
1466   if(name.empty()){
1467     if(copt.size() || fopt.size())
1468       Msg::Error("From now on you need to use the `Name' attribute to create a "
1469                  "ONELAB parameter: `Name \"%s\"'",
1470                  _getParameterName(key, copt).c_str());
1471     return;
1472   }
1473 
1474   std::vector<onelab::string> ps;
1475   _onelabClient->get(ps, name);
1476   bool noChoices = true, noClosed = true, noMultipleSelection = true;
1477   if(ps.size()){
1478     bool useLocalValue = ps[0].getReadOnly();
1479     if(fopt.count("ReadOnly")) useLocalValue = fopt["ReadOnly"][0];
1480     if(useLocalValue)
1481       ps[0].setValue(val); // use local value
1482     else
1483       val = ps[0].getValue(); // use value from server
1484     // keep track of these attributes, which can be changed server-side (unless
1485     // they are not visible)
1486     if(ps[0].getVisible()){
1487       if(ps[0].getChoices().size()) noChoices = false;
1488       if(ps[0].getAttribute("Closed").size()) noClosed = false;
1489       if(ps[0].getAttribute("MultipleSelection").size()) noMultipleSelection = false;
1490     }
1491   }
1492   else{
1493     ps.resize(1);
1494     ps[0].setName(name);
1495     ps[0].setValue(val);
1496   }
1497   if(copt.count("Kind")) ps[0].setKind(copt["Kind"][0]);
1498   if(noChoices && copt.count("Choices")) ps[0].setChoices(copt["Choices"]);
1499   if(noClosed && copt.count("Closed")) // for backward compatibility
1500     ps[0].setAttribute("Closed", copt["Closed"][0]);
1501   if(noClosed && fopt.count("Closed"))
1502     ps[0].setAttribute("Closed", fopt["Closed"][0] ? "1" : "0");
1503   if(noMultipleSelection && copt.count("MultipleSelection"))
1504     ps[0].setAttribute("MultipleSelection", copt["MultipleSelection"][0]);
1505   _setStandardOptions(&ps[0], fopt, copt);
1506   _setOtherAttributes(&ps[0], copt);
1507   _onelabClient->set(ps[0]);
1508 #endif
1509 }
1510 
UndefineOnelabParameter(const std::string & name)1511 void Msg::UndefineOnelabParameter(const std::string &name)
1512 {
1513 #if defined(HAVE_ONELAB)
1514   if(!_onelabClient) return;
1515   _onelabClient->clear(name);
1516 #endif
1517 }
1518 
ImportPhysicalGroupsInOnelab()1519 void Msg::ImportPhysicalGroupsInOnelab()
1520 {
1521 #if defined(HAVE_ONELAB)
1522   if(_onelabClient){
1523     std::vector<onelab::number> oldn;
1524     _onelabClient->get(oldn, "Gmsh/Number of physical groups");
1525     int oldsize = 0;
1526     if(oldn.size()) oldsize = (int)oldn[0].getValue();
1527 
1528     std::map<int, std::vector<GEntity*> > groups[4];
1529     GModel::current()->getPhysicalGroups(groups);
1530     int size = 0;
1531     for(int dim = 0; dim <= 3; dim++)
1532       size += groups[dim].size();
1533     onelab::number n("Gmsh/Number of physical groups", size);
1534     n.setReadOnly(true);
1535     n.setChangedValue(1);
1536     n.setVisible(false);
1537     n.setAttribute("Closed", "1");
1538     _onelabClient->set(n);
1539 
1540     onelab::number nd("Gmsh/Model dimension", GModel::current()->getDim());
1541     nd.setReadOnly(true);
1542     nd.setChangedValue(1);
1543     nd.setVisible(false);
1544     nd.setAttribute("Closed", "1");
1545     _onelabClient->set(nd);
1546 
1547     int index = 1;
1548     for(int dim = 0; dim <= 3; dim++){
1549       for(auto it = groups[dim].begin();
1550           it != groups[dim].end(); it++){
1551         int num = it->first;
1552         std::string name = GModel::current()->getPhysicalName(dim, it->first);
1553         char tmp[256];
1554         if(name.empty()){
1555           sprintf(tmp, "Physical %s %d", (dim == 3) ? "Volume" : (dim == 2) ? "Surface" :
1556                   (dim == 1) ? "Curve" : "Point", num);
1557           name = tmp;
1558         }
1559         sprintf(tmp, "Gmsh/Physical group %d/", index);
1560         std::string str = tmp;
1561         onelab::number n1(str + "Dimension", dim);
1562         n1.setReadOnly(true);
1563         n1.setChangedValue(1);
1564         n1.setVisible(false);
1565         _onelabClient->set(n1);
1566         onelab::number n2(str + "Number", num);
1567         n2.setReadOnly(true);
1568         n2.setChangedValue(1);
1569         n2.setVisible(false);
1570         _onelabClient->set(n2);
1571         onelab::string s(str + "Name", name);
1572         s.setReadOnly(true);
1573         s.setChangedValue(1);
1574         s.setVisible(false);
1575         _onelabClient->set(s);
1576         index++;
1577       }
1578     }
1579 
1580     // remove old ones
1581     for(int index = size + 1; index < oldsize + 1; index++){
1582       char tmp[256];
1583       sprintf(tmp, "Gmsh/Physical group %d/Dimension", index);
1584       _onelabClient->clear(tmp);
1585       sprintf(tmp, "Gmsh/Physical group %d/Number", index);
1586       _onelabClient->clear(tmp);
1587       sprintf(tmp, "Gmsh/Physical group %d/Name", index);
1588       _onelabClient->clear(tmp);
1589     }
1590 
1591 #if defined(HAVE_FLTK)
1592     if(FlGui::available()){
1593       FlGui::instance()->resetVisibility();
1594       FlGui::instance()->rebuildTree(false);
1595     }
1596 #endif
1597   }
1598 #endif
1599 }
1600 
RunOnelabClient(const std::string & name,const std::string & command)1601 void Msg::RunOnelabClient(const std::string &name, const std::string &command)
1602 {
1603 #if defined(HAVE_ONELAB)
1604   onelabUtils::runClient(name, command);
1605 #endif
1606 }
1607 
SetOnelabChanged(int value,const std::string & client)1608 void Msg::SetOnelabChanged(int value, const std::string &client)
1609 {
1610 #if defined(HAVE_ONELAB)
1611   onelab::server::instance()->setChanged(value, client);
1612 #endif
1613 }
1614 
Barrier()1615 void Msg::Barrier()
1616 {
1617 #if defined(HAVE_MPI)
1618   MPI_Barrier(MPI_COMM_WORLD);
1619 #endif
1620 }
1621 
1622 #if defined(_OPENMP)
1623 
GetNumThreads()1624 int Msg::GetNumThreads(){ return omp_get_num_threads(); }
SetNumThreads(int num)1625 void Msg::SetNumThreads(int num){ omp_set_num_threads(num); }
GetMaxThreads()1626 int Msg::GetMaxThreads(){ return omp_get_max_threads(); }
GetThreadNum()1627 int Msg::GetThreadNum(){ return omp_get_thread_num(); }
1628 
1629 #else
1630 
GetNumThreads()1631 int Msg::GetNumThreads(){ return 1; }
SetNumThreads(int num)1632 void Msg::SetNumThreads(int num)
1633 {
1634   if(num > 1)
1635     Msg::Warning("Setting number of threads to %d requires OpenMP", num);
1636 }
GetMaxThreads()1637 int Msg::GetMaxThreads(){ return 1; }
GetThreadNum()1638 int Msg::GetThreadNum(){ return 0; }
1639 
1640 #endif
1641 
MsgProgressStatus(int num)1642 MsgProgressStatus::MsgProgressStatus(int num)
1643   : _totalElementToTreat(num), _currentI(0), _nextIToCheck(0),
1644     _initialTime(Cpu()), _lastTime(_initialTime), _lastPercentage(0),
1645     _progressMeterStep(Msg::GetProgressMeterStep())
1646 {
1647   Msg::SetProgressMeterStep(1);
1648   Msg::StartProgressMeter(_totalElementToTreat);
1649 }
1650 
~MsgProgressStatus()1651 MsgProgressStatus::~MsgProgressStatus()
1652 {
1653   Msg::ProgressMeter(_totalElementToTreat, true, "done");
1654   Msg::SetProgressMeterStep(_progressMeterStep);
1655   Msg::StopProgressMeter();
1656 }
1657 
next()1658 void MsgProgressStatus::next()
1659 {
1660   if(Msg::GetCommRank() || Msg::GetNumThreads() > 1) return;
1661 
1662   ++_currentI;
1663   if (_currentI < _nextIToCheck) return;
1664 
1665   int currentPercentage = _currentI * 100 / _totalElementToTreat;
1666   // check every percentage only
1667   _nextIToCheck = (currentPercentage + 1) * _totalElementToTreat / 100 + 1;
1668 
1669   double currentTime = Cpu();
1670   if ((currentPercentage < 5                   && currentTime - _lastTime > 15.) ||
1671       (currentPercentage > _lastPercentage + 4 && currentTime - _lastTime > 10.)) {
1672     _lastPercentage = currentPercentage;
1673     _lastTime = currentTime;
1674     const double remaining = (currentTime - _initialTime) / (_currentI + 1) *
1675                              (_totalElementToTreat - _currentI - 1);
1676     if (remaining < 60*2)
1677       Msg::ProgressMeter(_currentI - 1, true,
1678                          "%d%% (remaining time ~%g seconds)",
1679                          currentPercentage, remaining);
1680     else if (remaining < 60*60*2)
1681       Msg::ProgressMeter(_currentI - 1, true,
1682                          "%d%% (remaining time ~%g minutes)",
1683                          currentPercentage, remaining / 60);
1684     else
1685       Msg::ProgressMeter(_currentI - 1, true,
1686                          "%d%% (remaining time ~%g hours)",
1687                          currentPercentage, remaining / 3600);
1688   }
1689 }
1690