]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_file_utils.cpp
Initial vogl checkin
[vogl] / src / voglcore / vogl_file_utils.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27 // File: vogl_file_utils.cpp
28 #include <fcntl.h>
29 #include "vogl_core.h"
30 #include "vogl_file_utils.h"
31 #include "vogl_strutils.h"
32 #include "vogl_cfile_stream.h"
33 #include "vogl_console.h"
34
35 #ifdef VOGL_USE_WIN32_API
36 #include "vogl_winhdr.h"
37 #endif
38
39 #ifdef WIN32
40 #include <direct.h>
41 #endif
42
43 #ifdef __GNUC__
44 #include <sys/stat.h>
45 #include <libgen.h>
46 #endif
47
48 namespace vogl
49 {
50 #ifdef VOGL_USE_WIN32_API
51     bool file_utils::is_read_only(const char *pFilename)
52     {
53         uint32 dst_file_attribs = GetFileAttributesA(pFilename);
54         if (dst_file_attribs == INVALID_FILE_ATTRIBUTES)
55             return false;
56         if (dst_file_attribs & FILE_ATTRIBUTE_READONLY)
57             return true;
58         return false;
59     }
60
61     bool file_utils::disable_read_only(const char *pFilename)
62     {
63         uint32 dst_file_attribs = GetFileAttributesA(pFilename);
64         if (dst_file_attribs == INVALID_FILE_ATTRIBUTES)
65             return false;
66         if (dst_file_attribs & FILE_ATTRIBUTE_READONLY)
67         {
68             dst_file_attribs &= ~FILE_ATTRIBUTE_READONLY;
69             if (SetFileAttributesA(pFilename, dst_file_attribs))
70                 return true;
71         }
72         return false;
73     }
74
75     bool file_utils::is_older_than(const char *pSrcFilename, const char *pDstFilename)
76     {
77         WIN32_FILE_ATTRIBUTE_DATA src_file_attribs;
78         const BOOL src_file_exists = GetFileAttributesExA(pSrcFilename, GetFileExInfoStandard, &src_file_attribs);
79
80         WIN32_FILE_ATTRIBUTE_DATA dst_file_attribs;
81         const BOOL dest_file_exists = GetFileAttributesExA(pDstFilename, GetFileExInfoStandard, &dst_file_attribs);
82
83         if ((dest_file_exists) && (src_file_exists))
84         {
85             LONG timeComp = CompareFileTime(&src_file_attribs.ftLastWriteTime, &dst_file_attribs.ftLastWriteTime);
86             if (timeComp < 0)
87                 return true;
88         }
89         return false;
90     }
91
92     bool file_utils::does_file_exist(const char *pFilename)
93     {
94         const DWORD fullAttributes = GetFileAttributesA(pFilename);
95
96         if (fullAttributes == INVALID_FILE_ATTRIBUTES)
97             return false;
98
99         if (fullAttributes & FILE_ATTRIBUTE_DIRECTORY)
100             return false;
101
102         return true;
103     }
104
105     bool file_utils::does_dir_exist(const char *pDir)
106     {
107         //-- Get the file attributes.
108         DWORD fullAttributes = GetFileAttributesA(pDir);
109
110         if (fullAttributes == INVALID_FILE_ATTRIBUTES)
111             return false;
112
113         if (fullAttributes & FILE_ATTRIBUTE_DIRECTORY)
114             return true;
115
116         return false;
117     }
118
119     bool file_utils::get_file_size(const char *pFilename, uint64_t &file_size)
120     {
121         file_size = 0;
122
123         WIN32_FILE_ATTRIBUTE_DATA attr;
124
125         if (0 == GetFileAttributesExA(pFilename, GetFileExInfoStandard, &attr))
126             return false;
127
128         if (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
129             return false;
130
131         file_size = static_cast<uint64_t>(attr.nFileSizeLow) | (static_cast<uint64_t>(attr.nFileSizeHigh) << 32U);
132
133         return true;
134     }
135 #elif defined(__GNUC__)
136     bool file_utils::is_read_only(const char *pFilename)
137     {
138         VOGL_NOTE_UNUSED(pFilename);
139         console::debug("%s: Unimplemented\n", VOGL_FUNCTION_NAME);
140         return false;
141     }
142
143     bool file_utils::disable_read_only(const char *pFilename)
144     {
145         VOGL_NOTE_UNUSED(pFilename);
146         console::debug("%s: Unimplemented\n", VOGL_FUNCTION_NAME);
147         return false;
148     }
149
150     bool file_utils::is_older_than(const char *pSrcFilename, const char *pDstFilename)
151     {
152         VOGL_NOTE_UNUSED(pSrcFilename);
153         VOGL_NOTE_UNUSED(pDstFilename);
154         console::debug("%s: Unimplemented\n", VOGL_FUNCTION_NAME);
155         return false;
156     }
157
158     bool file_utils::does_file_exist(const char *pFilename)
159     {
160         struct stat64 stat_buf;
161         int result = stat64(pFilename, &stat_buf);
162         if (result)
163         {
164             return false;
165         }
166         if (S_ISREG(stat_buf.st_mode))
167             return true;
168         return false;
169     }
170
171     bool file_utils::does_dir_exist(const char *pDir)
172     {
173         struct stat64 stat_buf;
174         int result = stat64(pDir, &stat_buf);
175         if (result)
176             return false;
177         if (S_ISDIR(stat_buf.st_mode) || S_ISLNK(stat_buf.st_mode))
178             return true;
179         return false;
180     }
181
182     bool file_utils::get_file_size(const char *pFilename, uint64_t &file_size)
183     {
184         file_size = 0;
185         struct stat64 stat_buf;
186         int result = stat64(pFilename, &stat_buf);
187         if (result)
188             return false;
189         if (!S_ISREG(stat_buf.st_mode))
190             return false;
191         file_size = stat_buf.st_size;
192         return true;
193     }
194 #else
195     bool file_utils::is_read_only(const char *pFilename)
196     {
197         return false;
198     }
199
200     bool file_utils::disable_read_only(const char *pFilename)
201     {
202         pFilename;
203         console::debug("%s: Unimplemented\n", VOGL_FUNCTION_NAME);
204         return false;
205     }
206
207     bool file_utils::is_older_than(const char *pSrcFilename, const char *pDstFilename)
208     {
209         console::debug("%s: Unimplemented\n", VOGL_FUNCTION_NAME);
210         return false;
211     }
212
213     bool file_utils::does_file_exist(const char *pFilename)
214     {
215         FILE *pFile = vogl_fopen(pFilename, "rb");
216         if (!pFile)
217             return false;
218         vogl_fclose(pFile);
219         return true;
220     }
221
222     bool file_utils::does_dir_exist(const char *pDir)
223     {
224         console::debug("%s: Unimplemented\n", VOGL_FUNCTION_NAME);
225         return false;
226     }
227
228     bool file_utils::get_file_size(const char *pFilename, uint64_t &file_size)
229     {
230         FILE *pFile = vogl_fopen(pFilename, "rb");
231         if (!pFile)
232             return false;
233         vogl_fseek(pFile, 0, SEEK_END);
234         file_size = vogl_ftell(pFile);
235         vogl_fclose(pFile);
236         return true;
237     }
238 #endif
239
240     bool file_utils::get_file_size(const char *pFilename, uint32 &file_size)
241     {
242         uint64_t file_size64;
243         if (!get_file_size(pFilename, file_size64))
244         {
245             file_size = 0;
246             return false;
247         }
248
249         if (file_size64 > cUINT32_MAX)
250             file_size64 = cUINT32_MAX;
251
252         file_size = static_cast<uint32>(file_size64);
253         return true;
254     }
255
256     void file_utils::delete_file(const char *pFilename)
257     {
258         remove(pFilename);
259     }
260
261     bool file_utils::is_path_separator(char c)
262     {
263 #ifdef WIN32
264         return (c == '/') || (c == '\\');
265 #else
266         return (c == '/');
267 #endif
268     }
269
270     bool file_utils::is_path_or_drive_separator(char c)
271     {
272 #ifdef WIN32
273         return (c == '/') || (c == '\\') || (c == ':');
274 #else
275         return (c == '/');
276 #endif
277     }
278
279     bool file_utils::is_drive_separator(char c)
280     {
281 #ifdef WIN32
282         return (c == ':');
283 #else
284         VOGL_NOTE_UNUSED(c);
285         return false;
286 #endif
287     }
288
289     bool file_utils::split_path(const char *p, dynamic_string *pDrive, dynamic_string *pDir, dynamic_string *pFilename, dynamic_string *pExt)
290     {
291         VOGL_ASSERT(p);
292
293 #ifdef WIN32
294         char drive_buf[_MAX_DRIVE];
295         char dir_buf[_MAX_DIR];
296         char fname_buf[_MAX_FNAME];
297         char ext_buf[_MAX_EXT];
298
299 #ifdef _MSC_VER
300         // Compiling with MSVC
301         errno_t error = _splitpath_s(p,
302                                      pDrive ? drive_buf : NULL, pDrive ? _MAX_DRIVE : 0,
303                                      pDir ? dir_buf : NULL, pDir ? _MAX_DIR : 0,
304                                      pFilename ? fname_buf : NULL, pFilename ? _MAX_FNAME : 0,
305                                      pExt ? ext_buf : NULL, pExt ? _MAX_EXT : 0);
306         if (error != 0)
307             return false;
308 #else
309         // Compiling with MinGW
310         _splitpath(p,
311                    pDrive ? drive_buf : NULL,
312                    pDir ? dir_buf : NULL,
313                    pFilename ? fname_buf : NULL,
314                    pExt ? ext_buf : NULL);
315 #endif
316
317         if (pDrive)
318             *pDrive = drive_buf;
319         if (pDir)
320             *pDir = dir_buf;
321         if (pFilename)
322             *pFilename = fname_buf;
323         if (pExt)
324             *pExt = ext_buf;
325 #else
326         char dirtmp[1024];
327         char nametmp[1024];
328         strcpy_safe(dirtmp, sizeof(dirtmp), p);
329         strcpy_safe(nametmp, sizeof(nametmp), p);
330
331         if (pDrive)
332             pDrive->clear();
333
334         const char *pDirName = dirname(dirtmp);
335         if (!pDirName)
336             return false;
337
338         if (pDir)
339         {
340             pDir->set(pDirName);
341             if ((!pDir->is_empty()) && (pDir->back() != '/'))
342                 pDir->append_char('/');
343         }
344
345         const char *pBaseName = basename(nametmp);
346         if (!pBaseName)
347             return false;
348
349         if (pFilename)
350         {
351             pFilename->set(pBaseName);
352             remove_extension(*pFilename);
353         }
354
355         if (pExt)
356         {
357             pExt->set(pBaseName);
358             get_extension(*pExt);
359             if (pExt->get_len())
360                 *pExt = "." + *pExt;
361         }
362 #endif // #ifdef WIN32
363
364         return true;
365     }
366
367     bool file_utils::split_path(const char *p, dynamic_string &path, dynamic_string &filename)
368     {
369         dynamic_string temp_drive, temp_path, temp_ext;
370         if (!split_path(p, &temp_drive, &temp_path, &filename, &temp_ext))
371             return false;
372
373         filename += temp_ext;
374
375         combine_path(path, temp_drive.get_ptr(), temp_path.get_ptr());
376         return true;
377     }
378
379     bool file_utils::get_pathname(const char *p, dynamic_string &path)
380     {
381         dynamic_string temp_drive, temp_path;
382         if (!split_path(p, &temp_drive, &temp_path, NULL, NULL))
383             return false;
384
385         combine_path(path, temp_drive.get_ptr(), temp_path.get_ptr());
386         return true;
387     }
388
389     bool file_utils::get_filename(const char *p, dynamic_string &filename)
390     {
391         dynamic_string temp_ext;
392         if (!split_path(p, NULL, NULL, &filename, &temp_ext))
393             return false;
394
395         filename += temp_ext;
396         return true;
397     }
398
399     void file_utils::combine_path(dynamic_string &dst, const char *pA, const char *pB)
400     {
401         dynamic_string temp(pA);
402         if ((!temp.is_empty()) && (!is_path_separator(pB[0])))
403         {
404             char c = temp[temp.get_len() - 1];
405             if (!is_path_separator(c))
406                 temp.append_char(VOGL_PATH_SEPERATOR_CHAR);
407         }
408         temp += pB;
409         dst.swap(temp);
410     }
411
412     void file_utils::combine_path(dynamic_string &dst, const char *pA, const char *pB, const char *pC)
413     {
414         combine_path(dst, pA, pB);
415         combine_path(dst, dst.get_ptr(), pC);
416     }
417
418     void file_utils::combine_path_and_extension(dynamic_string &dst, const char *pA, const char *pB, const char *pC, const char *pExt)
419     {
420         combine_path(dst, pA, pB, pC);
421
422         if ((!dst.ends_with(".")) && (pExt[0]) && (pExt[0] != '.'))
423             dst.append_char('.');
424
425         dst.append(pExt);
426     }
427
428     void file_utils::combine_path_and_extension(dynamic_string &dst, const dynamic_string *pA, const dynamic_string *pB, const dynamic_string *pC, const dynamic_string *pExt)
429     {
430         combine_path_and_extension(dst, pA ? pA->get_ptr() : "", pB ? pB->get_ptr() : "", pC ? pC->get_ptr() : "", pExt ? pExt->get_ptr() : "");
431     }
432
433     bool file_utils::full_path(dynamic_string &path)
434     {
435 #ifdef WIN32
436         char buf[1024];
437         char *p = _fullpath(buf, path.get_ptr(), sizeof(buf));
438         if (!p)
439             return false;
440 #else
441         char buf[PATH_MAX];
442         char *p;
443         dynamic_string pn, fn;
444         split_path(path.get_ptr(), pn, fn);
445         if ((fn == ".") || (fn == ".."))
446         {
447             p = realpath(path.get_ptr(), buf);
448             if (!p)
449                 return false;
450             path.set(buf);
451         }
452         else
453         {
454             if (pn.is_empty())
455                 pn = "./";
456             p = realpath(pn.get_ptr(), buf);
457             if (!p)
458                 return false;
459             combine_path(path, buf, fn.get_ptr());
460         }
461 #endif
462
463         return true;
464     }
465
466     bool file_utils::get_extension(dynamic_string &filename)
467     {
468         int sep = -1;
469 #ifdef WIN32
470         sep = filename.find_right('\\');
471 #endif
472         if (sep < 0)
473             sep = filename.find_right('/');
474
475         int dot = filename.find_right('.');
476         if (dot <= sep)
477         {
478             filename.set_len(0);
479             return false;
480         }
481
482         filename.right(dot + 1);
483
484         return true;
485     }
486
487     bool file_utils::remove_extension(dynamic_string &filename)
488     {
489         int sep = -1;
490 #ifdef WIN32
491         sep = filename.find_right('\\');
492 #endif
493         if (sep < 0)
494             sep = filename.find_right('/');
495
496         int dot = filename.find_right('.');
497         if (dot < sep)
498             return false;
499
500         filename.left(dot);
501
502         return true;
503     }
504
505     bool file_utils::create_directory(const char *pPath)
506     {
507 #ifdef WIN32
508         int status = _mkdir(pPath);
509 #else
510         int status = mkdir(pPath, S_IRWXU | S_IRWXG | S_IRWXO);
511 #endif
512         if (status == 0)
513             console::debug("%s: Created directory %s\n", VOGL_FUNCTION_NAME, pPath);
514         return !status;
515     }
516
517     bool file_utils::create_directories(const dynamic_string &path, bool remove_filename)
518     {
519         dynamic_string path_to_create(path);
520
521         full_path(path_to_create);
522
523         if (remove_filename)
524         {
525             dynamic_string pn, fn;
526             split_path(path_to_create.get_ptr(), pn, fn);
527             path_to_create = pn;
528         }
529
530         return create_directories_from_full_path(path_to_create);
531     }
532
533     bool file_utils::create_directories_from_full_path(const dynamic_string &fullpath)
534     {
535         bool got_unc = false;
536         VOGL_NOTE_UNUSED(got_unc);
537         dynamic_string cur_path;
538
539         const int l = fullpath.get_len();
540
541         int n = 0;
542         while (n < l)
543         {
544             const char c = fullpath.get_ptr()[n];
545
546             const bool sep = is_path_separator(c);
547             const bool back_sep = is_path_separator(cur_path.back());
548             const bool is_last_char = (n == (l - 1));
549
550             if (((sep) && (!back_sep)) || (is_last_char))
551             {
552                 if ((is_last_char) && (!sep))
553                     cur_path.append_char(c);
554
555                 bool valid = !cur_path.is_empty();
556
557 #ifdef WIN32
558                 // reject obvious stuff (drives, beginning of UNC paths):
559                 // c:\b\cool
560                 // \\machine\blah
561                 // \cool\blah
562                 if ((cur_path.get_len() == 2) && (cur_path[1] == ':'))
563                     valid = false;
564                 else if ((cur_path.get_len() >= 2) && (cur_path[0] == '\\') && (cur_path[1] == '\\'))
565                 {
566                     if (!got_unc)
567                         valid = false;
568                     got_unc = true;
569                 }
570                 else if (cur_path == "\\")
571                     valid = false;
572 #endif
573                 if (cur_path == "/")
574                     valid = false;
575
576                 if (valid)
577                 {
578                     create_directory(cur_path.get_ptr());
579                 }
580             }
581
582             cur_path.append_char(c);
583
584             n++;
585         }
586
587         return true;
588     }
589
590     void file_utils::trim_trailing_seperator(dynamic_string &path)
591     {
592         if ((path.get_len()) && (is_path_separator(path.back())))
593             path.truncate(path.get_len() - 1);
594     }
595
596     bool file_utils::add_default_extension(dynamic_string &path, const char *pExt)
597     {
598         dynamic_string ext(path);
599         get_extension(ext);
600         if (ext.is_empty())
601         {
602             path.append(pExt);
603             return true;
604         }
605         return false;
606     }
607
608     // See http://www.codeproject.com/KB/string/wildcmp.aspx
609     int file_utils::wildcmp(const char *pWild, const char *pString)
610     {
611         const char *cp = NULL, *mp = NULL;
612
613         while ((*pString) && (*pWild != '*'))
614         {
615             if ((*pWild != *pString) && (*pWild != '?'))
616                 return 0;
617             pWild++;
618             pString++;
619         }
620
621         // Either *pString=='\0' or *pWild='*' here.
622
623         while (*pString)
624         {
625             if (*pWild == '*')
626             {
627                 if (!*++pWild)
628                     return 1;
629                 mp = pWild;
630                 cp = pString + 1;
631             }
632             else if ((*pWild == *pString) || (*pWild == '?'))
633             {
634                 pWild++;
635                 pString++;
636             }
637             else
638             {
639                 pWild = mp;
640                 pString = cp++;
641             }
642         }
643
644         while (*pWild == '*')
645             pWild++;
646
647         return !*pWild;
648     }
649
650     bool file_utils::read_file_to_vec(const char *pPath, uint8_vec &data)
651     {
652         size_t data_size;
653         void *p = read_file_to_heap(pPath, data_size);
654         if (!p)
655             return false;
656
657         if (static_cast<uint>(data_size) != data_size)
658             return false;
659
660         if (!data.try_resize(static_cast<uint>(data_size)))
661             return false;
662
663         if (!data.grant_ownership(static_cast<uint8 *>(p), static_cast<uint>(data_size), static_cast<uint>(data_size)))
664         {
665             vogl_free(p);
666             return false;
667         }
668
669         return true;
670     }
671
672     void *file_utils::read_file_to_heap(const char *pPath, size_t &data_size)
673     {
674         data_size = 0;
675
676         FILE *pFile = vogl_fopen(pPath, "rb");
677         if (!pFile)
678             return NULL;
679
680         vogl_fseek(pFile, 0, SEEK_END);
681         uint64_t file_size = vogl_ftell(pFile);
682         vogl_fseek(pFile, 0, SEEK_SET);
683
684         if (file_size > VOGL_MAX_POSSIBLE_HEAP_BLOCK_SIZE)
685         {
686             vogl_fclose(pFile);
687             return NULL;
688         }
689
690         data_size = static_cast<size_t>(file_size);
691         VOGL_ASSERT(data_size == file_size);
692
693         void *p = vogl_malloc(data_size);
694         if (!p)
695         {
696             vogl_fclose(pFile);
697             data_size = 0;
698             return NULL;
699         }
700
701         bool success = (vogl_fread(p, 1, data_size, pFile) == data_size);
702
703         vogl_fclose(pFile);
704
705         if (!success)
706         {
707             vogl_free(p);
708             data_size = 0;
709             return NULL;
710         }
711
712         return p;
713     }
714
715     bool file_utils::read_proc_file(const char *filename, vogl::growable_array<char, 2048> &data)
716     {
717         bool ret = false;
718         uint file_length = 0;
719         int nbytes = data.cMaxFixedElements;
720     
721         int fd = open(filename, O_RDONLY);
722         if (fd != -1)
723         {
724             for (;;)
725             {
726                 // Make sure we've got enough room to read in another nbytes.
727                 data.resize(file_length + nbytes);
728     
729                 // Try to read in nbytes. read returns 0:end of file, -1:error.
730                 ssize_t length = read(fd, data.get_ptr() + file_length, nbytes);
731                 if (length < 0)
732                     break;
733     
734                 file_length += length;
735                 if (length != nbytes)
736                 {
737                     ret = true;
738                     break;
739                 }
740             }
741     
742             close(fd);
743         }
744     
745         // Trim trailing whitespace.
746         while ((file_length > 0) && vogl::vogl_isspace(data[file_length - 1]))
747             file_length--;
748     
749         data.resize(file_length + 1);
750         data[file_length] = 0;
751         return ret;
752     }
753
754     bool file_utils::write_buf_to_file(const char *pPath, const void *pData, size_t data_size)
755     {
756         FILE *pFile = vogl_fopen(pPath, "wb");
757         if (!pFile)
758             return false;
759
760         bool success = vogl_fwrite(pData, 1, data_size, pFile) == data_size;
761
762         if (vogl_fclose(pFile) == EOF)
763             return false;
764
765         return success;
766     }
767
768     bool file_utils::write_vec_to_file(const char *pPath, const uint8_vec &data)
769     {
770         return write_buf_to_file(pPath, data.get_ptr(), data.size());
771     }
772
773     // This helper only works on files with valid file sizes (i.e. it won't work on some files under proc such as /proc/self/status).
774     bool file_utils::read_text_file(const char *pPath, dynamic_string_array &lines, uint flags)
775     {
776         cfile_stream stream;
777         if (!stream.open(pPath, cDataStreamReadable))
778         {
779             if (flags & cRTFPrintErrorMessages)
780                 console::error("%s: Failed opening text file \"%s\" for reading\n", VOGL_FUNCTION_NAME, pPath);
781             else if (flags & cRTFPrintWarningMessages)
782                 console::warning("%s: Failed opening text file \"%s\" for reading\n", VOGL_FUNCTION_NAME, pPath);
783
784             return false;
785         }
786
787         dynamic_string line_str;
788         while (stream.get_remaining())
789         {
790             if (!stream.read_line(line_str))
791             {
792                 if (flags & cRTFPrintErrorMessages)
793                     console::error("%s: Failed reading from text file \"%s\"\n", VOGL_FUNCTION_NAME, pPath);
794                 else if (flags & cRTFPrintWarningMessages)
795                     console::warning("%s: Failed reading from text file \"%s\"\n", VOGL_FUNCTION_NAME, pPath);
796
797                 break;
798             }
799
800             if (flags & cRTFTrim)
801                 line_str.trim();
802
803             if (flags & cRTFTrimEnd)
804                 line_str.trim_end();
805
806             if (flags & cRTFIgnoreEmptyLines)
807             {
808                 if (line_str.is_empty())
809                     continue;
810             }
811
812             if ((flags & cRTFIgnoreCommentedLines) && (line_str.get_len() >= 2))
813             {
814                 bool found_comment = false;
815
816                 for (int i = 0; i < static_cast<int>(line_str.get_len()); ++i)
817                 {
818                     char c = line_str[i];
819
820                     if ((c == ' ') || (c == '\t'))
821                         continue;
822                     else if ((c == '/') && (line_str[i + 1] == '/'))
823                         found_comment = true;
824
825                     break;
826                 }
827
828                 if (found_comment)
829                     continue;
830             }
831
832             lines.push_back(line_str);
833         }
834
835         return true;
836     }
837
838     bool file_utils::read_text_file_crt(const char *pPath, dynamic_string_array &lines)
839     {
840         FILE *pFile = fopen(pPath, "r");
841         if (!pFile)
842             return false;
843
844         while (!feof(pFile))
845         {
846             char buf[4096];
847             buf[0] = '\0';
848             fgets(buf, sizeof(buf), pFile);
849             lines.push_back(buf);
850         }
851
852         fclose(pFile);
853
854         return true;
855     }
856
857     bool file_utils::write_text_file(const char *pPath, const dynamic_string_array &lines, bool unix_line_endings)
858     {
859         cfile_stream stream;
860         if (!stream.open(pPath, cDataStreamWritable))
861             return false;
862
863         for (uint i = 0; i < lines.size(); i++)
864         {
865             const dynamic_string &str = lines[i];
866             if (str.get_len())
867                 stream.write(str.get_ptr(), str.get_len());
868
869             if (unix_line_endings)
870                 stream.write("\n", 1);
871             else
872                 stream.write("\r\n", 2);
873         }
874
875         return !stream.get_error();
876     }
877
878     bool file_utils::write_string_to_file(const char *pPath, const dynamic_string &str)
879     {
880         cfile_stream stream;
881         if (!stream.open(pPath, cDataStreamWritable))
882             return false;
883
884         if (str.get_len())
885             stream.write(str.get_ptr(), str.get_len());
886
887         return !stream.get_error();
888     }
889
890     dynamic_string file_utils::generate_temp_filename(const char *pPath)
891     {
892         if (!pPath)
893             pPath = "/tmp/";
894
895         random rm;
896         rm.seed_from_urandom();
897
898         return dynamic_string(cVarArg, "%s__temp_%016" PRIX64 "%016" PRIX64 "__", pPath, rm.urand64(), rm.urand64());
899     }
900
901     bool file_utils::change_directory(const char *pPath)
902     {
903         return chdir(pPath) == 0;
904     }
905
906     // TODO: Windows version
907     char *file_utils::get_exec_filename(char *pPath, size_t dest_len)
908     {
909         ssize_t s = readlink("/proc/self/exe", pPath, dest_len);
910         if (s >= 0)
911         {
912             pPath[s] = '\0';
913             return pPath;
914         }
915
916         VOGL_VERIFY(0);
917         pPath[0] = '\0';
918
919         return pPath;
920     }
921
922 } // namespace vogl