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 "filefunc.hxx"
45 #include "stringfunc.hxx"
53 ** implementation of Stat
63 Stat::Stat(const std::string& path, bool follow_links)
65 stat(path,follow_links);
66 } // eo Stat::Stat(const std::string&,bool)
75 * @brief updates the internal data.
77 * In other words: stat()'s the file again.
83 // pass a copy of Path: otherwise clear() would leave an empty reference
84 stat(string(Path), FollowLinks);
86 } // eo Stat::recheck()
90 * @brief calls stat() or lstat() to get the information for the given path
91 * and stores that information.
92 * @param path the path which should be checked
93 * @param follow_links determine if (symbalic) links should be followed.
95 void Stat::stat(const std::string& path, bool follow_links)
99 FollowLinks= follow_links;
100 struct stat stat_info;
102 res = ( follow_links ? ::stat(path.c_str(), &stat_info) : ::lstat(path.c_str(), &stat_info) );
105 Device = stat_info.st_dev;
106 Inode = stat_info.st_ino;
107 Mode = (stat_info.st_mode & ~(S_IFMT));
108 NumLinks = stat_info.st_nlink;
109 Uid = stat_info.st_uid;
110 Gid = stat_info.st_gid;
111 DeviceType = stat_info.st_rdev;
112 Size = stat_info.st_size;
113 Atime = stat_info.st_atime;
114 Mtime = stat_info.st_mtime;
115 Ctime = stat_info.st_atime;
117 IsLink= S_ISLNK( stat_info.st_mode );
118 IsRegular= S_ISREG( stat_info.st_mode );
119 IsDirectory= S_ISDIR( stat_info.st_mode );
120 IsCharacterDevice= S_ISCHR( stat_info.st_mode );
121 IsBlockDevice= S_ISBLK( stat_info.st_mode );
122 IsFifo= S_ISFIFO( stat_info.st_mode );
123 IsSocket= S_ISSOCK( stat_info.st_mode );
126 } // eo Stat::stat(const std::string&,bool)
130 * @brief clears the internal data.
152 IsCharacterDevice= false;
153 IsBlockDevice= false;
156 } // eo Stat::clear()
160 * @brief checks if another instance describes the same file.
161 * @param rhs the other instance.
162 * @return @a true iff the other instance describes the same file.
164 * The "same file" means that the files are located on the same device and use the same inode.
165 * They might still have two different directory entries (different paths)!
167 bool Stat::is_same_as(const Stat& rhs)
169 return Valid and rhs.Valid
170 and ( Device == rhs.Device)
171 and ( Inode == rhs.Inode);
172 } // eo Stat::is_same_as(const Stat& rhs);
176 * @brief checks if this and the other instance describe the same device.
177 * @param rhs the other instance.
178 * @return @a true if we and the other instance describe a device and the same device.
180 * "Same device" means that the devices have the same type and the same major and minor id.
182 bool Stat::is_same_device_as(const Stat& rhs)
184 return is_device() and rhs.is_device()
185 and ( IsBlockDevice == rhs.IsBlockDevice )
186 and ( IsCharacterDevice == rhs.IsCharacterDevice )
187 and ( DeviceType == rhs.DeviceType);
188 } // eo Stat::is_same_device_as(const Stat&)
191 * @brief check existence of a path.
192 * @param path path which should be tested.
193 * @return @a true iff path exists.
195 bool path_exists(const std::string& path)
197 struct stat stat_info;
198 int res = ::stat(path.c_str(), &stat_info);
199 if (res) return false;
201 } // eo path_exists(const std::string&)
204 * @brief check existence of a regular file.
205 * @param path path which should be tested.
206 * @return @a true if path exists and is a regular file (or a link pointing to a regular file).
207 * @note this checks for regular files; not for the pure exitsnace of a path; use pathExists() for that.
210 bool file_exists(const std::string& path)
212 struct stat stat_info;
213 int res = ::stat(path.c_str(), &stat_info);
214 if (res) return false;
215 return S_ISREG(stat_info.st_mode);
216 } // eo file_exists(const std::string&)
218 // TODO: Use Stat class
221 * @param name filename to get size for
222 * @return file size or -1 if file does not exist
224 long file_size (const string &name)
228 struct stat statbuff;
230 if (lstat(name.c_str(), &statbuff) < 0)
233 if (!S_ISREG(statbuff.st_mode))
236 iReturn=statbuff.st_size;
242 * @brief tests the last modification time stamp of a path.
243 * @param path path which should be tested.
244 * @return the last modification time or 0 if the path doen't exist.
246 time_t file_mtime(const std::string& path)
248 struct stat stat_info;
249 int res = ::stat(path.c_str(), &stat_info);
251 return stat_info.st_mtime;
252 } // eo file_mtime(const std::string&)
256 * @brief reads the contents of a directory.
257 * @param path the path to the directory whose contents should be read.
258 * @param[out] result the resulting list of names.
259 * @param include_dot_names determines if dot-files should be included in the list.
260 * @return @a true if reading the directory was succesful, @a false on error.
263 const std::string& path,
264 std::vector< std::string >& result,
265 bool include_dot_names )
267 DIR* dir = ::opendir( path.c_str());
272 struct dirent store, *entry = NULL;
273 while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
275 std::string name( entry->d_name );
276 if (! include_dot_names && (name[0] == '.') )
280 result.push_back( name );
284 } // eo get_dir(const std::string&,std::vector< std::string >&,bool)
288 * @brief reads the contents of a directory
289 * @param path the path to the directory whose contents should be read.
290 * @param include_dot_names determines if dot-files should be included in the list.
291 * @return the list of names (empty on error).
293 std::vector< std::string > get_dir(const std::string& path, bool include_dot_names )
295 std::vector< std::string > result;
296 get_dir(path,result,include_dot_names);
298 } // eo get_dir(const std::string&,bool)
303 * @brief removes a file from a filesystem.
304 * @param path path to the file.
305 * @return @a true if the unlink was successful.
307 bool unlink( const std::string& path )
309 int res = ::unlink( path.c_str() );
311 } // eo unlink(const std::string&)
316 * @brief creates a symbolic link named @a link_name to @a target.
317 * @param target the target the link should point to.
318 * @param link_name the name of the link.
319 * @param force if @a true, the (file or link) @a link_name is removed if it exists.
320 * @return @a true iff the symlink was successfully created.
322 bool symlink(const std::string& target, const std::string& link_name, bool force)
325 if (target.empty() or link_name.empty() or target == link_name)
327 // no, we don't do this!
330 std::string n_target;
331 if (target[0] == '/') // absolute target?
335 else // relative target
337 // for stat'ing: prepend dir of link_name:
338 n_target= dirname(link_name)+"/"+ target;
340 Stat target_stat(n_target, false);
341 Stat link_name_stat(link_name, false);
342 if (target_stat.exists() && link_name_stat.exists())
344 if (link_name_stat.is_same_as(target_stat)
345 or link_name_stat.is_same_device_as(target_stat) )
349 //TODO: more consistency checks?!
351 if (link_name_stat.exists())
353 // the link name already exists.
356 // "force" as given, so try to remove the link_name:
359 link_name_stat.recheck();
362 if (link_name_stat.exists())
364 // well, if the link_name still exists; we cannot create that link:
368 res= ::symlink(target.c_str(), link_name.c_str());
370 } // eo symlink(const std::string&,const std::string&,bool)
375 * @brief reads the target of a symbolic link.
376 * @param path path to the symbolic link
377 * @return the target of the link or an empty string on error.
379 std::string read_link(const std::string& path)
382 Stat stat(path,false);
383 if (!stat || !stat.is_link())
385 return std::string();
387 int buffer_size= PATH_MAX+1 + 128;
388 boost::scoped_array<char> buffer_ptr( new char[buffer_size] );
389 int res= ::readlink( path.c_str(), buffer_ptr.get(), buffer_size-1 );
392 return std::string( buffer_ptr.get(), res );
394 return std::string();
395 } // eo read_link(const std::string&)
400 * @brief returns content of a file as string.
402 * A simple (q'n'd) function for retrieving content of a file as string.<br>
403 * Also able to read special (but regular) files which don't provide a size when stat'ed
404 * (like files in the /proc filesystem).
406 * @param path path to the file.
407 * @return the content of the file as string (empty if file could be opened).
409 std::string read_file(const std::string& path)
414 return std::string();
416 std::ifstream f( path.c_str(), std::ios::in | std::ios::binary );
420 // NOTE: there are cases where we don't know the correct size (/proc files...)
421 // therefore we only use the size for reserving space if we know it, but don't
422 // use it when reading the file!
425 // if we know the size, we reserve enough space.
426 result.reserve( stat.size() );
431 f.read(buffer, sizeof(buffer));
432 result.append(buffer, f.gcount());
436 } // eo read_file(const std::string&)
440 * @brief writes a string to a file.
441 * @param path path to the file
442 * @param data the data which should be written into the file
443 * @param trunc set the trunc flag when opening the file. Do not use for files in /proc and /sys
444 * @return @a true if the data was written to the file.
446 * A simple (q'n'd) function for writing a string to a file.
448 bool write_file(const std::string& path, const std::string& data, bool trunc)
450 // set the correct openmode flags
451 std::ios_base::openmode flags = std::ios::out | std::ios::binary;
453 flags |= std::ios::trunc;
455 std::ofstream f( path.c_str(), flags);
458 f.write( data.data(), data.size() );
465 } // eo write_file(const std::string&,const std::string&)
469 * Copy file in 4k blocks from source to target.
470 * Overwrites the target if it already exists.
472 * On error the target file gets removed.
474 * @param src source file
475 * @param dest target file
476 * @return true if all is ok, false on error
478 bool copy_file(const std::string& src, const std::string& dest)
480 std::ifstream input( src.c_str(), std::ios::in | std::ios::binary );
484 std::ofstream output( dest.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
488 // Out of disc space?
489 if (!copy_stream(input,output))
500 * Copy streams in 4k blocks.
502 * @param is source stream
503 * @param os target stream
504 * @return true if all is ok, false on error
506 bool copy_stream(std::istream& is, std::ostream& os)
517 is.read(buffer, sizeof(buffer));
518 os.write(buffer, is.gcount());
529 * @brief returns the filename part of a path (last component)
530 * @param path the path.
531 * @return the last component of the path.
533 std::string basename(const std::string& path)
535 std::string::size_type pos= path.rfind('/');
536 if (pos != std::string::npos)
538 return path.substr(pos+1);
541 } // eo basename(const std::string&)
545 * @brief returns the directory part of a path.
546 * @param path the path.
547 * @return the directory part of the path.
549 std::string dirname(const std::string& path)
551 std::string::size_type pos= path.rfind('/');
552 if (pos != std::string::npos)
554 std::string result(path,0,pos);
562 } // eo dirname(const std::string&)
566 * @brief normalizes a path.
568 * This method removes empty and "." elements.
569 * It also resolves ".." parts by removing previous path elements if possible.
570 * Leading ".." elements are preserved when a relative path was given; else they are removed.
571 * Trailing slashes are removed.
573 * @param path the path which should be normalized.
574 * @return the normalized path.
577 std::string normalize_path(const std::string& path)
581 return std::string();
583 // remember if the given path was absolute since this information vanishes when
584 // we split the path (since we split with omitting empty parts...)
585 bool is_absolute= (path[0]=='/');
586 std::list< std::string > parts;
587 std::list< std::string > result_parts;
589 split_string(path,parts,"/",true);
591 for(std::list< std::string >::const_iterator it_parts= parts.begin();
592 it_parts != parts.end();
595 std::string part(*it_parts); //convenience..
596 if (part == std::string(".") )
598 // single dot is "current path"; ignore!
601 if (part == std::string("..") )
603 // double dot is "one part back"
604 if (result_parts.empty())
608 // ignore since we cannot move behind / on absolute paths...
612 // on relative path, we need to store the "..":
613 result_parts.push_back(part);
616 else if (result_parts.back() == std::string("..") )
618 // if last element was already "..", we need to store the new one again...
619 // (PS: no need for "absolute" check; this can only be the case on relative path)
620 result_parts.push_back(part);
624 // remove last element.
625 result_parts.pop_back();
629 result_parts.push_back(part);
636 result+= join_string(result_parts,"/");
638 } // eo normalize_path(const std::string&)
642 * @brief calls fsync on a given directory to sync all it's metadata
643 * @param path the path of the directory.
644 * @return true if successful
646 bool dirsync(const std::string& path)
648 // sync the directory the file is in
649 DIR* dir=opendir(path.c_str());
653 int ret=fsync(dirfd(dir));
661 * @brief changes the file(/path) mode.
662 * @param path the path to change the mode for.
663 * @param mode the new file mode.
664 * @return @a true iff the file mode was sucessfully changed.
666 bool chmod(const std::string& path, int mode)
668 int res= ::chmod(path.c_str(), mode);
670 } // eo chmod(const std::string&,int)
674 * @brief changed the owner of a file(/path)
675 * @param path the path to change the owner for.
676 * @param user the new file owner.
677 * @param group the new file group.
678 * @return @a true iff the file owner was succesfully changed.
681 * the validity of user and group within the system is not checked.
682 * This is intentional since this way we can use id's which are not assigned.
684 bool chown(const std::string& path, const I2n::User& user, const I2n::Group& group)
687 if (uid<0) return false;
688 gid_t gid= group.Gid;
689 if (gid<0) gid= user.Gid;
690 if (gid<0) return false;
691 int res= ::chown( path.c_str(), uid, gid);
693 } // eo chown(const std::string&,const User&,const Group&)
696 * Recursive delete of files and directories
697 * @param path File or directory to delete
698 * @param error Will contain the error if the return value is false [optional]
699 * @return true on success, false otherwise
701 bool recursive_delete(const std::string &path, std::string *error)
707 if (stat(path.c_str(), &my_stat) != 0) {
708 throw runtime_error("can't stat " + path);
711 if (S_ISDIR(my_stat.st_mode)) {
712 DIR *dir = opendir(path.c_str());
714 throw runtime_error("can't open directory " + path);
717 struct dirent store, *entry = NULL;
718 while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
720 string filename = entry->d_name;
721 if (filename == "." || filename == "..") {
725 // Delete subdir or file.
726 rtn = recursive_delete(path + "/" + filename, error);
734 throw runtime_error("can't remove directory " + path);
738 throw runtime_error("can't unlink " + path);
741 } catch (exception &e) {
744 out << e.what() << " (" << strerror(errno) << ")";
751 out << "unknown error (" << strerror(errno) << ")";
758 } // eo recursive_delete(const std::string&,std::string*)
761 Create a unique temporary directory from path_template.
762 @param Path template. The last six characters must be XXXXXX.
763 @param error Will contain the error if the return value is false [optional]
764 @return Name of new directory or empty string on error.
766 std::string mkdtemp(const std::string &path_template, std::string *error)
768 boost::scoped_array<char> buf( new char[path_template.size()+1] );
769 path_template.copy(buf.get(), path_template.size());
770 buf[path_template.size()]=0;
772 char *unique_dir = ::mkdtemp(buf.get());
776 *error = strerror(errno);
780 // Scoped pointer is still valid
781 return std::string(unique_dir);
786 @param path Path to create
787 @param error Will contain the error if the return value is false [optional]
788 @return True on success, false on error
790 bool mkdir(const std::string &path, const mode_t &mode, std::string *error)
792 if ( ::mkdir(path.c_str(), mode) == 0)
796 *error = strerror(errno);
802 @param path Path to removed
803 @param error Will contain the error if the return value is false [optional]
804 @return True on successs, false otherwise
806 bool rmdir(const std::string &path, std::string *error)
808 if ( ::rmdir(path.c_str() ) == 0)
812 *error = strerror(errno);
816 /// Small helper class for scoped free
820 scoped_C_free(void *ptr)
821 : pointer_to_free(ptr)
827 free (pointer_to_free);
828 pointer_to_free = NULL;
832 void *pointer_to_free;
836 Get current working directory
837 @return Current working directory. Empty string on error.
841 char *cwd = ::getcwd(NULL, 0);
845 // Make deallocation of cwd exception safe
846 scoped_C_free holder(cwd);
848 string current_dir(cwd);
853 Change current working directory
854 @param path Path to change to
855 @param error Will contain the error if the return value is false [optional]
856 @return True on successs, false otherwise
858 bool chdir(const std::string &path, std::string *error)
860 if ( ::chdir(path.c_str() ) == 0)
864 *error = strerror(errno);
869 Set file mode creation mask
870 @param mask Creation mask
871 @return Previous creation mask (function call always succeeds)
873 mode_t umask(mode_t mask)
875 return ::umask(mask);
878 } // eo namespace I2n