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