1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 //
23 // http://www.opensource.org/licenses/mit-license.php
24 //
25 ////////////////////////////////////////////////////////////////////////////////
26
27 #include <cmake.h>
28 #include <FS.h>
29 #include <fstream>
30 #include <glob.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <stdlib.h>
34 #include <pwd.h>
35 #include <cstdio>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <dirent.h>
39 #include <errno.h>
40 #include <string.h>
41 #include <shared.h>
42 #include <format.h>
43
44 #if defined SOLARIS || defined NETBSD || defined FREEBSD || !defined(__GLIBC__)
45 #include <limits.h>
46 #endif
47
48 #if defined __APPLE__
49 #include <sys/syslimits.h>
50 #endif
51
52 // Fixes build with musl libc.
53 #ifndef GLOB_TILDE
54 #define GLOB_TILDE 0
55 #endif
56
57 #ifndef GLOB_BRACE
58 #define GLOB_BRACE 0
59 #endif
60
61 ////////////////////////////////////////////////////////////////////////////////
Path()62 Path::Path ()
63 {
64 }
65
66 ////////////////////////////////////////////////////////////////////////////////
Path(const Path & other)67 Path::Path (const Path& other)
68 {
69 if (this != &other)
70 {
71 _original = other._original;
72 _data = other._data;
73 }
74 }
75
76 ////////////////////////////////////////////////////////////////////////////////
Path(const std::string & in)77 Path::Path (const std::string& in)
78 {
79 _original = in;
80 _data = expand (in);
81 }
82
83 ////////////////////////////////////////////////////////////////////////////////
operator =(const Path & other)84 Path& Path::operator= (const Path& other)
85 {
86 if (this != &other)
87 {
88 this->_original = other._original;
89 this->_data = other._data;
90 }
91
92 return *this;
93 }
94
95 ////////////////////////////////////////////////////////////////////////////////
operator ==(const Path & other)96 bool Path::operator== (const Path& other)
97 {
98 return _data == other._data;
99 }
100
101 ////////////////////////////////////////////////////////////////////////////////
operator +=(const std::string & dir)102 Path& Path::operator+= (const std::string& dir)
103 {
104 _data += '/' + dir;
105 return *this;
106 }
107
108 ////////////////////////////////////////////////////////////////////////////////
operator std::string() const109 Path::operator std::string () const
110 {
111 return _data;
112 }
113
114 ////////////////////////////////////////////////////////////////////////////////
name() const115 std::string Path::name () const
116 {
117 if (_data.length ())
118 {
119 auto slash = _data.rfind ('/');
120 if (slash != std::string::npos)
121 return _data.substr (slash + 1, std::string::npos);
122 }
123
124 return _data;
125 }
126
127 ////////////////////////////////////////////////////////////////////////////////
parent() const128 std::string Path::parent () const
129 {
130 if (_data.length ())
131 {
132 auto slash = _data.rfind ('/');
133 if (slash != std::string::npos)
134 return _data.substr (0, slash);
135 }
136
137 return "";
138 }
139
140 ////////////////////////////////////////////////////////////////////////////////
extension() const141 std::string Path::extension () const
142 {
143 if (_data.length ())
144 {
145 auto dot = _data.rfind ('.');
146 if (dot != std::string::npos)
147 return _data.substr (dot + 1, std::string::npos);
148 }
149
150 return "";
151 }
152
153 ////////////////////////////////////////////////////////////////////////////////
exists() const154 bool Path::exists () const
155 {
156 return access (_data.c_str (), F_OK) ? false : true;
157 }
158
159 ////////////////////////////////////////////////////////////////////////////////
is_directory() const160 bool Path::is_directory () const
161 {
162 if (exists ())
163 {
164 struct stat s {};
165 if (stat (_data.c_str (), &s))
166 throw format ("stat error {1}: {2}", errno, strerror (errno));
167
168 return S_ISDIR (s.st_mode);
169 }
170
171 return false;
172 }
173
174 ////////////////////////////////////////////////////////////////////////////////
is_absolute() const175 bool Path::is_absolute () const
176 {
177 if (_data.length () && _data[0] == '/')
178 return true;
179
180 return false;
181 }
182
183 ////////////////////////////////////////////////////////////////////////////////
is_link() const184 bool Path::is_link () const
185 {
186 struct stat s {};
187 if (lstat (_data.c_str (), &s))
188 throw format ("lstat error {1}: {2}", errno, strerror (errno));
189
190 return S_ISLNK (s.st_mode);
191 }
192
193 ////////////////////////////////////////////////////////////////////////////////
194 // EACCES is a permissions problem which is exactly what this method is trying
195 // to determine.
readable() const196 bool Path::readable () const
197 {
198 auto status = access (_data.c_str (), R_OK);
199 if (status == -1 && errno != EACCES)
200 throw format ("access error {1}: {2}", errno, strerror (errno));
201
202 return status ? false : true;
203 }
204
205 ////////////////////////////////////////////////////////////////////////////////
206 // EACCES is a permissions problem which is exactly what this method is trying
207 // to determine.
writable() const208 bool Path::writable () const
209 {
210 auto status = access (_data.c_str (), W_OK);
211 if (status == -1 && errno != EACCES)
212 throw format ("access error {1}: {2}", errno, strerror (errno));
213
214 return status ? false : true;
215 }
216
217 ////////////////////////////////////////////////////////////////////////////////
218 // EACCES is a permissions problem which is exactly what this method is trying
219 // to determine.
executable() const220 bool Path::executable () const
221 {
222 auto status = access (_data.c_str (), X_OK);
223 if (status == -1 && errno != EACCES)
224 throw format ("access error {1}: {2}", errno, strerror (errno));
225
226 return status ? false : true;
227 }
228
229 ////////////////////////////////////////////////////////////////////////////////
rename(const std::string & new_name)230 bool Path::rename (const std::string& new_name)
231 {
232 auto expanded = expand (new_name);
233 if (_data != expanded)
234 {
235 if (std::rename (_data.c_str (), expanded.c_str ()) == 0)
236 {
237 _data = expanded;
238 return true;
239 }
240 }
241
242 return false;
243 }
244
245 ////////////////////////////////////////////////////////////////////////////////
246 // ~ --> /home/user
247 // ~foo/x --> /home/foo/s
248 // ~/x --> /home/foo/x
249 // ./x --> $PWD/x
250 // x --> $PWD/x
expand(const std::string & in)251 std::string Path::expand (const std::string& in)
252 {
253 std::string copy = in;
254
255 auto tilde = copy.find ('~');
256 std::string::size_type slash;
257
258 if (tilde != std::string::npos)
259 {
260 const char *home = getenv("HOME");
261 if (home == nullptr)
262 {
263 struct passwd* pw = getpwuid (getuid ());
264 home = pw->pw_dir;
265 }
266
267 // Convert: ~ --> /home/user
268 if (copy.length () == 1)
269 copy = home;
270
271 // Convert: ~/x --> /home/user/x
272 else if (copy.length () > tilde + 1 &&
273 copy[tilde + 1] == '/')
274 {
275 copy.replace (tilde, 1, home);
276 }
277
278 // Convert: ~foo/x --> /home/foo/x
279 else if ((slash = copy.find ('/', tilde)) != std::string::npos)
280 {
281 std::string name = copy.substr (tilde + 1, slash - tilde - 1);
282 struct passwd* pw = getpwnam (name.c_str ());
283 if (pw)
284 copy.replace (tilde, slash - tilde, pw->pw_dir);
285 }
286 }
287
288 // Relative paths
289 else if (in.length () > 2 &&
290 in.substr (0, 2) == "./")
291 {
292 copy = Directory::cwd () + in.substr (1);
293 }
294 else if (in.length () > 1 &&
295 in[0] != '.' &&
296 in[0] != '/')
297 {
298 copy = Directory::cwd () + '/' + in;
299 }
300
301 return copy;
302 }
303
304 ////////////////////////////////////////////////////////////////////////////////
glob(const std::string & pattern)305 std::vector <std::string> Path::glob (const std::string& pattern)
306 {
307 std::vector <std::string> results;
308
309 glob_t g;
310 #ifdef SOLARIS
311 if (!::glob (pattern.c_str (), GLOB_ERR, nullptr, &g))
312 #else
313 if (!::glob (pattern.c_str (), GLOB_ERR | GLOB_BRACE | GLOB_TILDE, nullptr, &g))
314 #endif
315 for (int i = 0; i < (int) g.gl_pathc; ++i)
316 results.push_back (g.gl_pathv[i]);
317
318 globfree (&g);
319 return results;
320 }
321
322 ////////////////////////////////////////////////////////////////////////////////
File()323 File::File ()
324 : Path::Path ()
325 , _fh (nullptr)
326 , _h (-1)
327 , _locked (false)
328 {
329 }
330
331 ////////////////////////////////////////////////////////////////////////////////
File(const Path & other)332 File::File (const Path& other)
333 : Path::Path (other)
334 , _fh (nullptr)
335 , _h (-1)
336 , _locked (false)
337 {
338 }
339
340 ////////////////////////////////////////////////////////////////////////////////
File(const File & other)341 File::File (const File& other)
342 : Path::Path (other)
343 , _fh (nullptr)
344 , _h (-1)
345 , _locked (false)
346 {
347 }
348
349 ////////////////////////////////////////////////////////////////////////////////
File(const std::string & in)350 File::File (const std::string& in)
351 : Path::Path (in)
352 , _fh (nullptr)
353 , _h (-1)
354 , _locked (false)
355 {
356 }
357
358 ////////////////////////////////////////////////////////////////////////////////
~File()359 File::~File ()
360 {
361 if (_fh)
362 close ();
363 }
364
365 ////////////////////////////////////////////////////////////////////////////////
operator =(const File & other)366 File& File::operator= (const File& other)
367 {
368 if (this != &other)
369 Path::operator= (other);
370
371 _locked = false;
372 return *this;
373 }
374
375 ////////////////////////////////////////////////////////////////////////////////
create(int mode)376 bool File::create (int mode /* = 0640 */)
377 {
378 if (open ())
379 {
380 fchmod (_h, mode);
381 close ();
382 return true;
383 }
384
385 return false;
386 }
387
388 ////////////////////////////////////////////////////////////////////////////////
remove() const389 bool File::remove () const
390 {
391 return unlink (_data.c_str ()) == 0 ? true : false;
392 }
393
394 ////////////////////////////////////////////////////////////////////////////////
removeBOM(const std::string & input)395 std::string File::removeBOM (const std::string& input)
396 {
397 if (input[0] && input[0] == '\xEF' &&
398 input[1] && input[1] == '\xBB' &&
399 input[2] && input[2] == '\xBF')
400 return input.substr (3);
401
402 return input;
403 }
404
405 ////////////////////////////////////////////////////////////////////////////////
open()406 bool File::open ()
407 {
408 if (_data != "")
409 {
410 if (! _fh)
411 {
412 bool already_exists = exists ();
413 if (already_exists)
414 if (!readable () || !writable ())
415 throw std::string (format ("Insufficient permissions for '{1}'.", _data));
416
417 _fh = fopen (_data.c_str (), (already_exists ? "r+" : "w+"));
418 if (_fh)
419 {
420 _h = fileno (_fh);
421 _locked = false;
422 return true;
423 }
424 else
425 throw format ("fopen error {1}: {2}", errno, strerror (errno));
426 }
427 else
428 return true;
429 }
430
431 return false;
432 }
433
434 ////////////////////////////////////////////////////////////////////////////////
close()435 void File::close ()
436 {
437 if (_fh)
438 {
439 if (_locked)
440 unlock ();
441
442 if (fclose (_fh))
443 throw format ("fclose error {1}: {2}", errno, strerror (errno));
444
445 _fh = nullptr;
446 _h = -1;
447 _locked = false;
448 }
449 }
450
451 ////////////////////////////////////////////////////////////////////////////////
lock()452 bool File::lock ()
453 {
454 _locked = false;
455 if (_fh && _h != -1)
456 {
457 #ifdef FREEBSD
458 // l_type l_whence l_start l_len l_pid l_sysid
459 struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0, 0 };
460 #elif defined(__DragonFly__)
461 // l_start l_len l_pid l_type l_whence
462 struct flock fl = {0, 0, 0, F_WRLCK, SEEK_SET };
463 #else
464 // l_type l_whence l_start l_len l_pid
465 struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0 };
466 #endif
467 fl.l_pid = getpid ();
468 if (fcntl (_h, F_SETLKW, &fl) == 0)
469 _locked = true;
470 }
471
472 return _locked;
473 }
474
475 ////////////////////////////////////////////////////////////////////////////////
unlock()476 void File::unlock ()
477 {
478 if (_locked)
479 {
480 #ifdef FREEBSD
481 // l_type l_whence l_start l_len l_pid l_sysid
482 struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0, 0 };
483 #elif defined(__DragonFly__)
484 // l_start l_len l_pid l_type l_whence
485 struct flock fl = {0, 0, 0, F_WRLCK, SEEK_SET };
486 #else
487 // l_type l_whence l_start l_len l_pid
488 struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0 };
489 #endif
490 fl.l_pid = getpid ();
491
492 fcntl (_h, F_SETLK, &fl);
493 _locked = false;
494 }
495 }
496
497 ////////////////////////////////////////////////////////////////////////////////
498 // Opens if necessary.
read(std::string & contents)499 void File::read (std::string& contents)
500 {
501 contents = "";
502 contents.reserve (size ());
503
504 std::ifstream in (_data.c_str ());
505 if (in.good ())
506 {
507 bool first = true;
508 std::string line;
509 line.reserve (512 * 1024);
510 while (getline (in, line))
511 {
512 // Detect forbidden BOM on first line.
513 if (first)
514 {
515 line = File::removeBOM (line);
516 first = false;
517 }
518
519 contents += line + '\n';
520 }
521
522 in.close ();
523 }
524 }
525
526 ////////////////////////////////////////////////////////////////////////////////
527 // Opens if necessary.
read(std::vector<std::string> & contents)528 void File::read (std::vector <std::string>& contents)
529 {
530 contents.clear ();
531
532 std::ifstream in (_data.c_str ());
533 if (in.good ())
534 {
535 bool first = true;
536 std::string line;
537 line.reserve (512 * 1024);
538 while (getline (in, line))
539 {
540 // Detect forbidden BOM on first line.
541 if (first)
542 {
543 line = File::removeBOM (line);
544 first = false;
545 }
546
547 contents.push_back (line);
548 }
549
550 in.close ();
551 }
552 }
553
554 ////////////////////////////////////////////////////////////////////////////////
555 // Opens if necessary.
append(const std::string & line)556 void File::append (const std::string& line)
557 {
558 if (!_fh)
559 open ();
560
561 if (_fh)
562 {
563 fseek (_fh, 0, SEEK_END);
564
565 if (fputs (line.c_str (), _fh) == EOF)
566 throw format ("fputs error {1}: {2}", errno, strerror (errno));
567 }
568 }
569
570 ////////////////////////////////////////////////////////////////////////////////
571 // Opens if necessary.
append(const std::vector<std::string> & lines)572 void File::append (const std::vector <std::string>& lines)
573 {
574 if (!_fh)
575 open ();
576
577 if (_fh)
578 {
579 fseek (_fh, 0, SEEK_END);
580
581 for (auto& line : lines)
582 if (fputs (line.c_str (), _fh) == EOF)
583 throw format ("fputs error {1}: {2}", errno, strerror (errno));
584 }
585 }
586
587 ////////////////////////////////////////////////////////////////////////////////
write_raw(const std::string & line)588 void File::write_raw (const std::string& line)
589 {
590 if (!_fh)
591 open ();
592
593 if (_fh)
594 if (fputs (line.c_str (), _fh) == EOF)
595 throw format ("fputs error {1}: {2}", errno, strerror (errno));
596 }
597
598 ////////////////////////////////////////////////////////////////////////////////
truncate()599 void File::truncate ()
600 {
601 if (!_fh)
602 open ();
603
604 if (_fh)
605 if (ftruncate (_h, 0))
606 throw format ("ftruncate error {1}: {2}", errno, strerror (errno));
607 }
608
609 ////////////////////////////////////////////////////////////////////////////////
610 // S_IFMT 0170000 type of file
611 // S_IFIFO 0010000 named pipe (fifo)
612 // S_IFCHR 0020000 character special
613 // S_IFDIR 0040000 directory
614 // S_IFBLK 0060000 block special
615 // S_IFREG 0100000 regular
616 // S_IFLNK 0120000 symbolic link
617 // S_IFSOCK 0140000 socket
618 // S_IFWHT 0160000 whiteout
619 // S_ISUID 0004000 set user id on execution
620 // S_ISGID 0002000 set group id on execution
621 // S_ISVTX 0001000 save swapped text even after use
622 // S_IRUSR 0000400 read permission, owner
623 // S_IWUSR 0000200 write permission, owner
624 // S_IXUSR 0000100 execute/search permission, owner
mode()625 mode_t File::mode ()
626 {
627 struct stat s;
628 if (stat (_data.c_str (), &s))
629 throw format ("stat error {1}: {2}", errno, strerror (errno));
630
631 return s.st_mode;
632 }
633
634 ////////////////////////////////////////////////////////////////////////////////
size() const635 size_t File::size () const
636 {
637 struct stat s;
638 if (stat (_data.c_str (), &s))
639 throw format ("stat error {1}: {2}", errno, strerror (errno));
640
641 return s.st_size;
642 }
643
644 ////////////////////////////////////////////////////////////////////////////////
mtime() const645 time_t File::mtime () const
646 {
647 struct stat s;
648 if (stat (_data.c_str (), &s))
649 throw format ("stat error {1}: {2}", errno, strerror (errno));
650
651 return s.st_mtime;
652 }
653
654 ////////////////////////////////////////////////////////////////////////////////
ctime() const655 time_t File::ctime () const
656 {
657 struct stat s;
658 if (stat (_data.c_str (), &s))
659 throw format ("stat error {1}: {2}", errno, strerror (errno));
660
661 return s.st_ctime;
662 }
663
664 ////////////////////////////////////////////////////////////////////////////////
btime() const665 time_t File::btime () const
666 {
667 struct stat s;
668 if (stat (_data.c_str (), &s))
669 throw format ("stat error {1}: {2}", errno, strerror (errno));
670
671 #ifdef HAVE_ST_BIRTHTIME
672 return s.st_birthtime;
673 #else
674 return s.st_ctime;
675 #endif
676 }
677
678 ////////////////////////////////////////////////////////////////////////////////
create(const std::string & name,int mode)679 bool File::create (const std::string& name, int mode /* = 0640 */)
680 {
681 std::string full_name = expand (name);
682 std::ofstream out (full_name.c_str ());
683 if (out.good ())
684 {
685 out.close ();
686 if (chmod (full_name.c_str (), mode))
687 throw format ("chmod error {1}: {2}", errno, strerror (errno));
688
689 return true;
690 }
691
692 return false;
693 }
694
695 ////////////////////////////////////////////////////////////////////////////////
read(const std::string & name,std::string & contents)696 bool File::read (const std::string& name, std::string& contents)
697 {
698 contents = "";
699
700 std::ifstream in (name.c_str ());
701 if (in.good ())
702 {
703 bool first = true;
704 std::string line;
705 line.reserve (1024);
706 while (getline (in, line))
707 {
708 // Detect forbidden BOM on first line.
709 if (first)
710 {
711 line = File::removeBOM (line);
712 first = false;
713 }
714
715 contents += line + '\n';
716 }
717
718 in.close ();
719 return true;
720 }
721
722 return false;
723 }
724
725 ////////////////////////////////////////////////////////////////////////////////
read(const std::string & name,std::vector<std::string> & contents)726 bool File::read (const std::string& name, std::vector <std::string>& contents)
727 {
728 contents.clear ();
729
730 std::ifstream in (name.c_str ());
731 if (in.good ())
732 {
733 bool first = true;
734 std::string line;
735 line.reserve (1024);
736 while (getline (in, line))
737 {
738 // Detect forbidden BOM on first line.
739 if (first)
740 {
741 line = File::removeBOM (line);
742 first = false;
743 }
744
745 contents.push_back (line);
746 }
747
748 in.close ();
749 return true;
750 }
751
752 return false;
753 }
754
755 ////////////////////////////////////////////////////////////////////////////////
write(const std::string & name,const std::string & contents)756 bool File::write (const std::string& name, const std::string& contents)
757 {
758 std::ofstream out (expand (name).c_str (),
759 std::ios_base::out | std::ios_base::trunc);
760 if (out.good ())
761 {
762 out << contents;
763 out.close ();
764 return true;
765 }
766
767 return false;
768 }
769
770 ////////////////////////////////////////////////////////////////////////////////
write(const std::string & name,const std::vector<std::string> & lines,bool addNewlines)771 bool File::write (
772 const std::string& name,
773 const std::vector <std::string>& lines,
774 bool addNewlines /* = true */)
775 {
776 std::ofstream out (expand (name).c_str (),
777 std::ios_base::out | std::ios_base::trunc);
778 if (out.good ())
779 {
780 for (auto& line : lines)
781 {
782 out << line;
783
784 if (addNewlines)
785 out << '\n';
786 }
787
788 out.close ();
789 return true;
790 }
791
792 return false;
793 }
794
795 ////////////////////////////////////////////////////////////////////////////////
remove(const std::string & name)796 bool File::remove (const std::string& name)
797 {
798 return unlink (expand (name).c_str ()) == 0 ? true : false;
799 }
800
801 ////////////////////////////////////////////////////////////////////////////////
copy(const std::string & from,const std::string & to)802 bool File::copy (const std::string& from, const std::string& to)
803 {
804 // 'from' must exist.
805 if (! access (from.c_str (), F_OK))
806 {
807 std::ifstream src (from, std::ios::binary);
808 std::ofstream dst (to, std::ios::binary);
809
810 dst << src.rdbuf ();
811 return true;
812 }
813
814 return false;
815 }
816
817 ////////////////////////////////////////////////////////////////////////////////
move(const std::string & from,const std::string & to)818 bool File::move (const std::string& from, const std::string& to)
819 {
820 auto expanded = expand (to);
821 if (from != expanded)
822 if (std::rename (from.c_str (), to.c_str ()) == 0)
823 return true;
824
825 return false;
826 }
827
828 ////////////////////////////////////////////////////////////////////////////////
Directory()829 Directory::Directory ()
830 {
831 }
832
833 ////////////////////////////////////////////////////////////////////////////////
Directory(const Directory & other)834 Directory::Directory (const Directory& other)
835 : File::File (other)
836 {
837 }
838
839 ////////////////////////////////////////////////////////////////////////////////
Directory(const File & other)840 Directory::Directory (const File& other)
841 : File::File (other)
842 {
843 }
844
845 ////////////////////////////////////////////////////////////////////////////////
Directory(const Path & other)846 Directory::Directory (const Path& other)
847 : File::File (other)
848 {
849 }
850
851 ////////////////////////////////////////////////////////////////////////////////
Directory(const std::string & in)852 Directory::Directory (const std::string& in)
853 : File::File (in)
854 {
855 }
856
857 ////////////////////////////////////////////////////////////////////////////////
operator =(const Directory & other)858 Directory& Directory::operator= (const Directory& other)
859 {
860 if (this != &other)
861 File::operator= (other);
862
863 return *this;
864 }
865
866 ////////////////////////////////////////////////////////////////////////////////
create(int mode)867 bool Directory::create (int mode /* = 0755 */)
868 {
869 // No error handling because we want failure to be silent, somewhat emulating
870 // "mkdir -p".
871 return mkdir (_data.c_str (), mode) == 0 ? true : false;
872 }
873
874 ////////////////////////////////////////////////////////////////////////////////
remove() const875 bool Directory::remove () const
876 {
877 return remove_directory (_data);
878 }
879
880 ////////////////////////////////////////////////////////////////////////////////
remove_directory(const std::string & dir) const881 bool Directory::remove_directory (const std::string& dir) const
882 {
883 DIR* dp = opendir (dir.c_str ());
884 if (dp != nullptr)
885 {
886 struct dirent* de;
887 while ((de = readdir (dp)) != nullptr)
888 {
889 if (! strcmp (de->d_name, ".") ||
890 ! strcmp (de->d_name, ".."))
891 continue;
892
893 #if defined (SOLARIS) || defined (HAIKU)
894 struct stat s;
895 if (lstat ((dir + '/' + de->d_name).c_str (), &s))
896 throw format ("lstat error {1}: {2}", errno, strerror (errno));
897
898 if (S_ISDIR (s.st_mode))
899 remove_directory (dir + '/' + de->d_name);
900 else
901 unlink ((dir + '/' + de->d_name).c_str ());
902 #else
903 if (de->d_type == DT_UNKNOWN)
904 {
905 struct stat s;
906 if (lstat ((dir + '/' + de->d_name).c_str (), &s))
907 throw format ("lstat error {1}: {2}", errno, strerror (errno));
908
909 if (S_ISDIR (s.st_mode))
910 de->d_type = DT_DIR;
911 }
912 if (de->d_type == DT_DIR)
913 remove_directory (dir + '/' + de->d_name);
914 else
915 unlink ((dir + '/' + de->d_name).c_str ());
916 #endif
917 }
918
919 closedir (dp);
920 }
921
922 return rmdir (dir.c_str ()) ? false : true;
923 }
924
925 ////////////////////////////////////////////////////////////////////////////////
list()926 std::vector <std::string> Directory::list ()
927 {
928 std::vector <std::string> files;
929 list (_data, files, false);
930 return files;
931 }
932
933 ////////////////////////////////////////////////////////////////////////////////
listRecursive()934 std::vector <std::string> Directory::listRecursive ()
935 {
936 std::vector <std::string> files;
937 list (_data, files, true);
938 return files;
939 }
940
941 ////////////////////////////////////////////////////////////////////////////////
cwd()942 std::string Directory::cwd ()
943 {
944 #ifdef HAVE_GET_CURRENT_DIR_NAME
945 char *buf = get_current_dir_name ();
946 if (buf == nullptr)
947 throw std::bad_alloc ();
948 std::string result (buf);
949 free (buf);
950 return result;
951 #else
952 char buf[PATH_MAX];
953 getcwd (buf, PATH_MAX - 1);
954 return std::string (buf);
955 #endif
956 }
957
958 ////////////////////////////////////////////////////////////////////////////////
up()959 bool Directory::up ()
960 {
961 if (_data == "/")
962 return false;
963
964 auto slash = _data.rfind ('/');
965 if (slash == 0)
966 {
967 _data = "/"; // Root dir should retain the slash.
968 return true;
969 }
970 else if (slash != std::string::npos)
971 {
972 _data = _data.substr (0, slash);
973 return true;
974 }
975
976 return false;
977 }
978
979 ////////////////////////////////////////////////////////////////////////////////
cd() const980 bool Directory::cd () const
981 {
982 return chdir (_data.c_str ()) == 0 ? true : false;
983 }
984
985 ////////////////////////////////////////////////////////////////////////////////
list(const std::string & base,std::vector<std::string> & results,bool recursive)986 void Directory::list (
987 const std::string& base,
988 std::vector <std::string>& results,
989 bool recursive)
990 {
991 DIR* dp = opendir (base.c_str ());
992 if (dp != nullptr)
993 {
994 struct dirent* de;
995 while ((de = readdir (dp)) != nullptr)
996 {
997 if (!strcmp (de->d_name, ".") ||
998 !strcmp (de->d_name, ".."))
999 continue;
1000
1001 #if defined (SOLARIS) || defined (HAIKU)
1002 struct stat s;
1003 if (stat ((base + '/' + de->d_name).c_str (), &s))
1004 throw format ("stat error {1}: {2}", errno, strerror (errno));
1005
1006 if (recursive && S_ISDIR (s.st_mode))
1007 list (base + '/' + de->d_name, results, recursive);
1008 else
1009 results.push_back (base + '/' + de->d_name);
1010 #else
1011 if (recursive && de->d_type == DT_UNKNOWN)
1012 {
1013 struct stat s;
1014 if (lstat ((base + '/' + de->d_name).c_str (), &s))
1015 throw format ("lstat error {1}: {2}", errno, strerror (errno));
1016
1017 if (S_ISDIR (s.st_mode))
1018 de->d_type = DT_DIR;
1019 }
1020 if (recursive && de->d_type == DT_DIR)
1021 list (base + '/' + de->d_name, results, recursive);
1022 else
1023 results.push_back (base + '/' + de->d_name);
1024 #endif
1025 }
1026
1027 closedir (dp);
1028 }
1029 }
1030
1031 ////////////////////////////////////////////////////////////////////////////////
1032