2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
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.
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.
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.
20 /***************************************************************************
21 escape.cpp - escaping of strings
23 begin : Sun Nov 14 1999
24 copyright : (C) 1999 by Intra2net AG
25 ***************************************************************************/
34 #include <sys/types.h>
36 #include <sys/statvfs.h>
44 #include <boost/scoped_array.hpp>
45 #include <boost/foreach.hpp>
46 #include "filefunc.hxx"
47 #include "stringfunc.hxx"
55 ** implementation of Stat
66 Stat::Stat(const std::string& path, bool follow_links)
68 stat(path,follow_links);
69 } // eo Stat::Stat(const std::string&,bool)
78 * @brief updates the internal data.
80 * In other words: stat()'s the file again.
86 // pass a copy of Path: otherwise clear() would leave an empty reference
87 stat(string(Path), FollowLinks);
89 } // eo Stat::recheck()
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.
98 void Stat::stat(const std::string& path, bool follow_links)
102 FollowLinks= follow_links;
103 struct stat stat_info;
105 res = ( follow_links ? ::stat(path.c_str(), &stat_info) : ::lstat(path.c_str(), &stat_info) );
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;
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;
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 );
128 IsBlockDevice= S_ISBLK( stat_info.st_mode );
129 IsFifo= S_ISFIFO( stat_info.st_mode );
130 IsSocket= S_ISSOCK( stat_info.st_mode );
133 } // eo Stat::stat(const std::string&,bool)
137 * @brief clears the internal data.
160 IsCharacterDevice= false;
161 IsBlockDevice= false;
164 } // eo Stat::clear()
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.
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)!
175 bool Stat::is_same_as(const Stat& rhs)
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);
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.
188 * "Same device" means that the devices have the same type and the same major and minor id.
190 bool Stat::is_same_device_as(const Stat& rhs)
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&)
199 * @brief check existence of a path.
200 * @param path path which should be tested.
201 * @return @a true iff path exists.
203 bool path_exists(const std::string& path)
205 struct stat stat_info;
206 int res = ::stat(path.c_str(), &stat_info);
207 if (res) return false;
209 } // eo path_exists(const std::string&)
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.
218 bool file_exists(const std::string& path)
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&)
226 // TODO: Use Stat class
229 * @param name filename to get size for
230 * @return file size or -1 if file does not exist
232 long file_size (const string &name)
236 struct stat statbuff;
238 if (lstat(name.c_str(), &statbuff) < 0)
241 if (!S_ISREG(statbuff.st_mode))
244 iReturn=statbuff.st_size;
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.
254 time_t file_mtime(const std::string& path)
256 struct stat stat_info;
257 int res = ::stat(path.c_str(), &stat_info);
259 return stat_info.st_mtime;
260 } // eo file_mtime(const std::string&)
264 * @brief Check if two files differ
266 * Note: Reads the whole file into memory
267 * if the file size is identical.
269 * @param old_filename Filename of old file
270 * @param new_filename Filename of new file
272 * @return bool True if files differ, false otherwise.
273 * If one file does not exist, also returns true
275 bool file_content_differs(const std::string &old_filename, const std::string &new_filename)
277 if (I2n::file_exists(old_filename) == false ||
278 I2n::file_exists(new_filename) == false)
281 // check if size differs
282 if (I2n::file_size(old_filename) != I2n::file_size(new_filename))
285 const std::string old_content = I2n::read_file(old_filename);
286 const std::string new_content = I2n::read_file(new_filename);
288 // check if content differs
289 if (old_content == new_content)
292 // Differ by default (fallback)
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.
305 const std::string& path,
306 std::vector< std::string >& result,
307 bool include_dot_names )
309 DIR* dir = ::opendir( path.c_str());
314 struct dirent store, *entry = NULL;
315 while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
317 std::string name( entry->d_name );
318 if (! include_dot_names && (name[0] == '.') )
322 result.push_back( name );
326 } // eo get_dir(const std::string&,std::vector< std::string >&,bool)
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).
335 std::vector< std::string > get_dir(const std::string& path, bool include_dot_names )
337 std::vector< std::string > result;
338 get_dir(path,result,include_dot_names);
340 } // eo get_dir(const std::string&,bool)
345 * @brief removes a file from a filesystem.
346 * @param path path to the file.
347 * @return @a true if the unlink was successful.
349 bool unlink( const std::string& path )
351 int res = ::unlink( path.c_str() );
353 } // eo unlink(const std::string&)
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.
364 bool symlink(const std::string& target, const std::string& link_name, bool force)
367 if (target.empty() or link_name.empty() or target == link_name)
369 // no, we don't do this!
372 std::string n_target;
373 if (target[0] == '/') // absolute target?
377 else // relative target
379 // for stat'ing: prepend dir of link_name:
380 n_target= dirname(link_name)+"/"+ target;
382 Stat target_stat(n_target, false);
383 Stat link_name_stat(link_name, false);
384 if (target_stat.exists() && link_name_stat.exists())
386 if (link_name_stat.is_same_as(target_stat)
387 or link_name_stat.is_same_device_as(target_stat) )
391 //TODO: more consistency checks?!
393 if (link_name_stat.exists())
395 // the link name already exists.
398 // "force" as given, so try to remove the link_name:
401 link_name_stat.recheck();
404 if (link_name_stat.exists())
406 // well, if the link_name still exists; we cannot create that link:
410 res= ::symlink(target.c_str(), link_name.c_str());
412 } // eo symlink(const std::string&,const std::string&,bool)
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.
421 std::string read_link(const std::string& path)
424 Stat stat(path,false);
425 if (!stat || !stat.is_link())
427 return std::string();
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 );
434 return std::string( buffer_ptr.get(), res );
436 return std::string();
437 } // eo read_link(const std::string&)
442 * @brief returns content of a file as string.
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).
448 * @param path path to the file.
449 * @return the content of the file as string (empty if file could be opened).
451 std::string read_file(const std::string& path)
456 return std::string();
458 std::ifstream f( path.c_str(), std::ios::in | std::ios::binary );
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!
467 // if we know the size, we reserve enough space.
468 result.reserve( stat.size() );
473 f.read(buffer, sizeof(buffer));
474 result.append(buffer, f.gcount());
478 } // eo read_file(const std::string&)
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
485 * @param trunc set the trunc flag when opening the file. Do not use for files in /proc and /sys
486 * @return @a true if the data was written to the file.
488 * A simple (q'n'd) function for writing a string to a file.
490 bool write_file(const std::string& path, const std::string& data, bool trunc)
492 // set the correct openmode flags
493 std::ios_base::openmode flags = std::ios::out | std::ios::binary;
495 flags |= std::ios::trunc;
497 std::ofstream f( path.c_str(), flags);
500 f.write( data.data(), data.size() );
507 } // eo write_file(const std::string&,const std::string&)
511 * Copy file in 4k blocks from source to target.
512 * Overwrites the target if it already exists.
514 * On error the target file gets removed.
516 * @param src source file
517 * @param dest target file
518 * @return true if all is ok, false on error
520 bool copy_file(const std::string& src, const std::string& dest)
522 std::ifstream input( src.c_str(), std::ios::in | std::ios::binary );
526 std::ofstream output( dest.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
530 // Out of disc space?
531 if (!copy_stream(input,output))
542 * Copy streams in 4k blocks.
544 * @param is source stream
545 * @param os target stream
546 * @return true if all is ok, false on error
548 bool copy_stream(std::istream& is, std::ostream& os)
559 is.read(buffer, sizeof(buffer));
560 os.write(buffer, is.gcount());
571 * @brief returns the filename part of a path (last component)
572 * @param path the path.
573 * @return the last component of the path.
575 std::string basename(const std::string& path)
577 std::string::size_type pos= path.rfind('/');
578 if (pos != std::string::npos)
580 return path.substr(pos+1);
583 } // eo basename(const std::string&)
587 * @brief returns the directory part of a path.
588 * @param path the path.
589 * @return the directory part of the path.
591 std::string dirname(const std::string& path)
593 std::string::size_type pos= path.rfind('/');
594 if (pos != std::string::npos)
596 std::string result(path,0,pos);
604 } // eo dirname(const std::string&)
608 * @brief normalizes a path.
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.
615 * @param path the path which should be normalized.
616 * @return the normalized path.
619 std::string normalize_path(const std::string& path)
623 return std::string();
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;
631 split_string(path,parts,"/",true);
633 for(std::list< std::string >::const_iterator it_parts= parts.begin();
634 it_parts != parts.end();
637 std::string part(*it_parts); //convenience..
638 if (part == std::string(".") )
640 // single dot is "current path"; ignore!
643 if (part == std::string("..") )
645 // double dot is "one part back"
646 if (result_parts.empty())
650 // ignore since we cannot move behind / on absolute paths...
654 // on relative path, we need to store the "..":
655 result_parts.push_back(part);
658 else if (result_parts.back() == std::string("..") )
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);
666 // remove last element.
667 result_parts.pop_back();
671 result_parts.push_back(part);
678 result+= join_string(result_parts,"/");
680 } // eo normalize_path(const std::string&)
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
688 bool dirsync(const std::string& path)
690 // sync the directory the file is in
691 DIR* dir=opendir(path.c_str());
695 int ret=fsync(dirfd(dir));
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.
708 bool chmod(const std::string& path, int mode)
710 int res= ::chmod(path.c_str(), mode);
712 } // eo chmod(const std::string&,int)
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.
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.
726 bool chown(const std::string& path, const I2n::User& user, const I2n::Group& group)
729 if (uid<0) return false;
730 gid_t gid= group.Gid;
731 if (gid<0) gid= user.Gid;
732 if (gid<0) return false;
733 int res= ::chown( path.c_str(), uid, gid);
735 } // eo chown(const std::string&,const User&,const Group&)
738 * Recursive delete of files and directories
739 * @param path File or directory to delete
740 * @param keep_parent_dir Keep parent directory (=empty out directory) [optional]
741 * @param error Will contain the error if the return value is false [optional]
742 * @return true on success, false otherwise
744 bool recursive_delete(const std::string &path,
745 bool keep_parent_dir,
753 if (stat(path.c_str(), &my_stat) != 0)
754 throw runtime_error("can't stat " + path);
756 if (S_ISDIR(my_stat.st_mode))
758 DIR *dir = opendir(path.c_str());
760 throw runtime_error("can't open directory " + path);
762 struct dirent store, *entry = NULL;
763 while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
765 string filename = entry->d_name;
766 if (filename == "." || filename == "..")
769 // Delete subdir or file.
770 rtn = recursive_delete(path + "/" + filename, false, error);
776 if (keep_parent_dir == false && !rmdir(path))
777 throw runtime_error("can't remove directory " + path);
782 throw runtime_error("can't unlink " + path);
790 out << e.what() << " (" << strerror(errno) << ")";
800 out << "unknown error (" << strerror(errno) << ")";
807 } // eo recursive_delete(const std::string&,std::string*)
810 Create a unique temporary directory from path_template.
811 @param Path template. The last six characters must be XXXXXX.
812 @param error Will contain the error if the return value is false [optional]
813 @return Name of new directory or empty string on error.
815 std::string mkdtemp(const std::string &path_template, std::string *error)
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;
821 char *unique_dir = ::mkdtemp(buf.get());
825 *error = strerror(errno);
829 // Scoped pointer is still valid
830 return std::string(unique_dir);
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
839 bool mkdir(const std::string &path, const mode_t &mode, std::string *error)
841 if ( ::mkdir(path.c_str(), mode) == 0)
845 *error = strerror(errno);
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
855 bool rmdir(const std::string &path, std::string *error)
857 if ( ::rmdir(path.c_str() ) == 0)
861 *error = strerror(errno);
865 /// Small helper class for scoped free
869 scoped_C_free(void *ptr)
870 : pointer_to_free(ptr)
876 free (pointer_to_free);
877 pointer_to_free = NULL;
881 void *pointer_to_free;
885 Get current working directory
886 @return Current working directory. Empty string on error.
890 char *cwd = ::getcwd(NULL, 0);
894 // Make deallocation of cwd exception safe
895 scoped_C_free holder(cwd);
897 string current_dir(cwd);
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
907 bool chdir(const std::string &path, std::string *error)
909 if ( ::chdir(path.c_str() ) == 0)
913 *error = strerror(errno);
918 Set file mode creation mask
919 @param mask Creation mask
920 @return Previous creation mask (function call always succeeds)
922 mode_t umask(mode_t mask)
924 return ::umask(mask);
929 * @brief Remove unlisted files
931 * @param directory Directory to look for files
932 * @param keep_files List of files or directories to keep
933 * @param prefix Filename prefix to match. Empty prefix matches all.
935 * @return bool True if the directory was scanned, false on error (directory not found, permission denied)
937 bool remove_unlisted_files(const std::string &directory,
938 const std::set<std::string> &keep_files,
939 const std::string &prefix)
941 std::vector<std::string> content;
942 if (!get_dir(directory, content, false))
945 bool all_fine = true;
946 BOOST_FOREACH(const std::string &file, content)
948 // Check for filename prefix (if any)
949 if (!prefix.empty() && file.find(prefix) != 0)
952 // Check if file is whitelisted
953 if (keep_files.find(file) != keep_files.end())
956 // Try to unlink file. (Continue on error)
957 if (!unlink(directory + "/" + file))
965 * @brief Get free size in bytes on a given path or filename
967 * @param path Directory or filename to look in
969 * @return Number of bytes available to a regular user, -1 in case of an error
971 long long get_free_diskspace(const std::string& path)
977 while ( ((ret=statvfs(path.c_str(),&sf)) == -1) && (errno==EINTR) && looplimit > 0)
982 // a real error occured
986 long long free_bytes=0;
989 free_bytes=sf.f_bsize;
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;
999 } // eo namespace I2n