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