Merge branch '138-toggle-free-look-with-hotkey' into 'main/atys-live'
[ryzomcore.git] / nel / src / misc / common.cpp
blob7e2c775d81e747cf6f65600138c085ccabb00e0f
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdmisc.h"
22 #include "nel/misc/types_nl.h"
23 #include "nel/misc/common.h"
25 #ifdef NL_OS_WINDOWS
26 # include <ShellAPI.h>
27 # include <io.h>
28 # include <tchar.h>
30 #define popen _popen
31 #define pclose _pclose
33 #elif defined NL_OS_MAC
34 # include <ApplicationServices/ApplicationServices.h>
35 #elif defined NL_OS_UNIX
36 # include <unistd.h>
37 # include <cerrno>
38 # include <pthread.h>
39 # include <sched.h>
40 #endif
42 #define MAX_LINE_WIDTH 256
44 #include "nel/misc/command.h"
45 #include "nel/misc/path.h"
46 #include "nel/misc/i18n.h"
48 using namespace std;
50 #ifndef NL_COMP_MINGW
51 #ifdef NL_OS_WINDOWS
52 # pragma message( " " )
54 # if FINAL_VERSION
55 # pragma message( "************************" )
56 # pragma message( "**** FINAL_VERSION *****" )
57 # pragma message( "************************" )
58 # else
59 # pragma message( "Not using FINAL_VERSION")
60 # endif // FINAL_VERSION
62 # ifdef ASSERT_THROW_EXCEPTION
63 # pragma message( "nlassert throws exceptions" )
64 # else
65 # pragma message( "nlassert does not throw exceptions" )
66 # endif // ASSERT_THROW_EXCEPTION
68 # ifdef _STLPORT_VERSION
69 # pragma message( "Using STLport" )
70 # else
71 # pragma message( "Using standard STL" )
72 # endif // _STLPORT_VERSION
74 # pragma message( " " )
76 # if (_MSC_VER >= 1200) && (_MSC_VER < 1400) && (WINVER < 0x0500)
77 //Using VC7 and later lib, need this to compile on VC6
78 extern "C" long _ftol2( double dblSource ) { return _ftol( dblSource ); }
79 # endif
82 #endif // NL_OS_WINDOWS
83 #endif // !NL_COMP_MINGW
86 #ifdef NL_USE_ALIGNED_MEMORY_OPERATORS
88 #ifdef NL_NO_EXCEPTION_SPECS
89 void *operator new(size_t size)
91 void *p = aligned_malloc(size, NL_DEFAULT_MEMORY_ALIGNMENT);
92 if (p == NULL) throw std::bad_alloc();
93 return p;
96 void *operator new[](size_t size)
98 void *p = aligned_malloc(size, NL_DEFAULT_MEMORY_ALIGNMENT);
99 if (p == NULL) throw std::bad_alloc();
100 return p;
103 void operator delete(void *p) noexcept
105 aligned_free(p);
108 void operator delete[](void *p) noexcept
110 aligned_free(p);
112 #else
113 void *operator new(size_t size) throw(std::bad_alloc)
115 void *p = aligned_malloc(size, NL_DEFAULT_MEMORY_ALIGNMENT);
116 if (p == NULL) throw std::bad_alloc();
117 return p;
120 void *operator new[](size_t size) throw(std::bad_alloc)
122 void *p = aligned_malloc(size, NL_DEFAULT_MEMORY_ALIGNMENT);
123 if (p == NULL) throw std::bad_alloc();
124 return p;
127 void operator delete(void *p) throw()
129 aligned_free(p);
132 void operator delete[](void *p) throw()
134 aligned_free(p);
136 #endif
138 #endif /* NL_HAS_SSE2 */
141 #ifdef DEBUG_NEW
142 #define new DEBUG_NEW
143 #endif
145 namespace NLMISC
149 * Portable Sleep() function that suspends the execution of the calling thread for a number of milliseconds.
150 * Note: the resolution of the timer is system-dependant and may be more than 1 millisecond.
152 void nlSleep( uint32 ms )
154 #ifdef NL_OS_WINDOWS
156 #ifdef NL_DEBUG
157 // a Sleep(0) "block" the other thread in DEBUG/_CONSOLE, so we clamp
158 ms = max(ms, (uint32)1);
159 #endif
161 Sleep( ms );
163 #elif defined NL_OS_UNIX
164 //usleep( ms*1000 ); // resolution: 20 ms!
166 timespec ts;
167 ts.tv_sec = ms/1000;
168 ts.tv_nsec = (ms%1000)*1000000;
169 int res;
172 res = nanosleep( &ts, &ts ); // resolution: 10 ms (with common scheduling policy)
174 while ( (res != 0) && (errno==EINTR) );
175 #endif
180 * Returns Thread Id (note: on Linux, Process Id is the same as the Thread Id)
182 size_t getThreadId()
184 #ifdef NL_OS_WINDOWS
185 return GetCurrentThreadId();
186 #elif defined NL_OS_UNIX
187 return size_t(pthread_self());
188 // doesnt work on linux kernel 2.6 return getpid();
189 #endif
195 * Returns a readable string from a vector of bytes. '\0' are replaced by ' '
197 string stringFromVector( const vector<uint8>& v, bool limited )
199 string s;
201 if (!v.empty())
203 int size = (int)v.size ();
204 if (limited && size > 1000)
206 string middle = "...<buf too big,skip middle part>...";
207 s.resize (1000 + middle.size());
208 memcpy (&*s.begin(), &*v.begin(), 500);
209 memcpy (&*s.begin()+500, &*middle.begin(), middle.size());
210 memcpy (&*s.begin()+500+middle.size(), &*v.begin()+size-500, 500);
212 else
214 s.resize (size);
215 memcpy( &*s.begin(), &*v.begin(), v.size() );
218 // Replace '\0' characters
219 string::iterator is;
220 for ( is=s.begin(); is!=s.end(); ++is )
222 // remplace non printable char and % with '?' chat
223 if ( ! isprint((uint8)(*is)) || (*is) == '%')
225 (*is) = '?';
230 if ( ! v.empty() )
232 // Copy contents
233 s.resize( v.size() );
234 memcpy( &*s.begin(), &*v.begin(), v.size() );
236 // Replace '\0' characters
237 string::iterator is;
238 for ( is=s.begin(); is!=s.end(); ++is )
240 // remplace non printable char and % with '?' chat
241 if ( ! isprint((*is)) || (*is) == '%')
243 (*is) = '?';
247 */ return s;
251 sint smprintf( char *buffer, size_t count, const char *format, ... )
253 sint ret;
255 va_list args;
256 va_start( args, format );
257 ret = vsnprintf( buffer, count, format, args );
258 if ( ret == -1 )
260 buffer[count-1] = '\0';
262 va_end( args );
264 return( ret );
268 sint64 atoiInt64 (const char *ident, sint64 base)
270 sint64 number = 0;
271 bool neg = false;
273 // NULL string
274 nlassert (ident != NULL);
276 // empty string
277 if (*ident == '\0') goto end;
279 // + sign
280 if (*ident == '+') ident++;
282 // - sign
283 if (*ident == '-') { neg = true; ident++; }
285 while (*ident != '\0')
287 if (isdigit((unsigned char)*ident))
289 number *= base;
290 number += (*ident)-'0';
292 else if (base > 10 && islower((unsigned char)*ident))
294 number *= base;
295 number += (*ident)-'a'+10;
297 else if (base > 10 && isupper((unsigned char)*ident))
299 number *= base;
300 number += (*ident)-'A'+10;
302 else
304 goto end;
306 ident++;
308 end:
309 if (neg) number = -number;
310 return number;
313 void itoaInt64 (sint64 number, char *str, sint64 base)
315 str[0] = '\0';
316 char b[256];
317 if(!number)
319 str[0] = '0';
320 str[1] = '\0';
321 return;
323 memset(b,'\0',255);
324 memset(b,'0',64);
325 sint n;
326 sint64 x = number;
327 if (x < 0) x = -x;
328 char baseTable[] = "0123456789abcdefghijklmnopqrstuvwyz";
329 for(n = 0; n < 64; n ++)
331 sint num = (sint)(x % base);
332 b[64 - n] = baseTable[num];
333 if(!x)
335 int k;
336 int j = 0;
338 if (number < 0)
340 str[j++] = '-';
343 for(k = 64 - n + 1; k <= 64; k++)
345 str[j ++] = b[k];
347 str[j] = '\0';
348 break;
350 x /= base;
354 uint raiseToNextPowerOf2(uint v)
356 uint res=1;
357 while(res<v)
358 res<<=1;
360 return res;
363 uint getPowerOf2(uint v)
365 uint res=1;
366 uint ret=0;
367 while(res<v)
369 ret++;
370 res<<=1;
373 return ret;
376 bool isPowerOf2(sint32 v)
378 while(v)
380 if(v&1)
382 v>>=1;
383 if(v)
384 return false;
386 else
387 v>>=1;
390 return true;
393 string bytesToHumanReadable (const std::string &bytes)
395 return bytesToHumanReadable (atoiInt64(bytes.c_str()));
398 string bytesToHumanReadable (uint64 bytes)
400 static const char *divTable[]= { "B", "KiB", "MiB", "GiB", "TiB" };
401 uint div = 0;
402 uint64 res = bytes;
403 uint64 newres = res;
404 for(;;)
406 newres /= 1024;
407 if(newres < 8 || div > 3)
408 break;
409 div++;
410 res = newres;
412 return toString ("%" NL_I64 "u %s", res, divTable[div]);
415 std::string bytesToHumanReadableUnits (uint64 bytes, const std::vector<std::string> &units)
417 if (units.empty()) return "";
419 uint div = 0;
420 uint last = units.size()-1;
421 uint64 res = bytes;
422 uint64 newres = res;
423 for(;;)
425 newres /= 1024;
426 if(newres < 8 || div > 3 || div == last)
427 break;
428 ++div;
429 res = newres;
431 return toString ("%" NL_I64 "u %s", res, units[div].c_str());
434 uint32 humanReadableToBytes (const string &str)
436 uint32 res;
438 if(str.empty())
439 return 0;
441 // not a number
442 if(str[0]<'0' || str[0]>'9')
443 return 0;
445 if (!fromString(str, res))
446 return 0;
448 if(str[str.size()-1] == 'B')
450 if (str.size()<3)
451 return res;
453 // there's no break and it's **normal**
454 switch (str[str.size()-2])
456 // kB/KB, MB, GB and TB are 1000 multiples
457 case 'T': res *= 1000;
458 case 'G': res *= 1000;
459 case 'M': res *= 1000;
460 case 'k': res *= 1000; break; // kilo symbol should be a lowercase K
461 case 'K': res *= 1000; break;
462 case 'i':
464 // KiB, MiB, GiB and TiB are 1024 multiples
465 if (str.size()<4)
466 return res;
468 switch (str[str.size()-3])
470 case 'T': res *= 1024;
471 case 'G': res *= 1024;
472 case 'M': res *= 1024;
473 case 'K': res *= 1024;
474 default: ;
477 default: ;
481 return res;
485 NLMISC_CATEGORISED_COMMAND(nel,btohr, "Convert a bytes number into an human readable number", "<int>")
487 nlunreferenced(rawCommandString);
488 nlunreferenced(quiet);
489 nlunreferenced(human);
491 if (args.size() != 1)
492 return false;
494 log.displayNL("%s -> %s", args[0].c_str(), bytesToHumanReadable(args[0]).c_str());
496 return true;
500 NLMISC_CATEGORISED_COMMAND(nel,hrtob, "Convert a human readable number into a bytes number", "<hr>")
502 nlunreferenced(rawCommandString);
503 nlunreferenced(quiet);
504 nlunreferenced(human);
506 if (args.size() != 1)
507 return false;
509 log.displayNL("%s -> %u", args[0].c_str(), humanReadableToBytes(args[0]));
511 return true;
515 string secondsToHumanReadable (uint32 time)
517 static const char *divTable[] = { "s", "mn", "h", "d" };
518 static uint divCoef[] = { 60, 60, 24 };
519 uint div = 0;
520 uint32 res = time;
521 uint32 newres = res;
522 for(;;)
524 if(div > 2)
525 break;
527 newres /= divCoef[div];
529 if(newres < 3)
530 break;
532 div++;
533 res = newres;
535 return toString ("%u%s", res, divTable[div]);
538 std::string timestampToHumanReadable(uint32 timestamp)
540 char buffer[30];
541 time_t dtime = timestamp;
542 tm *tms = localtime(&dtime);
544 if (tms)
546 strftime(buffer, 30, "%Y-%m-%d %H:%M:%S", tms);
547 return std::string(buffer);
550 return "";
553 uint32 fromHumanReadable (const std::string &str)
555 if (str.empty())
556 return 0;
558 uint32 val;
559 fromString(str, val);
561 switch (str[str.size()-1])
563 case 's': return val; // second
564 case 'n': return val*60; // minutes (mn)
565 case 'h': return val*60*60; // hour
566 case 'd': return val*60*60*24; // day
567 case 'b': // bytes
568 switch (str[str.size()-2])
570 case 'k': return val*1024;
571 case 'm': return val*1024*1024;
572 case 'g': return val*1024*1024*1024;
573 default : return val;
575 default: return val;
577 return 0;
580 NLMISC_CATEGORISED_COMMAND(nel,stohr, "Convert a second number into an human readable time", "<int>")
582 nlunreferenced(rawCommandString);
583 nlunreferenced(quiet);
584 nlunreferenced(human);
586 if (args.size() != 1)
587 return false;
589 uint32 seconds;
590 fromString(args[0], seconds);
591 log.displayNL("%s -> %s", args[0].c_str(), secondsToHumanReadable(seconds).c_str());
593 return true;
596 NLMISC_CATEGORISED_COMMAND(nel, toLower, "Convert a string to lowercase", "<string>")
598 nlunreferenced(args);
599 nlunreferenced(quiet);
600 nlunreferenced(human);
602 log.displayNL("%s", toLower(rawCommandString).c_str());
604 return true;
607 NLMISC_CATEGORISED_COMMAND(nel, toUpper, "Convert a string to uppercase", "<string>")
609 nlunreferenced(args);
610 nlunreferenced(quiet);
611 nlunreferenced(human);
613 log.displayNL("%s", toUpper(rawCommandString).c_str());
615 return true;
618 NLMISC_CATEGORISED_COMMAND(nel, toLowerAscii, "Convert a string's ascii-characters to lowercase", "<string>")
620 nlunreferenced(args);
621 nlunreferenced(quiet);
622 nlunreferenced(human);
624 log.displayNL("%s", toLowerAscii(rawCommandString).c_str());
626 return true;
629 NLMISC_CATEGORISED_COMMAND(nel, toUpperAscii, "Convert a string's ascii-characters to uppercase", "<string>")
631 nlunreferenced(args);
632 nlunreferenced(quiet);
633 nlunreferenced(human);
635 log.displayNL("%s", toUpperAscii(rawCommandString).c_str());
637 return true;
640 #if 0
642 std::string toLower(const char *str)
644 if (!str) return "";
646 uint len = strlen(str);
647 string res;
648 res.reserve(len);
649 for(uint i = 0; i < len; i++)
651 if( (str[i] >= 'A') && (str[i] <= 'Z') )
652 res += str[i] - 'A' + 'a';
653 else
654 res += str[i];
656 return res;
659 std::string toLower(const std::string &str)
661 string res;
662 res.reserve(str.size());
663 for(uint i = 0; i < str.size(); i++)
665 if( (str[i] >= 'A') && (str[i] <= 'Z') )
666 res += str[i] - 'A' + 'a';
667 else
668 res += str[i];
670 return res;
673 #endif
675 char toLower(const char ch)
677 if( (ch >= 'A') && (ch <= 'Z') )
679 return ch - 'A' + 'a';
681 else
683 return ch;
687 void toLower(char *str)
689 if (str == 0)
690 return;
692 while(*str != '\0')
694 if( (*str >= 'A') && (*str <= 'Z') )
696 *str = *str - 'A' + 'a';
698 str++;
702 #if 0
704 std::string toUpper(const std::string &str)
706 string res;
707 res.reserve(str.size());
708 for(uint i = 0; i < str.size(); i++)
710 if( (str[i] >= 'a') && (str[i] <= 'z') )
711 res += str[i] - 'a' + 'A';
712 else
713 res += str[i];
715 return res;
718 #endif
720 void toUpper(char *str)
722 if (str == 0)
723 return;
725 while(*str != '\0')
727 if( (*str >= 'a') && (*str <= 'z') )
729 *str = *str - 'a' + 'A';
731 str++;
735 std::string toLowerAscii(const std::string &str, char replacement)
737 std::string res;
738 res.reserve(str.size());
739 for (std::string::const_iterator it(str.begin()), end(str.end()); it != end; ++it)
741 char c = *it;
742 if ((sint8)(c + '\x01') < (sint8)(' ' + '\x01')) res += replacement;
743 else if (c >= 'A' && c <= 'Z') res += c + ('a' - 'A');
744 else res += c;
746 return res;
749 void toLowerAscii(char *str, char replacement)
751 for (ptrdiff_t i = 0; str[i]; ++i)
753 char c = str[i];
754 if ((sint8)(c + '\x01') < (sint8)(' ' + '\x01')) str[i] = replacement;
755 else if (c >= 'A' && c <= 'Z') str[i] = c + ('a' - 'A');
756 else str[i] = c;
760 std::string toUpperAscii(const std::string &str, char replacement)
762 std::string res;
763 res.reserve(str.size());
764 for (std::string::const_iterator it(str.begin()), end(str.end()); it != end; ++it)
766 char c = *it;
767 if ((sint8)(c + '\x01') < (sint8)(' ' + '\x01')) res += replacement;
768 else if (c >= 'a' && c <= 'z') res += c - ('a' - 'A');
769 else res += c;
771 return res;
774 void toUpperAscii(char *str, char replacement)
776 for (ptrdiff_t i = 0; str[i]; ++i)
778 char c = str[i];
779 if ((sint8)(c + '\x01') < (sint8)(' ' + '\x01')) str[i] = replacement;
780 else if (c >= 'a' && c <= 'z') str[i] = c - ('a' - 'A');
781 else str[i] = c;
785 std::string toLowerAscii(const std::string &str)
787 std::string res;
788 res.reserve(str.size());
789 for (std::string::const_iterator it(str.begin()), end(str.end()); it != end; ++it)
791 char c = *it;
792 if (c >= 'A' && c <= 'Z') res += c + ('a' - 'A');
793 else res += c;
795 return res;
798 void toLowerAscii(char *str)
800 for (ptrdiff_t i = 0; str[i]; ++i)
802 char c = str[i];
803 if (c >= 'A' && c <= 'Z') str[i] = c + ('a' - 'A');
804 else str[i] = c;
808 std::string toUpperAscii(const std::string &str)
810 std::string res;
811 res.reserve(str.size());
812 for (std::string::const_iterator it(str.begin()), end(str.end()); it != end; ++it)
814 char c = *it;
815 if (c >= 'a' && c <= 'z') res += c - ('a' - 'A');
816 else res += c;
818 return res;
821 void toUpperAscii(char *str)
823 for (ptrdiff_t i = 0; str[i]; ++i)
825 char c = str[i];
826 if (c >= 'a' && c <= 'z') str[i] = c - ('a' - 'A');
827 else str[i] = c;
831 std::string toHexa(const uint8 &b)
833 return toString("%02hhx", b);
836 std::string toHexa(const uint8 *data, uint size)
838 std::string res;
840 // hexadecimal string will be always twice the original size
841 res.reserve(size * 2);
843 // process each byte
844 for (uint i = 0; i < size; ++i)
846 res += toHexa(data[i]);
849 return res;
852 std::string toHexa(const std::string &str)
854 return toHexa((uint8*)str.c_str(), (uint)str.length());
857 std::string toHexa(const char *str)
859 return toHexa((uint8*)str, (uint)strlen(str));
862 bool fromHexa(const std::string &hexa, uint8 &b)
864 return fromHexa(hexa.c_str(), b);
867 bool fromHexa(const std::string &hexa, uint8 *data)
869 return fromHexa(hexa.c_str(), data);
872 bool fromHexa(const std::string &hexa, std::string &str)
874 return fromHexa(hexa.c_str(), str);
877 bool fromHexa(const char *hexa, uint8 &b)
879 char c1 = *hexa;
880 char c2 = *(hexa+1);
881 uint8 x1, x2;
882 if (!fromHexa(c1, x1)) return false;
883 if (!fromHexa(c2, x2)) return false;
885 b = (x1 << 4) | x2;
887 return true;
890 bool fromHexa(const char *hexa, uint8 *data)
892 // length of the string
893 uint len = strlen(hexa);
895 // process each byte
896 for (uint i = 0; i < len; i += 2)
898 if (!fromHexa(hexa + i, *(data++))) return false;
901 return true;
904 bool fromHexa(const char *hexa, std::string &str)
906 str.resize(strlen(hexa) * 2);
908 return fromHexa(hexa, (uint8*)str.c_str());
911 bool fromHexa(const char hexa, uint8 &b)
913 if (hexa >= '0' && hexa <= '9')
915 b = hexa - '0';
916 return true;
919 if (hexa >= 'A' && hexa <= 'F')
921 b = hexa - 'A' + 10;
922 return true;
925 if (hexa >= 'a' && hexa <= 'f')
927 b = hexa - 'a' + 10;
928 return true;
931 return false;
934 static std::vector<char> makeCharLookupTable(const std::string &chars)
936 std::vector<char> out(256, -1);
937 for(uint i = 0; i< chars.size(); i++)
938 out[chars[i]] = i;
940 return out;
943 std::string encodeURIComponent(const std::string &in)
945 static const char hexLookup[] = "0123456789ABCDEF";
946 static const std::vector<char> notEscaped(makeCharLookupTable(
947 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
948 "abcdefghijklmnopqrstuvwxyz"
949 "0123456789"
950 "-_.!~*'()"
953 if (in.empty())
954 return std::string();
956 std::string out;
958 size_t inSize = in.size();
959 size_t outSize = in.size();
961 // resize to worst case for smaller strings,
962 // give some replacements for free for larger strings
963 if (in.size() < 100)
964 out.reserve(in.size() * 3);
965 else
966 out.reserve(in.size() + 200);
968 for(size_t i = 0; i < inSize; i++)
970 char ch = in[i];
971 if (notEscaped[(uint8)ch] == -1)
973 out += '%';
974 out += hexLookup[(ch>>4)& 0x0F];
975 out += hexLookup[ch & 0x0F];
976 outSize += 2;
978 else
980 out += ch;
983 // resize back to correct size
984 out.resize(outSize);
986 return out;
989 std::string decodeURIComponent(const std::string &in)
991 if (in.find("%") == std::string::npos)
992 return in;
994 std::string out;
995 out.resize(in.size());
997 size_t outIndex = 0, inSize = in.size();
998 for(size_t i = 0; i < inSize; i++, outIndex++)
1000 if (in[i] == '%' && (i+2 < inSize))
1002 uint8 a;
1003 uint8 b;
1004 if (fromHexa(in[i+1], a) && fromHexa(in[i+2], b))
1006 out[outIndex] = (a << 4) | b;
1007 i += 2;
1008 } else {
1009 // not hex chars
1010 out[outIndex] = in[i];
1013 else
1015 out[outIndex] = in[i];
1018 out.resize(outIndex);
1019 return out;
1022 std::string formatThousands(const std::string& s)
1024 sint i, k;
1025 sint remaining = (sint)s.length() - 1;
1026 static std::string separator = NLMISC::CI18N::get("uiThousandsSeparator");
1028 // Don't add separator if the number is < 10k
1029 if (remaining < 4) return s;
1031 std::string ns;
1035 for (i = remaining, k = 0; i >= 0 && k < 3; --i, ++k )
1037 ns = s[i] + ns; // New char is added to front of ns
1038 if ( i > 0 && k == 2) ns = separator + ns; // j > 0 means still more digits
1041 remaining -= 3;
1043 while (remaining >= 0);
1045 return ns;
1049 // Exceptions
1052 Exception::Exception() : _Reason("Unknown Exception")
1054 // nlinfo("Exception will be launched: %s", _Reason.c_str());
1057 Exception::Exception(const std::string &reason) : _Reason(reason)
1059 nlinfo("Exception will be launched: %s", _Reason.c_str());
1062 Exception::Exception(const char *format, ...)
1064 NLMISC_CONVERT_VARGS (_Reason, format, NLMISC::MaxCStringSize);
1065 nlinfo("Exception will be launched: %s", _Reason.c_str());
1068 const char *Exception::what() const throw()
1070 return _Reason.c_str();
1073 bool killProgram(uint32 pid)
1075 #ifdef NL_OS_UNIX
1076 int res = kill(pid, SIGKILL);
1077 if(res == -1)
1079 char *err = strerror (errno);
1080 nlwarning("Failed to kill '%d' err %d: '%s'", pid, errno, err);
1082 return res == 0;
1083 /*#elif defined(NL_OS_WINDOWS)
1084 // it doesn't work because pid != handle and i don't know how to kill a pid or know the real handle of another service (not -1)
1085 int res = TerminateProcess((HANDLE)pid, 888);
1086 nlwarning("Failed to kill '%d' err %d: '%s'", pid, GetLastError (), lpMsgBuf);
1087 return res != 0;
1089 #else
1090 nlwarning("kill not implemented on this OS");
1091 return false;
1092 #endif
1095 bool abortProgram(uint32 pid)
1097 #ifdef NL_OS_UNIX
1098 int res = kill(pid, SIGABRT);
1099 if(res == -1)
1101 char *err = strerror (errno);
1102 nlwarning("Failed to abort '%d' err %d: '%s'", pid, errno, err);
1104 return res == 0;
1105 #else
1106 nlwarning("abort not implemented on this OS");
1107 return false;
1108 #endif
1111 #ifdef NL_OS_WINDOWS
1113 static bool createProcess(const std::string &programName, const std::string &arguments, bool log, PROCESS_INFORMATION &pi)
1115 STARTUPINFOW si;
1116 memset(&si, 0, sizeof(si));
1117 memset(&pi, 0, sizeof(pi));
1119 si.cb = sizeof(si);
1121 // Enable nlassert/nlstop to display the error reason & callstack
1122 const char *SE_TRANSLATOR_IN_MAIN_MODULE = "NEL_SE_TRANS";
1124 char envBuf[2];
1125 if (GetEnvironmentVariableA(SE_TRANSLATOR_IN_MAIN_MODULE, envBuf, 2) != 0)
1127 SetEnvironmentVariableA(SE_TRANSLATOR_IN_MAIN_MODULE, NULL);
1130 wchar_t *sProgramName = NULL;
1132 std::string args;
1134 // a .bat file must have first parameter to NULL and use 2nd parameter to pass filename
1135 if (CFile::getExtension(programName) == "bat")
1137 args = "\"" + programName + "\" " + arguments;
1139 else
1141 ucstring ucProgramName;
1142 ucProgramName.fromUtf8(programName);
1144 sProgramName = new wchar_t[MAX_PATH];
1145 wcscpy(sProgramName, (wchar_t*)ucProgramName.c_str());
1147 // important! we need to specify the executable full path as first argument
1148 args = toString("\"%s\" ", programName.c_str()) + arguments;
1151 // or 0 for a window
1152 BOOL res = CreateProcessW(sProgramName, (LPWSTR)nlUtf8ToWide(args), NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | CREATE_NO_WINDOW, NULL, NULL /* current dir */, &si, &pi);
1154 if (sProgramName)
1156 delete [] sProgramName;
1157 sProgramName = NULL;
1160 if (!res)
1162 if (log)
1164 sint lastError = getLastError();
1165 nlwarning("LAUNCH: Failed launched '%s' with arg '%s' err %d: '%s'", programName.c_str(), arguments.c_str(), lastError, formatErrorMessage(lastError).c_str());
1168 CloseHandle( pi.hProcess );
1169 CloseHandle( pi.hThread );
1171 return false;
1174 return true;
1177 #endif
1179 bool launchProgram(const std::string &programName, const std::string &arguments, bool log)
1181 #ifdef NL_OS_WINDOWS
1182 PROCESS_INFORMATION pi;
1184 if (!createProcess(programName, arguments, log, pi)) return false;
1186 //nldebug("LAUNCH: Successful launch '%s' with arg '%s'", programName.c_str(), arguments.c_str());
1187 CloseHandle( pi.hProcess );
1188 CloseHandle( pi.hThread );
1189 return true;
1190 #else
1192 #ifdef NL_OS_MAC
1193 // special OS X case with bundles
1194 if (toLowerAscii(CFile::getExtension(programName)) == ".app")
1196 // we need to open bundles with "open" command
1197 std::string command = NLMISC::toString("open \"%s\"", programName.c_str());
1199 // append arguments if any
1200 if (!arguments.empty())
1202 command += NLMISC::toString(" --args %s", arguments.c_str());
1205 int res = system(command.c_str());
1207 if (!res) return true;
1209 if (log)
1211 nlwarning ("LAUNCH: Failed launched '%s' with arg '%s' return code %d", programName.c_str(), arguments.c_str(), res);
1214 return false;
1216 #endif
1218 static bool firstLaunchProgram = true;
1220 if (firstLaunchProgram)
1222 // The aim of this is to avoid defunct process.
1224 // From "man signal":
1225 //------
1226 // According to POSIX (3.3.1.3) it is unspecified what happens when SIGCHLD is set to SIG_IGN. Here
1227 // the BSD and SYSV behaviours differ, causing BSD software that sets the action for SIGCHLD to
1228 // SIG_IGN to fail on Linux.
1229 //------
1231 // But it works fine on my GNU/Linux so I do this because it's easier :) and I don't know exactly
1232 // what to do to be portable.
1233 signal(SIGCHLD, SIG_IGN);
1235 firstLaunchProgram = false;
1238 // convert one arg into several args
1239 vector<string> args;
1240 explodeArguments(arguments, args);
1242 // Store the size of each arg
1243 vector<char *> argv(args.size()+2);
1244 uint i = 0;
1245 argv[i] = (char *)programName.c_str();
1246 for (; i < args.size(); i++)
1248 argv[i+1] = (char *) args[i].c_str();
1250 argv[i+1] = NULL;
1252 int status = vfork ();
1253 /////////////////////////////////////////////////////////
1254 // WARNING : NO MORE INSTRUCTION AFTER VFORK !
1255 // READ VFORK manual
1256 /////////////////////////////////////////////////////////
1257 if (status == -1)
1259 char *err = strerror (errno);
1260 if (log)
1261 nlwarning("LAUNCH: Failed launched '%s' with arg '%s' err %d: '%s'", programName.c_str(), arguments.c_str(), errno, err);
1263 else if (status == 0)
1265 // Exec (the only allowed instruction after vfork)
1266 status = execvp(programName.c_str(), &argv.front());
1268 if (status == -1)
1270 perror("Failed launched");
1271 _exit(EXIT_FAILURE);
1274 else
1276 //nldebug("LAUNCH: Successful launch '%s' with arg '%s'", programName.c_str(), arguments.c_str());
1278 return true;
1280 #endif
1282 return false;
1285 bool launchProgramArray (const std::string &programName, const std::vector<std::string> &arguments, bool log)
1287 #ifdef NL_OS_WINDOWS
1288 PROCESS_INFORMATION pi;
1290 std::string argumentsJoined = joinArguments(arguments);
1292 if (!createProcess(programName, argumentsJoined, log, pi)) return false;
1294 //nldebug("LAUNCH: Successful launch '%s' with arg '%s'", programName.c_str(), arguments.c_str());
1295 CloseHandle( pi.hProcess );
1296 CloseHandle( pi.hThread );
1297 return true;
1298 #else
1300 #ifdef NL_OS_MAC
1301 // special OS X case with bundles
1302 if (toLowerAscii(CFile::getExtension(programName)) == "app")
1304 // we need to open bundles with "open" command
1305 std::string command = NLMISC::toString("open \"%s\"", programName.c_str());
1307 std::string argumentsJoined = joinArguments(arguments);
1309 // append arguments if any
1310 if (!argumentsJoined.empty())
1312 command += NLMISC::toString(" --args %s", argumentsJoined.c_str());
1315 int res = system(command.c_str());
1317 if (!res) return true;
1319 if (log)
1321 nlwarning ("LAUNCH: Failed launched '%s' with arg '%s' return code %d", programName.c_str(), argumentsJoined.c_str(), res);
1324 return false;
1326 #endif
1328 static bool firstLaunchProgram = true;
1330 if (firstLaunchProgram)
1332 // The aim of this is to avoid defunct process.
1334 // From "man signal":
1335 //------
1336 // According to POSIX (3.3.1.3) it is unspecified what happens when SIGCHLD is set to SIG_IGN. Here
1337 // the BSD and SYSV behaviours differ, causing BSD software that sets the action for SIGCHLD to
1338 // SIG_IGN to fail on Linux.
1339 //------
1341 // But it works fine on my GNU/Linux so I do this because it's easier :) and I don't know exactly
1342 // what to do to be portable.
1343 signal(SIGCHLD, SIG_IGN);
1345 firstLaunchProgram = false;
1348 // Store the size of each arg
1349 vector<char *> argv(arguments.size()+2);
1350 uint i = 0;
1351 argv[i] = (char *)programName.c_str();
1352 for (; i < arguments.size(); i++)
1354 argv[i+1] = (char *) arguments[i].c_str();
1356 argv[i+1] = NULL;
1358 int status = vfork ();
1359 /////////////////////////////////////////////////////////
1360 // WARNING : NO MORE INSTRUCTION AFTER VFORK !
1361 // READ VFORK manual
1362 /////////////////////////////////////////////////////////
1363 if (status == -1)
1365 char *err = strerror (errno);
1366 if (log)
1367 nlwarning("LAUNCH: Failed launched '%s' with arg '%s' err %d: '%s'", programName.c_str(), joinArguments(arguments).c_str(), errno, err);
1369 else if (status == 0)
1371 // Exec (the only allowed instruction after vfork)
1372 status = execvp(programName.c_str(), &argv.front());
1374 if (status == -1)
1376 perror("Failed launched");
1377 _exit(EXIT_FAILURE);
1380 else
1382 //nldebug("LAUNCH: Successful launch '%s' with arg '%s'", programName.c_str(), arguments.c_str());
1384 return true;
1386 #endif
1388 return false;
1391 sint launchProgramAndWaitForResult(const std::string &programName, const std::string &arguments, bool log)
1393 #ifdef NL_OS_WINDOWS
1394 PROCESS_INFORMATION pi;
1396 if (!createProcess(programName, arguments, log, pi)) return -1;
1398 // Successfully created the process. Wait for it to finish.
1399 DWORD ret = WaitForSingleObject(pi.hProcess, INFINITE);
1401 if (ret == WAIT_OBJECT_0)
1403 // Get the exit code.
1404 DWORD exitCode = 0;
1405 BOOL ok = GetExitCodeProcess(pi.hProcess, &exitCode);
1407 //nldebug("LAUNCH: Successful launch '%s' with arg '%s'", programName.c_str(), arguments.c_str());
1408 CloseHandle(pi.hProcess);
1409 CloseHandle(pi.hThread);
1411 if (ok) return (sint)exitCode;
1414 if (log)
1416 std::string error = toString((uint)ret);
1418 if (ret == WAIT_FAILED)
1420 error += "(" + formatErrorMessage(getLastError()) +")";
1423 nlwarning("LAUNCH: Failed launched '%s' with arg '%s' and error: %s", programName.c_str(), arguments.c_str(), error.c_str());
1426 return -1;
1427 #else
1428 // program name is the only required string
1429 std::string command = programName;
1431 // only appends arguments if any
1432 if (!arguments.empty()) command += " " + arguments;
1434 // execute the command
1435 sint res = system(command.c_str());
1437 if (res && log)
1438 nlwarning ("LAUNCH: Failed launched '%s' with arg '%s' return code %d", programName.c_str(), arguments.c_str(), res);
1440 return res;
1441 #endif
1444 std::string getCommandOutput(const std::string &command)
1446 FILE *pipe = popen(command.c_str(), "r");
1448 if (!pipe) return "";
1450 char buffer[MAX_LINE_WIDTH];
1451 std::string result;
1453 while (!feof(pipe))
1455 if (fgets(buffer, MAX_LINE_WIDTH, pipe) != NULL) result += buffer;
1458 pclose(pipe);
1460 return result;
1463 std::string expandEnvironmentVariables(const std::string &s)
1465 size_t len = s.length();
1466 std::string ret;
1468 std::string::size_type pos1 = 0, pos2 = 0;
1470 // look for environement variables delimiters
1471 while(pos2 < len && (pos1 = s.find_first_of("%$", pos2)) != std::string::npos)
1473 // copy string unprocessed part
1474 ret += s.substr(pos2, pos1-pos2);
1476 // extract a valid variable name (a-zA-Z0-9_)
1477 pos2 = pos1+1;
1479 while(pos2 < len && (isalnum(s[pos2]) || s[pos2] == '_')) ++pos2;
1481 // check if variable name is empty
1482 bool found = pos2 > pos1+1;
1484 std::string name;
1486 if (found)
1488 // found at least 1 character
1489 name = s.substr(pos1+1, pos2-pos1-1);
1492 // Windows format needs a trailing % delimiter
1493 if (found && s[pos1] == '%')
1495 if (pos2 >= len || s[pos2] != '%')
1497 // not a variable name, because no trailing %
1498 found = false;
1500 else
1502 // found a trailing %, next character to check
1503 ++pos2;
1507 // get variable value if name found
1508 if (found)
1510 const char *value = getenv(name.c_str());
1512 if (value)
1514 // value found
1515 ret += std::string(value);
1517 else
1519 // value not found
1520 found = false;
1521 nlwarning("Environment variable '%s' not found, won't be replaced", name.c_str());
1525 if (!found)
1527 // variable or value not found, don't evaluate variable
1528 ret += s.substr(pos1, pos2-pos1);
1532 // copy last unprocessed part
1533 ret += s.substr(pos2);
1535 return ret;
1538 bool explodeArguments(const std::string &str, std::vector<std::string> &args)
1540 if (str.empty()) return false;
1542 std::string::size_type pos1 = 0, pos2 = 0;
1546 // Look for the first non space character
1547 pos1 = str.find_first_not_of (" ", pos2);
1548 if (pos1 == std::string::npos) break;
1550 // Look for the first space or "
1551 pos2 = str.find_first_of (" \"", pos1);
1552 if (pos2 != std::string::npos)
1554 // " ?
1555 if (str[pos2] == '"')
1557 // Look for the final \"
1558 pos2 = str.find_first_of ("\"", pos2+1);
1559 if (pos2 != std::string::npos)
1561 // Look for the first space
1562 pos2 = str.find_first_of (" ", pos2+1);
1567 // Compute the size of the string to extract
1568 std::string::difference_type length = (pos2 != std::string::npos) ? pos2-pos1 : std::string::npos;
1570 std::string tmp = str.substr (pos1, length);
1572 // remove escape " from argument
1573 if (tmp.length() > 1 && tmp[0] == '"' && tmp[tmp.length()-1] == '"') tmp = tmp.substr(1, tmp.length()-2);
1575 args.push_back (tmp);
1577 while(pos2 != std::string::npos);
1579 return true;
1582 std::string joinArguments(const std::vector<std::string> &args)
1584 std::string res;
1586 for(uint i = 0, len = (uint)args.size(); i < len; ++i)
1588 const std::string &arg = args[i];
1590 // prepend space
1591 if (!res.empty()) res += " ";
1593 // escape only if spaces or empty argument
1594 if (arg.empty() || arg.find(' ') != std::string::npos)
1596 res += "\"" + arg + "\"";
1598 else
1600 res += arg;
1604 return res;
1607 std::string escapeArgument(const std::string &arg)
1609 #ifdef NL_OS_WINDOWS
1610 // we can't escape %VARIABLE% on command-line under Windows
1611 return arg;
1612 #else
1613 // characters to escape, only " and $ (to prevent a $something replaced by an environment variable)
1614 static const char s_charsToEscape[] = "\"$";
1616 std::string res;
1617 std::string::size_type pos = 0, lastPos = 0;
1619 // to avoid reallocations
1620 res.reserve(arg.size() * 2);
1622 while ((pos = arg.find_first_of(s_charsToEscape, lastPos)) != std::string::npos)
1624 // add previous part
1625 res += arg.substr(lastPos, pos - lastPos);
1627 // not already escaped
1628 if (!pos || arg[pos - 1] != '\\') res += '\\';
1630 // add escaped character
1631 res += arg[pos];
1633 lastPos = pos+1;
1636 res += arg.substr(lastPos);
1638 return res;
1639 #endif
1643 * Display the bits (with 0 and 1) composing a byte (from right to left)
1645 void displayByteBits( uint8 b, uint nbits, sint beginpos, bool displayBegin, NLMISC::CLog *log )
1647 string s1, s2;
1648 sint i;
1649 for ( i=nbits-1; i!=-1; --i )
1651 s1 += ( (b >> i) & 1 ) ? '1' : '0';
1653 log->displayRawNL( "%s", s1.c_str() );
1654 if ( displayBegin )
1656 for ( i=nbits; i>beginpos+1; --i )
1658 s2 += " ";
1660 s2 += "^";
1661 log->displayRawNL( "%s beginpos=%u", s2.c_str(), beginpos );
1666 //#define displayDwordBits(a,b,c)
1669 * Display the bits (with 0 and 1) composing a number (uint32) (from right to left)
1671 void displayDwordBits( uint32 b, uint nbits, sint beginpos, bool displayBegin, NLMISC::CLog *log )
1673 string s1, s2;
1674 sint i;
1675 for ( i=nbits-1; i!=-1; --i )
1677 s1 += ( (b >> i) & 1 ) ? '1' : '0';
1679 log->displayRawNL( "%s", s1.c_str() );
1680 if ( displayBegin )
1682 for ( i=nbits; i>beginpos+1; --i )
1684 s2 += " ";
1686 s2 += "^";
1687 log->displayRawNL( "%s beginpos=%u", s2.c_str(), beginpos );
1691 FILE* nlfopen(const std::string &filename, const std::string &mode)
1693 #ifdef NL_OS_WINDOWS
1694 return _wfopen(nlUtf8ToWide(filename), nlUtf8ToWide(mode));
1695 #else
1696 return fopen(filename.c_str(), mode.c_str());
1697 #endif
1700 int nlfseek64( FILE *stream, sint64 offset, int origin )
1702 #ifdef NL_OS_WINDOWS
1705 fpos_t pos64 = 0;
1706 switch (origin)
1708 case SEEK_CUR:
1709 if (fgetpos(stream, &pos64) != 0)
1710 return -1;
1711 case SEEK_END:
1712 pos64 = _filelengthi64(_fileno(stream));
1713 if (pos64 == -1L)
1714 return -1;
1717 // Seek
1718 pos64 += offset;
1720 // Set the final position
1721 return fsetpos (stream, &pos64);
1723 #else // NL_OS_WINDOWS
1724 // TODO: to fix for Linux and Mac OS X
1726 // This code doesn't work under windows : fseek() implementation uses a signed 32 bits offset. What ever we do, it can't seek more than 2 Go.
1727 // For the moment, i don't know if it works under linux for seek of more than 2 Go.
1729 nlassert ((offset < SINT64_CONSTANT(2147483647)) && (offset > SINT64_CONSTANT(-2147483648)));
1731 bool first = true;
1734 // Get the size of the next fseek
1735 sint nextSeek;
1736 if (offset > 0)
1737 nextSeek = (sint)std::min ((sint64)SINT64_CONSTANT(2147483647), offset);
1738 else
1739 nextSeek = (sint)std::max ((sint64)-SINT64_CONSTANT(2147483648), offset);
1741 // Make a seek
1742 int result = fseek ( stream, nextSeek, first?origin:SEEK_CUR );
1743 if (result != 0)
1744 return result;
1746 // Remaining
1747 offset -= nextSeek;
1748 first = false;
1750 while (offset);
1752 return 0;
1754 #endif // NL_OS_WINDOWS
1757 sint64 nlftell64(FILE *stream)
1759 #ifdef NL_OS_WINDOWS
1760 fpos_t pos64 = 0;
1761 if (fgetpos(stream, &pos64) == 0)
1763 return (sint64) pos64;
1765 else return -1;
1766 #else
1767 nlunreferenced(stream);
1769 // TODO: implement for Linux and Mac OS X
1770 nlerror("Not implemented");
1771 return -1;
1772 #endif
1776 //////////////////////////////////////////////////////////////////////////
1777 //////////////////////////////////////////////////////////////////////////
1778 /// Commands
1779 //////////////////////////////////////////////////////////////////////////
1780 //////////////////////////////////////////////////////////////////////////
1782 NLMISC_CATEGORISED_COMMAND(nel, sleep, "Freeze the service for N seconds (for debug purpose)", "<N>")
1784 nlunreferenced(rawCommandString);
1785 nlunreferenced(quiet);
1786 nlunreferenced(human);
1788 if(args.size() != 1) return false;
1790 sint32 n;
1791 fromString(args[0], n);
1793 log.displayNL ("Sleeping during %d seconds", n);
1795 nlSleep(n * 1000);
1796 return true;
1799 NLMISC_CATEGORISED_COMMAND(nel, system, "Execute the command line using system() function call (wait until the end of the command)", "<commandline>")
1801 nlunreferenced(rawCommandString);
1802 nlunreferenced(quiet);
1803 nlunreferenced(human);
1805 if(args.size() != 1) return false;
1807 string cmd = args[0];
1808 log.displayNL ("Executing '%s'", cmd.c_str());
1809 sint error = system(cmd.c_str());
1810 if (error)
1812 log.displayNL ("Execution of '%s' failed with error code %d", cmd.c_str(), error);
1814 else
1816 log.displayNL ("End of Execution of '%s'", cmd.c_str());
1818 return true;
1821 NLMISC_CATEGORISED_COMMAND(nel, launchProgram, "Execute the command line using launcProgram() function call (launch in background task without waiting the end of the execution)", "<programName> <arguments>")
1823 nlunreferenced(rawCommandString);
1824 nlunreferenced(quiet);
1825 nlunreferenced(human);
1827 if(args.size() != 2) return false;
1829 string cmd = args[0];
1830 string arg = args[1];
1831 log.displayNL ("Executing '%s' with argument '%s'", cmd.c_str(), arg.c_str());
1832 launchProgram(cmd, arg);
1833 log.displayNL ("End of Execution of '%s' with argument '%s'", cmd.c_str(), arg.c_str());
1834 return true;
1837 NLMISC_CATEGORISED_COMMAND(nel, killProgram, "kill a program given the pid", "<pid>")
1839 nlunreferenced(rawCommandString);
1840 nlunreferenced(quiet);
1841 nlunreferenced(human);
1843 if(args.size() != 1) return false;
1844 uint32 pid;
1845 fromString(args[0], pid);
1846 killProgram(pid);
1847 return true;
1850 #ifdef NL_OS_WINDOWS
1851 LONG GetRegKey(HKEY key, LPCWSTR subkey, LPWSTR retdata)
1853 HKEY hkey;
1854 LONG retval = RegOpenKeyExW(key, subkey, 0, KEY_QUERY_VALUE, &hkey);
1856 if (retval == ERROR_SUCCESS)
1858 long datasize = MAX_PATH;
1859 wchar_t data[MAX_PATH];
1860 RegQueryValueW(hkey, NULL, data, &datasize);
1861 lstrcpyW(retdata, data);
1862 RegCloseKey(hkey);
1865 return retval;
1867 #endif // NL_OS_WINDOWS
1869 static bool openDocWithExtension (const std::string &document, const std::string &ext)
1871 #ifdef NL_OS_WINDOWS
1872 // First try ShellExecute()
1873 HINSTANCE result = ShellExecuteW(NULL, L"open", nlUtf8ToWide(document), NULL, NULL, SW_SHOWDEFAULT);
1875 // If it failed, get the .htm regkey and lookup the program
1876 if ((uintptr_t)result <= HINSTANCE_ERROR)
1878 wchar_t key[MAX_PATH + MAX_PATH];
1880 // get the type of the extension
1881 if (GetRegKey(HKEY_CLASSES_ROOT, nlUtf8ToWide("." + ext), key) == ERROR_SUCCESS)
1883 lstrcatW(key, L"\\shell\\open\\command");
1885 // get the command used to open a file with this extension
1886 if (GetRegKey(HKEY_CLASSES_ROOT, key, key) == ERROR_SUCCESS)
1888 std::string program = wideToUtf8(key);
1890 // empty program
1891 if (program.empty()) return false;
1893 if (program[0] == '"')
1895 // program is quoted
1896 std::string::size_type pos = program.find('"', 1);
1898 if (pos != std::string::npos)
1900 // take part before next quote
1901 program = program.substr(1, pos - 1);
1904 else
1906 // program has a parameter
1907 std::string::size_type pos = program.find(' ', 1);
1909 if (pos != std::string::npos)
1911 // take part before first space
1912 program = program.substr(0, pos);
1916 // create process
1917 PROCESS_INFORMATION pi;
1918 return createProcess(program, document, false, pi);
1922 else
1924 return true;
1926 #elif defined(NL_OS_MAC)
1927 CFURLRef url = CFURLCreateWithBytes(NULL, (const UInt8 *)document.c_str(), document.length(), kCFStringEncodingUTF8, NULL);
1929 if (url)
1931 OSStatus res = LSOpenCFURLRef(url, 0);
1932 CFRelease(url);
1934 if (res != 0)
1936 nlwarning("LSOpenCFURLRef %s returned %d", document.c_str(), (sint)res);
1937 return false;
1940 return true;
1942 else
1944 nlwarning("Unable to create URL from %s", document.c_str());
1945 return false;
1947 #else
1948 std::string command = "/usr/bin/xdg-open";
1950 if (!CFile::fileExists(command))
1952 if (ext == "htm")
1954 command = "/etc/alternatives/x-www-browser";
1956 if (!CFile::fileExists(command))
1958 command.clear();
1961 else
1963 command.clear();
1967 if (command.empty())
1969 nlwarning("Unable to open %s", document.c_str());
1970 return false;
1973 // save LD_LIBRARY_PATH
1974 const char *previousEnv = getenv("LD_LIBRARY_PATH");
1976 // clear LD_LIBRARY_PATH to avoid problems with Steam Runtime
1977 if (previousEnv) setenv("LD_LIBRARY_PATH", "", 1);
1979 bool res = launchProgram(command, document);
1981 // restore previous LD_LIBRARY_PATH
1982 if (previousEnv) setenv("LD_LIBRARY_PATH", previousEnv, 1);
1984 return res;
1985 #endif // NL_OS_WINDOWS
1987 return false;
1990 bool openURL(const std::string &url)
1992 return openDocWithExtension(url, "htm");
1995 bool openDoc(const std::string &document)
1997 // get extension from document fullpath
1998 string ext = CFile::getExtension(document);
2000 // try to open document
2001 return openDocWithExtension(document, ext);
2004 } // NLMISC