[doc_tool] Moved executing of external programms from Model to FSHelper
[hsd_doku_tool.git] / src / FSHelper.cxx
1 ///////////////////////////////////////////////////////////////////////////
2 // Workfile: FSHelper.cxx
3 // Author: Daniel Giritzer <daniel@giritzer.eu>
4 // Date: 26.04.2018
5 // Description: Diese Klasse stellt plattformunabhängige funktionen auf das
6 //              Dateisystem zur verfügung. Bei fehler werden exeptions
7 //              des Typs std::string geworfen.
8 // Remarks: -
9 ///////////////////////////////////////////////////////////////////////////
10 #include "FSHelper.h"
11 #include <fstream>
12 #include <dirent.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 #include <sys/stat.h>
16 #include <cstring>
17 #include <sstream>
18 #include <algorithm>
19
20 #ifndef _WIN32
21 #include <sys/wait.h>
22 #endif
23
24 std::vector<FSHelper::File> FSHelper::listFiles(std::string const &src)
25 {
26     std::vector<File> retVal;
27     DIR *dir;
28     struct dirent *ent;
29
30     if((dir = opendir(src.c_str())) != nullptr)
31     {
32         while((ent = readdir(dir)) != nullptr)
33         {
34             File curFile;
35             curFile.first = addDirLastSlash(std::string(src));
36             curFile.second = std::string(ent->d_name);
37             retVal.push_back(curFile);
38         }
39         closedir(dir);
40     }
41     else
42         throw std::string("Directory could not be opened: " + src);
43
44     return retVal;
45 }
46
47 void FSHelper::copyFile(std::string const &src, std::string const &dest)
48 {
49     std::ifstream srcStream(src, std::ios::in | std::ios_base::binary);
50
51     if(!srcStream.good())
52         throw std::string("Failed to open input file: " + src);
53
54     std::ofstream destStream(dest, std::ios::out |std::ios_base::binary);
55
56     if(!destStream.good())
57         throw std::string("Failed to open output file: " + dest);
58
59     destStream << srcStream.rdbuf();
60
61     destStream.close();
62     srcStream.close();
63
64 }
65
66 void FSHelper::makeDir(std::string const& dir)
67 {
68     if(!dirExists(dir))
69 #if defined(_WIN32)
70         if(mkdir(dir.c_str()) < 0)
71 #else
72         if(mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0)
73 #endif
74             throw std::string("Failed to create directory: " + dir);
75
76 }
77
78 void FSHelper::createFile(std::string const& path)
79 {
80     std::ofstream file(path);
81
82     if(!file.good())
83         throw std::string("Failed to create file: " + path);
84
85     file.close();
86 }
87
88 void FSHelper::copyDir(std::string const &src, std::string const &dest)
89 {
90     std::string from = addDirLastSlash(src);
91     std::string to = addDirLastSlash(dest);
92
93     if(!dirExists(addDirLastSlash(from)))
94         throw std::string("Source folder does not exist: " + from);
95
96     try
97     {
98         makeDir(to);
99     }
100     catch(std::string& e)
101     {
102         throw std::string("Could not create destination folder: " + to);
103     }
104
105     std::vector<File> files;
106     if(!listFilesRecursive(from, files, true))
107         throw std::string("Could not fetch source folder content: " + from);
108
109     std::vector<std::string> folders;
110     if(!listDirsRecursive(from, folders, true))
111         throw std::string("Could not fetch source folder directory tree: " + from);
112
113     // Erstelle Ordnerbaum
114     for(auto folder : folders)
115     {
116         folder.replace(folder.find(from), from.length(), to);
117         try
118         {
119             makeDir(addDirLastSlash(folder));
120         }
121         catch(std::string& e)
122         {
123             throw std::string("Could not create directory tree in destination directory! Failed on: " + folder);
124         }
125
126     }
127
128     // Kopiere Dateien
129     for(auto inFile : files)
130     {
131         File outFile = inFile;
132         unsigned int idx = 0;
133
134         // ersetze Qellordner mit zielordner
135         outFile.first.replace(outFile.first.find(from), from.length(), to);
136         try
137         {
138             makeDir(addDirLastSlash(outFile.first));
139         }
140         catch(std::string& e)
141         {
142             throw std::string("Could not create subfolder in destination directory! Failed on: " + outFile.first);
143         }
144
145         try
146         {
147             copyFile(inFile.first + inFile.second, outFile.first + outFile.second);
148         }
149         catch(std::string& e)
150         {
151             throw std::string("Could not copy file: " + inFile.first + inFile.second);
152         }
153
154     }
155 }
156
157
158 bool FSHelper::validFileName(std::string const &fName)
159 {
160     char invalidChars[] = { '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '~', '`', ' ', '\\',
161                             ';', ':', '+', '=', '*', '/', '<', '>', '?', ',', '[', ']', '{', '}'
162                           };
163
164     int pos = fName.find_first_of(invalidChars, 0, sizeof(invalidChars));
165     if (pos != std::string::npos)
166         return false;
167
168     return true;
169 }
170
171 bool FSHelper::dirExists(std::string const &directory)
172 {
173     std::string dir = rmDirLastSlash(directory);
174
175     // überprüfe existenz
176     struct stat info;
177     if( stat( dir.c_str(), &info ) != 0 )
178         return false;
179     if( info.st_mode & S_IFDIR )
180         return true;
181     else
182         return false;
183 }
184
185 bool FSHelper::fileExists(std::string const &file)
186 {
187     std::ifstream f(file);
188     bool retVal = f.good();
189     f.close();
190     return retVal;
191 }
192
193 std::string FSHelper::addDirLastSlash(std::string const& dir)
194 {
195     if(dir.back() != '\\' && dir.back() != '/')
196 #if defined(_WIN32)
197         return dir + "\\";
198 #else
199         return dir + "/";
200 #endif
201     else
202         return dir;
203 }
204
205 std::string FSHelper::rmDirLastSlash(std::string const& dir)
206 {
207     if(dir.back() == '\\' || dir.back() == '/')
208         return dir.substr(0, dir.size()-1);
209     else
210         return dir;
211 }
212
213 bool FSHelper::listFilesRecursive(const std::string& path, std::vector<FSHelper::File>& files,const bool showHiddenDirs)
214 {
215     std::string dir = addDirLastSlash(path);
216     DIR *dpdf;
217     struct dirent *epdf;
218     dpdf = opendir(dir.c_str());
219     if(dpdf != nullptr)
220     {
221         while((epdf = readdir(dpdf)) != nullptr)
222         {
223             struct stat s;
224
225
226             if(stat((dir + std::string(epdf->d_name)).c_str(), &s) < 0)
227                 return false;
228
229             if(S_ISREG(s.st_mode))
230             {
231                 File curFile;
232
233                 curFile.first = std::string(addDirLastSlash(dir));
234                 curFile.second = std::string(epdf->d_name);
235                 files.push_back(curFile);
236             }
237             else if(showHiddenDirs ? (S_ISDIR(s.st_mode) && std::string(epdf->d_name) != ".." && std::string(epdf->d_name) != "." ) :
238                     (S_ISDIR(s.st_mode) && strstr(epdf->d_name,"..") == NULL && strstr(epdf->d_name,".") == NULL ))
239             {
240                 listFilesRecursive(dir+epdf->d_name,files, showHiddenDirs);
241             }
242         }
243     }
244     closedir(dpdf);
245     return true;
246 }
247
248
249 bool FSHelper::listDirsRecursive(const std::string& path, std::vector<std::string>& folders,const bool showHiddenDirs)
250 {
251     std::string dir = addDirLastSlash(path);
252     DIR *dpdf;
253     struct dirent *epdf;
254     dpdf = opendir(dir.c_str());
255     if(dpdf != nullptr)
256     {
257         while((epdf = readdir(dpdf)) != nullptr)
258         {
259             struct stat s;
260
261             if(stat((dir + std::string(epdf->d_name)).c_str(), &s) < 0)
262                 return false;
263
264             else if(showHiddenDirs ? (S_ISDIR(s.st_mode) && std::string(epdf->d_name) != ".." && std::string(epdf->d_name) != "." ) :
265                     (S_ISDIR(s.st_mode) && strstr(epdf->d_name,"..") == NULL && strstr(epdf->d_name,".") == NULL ))
266             {
267                 if(!(std::find(folders.begin(), folders.end(), dir+epdf->d_name) != folders.end()))
268                     folders.push_back(addDirLastSlash(dir+epdf->d_name));
269                 listDirsRecursive(dir+epdf->d_name,folders, showHiddenDirs);
270             }
271         }
272     }
273     closedir(dpdf);
274     return true;
275 }
276
277 std::string FSHelper::getCurrentWorkingDir()
278 {
279     char temp[PATH_MAX];
280
281     if(getcwd(temp, PATH_MAX))
282 #if defined(_WIN32)
283         return convertPathToWindows(temp);
284 #else
285         return convertPathToUnix(temp);
286 #endif
287
288     // leerer string bei fehler
289     return std::string("");
290 }
291
292 std::string FSHelper::convertPathToWindows(std::string dir)
293 {
294     std::replace(dir.begin(), dir.end(), '/', '\\');
295     return dir;
296 }
297
298 std::string FSHelper::convertPathToUnix(std::string dir)
299 {
300     std::replace(dir.begin(), dir.end(), '\\', '/');
301     return dir;
302 }
303
304 std::string FSHelper::findExecutableInPath(std::string executable)
305 {
306     std::string path = getenv("PATH");
307
308 #if defined(_WIN32)
309     char delim = ';';
310 #else
311     char delim = ':';
312 #endif
313
314     std::vector<std::string> tokens;
315     std::string token;
316     std::istringstream tokenStream(path);
317     while (std::getline(tokenStream, token, delim))
318     {
319         if(fileExists(addDirLastSlash(token) + executable))
320             return addDirLastSlash(token) + executable;
321
322         if(fileExists(addDirLastSlash(token) + executable + ".exe"))
323             return addDirLastSlash(token) + executable + ".exe";
324     }
325     throw std::string("Executable not found in PATH!: " + executable);
326 }
327
328 void FSHelper::executeCommand(const std::string& path, const std::string& command, std::vector<std::string> params, std::string log)
329 {
330
331     if((!path.empty()) && !dirExists(path))
332         throw std::string("Path does not exist: " + path);
333
334     if(!log.empty() && !fileExists(log))
335         createFile(log);
336
337     std::string execCommand = findExecutableInPath(command);
338
339 #if defined(_WIN32)
340     std::string paramString;
341
342     for(auto curParam : params)
343         paramString = curParam + " ";
344
345     if(!log.empty())
346         paramString = paramString + " >> " + log;
347
348     // Benutze System, da CreateProcess nicht POSIX standardkonform ist
349     if(!path.empty())
350     {
351         // Laufwerkbuchstaben ermitteln
352         unsigned int ind = path.find_first_of(":");
353         if(ind ==  std::string::npos)
354             throw std::string("Illegal Path! Path does not cointain drive letter! :" + path);
355
356         std::string driveLetter = path.substr(0, ind+1);
357         system(std::string(driveLetter + " && cd " + path + " && " + execCommand + " " + paramString).c_str());
358     }
359     else
360         system(std::string(execCommand + " " + paramString).c_str());
361 #else
362     // String vector zu char array
363     std::vector<char*> charParams;
364
365     if(!log.empty())
366         FILE* logFile = freopen(log.c_str(),"a",stdout);
367
368     // Falls DynLoader gesetzt ist dynloader erster parameter
369     // im array
370     if(!mLinuxDynLoader.empty())
371     {
372         char *pc = new char[mLinuxDynLoader.size()+1];
373         std::strcpy(pc, mLinuxDynLoader.c_str());
374         charParams.emplace_back(pc);
375         execCommand = mLinuxDynLoader;
376     }
377
378     // command muss auch immer im array gespeichert sein
379     char *pc = new char[findExecutableInPath(command).size()+1];
380     std::strcpy(pc, findExecutableInPath(command).c_str());
381     charParams.emplace_back(pc);
382
383     // vector zu char array convertieren
384     std::transform(params.begin(), params.end(), std::back_inserter(charParams),
385                    [](const std::string & s)
386
387     {
388         char *pc = new char[s.size()+1];
389         std::strcpy(pc, s.c_str());
390         return pc;
391         ;
392     });
393     charParams.emplace_back(nullptr); //Letztes element muss ein nullpointer sein
394
395     int status;
396     pid_t parent_pid;
397     pid_t child_pid;
398     parent_pid = getpid();
399
400     child_pid = fork();
401     switch(child_pid)
402     {
403     case -1:
404         //Fork Fehler
405         std::exit(EXIT_FAILURE);
406         break;
407
408     case 0:
409         if(!path.empty())
410             if(chdir(path.c_str())!=0)
411                 std::exit(EXIT_FAILURE);
412
413         execvp(execCommand.c_str(), &charParams[0]);
414
415         // bei fehler Char array im Child freigeben
416         for(auto curParam : charParams)
417             delete [] curParam;
418
419         // Soll nicht erreicht werden. Falls ja
420         // Child prozess beenden
421         std::exit(EXIT_FAILURE);
422         break;
423
424     default:
425         // OK
426         break;
427     }
428
429     // Auf child warten
430     wait(&status);
431
432     // Char array freigeben
433     for(auto curParam : charParams)
434         delete [] curParam;
435
436     // Logfile schliessen
437     fcloseall();
438
439     if(!WIFEXITED(status))
440         throw std::string("Could not execute command: " + command);
441 #endif
442 }
443
444
445 void FSHelper::setLinuxDynLoader(std::string Loader)
446 {
447 #ifndef _WIN32
448     if(!fileExists(Loader))
449         throw std::string("Specified Loader does not exist: " + Loader);
450 #endif
451
452     mLinuxDynLoader = Loader;
453 }
454
455
456 std::string FSHelper::getLinuxDynLoader()
457 {
458     return mLinuxDynLoader;
459 }
460
461
462 void FSHelper::removeLinuxDynLoader()
463 {
464     mLinuxDynLoader.clear();
465 }