1 /***************************************************************************
2 escape.cpp - escaping of strings
4 begin : Sun Nov 14 1999
5 copyright : (C) 1999 by Intra2net AG
6 email : info@intra2net.com
7 ***************************************************************************/
16 #include <sys/types.h>
24 #include <boost/scoped_array.hpp>
25 #include "filefunc.hxx"
26 #include "stringfunc.hxx"
28 // TODO: Remove me once libi2ncommon is completly switched to I2n
37 ** implementation of Stat
47 Stat::Stat(const std::string& path, bool follow_links)
49 stat(path,follow_links);
50 } // eo Stat::Stat(const std::string&,bool)
59 * @brief updates the internal data.
61 * In other words: stat()'s the file again.
67 stat(Path, FollowLinks);
69 } // eo Stat::recheck()
73 * @brief calls stat() or lstat() to get the information for the given path
74 * and stores that information.
75 * @param path the path which should be checked
76 * @param follow_links determine if (symbalic) links should be followed.
78 void Stat::stat(const std::string& path, bool follow_links)
82 FollowLinks= follow_links;
83 struct stat stat_info;
85 res = ( follow_links ? ::stat(path.c_str(), &stat_info) : ::lstat(path.c_str(), &stat_info) );
88 Device = stat_info.st_dev;
89 Inode = stat_info.st_ino;
90 Mode = (stat_info.st_mode & ~(S_IFMT));
91 NumLinks = stat_info.st_nlink;
92 Uid = stat_info.st_uid;
93 Gid = stat_info.st_gid;
94 DeviceType = stat_info.st_rdev;
95 Size = stat_info.st_size;
96 Atime = stat_info.st_atime;
97 Mtime = stat_info.st_mtime;
98 Ctime = stat_info.st_atime;
100 IsLink= S_ISLNK( stat_info.st_mode );
101 IsRegular= S_ISREG( stat_info.st_mode );
102 IsDirectory= S_ISDIR( stat_info.st_mode );
103 IsCharacterDevice= S_ISCHR( stat_info.st_mode );
104 IsBlockDevice= S_ISBLK( stat_info.st_mode );
105 IsFifo= S_ISFIFO( stat_info.st_mode );
106 IsSocket= S_ISSOCK( stat_info.st_mode );
109 } // eo Stat::stat(const std::string&,bool)
113 * @brief clears the internal data.
135 IsCharacterDevice= false;
136 IsBlockDevice= false;
139 } // eo Stat::clear()
143 * @brief checks if another instance describes the same file.
144 * @param rhs the other instance.
145 * @return @a true iff the other instance describes the same file.
147 * The "same file" means that the files are located on the same device and use the same inode.
148 * They might still have two different directory entries (different paths)!
150 bool Stat::is_same_as(const Stat& rhs)
152 return Valid and rhs.Valid
153 and ( Device == rhs.Device)
154 and ( Inode == rhs.Inode);
155 } // eo Stat::is_same_as(const Stat& rhs);
159 * @brief checks if this and the other instance describe the same device.
160 * @param rhs the other instance.
161 * @return @a true if we and the other instance describe a device and the same device.
163 * "Same device" means that the devices have the same type and the same major and minor id.
165 bool Stat::is_same_device_as(const Stat& rhs)
167 return is_device() and rhs.is_device()
168 and ( IsBlockDevice == rhs.IsBlockDevice )
169 and ( IsCharacterDevice == rhs.IsCharacterDevice )
170 and ( DeviceType == rhs.DeviceType);
171 } // eo Stat::is_same_device_as(const Stat&)
174 * @brief check existence of a path.
175 * @param path path which should be tested.
176 * @return @a true iff path exists.
178 bool path_exists(const std::string& path)
180 struct stat stat_info;
181 int res = ::stat(path.c_str(), &stat_info);
182 if (res) return false;
184 } // eo path_exists(const std::string&)
187 * @brief check existence of a regular file.
188 * @param path path which should be tested.
189 * @return @a true if path exists and is a regular file (or a link pointing to a regular file).
190 * @note this checks for regular files; not for the pure exitsnace of a path; use pathExists() for that.
193 bool file_exists(const std::string& path)
195 struct stat stat_info;
196 int res = ::stat(path.c_str(), &stat_info);
197 if (res) return false;
198 return S_ISREG(stat_info.st_mode);
199 } // eo file_exists(const std::string&)
201 // TODO: Use Stat class
204 * @param name filename to get size for
205 * @return file size or -1 if file does not exist
207 long file_size (const string &name)
211 struct stat statbuff;
213 if (lstat(name.c_str(), &statbuff) < 0)
216 if (!S_ISREG(statbuff.st_mode))
219 iReturn=statbuff.st_size;
225 * @brief tests the last modification time stamp of a path.
226 * @param path path which should be tested.
227 * @return the last modification time or 0 if the path doen't exist.
229 time_t file_mtime(const std::string& path)
231 struct stat stat_info;
232 int res = ::stat(path.c_str(), &stat_info);
234 return stat_info.st_mtime;
235 } // eo fileMTime(const std::string&)
239 * @brief reads the contents of a directory.
240 * @param path the path to the directory whose contents should be read.
241 * @param[out] result the resulting list of names.
242 * @param include_dot_names determines if dot-files should be included in the list.
243 * @return @a true if reading the directory was succesful, @a false on error.
246 const std::string& path,
247 std::vector< std::string >& result,
248 bool include_dot_names )
250 DIR* dir = ::opendir( path.c_str());
255 struct dirent *entry;
256 while ( NULL != (entry = ::readdir(dir)) )
258 std::string name( entry->d_name );
259 if (! include_dot_names && (name[0] == '.') )
263 result.push_back( name );
267 } // eo getDir(const std::string&,std::vector< std::string >&,bool)
271 * @brief reads the contents of a directory
272 * @param path the path to the directory whose contents should be read.
273 * @param include_dot_names determines if dot-files should be included in the list.
274 * @return the list of names (empty on error).
276 std::vector< std::string > get_dir(const std::string& path, bool include_dot_names )
278 std::vector< std::string > result;
279 get_dir(path,result,include_dot_names);
281 } // eo getDir(const std::string&,bool)
286 * @brief removes a file from a filesystem.
287 * @param path path to the file.
288 * @return @a true iff the unlink was successful.
290 bool unlink( const std::string& path )
292 int res = ::unlink( path.c_str() );
294 } // eo unlink(const std::string&)
299 * @brief creates a symbolic link named @a link_name to @a target.
300 * @param target the target the link should point to.
301 * @param link_name the name of the link.
302 * @param force if @a true, the (file or link) @a link_name is removed if it exists.
303 * @return @a true iff the symlink was successfully created.
305 bool symlink(const std::string& target, const std::string& link_name, bool force)
308 if (target.empty() or link_name.empty() or target == link_name)
310 // no, we don't do this!
313 std::string n_target;
314 if (target[0] == '/') // absolute target?
318 else // relative target
320 // for stat'ing: prepend dir of link_name:
321 n_target= dirname(link_name)+"/"+ target;
323 Stat target_stat(n_target, false);
324 Stat link_name_stat(link_name, false);
325 if (target_stat.exists() && link_name_stat.exists())
327 if (link_name_stat.is_same_as(target_stat)
328 or link_name_stat.is_same_device_as(target_stat) )
332 //TODO: more consistency checks?!
334 if (link_name_stat.exists())
336 // the link name already exists.
339 // "force" as given, so try to remove the link_name:
342 link_name_stat.recheck();
345 if (link_name_stat.exists())
347 // well, if the link_name still exists; we cannot create that link:
351 res= ::symlink(target.c_str(), link_name.c_str());
353 } // eo symlink(const std::string&,const std::string&,bool)
358 * @brief reads the target of a symbolic link.
359 * @param path path to the symbolic link
360 * @return the target of the link or an empty string on error.
362 std::string read_link(const std::string& path)
365 Stat stat(path,false);
366 if (!stat || !stat.is_link())
368 return std::string();
370 int buffer_size= PATH_MAX+1 + 128;
371 boost::scoped_array<char> buffer_ptr( new char[buffer_size] );
372 int res= ::readlink( path.c_str(), buffer_ptr.get(), buffer_size-1 );
375 return std::string( buffer_ptr.get(), res );
377 return std::string();
378 } // eo readLink(const std::string&)
383 * @brief returns content of a file as string.
385 * A simple (q'n'd) function for retrieving content of a file as string.<br>
386 * Also able to read special (but regular) files which don't provide a size when stat'ed
387 * (like files in the /proc filesystem).
389 * @param path path to the file.
390 * @return the content of the file as string (empty if file could be opened).
392 std::string read_file(const std::string& path)
397 return std::string();
399 std::ifstream f( path.c_str(), std::ios::in | std::ios::binary );
403 // NOTE: there are cases where we don't know the correct size (/proc files...)
404 // therefore we only use the size for reserving space if we know it, but don't
405 // use it when reading the file!
408 // if we know the size, we reserve enough space.
409 result.reserve( stat.size() );
414 f.read(buffer, sizeof(buffer));
415 result.append(buffer, f.gcount());
423 * @brief writes a string to a file.
424 * @param path path to the file
425 * @param data the data which should be written into the file
426 * @return @a true if the data was written to the file.
428 * A simple (q'n'd) function for writing a string to a file.
430 bool write_file(const std::string& path, const std::string& data)
432 std::ofstream f( path.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
435 f.write( data.data(), data.size() );
446 * @brief returns the filename part of a path (last component)
447 * @param path the path.
448 * @return the last component of the path.
450 std::string basename(const std::string& path)
452 std::string::size_type pos= path.rfind('/');
453 if (pos != std::string::npos)
455 return path.substr(pos+1);
458 } // eo basename(const std::string&)
462 * @brief returns the directory part of a path.
463 * @param path the path.
464 * @return the directory part of the path.
466 std::string dirname(const std::string& path)
468 std::string::size_type pos= path.rfind('/');
469 if (pos != std::string::npos)
471 std::string result(path,0,pos);
479 } // eo dirname(const std::string&)
483 * @brief normalizes a path.
485 * This method removes empty and "." elements.
486 * It also resolves ".." parts by removing previous path elements if possible.
487 * Leading ".." elements are preserved when a relative path was given; else they are removed.
488 * Trailing slashes are removed.
490 * @param path the path which should be normalized.
491 * @return the normalized path.
494 std::string normalize_path(const std::string& path)
498 return std::string();
500 // remember if the given path was absolute since this information vanishes when
501 // we split the path (since we split with omitting empty parts...)
502 bool is_absolute= (path[0]=='/');
503 std::list< std::string > parts;
504 std::list< std::string > result_parts;
506 splitString(path,parts,"/",true);
508 for(std::list< std::string >::const_iterator it_parts= parts.begin();
509 it_parts != parts.end();
512 std::string part(*it_parts); //convenience..
513 if (part == std::string(".") )
515 // single dot is "current path"; ignore!
518 if (part == std::string("..") )
520 // double dot is "one part back"
521 if (result_parts.empty())
525 // ignore since we cannot move behind / on absolute paths...
529 // on relative path, we need to store the "..":
530 result_parts.push_back(part);
533 else if (result_parts.back() == std::string("..") )
535 // if last element was already "..", we need to store the new one again...
536 // (PS: no need for "absolute" check; this can only be the case on relative path)
537 result_parts.push_back(part);
541 // remove last element.
542 result_parts.pop_back();
546 result_parts.push_back(part);
553 result+= joinString(result_parts,"/");
555 } // eo normalizePath(const std::string&)
559 * @brief changes the file(/path) mode.
560 * @param path the path to change the mode for.
561 * @param mode the new file mode.
562 * @return @a true iff the file mode was sucessfully changed.
564 bool chmod(const std::string& path, int mode)
566 int res= ::chmod(path.c_str(), mode);
568 } // eo chmod(const std::string&,int)
572 * @brief changed the owner of a file(/path)
573 * @param path the path to change the owner for.
574 * @param user the new file owner.
575 * @param group the new file group.
576 * @return @a true iff the file owner was succesfully changed.
579 * the validity of user and group within the system is not checked.
580 * This is intentional since this way we can use id's which are not assigned.
582 bool chown(const std::string& path, const I2n::User& user, const I2n::Group& group)
585 if (uid<0) return false;
586 gid_t gid= group.Gid;
587 if (gid<0) gid= user.Gid;
588 if (gid<0) return false;
589 int res= ::chown( path.c_str(), uid, gid);
591 } // eo chown(const std::string&,const User&,const Group&)
594 * Recursive delete of files and directories
595 * @param path File or directory to delete
596 * @param error Will contain the error if the return value is false
597 * @return true on success, false otherwise
599 bool recursive_delete(const std::string &path, std::string *error)
605 if (stat(path.c_str(), &my_stat) != 0) {
606 throw runtime_error("can't stat " + path);
609 if (S_ISDIR(my_stat.st_mode)) {
610 DIR *dir = opendir(path.c_str());
612 throw runtime_error("can't open directory " + path);
615 struct dirent *entry;
616 while ((entry = readdir(dir))) {
617 string filename = entry->d_name;
618 if (filename == "." || filename == "..") {
622 // Delete subdir or file.
623 rtn = recursive_delete(path + "/" + filename, error);
630 if (rmdir(path.c_str()) != 0) {
631 throw runtime_error("can't remove directory " + path);
634 if (unlink(path.c_str()) != 0) {
635 throw runtime_error("can't unlink " + path);
638 } catch (exception &e) {
641 out << e.what() << " (" << strerror(errno) << ")";
648 out << "unknown error (" << strerror(errno) << ")";