#include "portable.h" #include #include #if defined(_WIN32) && !defined(__CYGWIN__) #undef UNICODE #define _WIN32_DCOM #include #else #include #include #include #include extern char **environ; #endif #include #include #include #include #include #include #include #include "util.h" #ifndef NODEBUG #include "debug.h" #endif #if !defined(_WIN32) || defined(__CYGWIN__) static bool environmentLoaded = false; static std::map proc_env = std::map(); #endif static double g_sysElapsedTime; static QTime g_time; int Portable::system(const char *command,const char *args,bool commandHasConsole) { if (command==0) return 1; #if defined(_WIN32) && !defined(__CYGWIN__) QCString commandCorrectedPath = substitute(command,'/','\\'); QCString fullCmd=commandCorrectedPath; #else QCString fullCmd=command; #endif fullCmd=fullCmd.stripWhiteSpace(); if (fullCmd.at(0)!='"' && fullCmd.find(' ')!=-1) { // add quotes around command as it contains spaces and is not quoted already fullCmd="\""+fullCmd+"\""; } fullCmd += " "; fullCmd += args; #ifndef NODEBUG Debug::print(Debug::ExtCmd,0,"Executing external command `%s`\n",qPrint(fullCmd)); #endif #if !defined(_WIN32) || defined(__CYGWIN__) (void)commandHasConsole; /*! taken from the system() manpage on my Linux box */ int pid,status=0; #ifdef _OS_SOLARIS // for Solaris we use vfork since it is more memory efficient // on Solaris fork() duplicates the memory usage // so we use vfork instead // spawn shell if ((pid=vfork())<0) { status=-1; } else if (pid==0) { execl("/bin/sh","sh","-c",fullCmd.data(),(char*)0); _exit(127); } else { while (waitpid(pid,&status,0 )<0) { if (errno!=EINTR) { status=-1; break; } } } return status; #else // Other Unices just use fork pid = fork(); if (pid==-1) { perror("fork error"); return -1; } if (pid==0) { const char * argv[4]; argv[0] = "sh"; argv[1] = "-c"; argv[2] = fullCmd.data(); argv[3] = 0; execve("/bin/sh",(char * const *)argv,environ); exit(127); } for (;;) { if (waitpid(pid,&status,0)==-1) { if (errno!=EINTR) return -1; } else { if (WIFEXITED(status)) { return WEXITSTATUS(status); } else { return status; } } } #endif // !_OS_SOLARIS #else // Win32 specific if (commandHasConsole) { return ::system(fullCmd); } else { // Because ShellExecuteEx can delegate execution to Shell extensions // (data sources, context menu handlers, verb implementations) that // are activated using Component Object Model (COM), COM should be // initialized before ShellExecuteEx is called. Some Shell extensions // require the COM single-threaded apartment (STA) type. // For that case COM is initialized as follows CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); QString commandw = QString::fromUtf8( commandCorrectedPath ); QString argsw = QString::fromUtf8( args ); // gswin32 is a GUI api which will pop up a window and run // asynchronously. To prevent both, we use ShellExecuteEx and // WaitForSingleObject (thanks to Robert Golias for the code) SHELLEXECUTEINFOW sInfo = { sizeof(SHELLEXECUTEINFOW), /* structure size */ SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI, /* tell us the process * handle so we can wait till it's done | * do not display msg box if error */ NULL, /* window handle */ NULL, /* action to perform: open */ (LPCWSTR)commandw.ucs2(), /* file to execute */ (LPCWSTR)argsw.ucs2(), /* argument list */ NULL, /* use current working dir */ SW_HIDE, /* minimize on start-up */ 0, /* application instance handle */ NULL, /* ignored: id list */ NULL, /* ignored: class name */ NULL, /* ignored: key class */ 0, /* ignored: hot key */ NULL, /* ignored: icon */ NULL /* resulting application handle */ }; if (!ShellExecuteExW(&sInfo)) { return -1; } else if (sInfo.hProcess) /* executable was launched, wait for it to finish */ { WaitForSingleObject(sInfo.hProcess,INFINITE); /* get process exit code */ DWORD exitCode; if (!GetExitCodeProcess(sInfo.hProcess,&exitCode)) { exitCode = -1; } CloseHandle(sInfo.hProcess); return exitCode; } } #endif return 1; // we should never get here } unsigned int Portable::pid() { unsigned int pid; #if !defined(_WIN32) || defined(__CYGWIN__) pid = (unsigned int)getpid(); #else pid = (unsigned int)GetCurrentProcessId(); #endif return pid; } #if !defined(_WIN32) || defined(__CYGWIN__) void loadEnvironment() { if(environ != NULL) { unsigned int i = 0; char* current = environ[i]; while(current != NULL) // parse all strings contained by environ til the last element (NULL) { std::string env_var(current); // load current environment variable string size_t pos = env_var.find("="); if(pos != std::string::npos) // only parse the variable, if it is a valid environment variable... { // ...which has to contain an equal sign as delimiter by definition std::string name = env_var.substr(0,pos); // the string til the equal sign contains the name std::string value = env_var.substr(pos + 1); // the string from the equal sign contains the value proc_env[name] = value; // save the value by the name as its key in the classes map } i++; current = environ[i]; } } environmentLoaded = true; } #endif void Portable::setenv(const char *name,const char *value) { if (value==0) value=""; #if defined(_WIN32) && !defined(__CYGWIN__) SetEnvironmentVariable(name,value); #else if(!environmentLoaded) // if the environment variables are not loaded already... { // ...call loadEnvironment to store them in class loadEnvironment(); } proc_env[name] = std::string(value); // create or replace existing value #endif } void Portable::unsetenv(const char *variable) { #if defined(_WIN32) && !defined(__CYGWIN__) SetEnvironmentVariable(variable,0); #else /* Some systems don't have unsetenv(), so we do it ourselves */ if (variable == NULL || *variable == '\0' || strchr (variable, '=') != NULL) { return; // not properly formatted } if(proc_env.find(variable) != proc_env.end()) { proc_env[variable].erase(); } #endif } const char *Portable::getenv(const char *variable) { #if defined(_WIN32) && !defined(__CYGWIN__) return ::getenv(variable); #else if(!environmentLoaded) // if the environment variables are not loaded already... { // ...call loadEnvironment to store them in class loadEnvironment(); } if(proc_env.find(variable) != proc_env.end()) { return proc_env[variable].c_str(); } else { return NULL; } #endif } portable_off_t Portable::fseek(FILE *f,portable_off_t offset, int whence) { #if defined(__MINGW32__) return fseeko64(f,offset,whence); #elif defined(_WIN32) && !defined(__CYGWIN__) return _fseeki64(f,offset,whence); #else return fseeko(f,offset,whence); #endif } portable_off_t Portable::ftell(FILE *f) { #if defined(__MINGW32__) return ftello64(f); #elif defined(_WIN32) && !defined(__CYGWIN__) return _ftelli64(f); #else return ftello(f); #endif } FILE *Portable::fopen(const char *fileName,const char *mode) { #if defined(_WIN32) && !defined(__CYGWIN__) QString fn(fileName); QString m(mode); return _wfopen((wchar_t*)fn.ucs2(),(wchar_t*)m.ucs2()); #else return ::fopen(fileName,mode); #endif } char Portable::pathSeparator() { #if defined(_WIN32) && !defined(__CYGWIN__) return '\\'; #else return '/'; #endif } char Portable::pathListSeparator() { #if defined(_WIN32) && !defined(__CYGWIN__) return ';'; #else return ':'; #endif } static bool ExistsOnPath(const char *fileName) { QFileInfo fi1(fileName); if (fi1.exists()) return true; const char *p = Portable::getenv("PATH"); char listSep = Portable::pathListSeparator(); char pathSep = Portable::pathSeparator(); QCString paths(p); int strt = 0; int idx; while ((idx = paths.find(listSep,strt)) != -1) { QCString locFile(paths.mid(strt,idx-strt)); locFile += pathSep; locFile += fileName; QFileInfo fi(locFile); if (fi.exists()) return true; strt = idx + 1; } // to be sure the last path component is checked as well QCString locFile(paths.mid(strt)); if (!locFile.isEmpty()) { locFile += pathSep; locFile += fileName; QFileInfo fi(locFile); if (fi.exists()) return true; } return false; } bool Portable::checkForExecutable(const char *fileName) { #if defined(_WIN32) && !defined(__CYGWIN__) char *extensions[] = {".bat",".com",".exe"}; for (int i = 0; i < sizeof(extensions) / sizeof(*extensions); i++) { if (ExistsOnPath(QCString(fileName) + extensions[i])) return true; } return false; #else return ExistsOnPath(fileName); #endif } const char *Portable::ghostScriptCommand() { #if defined(_WIN32) && !defined(__CYGWIN__) static char *gsexe = NULL; if (!gsexe) { char *gsExec[] = {"gswin32c.exe","gswin64c.exe"}; for (int i = 0; i < sizeof(gsExec) / sizeof(*gsExec); i++) { if (ExistsOnPath(gsExec[i])) { gsexe = gsExec[i]; return gsexe; } } gsexe = gsExec[0]; return gsexe; } return gsexe; #else return "gs"; #endif } const char *Portable::commandExtension() { #if defined(_WIN32) && !defined(__CYGWIN__) return ".exe"; #else return ""; #endif } bool Portable::fileSystemIsCaseSensitive() { #if defined(_WIN32) || defined(macintosh) || defined(__MACOSX__) || defined(__APPLE__) || defined(__CYGWIN__) return FALSE; #else return TRUE; #endif } FILE * Portable::popen(const char *name,const char *type) { #if defined(_MSC_VER) || defined(__BORLANDC__) return ::_popen(name,type); #else return ::popen(name,type); #endif } int Portable::pclose(FILE *stream) { #if defined(_MSC_VER) || defined(__BORLANDC__) return ::_pclose(stream); #else return ::pclose(stream); #endif } void Portable::sysTimerStart() { g_time.start(); } void Portable::sysTimerStop() { g_sysElapsedTime+=((double)g_time.elapsed())/1000.0; } double Portable::getSysElapsedTime() { return g_sysElapsedTime; } void Portable::sleep(int ms) { #if defined(_WIN32) && !defined(__CYGWIN__) Sleep(ms); #else usleep(1000*ms); #endif } bool Portable::isAbsolutePath(const char *fileName) { # ifdef _WIN32 if (isalpha (fileName [0]) && fileName[1] == ':') fileName += 2; # endif char const fst = fileName [0]; if (fst == '/') { return true; } # ifdef _WIN32 if (fst == '\\') return true; # endif return false; } /** * Correct a possible wrong PATH variable * * This routine was inspired by the cause for bug 766059 was that in the Windows path there were forward slashes. */ void Portable::correct_path() { #if defined(_WIN32) && !defined(__CYGWIN__) const char *p = Portable::getenv("PATH"); if (!p) return; // no path nothing to correct QCString result = substitute(p,'/','\\'); if (result!=p) Portable::setenv("PATH",result.data()); #endif } void Portable::unlink(const char *fileName) { #if defined(_WIN32) && !defined(__CYGWIN__) _unlink(fileName); #else ::unlink(fileName); #endif } void Portable::setShortDir() { #if defined(_WIN32) && !defined(__CYGWIN__) long length = 0; TCHAR* buffer = NULL; // First obtain the size needed by passing NULL and 0. length = GetShortPathName(QDir::currentDirPath().data(), NULL, 0); // Dynamically allocate the correct size // (terminating null char was included in length) buffer = new TCHAR[length]; // Now simply call again using same (long) path. length = GetShortPathName(QDir::currentDirPath().data(), buffer, length); // Set the correct directory (short name) QDir::setCurrent(buffer); delete [] buffer; #endif } /* Return the first occurrence of NEEDLE in HAYSTACK. */ static const char * portable_memmem (const char *haystack, size_t haystack_len, const char *needle, size_t needle_len) { const char *const last_possible = haystack + haystack_len - needle_len; if (needle_len == 0) // The first occurrence of the empty string should to occur at the beginning of the string. { return haystack; } // Sanity check if (haystack_len < needle_len) { return 0; } for (const char *begin = haystack; begin <= last_possible; ++begin) { if (begin[0] == needle[0] && !memcmp(&begin[1], needle + 1, needle_len - 1)) { return begin; } } return 0; } const char *Portable::strnstr(const char *haystack, const char *needle, size_t haystack_len) { size_t needle_len = strnlen(needle, haystack_len); if (needle_len < haystack_len || !needle[needle_len]) { const char *x = portable_memmem(haystack, haystack_len, needle, needle_len); if (x && !memchr(haystack, 0, x - haystack)) { return x; } } return 0; } const char *Portable::devNull() { #if defined(_WIN32) && !defined(__CYGWIN__) return "NUL"; #else return "/dev/null"; #endif }