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>
43 #include <boost/scoped_array.hpp>
44 #include <boost/foreach.hpp>
45 #include "filefunc.hxx"
46 #include "stringfunc.hxx"
54 ** implementation of Stat
65 Stat::Stat(const std::string& path, bool follow_links)
67 stat(path,follow_links);
68 } // eo Stat::Stat(const std::string&,bool)
77 * @brief updates the internal data.
79 * In other words: stat()'s the file again.
85 // pass a copy of Path: otherwise clear() would leave an empty reference
86 stat(string(Path), FollowLinks);
88 } // eo Stat::recheck()
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.
97 void Stat::stat(const std::string& path, bool follow_links)
101 FollowLinks= follow_links;
102 struct stat stat_info;
104 res = ( follow_links ? ::stat(path.c_str(), &stat_info) : ::lstat(path.c_str(), &stat_info) );
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;
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 );
128 } // eo Stat::stat(const std::string&,bool)
132 * @brief clears the internal data.
154 IsCharacterDevice= false;
155 IsBlockDevice= false;
158 } // eo Stat::clear()
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.
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)!
169 bool Stat::is_same_as(const Stat& rhs)
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);
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.
182 * "Same device" means that the devices have the same type and the same major and minor id.
184 bool Stat::is_same_device_as(const Stat& rhs)
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&)
193 * @brief check existence of a path.
194 * @param path path which should be tested.
195 * @return @a true iff path exists.
197 bool path_exists(const std::string& path)
199 struct stat stat_info;
200 int res = ::stat(path.c_str(), &stat_info);
201 if (res) return false;
203 } // eo path_exists(const std::string&)
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.
212 bool file_exists(const std::string& path)
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&)
220 // TODO: Use Stat class
223 * @param name filename to get size for
224 * @return file size or -1 if file does not exist
226 long file_size (const string &name)
230 struct stat statbuff;
232 if (lstat(name.c_str(), &statbuff) < 0)
235 if (!S_ISREG(statbuff.st_mode))
238 iReturn=statbuff.st_size;
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.
248 time_t file_mtime(const std::string& path)
250 struct stat stat_info;
251 int res = ::stat(path.c_str(), &stat_info);
253 return stat_info.st_mtime;
254 } // eo file_mtime(const std::string&)
258 * @brief Check if two files differ
260 * Note: Reads the whole file into memory
261 * if the file size is identical.
263 * @param old_filename Filename of old file
264 * @param new_filename Filename of new file
266 * @return bool True if files differ, false otherwise.
267 * If one file does not exist, also returns true
269 bool file_content_differs(const std::string &old_filename, const std::string &new_filename)
271 if (I2n::file_exists(old_filename) == false ||
272 I2n::file_exists(new_filename) == false)
275 // check if size differs
276 if (I2n::file_size(old_filename) != I2n::file_size(new_filename))
279 const std::string old_content = I2n::read_file(old_filename);
280 const std::string new_content = I2n::read_file(new_filename);
282 // check if content differs
283 if (old_content == new_content)
286 // Differ by default (fallback)
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.
299 const std::string& path,
300 std::vector< std::string >& result,
301 bool include_dot_names )
303 DIR* dir = ::opendir( path.c_str());
308 struct dirent store, *entry = NULL;
309 while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
311 std::string name( entry->d_name );
312 if (! include_dot_names && (name[0] == '.') )
316 result.push_back( name );
320 } // eo get_dir(const std::string&,std::vector< std::string >&,bool)
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).
329 std::vector< std::string > get_dir(const std::string& path, bool include_dot_names )
331 std::vector< std::string > result;
332 get_dir(path,result,include_dot_names);
334 } // eo get_dir(const std::string&,bool)
339 * @brief removes a file from a filesystem.
340 * @param path path to the file.
341 * @return @a true if the unlink was successful.
343 bool unlink( const std::string& path )
345 int res = ::unlink( path.c_str() );
347 } // eo unlink(const std::string&)
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.
358 bool symlink(const std::string& target, const std::string& link_name, bool force)
361 if (target.empty() or link_name.empty() or target == link_name)
363 // no, we don't do this!
366 std::string n_target;
367 if (target[0] == '/') // absolute target?
371 else // relative target
373 // for stat'ing: prepend dir of link_name:
374 n_target= dirname(link_name)+"/"+ target;
376 Stat target_stat(n_target, false);
377 Stat link_name_stat(link_name, false);
378 if (target_stat.exists() && link_name_stat.exists())
380 if (link_name_stat.is_same_as(target_stat)
381 or link_name_stat.is_same_device_as(target_stat) )
385 //TODO: more consistency checks?!
387 if (link_name_stat.exists())
389 // the link name already exists.
392 // "force" as given, so try to remove the link_name:
395 link_name_stat.recheck();
398 if (link_name_stat.exists())
400 // well, if the link_name still exists; we cannot create that link:
404 res= ::symlink(target.c_str(), link_name.c_str());
406 } // eo symlink(const std::string&,const std::string&,bool)
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.
415 std::string read_link(const std::string& path)
418 Stat stat(path,false);
419 if (!stat || !stat.is_link())
421 return std::string();
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 );
428 return std::string( buffer_ptr.get(), res );
430 return std::string();
431 } // eo read_link(const std::string&)
436 * @brief returns content of a file as string.
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).
442 * @param path path to the file.
443 * @return the content of the file as string (empty if file could be opened).
445 std::string read_file(const std::string& path)
450 return std::string();
452 std::ifstream f( path.c_str(), std::ios::in | std::ios::binary );
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!
461 // if we know the size, we reserve enough space.
462 result.reserve( stat.size() );
467 f.read(buffer, sizeof(buffer));
468 result.append(buffer, f.gcount());
472 } // eo read_file(const std::string&)
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.
482 * A simple (q'n'd) function for writing a string to a file.
484 bool write_file(const std::string& path, const std::string& data, bool trunc)
486 // set the correct openmode flags
487 std::ios_base::openmode flags = std::ios::out | std::ios::binary;
489 flags |= std::ios::trunc;
491 std::ofstream f( path.c_str(), flags);
494 f.write( data.data(), data.size() );
501 } // eo write_file(const std::string&,const std::string&)
505 * Copy file in 4k blocks from source to target.
506 * Overwrites the target if it already exists.
508 * On error the target file gets removed.
510 * @param src source file
511 * @param dest target file
512 * @return true if all is ok, false on error
514 bool copy_file(const std::string& src, const std::string& dest)
516 std::ifstream input( src.c_str(), std::ios::in | std::ios::binary );
520 std::ofstream output( dest.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
524 // Out of disc space?
525 if (!copy_stream(input,output))
536 * Copy streams in 4k blocks.
538 * @param is source stream
539 * @param os target stream
540 * @return true if all is ok, false on error
542 bool copy_stream(std::istream& is, std::ostream& os)
553 is.read(buffer, sizeof(buffer));
554 os.write(buffer, is.gcount());
565 * @brief returns the filename part of a path (last component)
566 * @param path the path.
567 * @return the last component of the path.
569 std::string basename(const std::string& path)
571 std::string::size_type pos= path.rfind('/');
572 if (pos != std::string::npos)
574 return path.substr(pos+1);
577 } // eo basename(const std::string&)
581 * @brief returns the directory part of a path.
582 * @param path the path.
583 * @return the directory part of the path.
585 std::string dirname(const std::string& path)
587 std::string::size_type pos= path.rfind('/');
588 if (pos != std::string::npos)
590 std::string result(path,0,pos);
598 } // eo dirname(const std::string&)
602 * @brief normalizes a path.
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.
609 * @param path the path which should be normalized.
610 * @return the normalized path.
613 std::string normalize_path(const std::string& path)
617 return std::string();
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;
625 split_string(path,parts,"/",true);
627 for(std::list< std::string >::const_iterator it_parts= parts.begin();
628 it_parts != parts.end();
631 std::string part(*it_parts); //convenience..
632 if (part == std::string(".") )
634 // single dot is "current path"; ignore!
637 if (part == std::string("..") )
639 // double dot is "one part back"
640 if (result_parts.empty())
644 // ignore since we cannot move behind / on absolute paths...
648 // on relative path, we need to store the "..":
649 result_parts.push_back(part);
652 else if (result_parts.back() == std::string("..") )
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);
660 // remove last element.
661 result_parts.pop_back();
665 result_parts.push_back(part);
672 result+= join_string(result_parts,"/");
674 } // eo normalize_path(const std::string&)
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
682 bool dirsync(const std::string& path)
684 // sync the directory the file is in
685 DIR* dir=opendir(path.c_str());
689 int ret=fsync(dirfd(dir));
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.
702 bool chmod(const std::string& path, int mode)
704 int res= ::chmod(path.c_str(), mode);
706 } // eo chmod(const std::string&,int)
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.
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.
720 bool chown(const std::string& path, const I2n::User& user, const I2n::Group& group)
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);
729 } // eo chown(const std::string&,const User&,const Group&)
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
738 bool recursive_delete(const std::string &path,
739 bool keep_parent_dir,
746 if (stat(path.c_str(), &my_stat) != 0) {
747 throw runtime_error("can't stat " + path);
750 if (S_ISDIR(my_stat.st_mode)) {
751 DIR *dir = opendir(path.c_str());
753 throw runtime_error("can't open directory " + path);
756 struct dirent store, *entry = NULL;
757 while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
759 string filename = entry->d_name;
760 if (filename == "." || filename == "..") {
764 // Delete subdir or file.
765 rtn = recursive_delete(path + "/" + filename, false, error);
772 if (keep_parent_dir == false && !rmdir(path)) {
773 throw runtime_error("can't remove directory " + path);
777 throw runtime_error("can't unlink " + path);
780 } catch (exception &e) {
783 out << e.what() << " (" << strerror(errno) << ")";
790 out << "unknown error (" << strerror(errno) << ")";
797 } // eo recursive_delete(const std::string&,std::string*)
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.
805 std::string mkdtemp(const std::string &path_template, std::string *error)
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;
811 char *unique_dir = ::mkdtemp(buf.get());
815 *error = strerror(errno);
819 // Scoped pointer is still valid
820 return std::string(unique_dir);
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
829 bool mkdir(const std::string &path, const mode_t &mode, std::string *error)
831 if ( ::mkdir(path.c_str(), mode) == 0)
835 *error = strerror(errno);
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
845 bool rmdir(const std::string &path, std::string *error)
847 if ( ::rmdir(path.c_str() ) == 0)
851 *error = strerror(errno);
855 /// Small helper class for scoped free
859 scoped_C_free(void *ptr)
860 : pointer_to_free(ptr)
866 free (pointer_to_free);
867 pointer_to_free = NULL;
871 void *pointer_to_free;
875 Get current working directory
876 @return Current working directory. Empty string on error.
880 char *cwd = ::getcwd(NULL, 0);
884 // Make deallocation of cwd exception safe
885 scoped_C_free holder(cwd);
887 string current_dir(cwd);
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
897 bool chdir(const std::string &path, std::string *error)
899 if ( ::chdir(path.c_str() ) == 0)
903 *error = strerror(errno);
908 Set file mode creation mask
909 @param mask Creation mask
910 @return Previous creation mask (function call always succeeds)
912 mode_t umask(mode_t mask)
914 return ::umask(mask);
919 * @brief Remove unlisted files
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.
925 * @return bool True if the directory was scanned, false on error (directory not found, permission denied)
927 bool remove_unlisted_files(const std::string &directory,
928 const std::set<std::string> &keep_files,
929 const std::string &prefix)
931 std::vector<std::string> content;
932 if (!get_dir(directory, content, false))
935 bool all_fine = true;
936 BOOST_FOREACH(const std::string &file, content)
938 // Check for filename prefix (if any)
939 if (!prefix.empty() && file.find(prefix) != 0)
942 // Check if file is whitelisted
943 if (keep_files.find(file) != keep_files.end())
946 // Try to unlink file. (Continue on error)
947 if (!unlink(directory + "/" + file))
955 } // eo namespace I2n