41c7f84340a6bed95042fb6b7d31bb04b595e9d3
[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, std::ios::out);
81
82     if(!file.good())
83         throw std::string("Failed to create file: " + path);
84
85     file.close();
86 }
87
88 void FSHelper::removeFile(std::string const& path)
89 {
90     if(!fileExists(path))
91         return;
92
93     remove(path.c_str());
94 }
95
96 void FSHelper::copyDir(std::string const &src, std::string const &dest)
97 {
98     std::string from = addDirLastSlash(src);
99     std::string to = addDirLastSlash(dest);
100
101     if(!dirExists(addDirLastSlash(from)))
102         throw std::string("Source folder does not exist: " + from);
103
104     try
105     {
106         makeDir(to);
107     }
108     catch(std::string& e)
109     {
110         throw std::string("Could not create destination folder: " + to);
111     }
112
113     std::vector<File> files;
114     if(!listFilesRecursive(from, files, true))
115         throw std::string("Could not fetch source folder content: " + from);
116
117     std::vector<std::string> folders;
118     if(!listDirsRecursive(from, folders, true))
119         throw std::string("Could not fetch source folder directory tree: " + from);
120
121     // Erstelle Ordnerbaum
122     for(auto folder : folders)
123     {
124         folder.replace(folder.find(from), from.length(), to);
125         try
126         {
127             makeDir(addDirLastSlash(folder));
128         }
129         catch(std::string& e)
130         {
131             throw std::string("Could not create directory tree in destination directory! Failed on: " + folder);
132         }
133
134     }
135
136     // Kopiere Dateien
137     for(auto inFile : files)
138     {
139         File outFile = inFile;
140         unsigned int idx = 0;
141
142         // ersetze Qellordner mit zielordner
143         outFile.first.replace(outFile.first.find(from), from.length(), to);
144         try
145         {
146             makeDir(addDirLastSlash(outFile.first));
147         }
148         catch(std::string& e)
149         {
150             throw std::string("Could not create subfolder in destination directory! Failed on: " + outFile.first);
151         }
152
153         try
154         {
155             copyFile(inFile.first + inFile.second, outFile.first + outFile.second);
156         }
157         catch(std::string& e)
158         {
159             throw std::string("Could not copy file: " + inFile.first + inFile.second);
160         }
161
162     }
163 }
164
165
166 bool FSHelper::validFileName(std::string const &fName)
167 {
168     char invalidChars[] = { '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '~', '`', ' ', '\\',
169                             ';', ':', '+', '=', '*', '/', '<', '>', '?', ',', '[', ']', '{', '}'
170                           };
171
172     int pos = fName.find_first_of(invalidChars, 0, sizeof(invalidChars));
173     if (pos != std::string::npos)
174         return false;
175
176     return true;
177 }
178
179 bool FSHelper::dirExists(std::string const &directory)
180 {
181     std::string dir = rmDirLastSlash(directory);
182
183     // überprüfe existenz
184     struct stat info;
185     if( stat( dir.c_str(), &info ) != 0 )
186         return false;
187     if( info.st_mode & S_IFDIR )
188         return true;
189     else
190         return false;
191 }
192
193 bool FSHelper::fileExists(std::string const &file)
194 {
195     std::ifstream f(file);
196     bool retVal = f.good();
197     f.close();
198     return retVal;
199 }
200
201 std::string FSHelper::addDirLastSlash(std::string const& dir)
202 {
203     if(dir.back() != '\\' && dir.back() != '/')
204 #if defined(_WIN32)
205         return dir + "\\";
206 #else
207         return dir + "/";
208 #endif
209     else
210         return dir;
211 }
212
213 std::string FSHelper::rmDirLastSlash(std::string const& dir)
214 {
215     if(dir.back() == '\\' || dir.back() == '/')
216         return dir.substr(0, dir.size()-1);
217     else
218         return dir;
219 }
220
221 bool FSHelper::listFilesRecursive(const std::string& path, std::vector<FSHelper::File>& files,const bool showHiddenDirs)
222 {
223     std::string dir = addDirLastSlash(path);
224     DIR *dpdf;
225     struct dirent *epdf;
226     dpdf = opendir(dir.c_str());
227     if(dpdf != nullptr)
228     {
229         while((epdf = readdir(dpdf)) != nullptr)
230         {
231             struct stat s;
232
233
234             if(stat((dir + std::string(epdf->d_name)).c_str(), &s) < 0)
235                 return false;
236
237             if(S_ISREG(s.st_mode))
238             {
239                 File curFile;
240
241                 curFile.first = std::string(addDirLastSlash(dir));
242                 curFile.second = std::string(epdf->d_name);
243                 files.push_back(curFile);
244             }
245             else if(showHiddenDirs ? (S_ISDIR(s.st_mode) && std::string(epdf->d_name) != ".." && std::string(epdf->d_name) != "." ) :
246                     (S_ISDIR(s.st_mode) && strstr(epdf->d_name,"..") == NULL && strstr(epdf->d_name,".") == NULL ))
247             {
248                 listFilesRecursive(dir+epdf->d_name,files, showHiddenDirs);
249             }
250         }
251     }
252     closedir(dpdf);
253     return true;
254 }
255
256
257 bool FSHelper::listDirsRecursive(const std::string& path, std::vector<std::string>& folders,const bool showHiddenDirs)
258 {
259     std::string dir = addDirLastSlash(path);
260     DIR *dpdf;
261     struct dirent *epdf;
262     dpdf = opendir(dir.c_str());
263     if(dpdf != nullptr)
264     {
265         while((epdf = readdir(dpdf)) != nullptr)
266         {
267             struct stat s;
268
269             if(stat((dir + std::string(epdf->d_name)).c_str(), &s) < 0)
270                 return false;
271
272             else if(showHiddenDirs ? (S_ISDIR(s.st_mode) && std::string(epdf->d_name) != ".." && std::string(epdf->d_name) != "." ) :
273                     (S_ISDIR(s.st_mode) && strstr(epdf->d_name,"..") == NULL && strstr(epdf->d_name,".") == NULL ))
274             {
275                 if(!(std::find(folders.begin(), folders.end(), dir+epdf->d_name) != folders.end()))
276                     folders.push_back(addDirLastSlash(dir+epdf->d_name));
277                 listDirsRecursive(dir+epdf->d_name,folders, showHiddenDirs);
278             }
279         }
280     }
281     closedir(dpdf);
282     return true;
283 }
284
285 std::string FSHelper::getCurrentWorkingDir()
286 {
287     char temp[PATH_MAX];
288
289     if(getcwd(temp, PATH_MAX))
290 #if defined(_WIN32)
291         return convertPathToWindows(temp);
292 #else
293         return convertPathToUnix(temp);
294 #endif
295
296     // leerer string bei fehler
297     return std::string("");
298 }
299
300 std::string FSHelper::convertPathToWindows(std::string dir)
301 {
302     std::replace(dir.begin(), dir.end(), '/', '\\');
303     return dir;
304 }
305
306 std::string FSHelper::convertPathToUnix(std::string dir)
307 {
308     std::replace(dir.begin(), dir.end(), '\\', '/');
309     return dir;
310 }
311
312 std::string FSHelper::findExecutableInPath(std::string executable)
313 {
314     std::string path = getenv("PATH");
315
316 #if defined(_WIN32)
317     char delim = ';';
318 #else
319     char delim = ':';
320 #endif
321
322     std::vector<std::string> tokens;
323     std::string token;
324     std::istringstream tokenStream(path);
325     while (std::getline(tokenStream, token, delim))
326     {
327         if(fileExists(addDirLastSlash(token) + executable))
328             return addDirLastSlash(token) + executable;
329
330         if(fileExists(addDirLastSlash(token) + executable + ".exe"))
331             return addDirLastSlash(token) + executable + ".exe";
332     }
333     throw std::string("Executable not found in PATH!: " + executable);
334 }
335
336 std::string FSHelper::getFileName(std::string file)
337 {
338     unsigned int ind = file.find_last_of("/");
339     if(ind !=  std::string::npos)
340         file = file.substr(ind + 1, file.size());
341
342     ind = file.find_last_of("\\");
343     if(ind !=  std::string::npos)
344         file = file.substr(ind + 1, file.size());
345
346     return file;
347 }
348
349 std::string FSHelper::getPath(std::string file)
350 {
351     unsigned int ind = file.find_last_of("/");
352     if(ind !=  std::string::npos)
353         file = file.substr(0, ind + 1);
354
355     ind = file.find_last_of("\\");
356     if(ind !=  std::string::npos)
357         file = file.substr(0, ind + 1);
358
359     return file;
360 }
361
362 void FSHelper::executeCommand(const std::string& path, const std::string& command, std::vector<std::string> params, std::string log)
363 {
364
365     if((!path.empty()) && !dirExists(path))
366         throw std::string("Path does not exist: " + path);
367
368     std::string execCommand = findExecutableInPath(command);
369
370     if(!log.empty() && !fileExists(log))
371         createFile(log);
372
373
374 #if defined(_WIN32)
375     std::string paramString;
376
377     for(auto curParam : params)
378         paramString = curParam + " ";
379
380     if(!log.empty())
381         paramString = paramString + " >> " + log;
382
383     // Benutze System, da CreateProcess nicht POSIX standardkonform ist
384     if(!path.empty())
385     {
386         // Laufwerkbuchstaben ermitteln
387         unsigned int ind = path.find_first_of(":");
388         if(ind ==  std::string::npos)
389             throw std::string("Illegal Path! Path does not cointain drive letter! :" + path);
390
391         std::string driveLetter = path.substr(0, ind+1);
392         system(std::string(driveLetter + " && cd " + path + " && " + execCommand + " " + paramString).c_str());
393     }
394     else
395         system(std::string(execCommand + " " + paramString).c_str());
396 #else
397     // String vector zu char array
398     std::vector<char*> charParams;
399
400
401
402     if(!log.empty())
403         FILE* logFile = freopen(log.c_str(),"a",stdout);
404
405     // Param Array muss wie folgt aufgebaut sein:
406     // 0 -> command
407     // 1 -> param 1
408     // x -> param x
409     // last -> nullptr
410
411     // Falls DynLoader gesetzt ist dynloader erster parameter
412     // im array (weil command)
413     if(!mLinuxDynLoader.empty())
414     {
415         char *pc = new char[mLinuxDynLoader.size()+1];
416         std::strcpy(pc, mLinuxDynLoader.c_str());
417         charParams.emplace_back(pc);
418         execCommand = mLinuxDynLoader; //Auszuführender befehl ist dynloader, kommando
419         //ist dann erster parameter (= zweites element im array)
420     }
421
422     // Kommando zu array
423     char *pc = new char[findExecutableInPath(command).size()+1];
424     std::strcpy(pc, findExecutableInPath(command).c_str());
425     charParams.emplace_back(pc);
426
427     // param vector zu char array convertieren
428     std::transform(params.begin(), params.end(), std::back_inserter(charParams),
429                    [](const std::string & s)
430
431     {
432         char *pc = new char[s.size()+1];
433         std::strcpy(pc, s.c_str());
434         return pc;
435         ;
436     });
437     charParams.emplace_back(nullptr); //Letztes element muss ein nullpointer sein
438
439     int status;
440     pid_t parent_pid;
441     pid_t child_pid;
442     parent_pid = getpid();
443
444     child_pid = fork();
445     switch(child_pid)
446     {
447     case -1:
448         //Fork Fehler
449         std::exit(EXIT_FAILURE);
450         break;
451
452     case 0:
453         if(!path.empty())
454             if(chdir(path.c_str())!=0)
455                 std::exit(EXIT_FAILURE);
456
457         execvp(execCommand.c_str(), &charParams[0]);
458
459         // bei fehler Char array im Child freigeben
460         for(auto curParam : charParams)
461             delete [] curParam;
462
463         // Soll nicht erreicht werden. Falls ja
464         // Child prozess beenden
465         std::exit(EXIT_FAILURE);
466         break;
467
468     default:
469         // OK
470         break;
471     }
472
473     // Auf child warten
474     wait(&status);
475
476     // Char array freigeben
477     for(auto curParam : charParams)
478         delete [] curParam;
479
480     // Logfile schliessen
481     fcloseall();
482
483     if(!WIFEXITED(status))
484         throw std::string("Could not execute command: " + command);
485 #endif
486 }
487
488
489 void FSHelper::setLinuxDynLoader(std::string Loader)
490 {
491 #ifndef _WIN32
492     if(!fileExists(Loader))
493         throw std::string("Specified Loader does not exist: " + Loader);
494 #endif
495
496     mLinuxDynLoader = Loader;
497 }
498
499
500 std::string FSHelper::getLinuxDynLoader()
501 {
502     return mLinuxDynLoader;
503 }
504
505
506 void FSHelper::removeLinuxDynLoader()
507 {
508     mLinuxDynLoader.clear();
509 }