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