Properly initialize FollowLinks in the default constructor
[libi2ncommon] / src / filefunc.cpp
CommitLineData
0e23f538
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
e93545dd
GE
20/***************************************************************************
21 escape.cpp - escaping of strings
22 -------------------
23 begin : Sun Nov 14 1999
24 copyright : (C) 1999 by Intra2net AG
e93545dd
GE
25 ***************************************************************************/
26
6a93d84a 27#include <list>
e93545dd 28#include <string>
6a93d84a 29#include <fstream>
0a654ec0 30#include <sstream>
e93545dd 31#include <iostream>
0a654ec0 32#include <stdexcept>
e93545dd
GE
33
34#include <sys/types.h>
35#include <sys/stat.h>
0a654ec0 36#include <dirent.h>
e93545dd
GE
37#include <pwd.h>
38#include <grp.h>
39#include <unistd.h>
0a654ec0 40#include <errno.h>
5efd35b1 41#include <string.h>
e93545dd 42
6a93d84a 43#include <boost/scoped_array.hpp>
deab8f59 44#include <boost/foreach.hpp>
6a93d84a
TJ
45#include "filefunc.hxx"
46#include "stringfunc.hxx"
47
a287a306 48namespace I2n
6a93d84a 49{
e93545dd
GE
50
51using namespace std;
52
6a93d84a
TJ
53/*
54** implementation of Stat
55*/
56
57Stat::Stat()
027e0a1c
TJ
58 : FollowLinks(true)
59 , Valid(false)
0107a75b 60{
a287a306 61 clear();
6a93d84a
TJ
62} // eo Stat::Stat()
63
64
65Stat::Stat(const std::string& path, bool follow_links)
66{
67 stat(path,follow_links);
68} // eo Stat::Stat(const std::string&,bool)
69
70
71Stat::~Stat()
72{
73} // eo Stat::~Stat()
74
75
76/**
77 * @brief updates the internal data.
78 *
79 * In other words: stat()'s the file again.
80 */
81void Stat::recheck()
82{
a287a306 83 if (! Path.empty())
6a93d84a 84 {
96fe2dc5
GE
85 // pass a copy of Path: otherwise clear() would leave an empty reference
86 stat(string(Path), FollowLinks);
6a93d84a
TJ
87 }
88} // eo Stat::recheck()
89
90
91/**
92 * @brief calls stat() or lstat() to get the information for the given path
93 * and stores that information.
94 * @param path the path which should be checked
95 * @param follow_links determine if (symbalic) links should be followed.
96 */
97void Stat::stat(const std::string& path, bool follow_links)
98{
99 clear();
a287a306
TJ
100 Path= path;
101 FollowLinks= follow_links;
6a93d84a
TJ
102 struct stat stat_info;
103 int res;
104 res = ( follow_links ? ::stat(path.c_str(), &stat_info) : ::lstat(path.c_str(), &stat_info) );
105 if ( 0 == res )
106 {
a287a306
TJ
107 Device = stat_info.st_dev;
108 Inode = stat_info.st_ino;
109 Mode = (stat_info.st_mode & ~(S_IFMT));
110 NumLinks = stat_info.st_nlink;
111 Uid = stat_info.st_uid;
112 Gid = stat_info.st_gid;
113 DeviceType = stat_info.st_rdev;
114 Size = stat_info.st_size;
115 Atime = stat_info.st_atime;
116 Mtime = stat_info.st_mtime;
117 Ctime = stat_info.st_atime;
118
119 IsLink= S_ISLNK( stat_info.st_mode );
120 IsRegular= S_ISREG( stat_info.st_mode );
121 IsDirectory= S_ISDIR( stat_info.st_mode );
122 IsCharacterDevice= S_ISCHR( stat_info.st_mode );
6ab3bc95 123 IsBlockDevice= S_ISBLK( stat_info.st_mode );
a287a306
TJ
124 IsFifo= S_ISFIFO( stat_info.st_mode );
125 IsSocket= S_ISSOCK( stat_info.st_mode );
6a93d84a 126 }
a287a306 127 Valid = (0 == res);
6a93d84a 128} // eo Stat::stat(const std::string&,bool)
0a654ec0 129
0107a75b 130
6a93d84a
TJ
131/**
132 * @brief clears the internal data.
133 */
134void Stat::clear()
135{
a287a306
TJ
136 Path.clear();
137 Valid= false;
138
139 Device = 0;
140 Inode = 0;
141 Mode = 0;
142 NumLinks = 0;
143 Uid = 0;
144 Gid = 0;
145 DeviceType = 0;
146 Size = 0;
147 Atime = 0;
148 Mtime = 0;
149 Ctime = 0;
150
151 IsLink= false;
152 IsRegular= false;
153 IsDirectory= false;
154 IsCharacterDevice= false;
155 IsBlockDevice= false;
156 IsFifo= false;
157 IsSocket= false;
6a93d84a
TJ
158} // eo Stat::clear()
159
160
161/**
162 * @brief checks if another instance describes the same file.
163 * @param rhs the other instance.
164 * @return @a true iff the other instance describes the same file.
165 * @note
166 * The "same file" means that the files are located on the same device and use the same inode.
167 * They might still have two different directory entries (different paths)!
168 */
a287a306 169bool Stat::is_same_as(const Stat& rhs)
6a93d84a 170{
a287a306
TJ
171 return Valid and rhs.Valid
172 and ( Device == rhs.Device)
173 and ( Inode == rhs.Inode);
174} // eo Stat::is_same_as(const Stat& rhs);
6a93d84a
TJ
175
176
177/**
178 * @brief checks if this and the other instance describe the same device.
179 * @param rhs the other instance.
180 * @return @a true if we and the other instance describe a device and the same device.
181 *
182 * "Same device" means that the devices have the same type and the same major and minor id.
183 */
a287a306 184bool Stat::is_same_device_as(const Stat& rhs)
6a93d84a 185{
a287a306
TJ
186 return is_device() and rhs.is_device()
187 and ( IsBlockDevice == rhs.IsBlockDevice )
188 and ( IsCharacterDevice == rhs.IsCharacterDevice )
189 and ( DeviceType == rhs.DeviceType);
190} // eo Stat::is_same_device_as(const Stat&)
6a93d84a
TJ
191
192/**
193 * @brief check existence of a path.
194 * @param path path which should be tested.
195 * @return @a true iff path exists.
196 */
197bool path_exists(const std::string& path)
198{
199 struct stat stat_info;
200 int res = ::stat(path.c_str(), &stat_info);
201 if (res) return false;
202 return true;
203} // eo path_exists(const std::string&)
204
205/**
206 * @brief check existence of a regular file.
207 * @param path path which should be tested.
208 * @return @a true if path exists and is a regular file (or a link pointing to a regular file).
209 * @note this checks for regular files; not for the pure exitsnace of a path; use pathExists() for that.
210 * @see pathExists
211 */
212bool file_exists(const std::string& path)
213{
214 struct stat stat_info;
215 int res = ::stat(path.c_str(), &stat_info);
216 if (res) return false;
217 return S_ISREG(stat_info.st_mode);
218} // eo file_exists(const std::string&)
219
220// TODO: Use Stat class
221/**
222 * Get size of file
223 * @param name filename to get size for
224 * @return file size or -1 if file does not exist
225 */
226long file_size (const string &name)
e93545dd
GE
227{
228 long iReturn = -1;
0a654ec0 229
e93545dd 230 struct stat statbuff;
0a654ec0 231
e93545dd
GE
232 if (lstat(name.c_str(), &statbuff) < 0)
233 return -1;
234
235 if (!S_ISREG(statbuff.st_mode))
236 return -1;
0a654ec0 237
e93545dd
GE
238 iReturn=statbuff.st_size;
239
240 return iReturn;
241}
242
6a93d84a
TJ
243/**
244 * @brief tests the last modification time stamp of a path.
245 * @param path path which should be tested.
246 * @return the last modification time or 0 if the path doen't exist.
247 */
a287a306 248time_t file_mtime(const std::string& path)
e93545dd 249{
6a93d84a
TJ
250 struct stat stat_info;
251 int res = ::stat(path.c_str(), &stat_info);
252 if (res) return 0;
253 return stat_info.st_mtime;
6ab3bc95 254} // eo file_mtime(const std::string&)
e93545dd 255
e93545dd 256
6a93d84a
TJ
257/**
258 * @brief reads the contents of a directory.
259 * @param path the path to the directory whose contents should be read.
260 * @param[out] result the resulting list of names.
261 * @param include_dot_names determines if dot-files should be included in the list.
262 * @return @a true if reading the directory was succesful, @a false on error.
263 */
a287a306 264bool get_dir(
6a93d84a
TJ
265 const std::string& path,
266 std::vector< std::string >& result,
267 bool include_dot_names )
268{
269 DIR* dir = ::opendir( path.c_str());
270 if (!dir)
271 {
272 return false;
273 }
10c3af2d
TJ
274 struct dirent store, *entry = NULL;
275 while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
e93545dd 276 {
6a93d84a
TJ
277 std::string name( entry->d_name );
278 if (! include_dot_names && (name[0] == '.') )
279 {
280 continue;
281 }
282 result.push_back( name );
e93545dd 283 }
6a93d84a
TJ
284 ::closedir(dir);
285 return true;
6ab3bc95 286} // eo get_dir(const std::string&,std::vector< std::string >&,bool)
6a93d84a
TJ
287
288
289/**
290 * @brief reads the contents of a directory
291 * @param path the path to the directory whose contents should be read.
292 * @param include_dot_names determines if dot-files should be included in the list.
293 * @return the list of names (empty on error).
294 */
a287a306 295std::vector< std::string > get_dir(const std::string& path, bool include_dot_names )
6a93d84a
TJ
296{
297 std::vector< std::string > result;
a287a306 298 get_dir(path,result,include_dot_names);
6a93d84a 299 return result;
6ab3bc95 300} // eo get_dir(const std::string&,bool)
e93545dd 301
e93545dd 302
e93545dd 303
6a93d84a
TJ
304/**
305 * @brief removes a file from a filesystem.
306 * @param path path to the file.
44725532 307 * @return @a true if the unlink was successful.
6a93d84a
TJ
308 */
309bool unlink( const std::string& path )
e93545dd 310{
6a93d84a
TJ
311 int res = ::unlink( path.c_str() );
312 return (res == 0);
313} // eo unlink(const std::string&)
314
e93545dd 315
e93545dd 316
6a93d84a
TJ
317/**
318 * @brief creates a symbolic link named @a link_name to @a target.
319 * @param target the target the link should point to.
320 * @param link_name the name of the link.
321 * @param force if @a true, the (file or link) @a link_name is removed if it exists.
322 * @return @a true iff the symlink was successfully created.
323 */
324bool symlink(const std::string& target, const std::string& link_name, bool force)
325{
326 int res= -1;
327 if (target.empty() or link_name.empty() or target == link_name)
328 {
329 // no, we don't do this!
e93545dd 330 return false;
6a93d84a
TJ
331 }
332 std::string n_target;
333 if (target[0] == '/') // absolute target?
334 {
335 n_target= target;
336 }
337 else // relative target
338 {
339 // for stat'ing: prepend dir of link_name:
340 n_target= dirname(link_name)+"/"+ target;
341 }
342 Stat target_stat(n_target, false);
343 Stat link_name_stat(link_name, false);
344 if (target_stat.exists() && link_name_stat.exists())
345 {
a287a306
TJ
346 if (link_name_stat.is_same_as(target_stat)
347 or link_name_stat.is_same_device_as(target_stat) )
6a93d84a
TJ
348 {
349 return false;
350 }
351 //TODO: more consistency checks?!
352 }
353 if (link_name_stat.exists())
354 {
355 // the link name already exists.
356 if (force)
357 {
358 // "force" as given, so try to remove the link_name:
359 unlink(link_name);
360 // update the stat:
361 link_name_stat.recheck();
362 }
363 }
364 if (link_name_stat.exists())
365 {
366 // well, if the link_name still exists; we cannot create that link:
367 errno = EEXIST;
368 return false;
369 }
370 res= ::symlink(target.c_str(), link_name.c_str());
371 return (0 == res);
372} // eo symlink(const std::string&,const std::string&,bool)
373
e93545dd 374
6a93d84a
TJ
375
376/**
377 * @brief reads the target of a symbolic link.
378 * @param path path to the symbolic link
379 * @return the target of the link or an empty string on error.
380 */
a287a306 381std::string read_link(const std::string& path)
6a93d84a
TJ
382{
383 errno= 0;
384 Stat stat(path,false);
a287a306 385 if (!stat || !stat.is_link())
6a93d84a
TJ
386 {
387 return std::string();
388 }
389 int buffer_size= PATH_MAX+1 + 128;
390 boost::scoped_array<char> buffer_ptr( new char[buffer_size] );
391 int res= ::readlink( path.c_str(), buffer_ptr.get(), buffer_size-1 );
392 if (res >= 0)
393 {
394 return std::string( buffer_ptr.get(), res );
395 }
396 return std::string();
6ab3bc95 397} // eo read_link(const std::string&)
6a93d84a
TJ
398
399
400
401/**
402 * @brief returns content of a file as string.
403 *
404 * A simple (q'n'd) function for retrieving content of a file as string.<br>
405 * Also able to read special (but regular) files which don't provide a size when stat'ed
406 * (like files in the /proc filesystem).
407 *
408 * @param path path to the file.
409 * @return the content of the file as string (empty if file could be opened).
410 */
a287a306 411std::string read_file(const std::string& path)
6a93d84a
TJ
412{
413 Stat stat(path);
a287a306 414 if (!stat.is_reg())
6a93d84a
TJ
415 {
416 return std::string();
417 }
418 std::ifstream f( path.c_str(), std::ios::in | std::ios::binary );
419 std::string result;
420 if (f.is_open())
421 {
422 // NOTE: there are cases where we don't know the correct size (/proc files...)
423 // therefore we only use the size for reserving space if we know it, but don't
424 // use it when reading the file!
425 if (stat.size() > 0)
426 {
427 // if we know the size, we reserve enough space.
428 result.reserve( stat.size() );
429 }
430 char buffer[2048];
431 while (f.good())
432 {
433 f.read(buffer, sizeof(buffer));
434 result.append(buffer, f.gcount());
435 }
436 }
437 return result;
6ab3bc95 438} // eo read_file(const std::string&)
6a93d84a
TJ
439
440
441/**
442 * @brief writes a string to a file.
443 * @param path path to the file
444 * @param data the data which should be written into the file
71f7bfb8 445 * @param trunc set the trunc flag when opening the file. Do not use for files in /proc and /sys
6a93d84a
TJ
446 * @return @a true if the data was written to the file.
447 *
448 * A simple (q'n'd) function for writing a string to a file.
449 */
71f7bfb8 450bool write_file(const std::string& path, const std::string& data, bool trunc)
6a93d84a 451{
71f7bfb8
GE
452 // set the correct openmode flags
453 std::ios_base::openmode flags = std::ios::out | std::ios::binary;
454 if (trunc)
455 flags |= std::ios::trunc;
456
457 std::ofstream f( path.c_str(), flags);
6a93d84a
TJ
458 if (f.good())
459 {
460 f.write( data.data(), data.size() );
461 return f.good();
462 }
e93545dd 463 else
6a93d84a 464 {
e93545dd 465 return false;
6a93d84a 466 }
6ab3bc95 467} // eo write_file(const std::string&,const std::string&)
6a93d84a
TJ
468
469
470/**
1a2fc4e8
TJ
471 * Copy file in 4k blocks from source to target.
472 * Overwrites the target if it already exists.
473 *
474 * On error the target file gets removed.
475 *
476 * @param src source file
477 * @param dest target file
478 * @return true if all is ok, false on error
479 */
480bool copy_file(const std::string& src, const std::string& dest)
481{
a1e7d2a7
GE
482 std::ifstream input( src.c_str(), std::ios::in | std::ios::binary );
483 if (!input)
484 return false;
485
486 std::ofstream output( dest.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
487 if (!output)
488 return false;
489
490 // Out of disc space?
491 if (!copy_stream(input,output))
492 {
493 output.close();
494 unlink(dest);
495 return false;
496 }
497
498 return true;
499}
500
501/**
502 * Copy streams in 4k blocks.
503 *
504 * @param is source stream
505 * @param os target stream
506 * @return true if all is ok, false on error
507 */
508bool copy_stream(std::istream& is, std::ostream& os)
509{
510 if (!is)
1a2fc4e8
TJ
511 return false;
512
a1e7d2a7 513 if (!os)
1a2fc4e8
TJ
514 return false;
515
516 char buffer[4096];
a1e7d2a7 517 while (is.good())
1a2fc4e8 518 {
a1e7d2a7
GE
519 is.read(buffer, sizeof(buffer));
520 os.write(buffer, is.gcount());
521
522 // Can't write?
523 if (!os.good())
1a2fc4e8 524 return false;
1a2fc4e8
TJ
525 }
526
527 return true;
528}
529
530/**
6a93d84a
TJ
531 * @brief returns the filename part of a path (last component)
532 * @param path the path.
533 * @return the last component of the path.
534 */
535std::string basename(const std::string& path)
536{
537 std::string::size_type pos= path.rfind('/');
538 if (pos != std::string::npos)
539 {
540 return path.substr(pos+1);
541 }
542 return path;
543} // eo basename(const std::string&)
544
545
546/**
547 * @brief returns the directory part of a path.
548 * @param path the path.
549 * @return the directory part of the path.
550 */
551std::string dirname(const std::string& path)
552{
553 std::string::size_type pos= path.rfind('/');
554 if (pos != std::string::npos)
555 {
556 std::string result(path,0,pos);
557 if (result.empty())
558 {
559 return ".";
560 }
561 return result;
562 }
563 return ".";
564} // eo dirname(const std::string&)
565
566
567/**
568 * @brief normalizes a path.
569 *
570 * This method removes empty and "." elements.
571 * It also resolves ".." parts by removing previous path elements if possible.
572 * Leading ".." elements are preserved when a relative path was given; else they are removed.
573 * Trailing slashes are removed.
574 *
575 * @param path the path which should be normalized.
576 * @return the normalized path.
577 */
578
a287a306 579std::string normalize_path(const std::string& path)
6a93d84a
TJ
580{
581 if (path.empty())
582 {
583 return std::string();
584 }
585 // remember if the given path was absolute since this information vanishes when
586 // we split the path (since we split with omitting empty parts...)
587 bool is_absolute= (path[0]=='/');
588 std::list< std::string > parts;
589 std::list< std::string > result_parts;
590
6ab3bc95 591 split_string(path,parts,"/",true);
6a93d84a
TJ
592
593 for(std::list< std::string >::const_iterator it_parts= parts.begin();
594 it_parts != parts.end();
595 ++it_parts)
596 {
597 std::string part(*it_parts); //convenience..
598 if (part == std::string(".") )
599 {
600 // single dot is "current path"; ignore!
601 continue;
602 }
603 if (part == std::string("..") )
604 {
605 // double dot is "one part back"
606 if (result_parts.empty())
607 {
608 if (is_absolute)
609 {
610 // ignore since we cannot move behind / on absolute paths...
611 }
612 else
613 {
614 // on relative path, we need to store the "..":
615 result_parts.push_back(part);
616 }
617 }
618 else if (result_parts.back() == std::string("..") )
619 {
620 // if last element was already "..", we need to store the new one again...
621 // (PS: no need for "absolute" check; this can only be the case on relative path)
622 result_parts.push_back(part);
623 }
624 else
625 {
626 // remove last element.
627 result_parts.pop_back();
628 }
629 continue;
630 }
631 result_parts.push_back(part);
632 }
633 std::string result;
634 if (is_absolute)
635 {
636 result= "/";
637 }
6ab3bc95 638 result+= join_string(result_parts,"/");
6a93d84a 639 return result;
6ab3bc95 640} // eo normalize_path(const std::string&)
6a93d84a
TJ
641
642
643/**
f002679a
GE
644 * @brief calls fsync on a given directory to sync all it's metadata
645 * @param path the path of the directory.
646 * @return true if successful
647 */
648bool dirsync(const std::string& path)
649{
650 // sync the directory the file is in
651 DIR* dir=opendir(path.c_str());
652 if (dir == NULL)
653 return false;
654
655 int ret=fsync(dirfd(dir));
656
657 closedir(dir);
658
659 return (ret==0);
660}
661
662/**
6a93d84a
TJ
663 * @brief changes the file(/path) mode.
664 * @param path the path to change the mode for.
665 * @param mode the new file mode.
666 * @return @a true iff the file mode was sucessfully changed.
667 */
668bool chmod(const std::string& path, int mode)
669{
670 int res= ::chmod(path.c_str(), mode);
671 return (res==0);
672} // eo chmod(const std::string&,int)
673
674
675/**
676 * @brief changed the owner of a file(/path)
677 * @param path the path to change the owner for.
678 * @param user the new file owner.
679 * @param group the new file group.
680 * @return @a true iff the file owner was succesfully changed.
681 *
682 * @note
683 * the validity of user and group within the system is not checked.
684 * This is intentional since this way we can use id's which are not assigned.
685 */
a287a306 686bool chown(const std::string& path, const I2n::User& user, const I2n::Group& group)
6a93d84a 687{
a287a306 688 uid_t uid= user.Uid;
6a93d84a 689 if (uid<0) return false;
a287a306
TJ
690 gid_t gid= group.Gid;
691 if (gid<0) gid= user.Gid;
6a93d84a
TJ
692 if (gid<0) return false;
693 int res= ::chown( path.c_str(), uid, gid);
694 return (res==0);
695} // eo chown(const std::string&,const User&,const Group&)
e93545dd 696
0a654ec0
TJ
697/**
698 * Recursive delete of files and directories
699 * @param path File or directory to delete
44725532 700 * @param error Will contain the error if the return value is false [optional]
0a654ec0
TJ
701 * @return true on success, false otherwise
702 */
703bool recursive_delete(const std::string &path, std::string *error)
704{
705 bool rtn = true;
706
707 try {
708 struct stat my_stat;
709 if (stat(path.c_str(), &my_stat) != 0) {
710 throw runtime_error("can't stat " + path);
711 }
712
713 if (S_ISDIR(my_stat.st_mode)) {
714 DIR *dir = opendir(path.c_str());
715 if (!dir) {
716 throw runtime_error("can't open directory " + path);
717 }
718
10c3af2d
TJ
719 struct dirent store, *entry = NULL;
720 while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
721 {
0a654ec0
TJ
722 string filename = entry->d_name;
723 if (filename == "." || filename == "..") {
724 continue;
725 }
726
727 // Delete subdir or file.
728 rtn = recursive_delete(path + "/" + filename, error);
729 if (rtn == false) {
730 break;
731 }
732 }
733
734 closedir(dir);
44725532 735 if (!rmdir(path)) {
0a654ec0
TJ
736 throw runtime_error("can't remove directory " + path);
737 }
738 } else {
44725532 739 if (!unlink(path)) {
0a654ec0
TJ
740 throw runtime_error("can't unlink " + path);
741 }
742 }
743 } catch (exception &e) {
744 if (error) {
745 ostringstream out;
746 out << e.what() << " (" << strerror(errno) << ")";
747 *error = out.str();
748 }
749 rtn = false;
750 } catch (...) {
751 if (error) {
752 ostringstream out;
753 out << "unknown error (" << strerror(errno) << ")";
754 *error = out.str();
755 }
756 rtn = false;
757 }
e93545dd 758
0a654ec0 759 return rtn;
6ab3bc95 760} // eo recursive_delete(const std::string&,std::string*)
6a93d84a 761
faf8475b
TJ
762/**
763 Create a unique temporary directory from path_template.
764 @param Path template. The last six characters must be XXXXXX.
44725532 765 @param error Will contain the error if the return value is false [optional]
faf8475b
TJ
766 @return Name of new directory or empty string on error.
767*/
44725532 768std::string mkdtemp(const std::string &path_template, std::string *error)
faf8475b
TJ
769{
770 boost::scoped_array<char> buf( new char[path_template.size()+1] );
771 path_template.copy(buf.get(), path_template.size());
772 buf[path_template.size()]=0;
773
774 char *unique_dir = ::mkdtemp(buf.get());
775 if (!unique_dir)
44725532
TJ
776 {
777 if (error)
778 *error = strerror(errno);
faf8475b 779 return "";
44725532 780 }
faf8475b
TJ
781
782 // Scoped pointer is still valid
783 return std::string(unique_dir);
784}
785
44725532
TJ
786/**
787 Create directory
788 @param path Path to create
789 @param error Will contain the error if the return value is false [optional]
790 @return True on success, false on error
791*/
792bool mkdir(const std::string &path, const mode_t &mode, std::string *error)
793{
794 if ( ::mkdir(path.c_str(), mode) == 0)
795 return true;
796
797 if (error)
798 *error = strerror(errno);
799 return false;
800}
801
802/**
803 Remove directory
804 @param path Path to removed
805 @param error Will contain the error if the return value is false [optional]
806 @return True on successs, false otherwise
807*/
808bool rmdir(const std::string &path, std::string *error)
809{
810 if ( ::rmdir(path.c_str() ) == 0)
811 return true;
812
813 if (error)
814 *error = strerror(errno);
815 return false;
816}
817
818/// Small helper class for scoped free
819class scoped_C_free
820{
821public:
822 scoped_C_free(void *ptr)
823 : pointer_to_free(ptr)
824 {
825 }
826
827 ~scoped_C_free()
828 {
829 free (pointer_to_free);
830 pointer_to_free = NULL;
831 }
832
833private:
834 void *pointer_to_free;
835};
836
837/**
838 Get current working directory
839 @return Current working directory. Empty string on error.
840*/
841std::string getcwd()
842{
843 char *cwd = ::getcwd(NULL, 0);
844 if (!cwd)
845 return "";
846
847 // Make deallocation of cwd exception safe
848 scoped_C_free holder(cwd);
849
850 string current_dir(cwd);
851 return current_dir;
852}
853
854/**
855 Change current working directory
856 @param path Path to change to
857 @param error Will contain the error if the return value is false [optional]
858 @return True on successs, false otherwise
859*/
860bool chdir(const std::string &path, std::string *error)
861{
862 if ( ::chdir(path.c_str() ) == 0)
863 return true;
864
865 if (error)
866 *error = strerror(errno);
867 return false;
868}
faf8475b 869
901e2943
TJ
870/**
871 Set file mode creation mask
872 @param mask Creation mask
873 @return Previous creation mask (function call always succeeds)
874*/
875mode_t umask(mode_t mask)
876{
877 return ::umask(mask);
878}
879
deab8f59
CH
880
881/**
882 * @brief Remove unlisted files
883 *
884 * @param directory Direcotry to look for files
885 * @param keep_files List of files or directories to keep
886 * @param prefix Filename prefix to match. Empty prefix matches all.
887 *
888 * @return bool True if the directory was scanned, false on error (directory not found, permission denied)
889 **/
890bool remove_unlisted_files(const std::string &directory,
891 const std::set<std::string> &keep_files,
892 const std::string &prefix)
893{
894 std::vector<std::string> content;
895 if (!get_dir(directory, content, false))
896 return false;
897
898 bool all_fine = true;
899 BOOST_FOREACH(const std::string &file, content)
900 {
901 // Check for filename prefix (if any)
902 if (!prefix.empty() && file.find(prefix) != 0)
903 continue;
904
905 // Check if file is whitelisted
906 if (keep_files.find(file) != keep_files.end())
907 continue;
908
909 // Try to unlink file. (Continue on error)
910 if (!unlink(directory + "/" + file))
911 all_fine = false;
912 }
913
914 return all_fine;
915}
916
917
6ab3bc95 918} // eo namespace I2n