1 //                                               -*- C++ -*-
2 /**
3  *  @brief This class provides operating system specific variables
4  *
5  *  Copyright 2005-2021 Airbus-EDF-IMACS-ONERA-Phimeca
6  *
7  *  This library is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This library 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 Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with this library.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include "openturns/OTwindows.h" // For CreateProcess
23 #include "openturns/Os.hxx"
24 #include "openturns/Log.hxx"
25 #include "openturns/OSS.hxx"
26 #include "openturns/ResourceMap.hxx" // For ResourceMap
27 
28 // include OTConfig that defines OPENTURNS_HAVE_XXX
29 #include "openturns/OTconfig.hxx"
30 
31 #ifdef OPENTURNS_HAVE_UNISTD_H
32 # include <unistd.h>  // for rmdir, unlink
33 #endif
34 
35 #include <cstdlib>   // for system(3)
36 
37 #ifdef OPENTURNS_HAVE_SYS_TYPES_H
38 # include <sys/types.h>            // for stat
39 #endif
40 
41 #ifdef OPENTURNS_HAVE_SYS_STAT_H
42 # include <sys/stat.h>             // for stat
43 #endif
44 
45 #include <fcntl.h>
46 #ifndef _WIN32
47 #include <ftw.h>       // for stat(2)
48 #endif
49 
50 #ifdef _MSC_VER
51 # include <direct.h>
52 # define MKDIR(p, mode)  _mkdir(p)
53 # if !defined(S_ISDIR)
54 #  define S_ISDIR(mode) (((mode) & S_IFDIR) != 0)
55 # endif
56 # if !defined(S_ISREG)
57 #  define S_ISREG(mode) (((mode) & S_IFREG) != 0)
58 # endif
59 #elif defined(_WIN32)
60 # define MKDIR(p, mode)  mkdir(p)
61 #else
62 # define MKDIR(p, mode)  mkdir(p, mode)
63 #endif
64 
65 BEGIN_NAMESPACE_OPENTURNS
66 
GetDirectorySeparator()67 const char * Os::GetDirectorySeparator()
68 {
69 #ifndef _WIN32
70   return "/";
71 #else
72   return "\\";
73 #endif
74 }
75 
GetDirectoryListSeparator()76 const char * Os::GetDirectoryListSeparator()
77 {
78 #ifndef _WIN32
79   return ":";
80 #else
81   return ";";
82 #endif
83 }
84 
GetEndOfLine()85 const char * Os::GetEndOfLine()
86 {
87   return "\n";
88 }
89 
GetDeleteCommandOutput()90 String Os::GetDeleteCommandOutput()
91 {
92 #ifndef _WIN32
93   return " > /dev/null 2>&1";
94 #else
95   return " > NUL";
96 #endif
97 }
98 
99 // Returns 0 if no error
ExecuteCommand(const String & command)100 int Os::ExecuteCommand(const String & command)
101 {
102   int rc = -1;
103   LOGINFO( OSS() << "Execute command=" << command );
104 #ifdef _WIN32
105   if ( ResourceMap::GetAsBool("Os-CreateProcess"))
106   {
107     // Startup information
108     STARTUPINFO si;
109     ZeroMemory(&si, sizeof(si));
110     si.cb = sizeof(si);
111     si.dwFlags = STARTF_USESHOWWINDOW;
112     si.wShowWindow = SW_HIDE;
113 
114     // Process information
115     PROCESS_INFORMATION pi;
116     ZeroMemory(&pi, sizeof(pi));
117 
118     // Create the process
119     DWORD dwProcessFlags = 0;
120     char * cmd = strdup(command.c_str());
121     const Bool processOk = CreateProcess(NULL, cmd, NULL, NULL, true, dwProcessFlags, NULL, NULL, &si, &pi) != 0;
122     free(cmd);
123     if ( processOk )
124     {
125       // Wait for the external application to finish
126       DWORD waitRc = WaitForSingleObject(pi.hProcess, INFINITE);
127       if ( waitRc != WAIT_FAILED )
128       {
129         DWORD exit_code = 0;
130         const Bool codeOk = GetExitCodeProcess(pi.hProcess, &exit_code) != 0;
131         if (codeOk)
132         {
133           rc = exit_code;
134         }
135         else
136         {
137           rc = GetLastError();
138         }
139       }
140 
141       // Close everything
142       CloseHandle(pi.hProcess);
143       CloseHandle(pi.hThread);
144     }
145     else
146     {
147       rc = GetLastError();
148     }
149   } // use create process
150   else
151 #endif
152   {
153     rc = system(command.c_str());
154   }
155   LOGINFO( OSS() << "Return code=" << rc << " for command=" << command );
156   return rc;
157 }
158 
159 
Remove(const String & fileName)160 void Os::Remove(const String& fileName)
161 {
162   if (!ResourceMap::GetAsBool("Os-RemoveFiles")) return;
163   if (remove(fileName.c_str()) == -1)
164   {
165     Log::Warn(OSS() << "Warning: cannot remove file " << fileName);
166   }
167 }
168 
169 // Function helper for Os::MakeDirectory: replace backslash by slash
170 static void
convert_backslashes(String & path)171 convert_backslashes(String & path)
172 {
173 #ifdef _WIN32
174   const char* current_char = path.c_str();
175   String::size_type pos = 0;
176   // On Windows, leading \\ is for network paths and must not be stripped
177   if (*current_char == '\\' && *(current_char + 1) == '\\')
178   {
179     pos = 2;
180     current_char += pos;
181   }
182   for ( ; *current_char != '\0'; ++pos, ++current_char )
183   {
184     if (*current_char == '\\') path[pos] = '/';
185   }
186 #else
187   (void) path;
188 #endif
189 }
190 
IsDirectory(const String & fileName)191 Bool Os::IsDirectory(const String & fileName)
192 {
193   struct stat dir_stat;
194   if(stat(fileName.c_str(), &dir_stat) != 0) return false;
195   return S_ISDIR(dir_stat.st_mode);
196 }
197 
IsFile(const String & fileName)198 Bool Os::IsFile(const String & fileName)
199 {
200   struct stat dir_stat;
201   if(stat(fileName.c_str(), &dir_stat) != 0) return false;
202   return S_ISREG(dir_stat.st_mode);
203 }
204 
205 // Returns 0 if no error
MakeDirectory(const String & path)206 int Os::MakeDirectory(const String & path)
207 {
208   if (path.empty()) return 1;
209   if (IsDirectory(path)) return 0;
210 
211   String slashPath(path);
212   convert_backslashes(slashPath);
213 
214   String::size_type pos = 0;
215   while((pos = slashPath.find('/', pos)) != String::npos)
216   {
217     String current_dir(path.substr(0, pos));
218     const char * cpath = current_dir.c_str();
219     if (!IsDirectory(current_dir) && (0 != MKDIR(cpath, 0777))) return 1;
220     pos++;
221   }
222 
223   return 0;
224 }
225 
226 #ifndef _WIN32
deleteRegularFileOrDirectory(const char * path,const struct stat *,int typeflag,struct FTW *)227 static int deleteRegularFileOrDirectory(const char * path,
228                                         const struct stat *,
229                                         int typeflag,
230                                         struct FTW * )
231 {
232   int rc;
233 
234   switch (typeflag)
235   {
236     case FTW_DP:
237       rc = rmdir( path );
238       if ( rc < 0 ) return 1;
239       break;
240 
241     case FTW_SL:
242     case FTW_SLN:
243     case FTW_F:
244       rc = unlink( path );
245       if ( rc < 0 ) return 1;
246       break;
247 
248   } /* end switch */
249 
250   return 0;
251 }
252 #endif /* !WIN32 */
253 
254 
255 
256 // Delete a directory and its contents recursively. Returns 0 if no error
DeleteDirectory(const String & path)257 int Os::DeleteDirectory(const String & path)
258 {
259   if (path.empty()) return 1;
260   if (!IsDirectory(path)) return 1;
261 
262   // Refuse to delete root directory (/) and current directory (.)
263   if (path == "/" || path == ".") return 1;
264 
265   const char * directory = path.c_str();
266 #ifdef _WIN32
267   if ( ((strlen( directory ) == 3) && (directory[1] == ':') && (directory[2] == '\\' || directory[2] == '/')) ||
268        ((strlen( directory ) == 2) && (directory[1] == ':')) )
269   {
270     // do not delete root directory
271     return 1;
272   }
273 #endif
274 
275   int rc = 0;
276 
277 #ifndef _WIN32
278 
279   rc = nftw(directory, deleteRegularFileOrDirectory, 20, FTW_DEPTH);
280 
281 #else /* WIN32 */
282 
283   UnsignedInteger countdown = ResourceMap::GetAsUnsignedInteger("OS-DeleteTimeout");
284   const String rmdirCmd("rmdir /Q /S \"" + path + "\"" + " > NUL 2>&1");
285   Bool directoryExists = true;
286 
287   do
288   {
289     rc = system(rmdirCmd.c_str());
290 
291     // check if directory still there (rmdir dos command always return 0)
292     directoryExists = IsDirectory(path);
293     if (directoryExists)
294     {
295       if (countdown == 0) return 1;
296       -- countdown;
297     }
298     Sleep(1000);
299   }
300   while (directoryExists);
301 
302 #endif /* WIN32 */
303 
304   return rc;
305 }
306 
307 
308 END_NAMESPACE_OPENTURNS
309