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
64 Stat::Stat(const std::string& path, bool follow_links)
66 stat(path,follow_links);
67 } // eo Stat::Stat(const std::string&,bool)
76 * @brief updates the internal data.
78 * In other words: stat()'s the file again.
84 // pass a copy of Path: otherwise clear() would leave an empty reference
85 stat(string(Path), FollowLinks);
87 } // eo Stat::recheck()
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.
96 void Stat::stat(const std::string& path, bool follow_links)
100 FollowLinks= follow_links;
101 struct stat stat_info;
103 res = ( follow_links ? ::stat(path.c_str(), &stat_info) : ::lstat(path.c_str(), &stat_info) );
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;
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 );
122 IsBlockDevice= S_ISBLK( stat_info.st_mode );
123 IsFifo= S_ISFIFO( stat_info.st_mode );
124 IsSocket= S_ISSOCK( stat_info.st_mode );
127 } // eo Stat::stat(const std::string&,bool)
131 * @brief clears the internal data.
153 IsCharacterDevice= false;
154 IsBlockDevice= false;
157 } // eo Stat::clear()
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.
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)!
168 bool Stat::is_same_as(const Stat& rhs)
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);
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.
181 * "Same device" means that the devices have the same type and the same major and minor id.
183 bool Stat::is_same_device_as(const Stat& rhs)
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&)
192 * @brief check existence of a path.
193 * @param path path which should be tested.
194 * @return @a true iff path exists.
196 bool path_exists(const std::string& path)
198 struct stat stat_info;
199 int res = ::stat(path.c_str(), &stat_info);
200 if (res) return false;
202 } // eo path_exists(const std::string&)
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.
211 bool file_exists(const std::string& path)
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&)
219 // TODO: Use Stat class
222 * @param name filename to get size for
223 * @return file size or -1 if file does not exist
225 long file_size (const string &name)
229 struct stat statbuff;
231 if (lstat(name.c_str(), &statbuff) < 0)
234 if (!S_ISREG(statbuff.st_mode))
237 iReturn=statbuff.st_size;
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.
247 time_t file_mtime(const std::string& path)
249 struct stat stat_info;
250 int res = ::stat(path.c_str(), &stat_info);
252 return stat_info.st_mtime;
253 } // eo file_mtime(const std::string&)
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.
264 const std::string& path,
265 std::vector< std::string >& result,
266 bool include_dot_names )
268 DIR* dir = ::opendir( path.c_str());
273 struct dirent store, *entry = NULL;
274 while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
276 std::string name( entry->d_name );
277 if (! include_dot_names && (name[0] == '.') )
281 result.push_back( name );
285 } // eo get_dir(const std::string&,std::vector< std::string >&,bool)
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).
294 std::vector< std::string > get_dir(const std::string& path, bool include_dot_names )
296 std::vector< std::string > result;
297 get_dir(path,result,include_dot_names);
299 } // eo get_dir(const std::string&,bool)
304 * @brief removes a file from a filesystem.
305 * @param path path to the file.
306 * @return @a true if the unlink was successful.
308 bool unlink( const std::string& path )
310 int res = ::unlink( path.c_str() );
312 } // eo unlink(const std::string&)
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.
323 bool symlink(const std::string& target, const std::string& link_name, bool force)
326 if (target.empty() or link_name.empty() or target == link_name)
328 // no, we don't do this!
331 std::string n_target;
332 if (target[0] == '/') // absolute target?
336 else // relative target
338 // for stat'ing: prepend dir of link_name:
339 n_target= dirname(link_name)+"/"+ target;
341 Stat target_stat(n_target, false);
342 Stat link_name_stat(link_name, false);
343 if (target_stat.exists() && link_name_stat.exists())
345 if (link_name_stat.is_same_as(target_stat)
346 or link_name_stat.is_same_device_as(target_stat) )
350 //TODO: more consistency checks?!
352 if (link_name_stat.exists())
354 // the link name already exists.
357 // "force" as given, so try to remove the link_name:
360 link_name_stat.recheck();
363 if (link_name_stat.exists())
365 // well, if the link_name still exists; we cannot create that link:
369 res= ::symlink(target.c_str(), link_name.c_str());
371 } // eo symlink(const std::string&,const std::string&,bool)
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.
380 std::string read_link(const std::string& path)
383 Stat stat(path,false);
384 if (!stat || !stat.is_link())
386 return std::string();
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 );
393 return std::string( buffer_ptr.get(), res );
395 return std::string();
396 } // eo read_link(const std::string&)
401 * @brief returns content of a file as string.
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).
407 * @param path path to the file.
408 * @return the content of the file as string (empty if file could be opened).
410 std::string read_file(const std::string& path)
415 return std::string();
417 std::ifstream f( path.c_str(), std::ios::in | std::ios::binary );
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!
426 // if we know the size, we reserve enough space.
427 result.reserve( stat.size() );
432 f.read(buffer, sizeof(buffer));
433 result.append(buffer, f.gcount());
437 } // eo read_file(const std::string&)
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
444 * @param trunc set the trunc flag when opening the file. Do not use for files in /proc and /sys
445 * @return @a true if the data was written to the file.
447 * A simple (q'n'd) function for writing a string to a file.
449 bool write_file(const std::string& path, const std::string& data, bool trunc)
451 // set the correct openmode flags
452 std::ios_base::openmode flags = std::ios::out | std::ios::binary;
454 flags |= std::ios::trunc;
456 std::ofstream f( path.c_str(), flags);
459 f.write( data.data(), data.size() );
466 } // eo write_file(const std::string&,const std::string&)
470 * Copy file in 4k blocks from source to target.
471 * Overwrites the target if it already exists.
473 * On error the target file gets removed.
475 * @param src source file
476 * @param dest target file
477 * @return true if all is ok, false on error
479 bool copy_file(const std::string& src, const std::string& dest)
481 std::ifstream input( src.c_str(), std::ios::in | std::ios::binary );
485 std::ofstream output( dest.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
489 // Out of disc space?
490 if (!copy_stream(input,output))
501 * Copy streams in 4k blocks.
503 * @param is source stream
504 * @param os target stream
505 * @return true if all is ok, false on error
507 bool copy_stream(std::istream& is, std::ostream& os)
518 is.read(buffer, sizeof(buffer));
519 os.write(buffer, is.gcount());
530 * @brief returns the filename part of a path (last component)
531 * @param path the path.
532 * @return the last component of the path.
534 std::string basename(const std::string& path)
536 std::string::size_type pos= path.rfind('/');
537 if (pos != std::string::npos)
539 return path.substr(pos+1);
542 } // eo basename(const std::string&)
546 * @brief returns the directory part of a path.
547 * @param path the path.
548 * @return the directory part of the path.
550 std::string dirname(const std::string& path)
552 std::string::size_type pos= path.rfind('/');
553 if (pos != std::string::npos)
555 std::string result(path,0,pos);
563 } // eo dirname(const std::string&)
567 * @brief normalizes a path.
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.
574 * @param path the path which should be normalized.
575 * @return the normalized path.
578 std::string normalize_path(const std::string& path)
582 return std::string();
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;
590 split_string(path,parts,"/",true);
592 for(std::list< std::string >::const_iterator it_parts= parts.begin();
593 it_parts != parts.end();
596 std::string part(*it_parts); //convenience..
597 if (part == std::string(".") )
599 // single dot is "current path"; ignore!
602 if (part == std::string("..") )
604 // double dot is "one part back"
605 if (result_parts.empty())
609 // ignore since we cannot move behind / on absolute paths...
613 // on relative path, we need to store the "..":
614 result_parts.push_back(part);
617 else if (result_parts.back() == std::string("..") )
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);
625 // remove last element.
626 result_parts.pop_back();
630 result_parts.push_back(part);
637 result+= join_string(result_parts,"/");
639 } // eo normalize_path(const std::string&)
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
647 bool dirsync(const std::string& path)
649 // sync the directory the file is in
650 DIR* dir=opendir(path.c_str());
654 int ret=fsync(dirfd(dir));
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.
667 bool chmod(const std::string& path, int mode)
669 int res= ::chmod(path.c_str(), mode);
671 } // eo chmod(const std::string&,int)
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.
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.
685 bool chown(const std::string& path, const I2n::User& user, const I2n::Group& group)
688 if (uid<0) return false;
689 gid_t gid= group.Gid;
690 if (gid<0) gid= user.Gid;
691 if (gid<0) return false;
692 int res= ::chown( path.c_str(), uid, gid);
694 } // eo chown(const std::string&,const User&,const Group&)
697 * Recursive delete of files and directories
698 * @param path File or directory to delete
699 * @param error Will contain the error if the return value is false [optional]
700 * @return true on success, false otherwise
702 bool recursive_delete(const std::string &path, std::string *error)
708 if (stat(path.c_str(), &my_stat) != 0) {
709 throw runtime_error("can't stat " + path);
712 if (S_ISDIR(my_stat.st_mode)) {
713 DIR *dir = opendir(path.c_str());
715 throw runtime_error("can't open directory " + path);
718 struct dirent store, *entry = NULL;
719 while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
721 string filename = entry->d_name;
722 if (filename == "." || filename == "..") {
726 // Delete subdir or file.
727 rtn = recursive_delete(path + "/" + filename, error);
735 throw runtime_error("can't remove directory " + path);
739 throw runtime_error("can't unlink " + path);
742 } catch (exception &e) {
745 out << e.what() << " (" << strerror(errno) << ")";
752 out << "unknown error (" << strerror(errno) << ")";
759 } // eo recursive_delete(const std::string&,std::string*)
762 Create a unique temporary directory from path_template.
763 @param Path template. The last six characters must be XXXXXX.
764 @param error Will contain the error if the return value is false [optional]
765 @return Name of new directory or empty string on error.
767 std::string mkdtemp(const std::string &path_template, std::string *error)
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;
773 char *unique_dir = ::mkdtemp(buf.get());
777 *error = strerror(errno);
781 // Scoped pointer is still valid
782 return std::string(unique_dir);
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
791 bool mkdir(const std::string &path, const mode_t &mode, std::string *error)
793 if ( ::mkdir(path.c_str(), mode) == 0)
797 *error = strerror(errno);
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
807 bool rmdir(const std::string &path, std::string *error)
809 if ( ::rmdir(path.c_str() ) == 0)
813 *error = strerror(errno);
817 /// Small helper class for scoped free
821 scoped_C_free(void *ptr)
822 : pointer_to_free(ptr)
828 free (pointer_to_free);
829 pointer_to_free = NULL;
833 void *pointer_to_free;
837 Get current working directory
838 @return Current working directory. Empty string on error.
842 char *cwd = ::getcwd(NULL, 0);
846 // Make deallocation of cwd exception safe
847 scoped_C_free holder(cwd);
849 string current_dir(cwd);
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
859 bool chdir(const std::string &path, std::string *error)
861 if ( ::chdir(path.c_str() ) == 0)
865 *error = strerror(errno);
870 Set file mode creation mask
871 @param mask Creation mask
872 @return Previous creation mask (function call always succeeds)
874 mode_t umask(mode_t mask)
876 return ::umask(mask);
881 * @brief Remove unlisted files
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.
887 * @return bool True if the directory was scanned, false on error (directory not found, permission denied)
889 bool remove_unlisted_files(const std::string &directory,
890 const std::set<std::string> &keep_files,
891 const std::string &prefix)
893 std::vector<std::string> content;
894 if (!get_dir(directory, content, false))
897 bool all_fine = true;
898 BOOST_FOREACH(const std::string &file, content)
900 // Check for filename prefix (if any)
901 if (!prefix.empty() && file.find(prefix) != 0)
904 // Check if file is whitelisted
905 if (keep_files.find(file) != keep_files.end())
908 // Try to unlink file. (Continue on error)
909 if (!unlink(directory + "/" + file))
917 } // eo namespace I2n