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