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"
34 ** implementation of Stat
44 Stat::Stat(const std::string& path, bool follow_links)
46 stat(path,follow_links);
47 } // eo Stat::Stat(const std::string&,bool)
56 * @brief updates the internal data.
58 * In other words: stat()'s the file again.
64 stat(m_path, m_follow_links);
66 } // eo Stat::recheck()
70 * @brief calls stat() or lstat() to get the information for the given path
71 * and stores that information.
72 * @param path the path which should be checked
73 * @param follow_links determine if (symbalic) links should be followed.
75 void Stat::stat(const std::string& path, bool follow_links)
79 m_follow_links= follow_links;
80 struct stat stat_info;
82 res = ( follow_links ? ::stat(path.c_str(), &stat_info) : ::lstat(path.c_str(), &stat_info) );
85 m_device = stat_info.st_dev;
86 m_inode = stat_info.st_ino;
87 m_mode = (stat_info.st_mode & ~(S_IFMT));
88 m_num_links = stat_info.st_nlink;
89 m_uid = stat_info.st_uid;
90 m_gid = stat_info.st_gid;
91 m_device_type = stat_info.st_rdev;
92 m_size = stat_info.st_size;
93 m_atime = stat_info.st_atime;
94 m_mtime = stat_info.st_mtime;
95 m_ctime = stat_info.st_atime;
97 m_is_link= S_ISLNK( stat_info.st_mode );
98 m_is_regular= S_ISREG( stat_info.st_mode );
99 m_is_directory= S_ISDIR( stat_info.st_mode );
100 m_is_character_device= S_ISCHR( stat_info.st_mode );
101 m_is_block_device= S_ISBLK( stat_info.st_mode );
102 m_is_fifo= S_ISFIFO( stat_info.st_mode );
103 m_is_socket= S_ISSOCK( stat_info.st_mode );
105 m_valid = (0 == res);
106 } // eo Stat::stat(const std::string&,bool)
110 * @brief clears the internal data.
131 m_is_directory= false;
132 m_is_character_device= false;
133 m_is_block_device= false;
136 } // eo Stat::clear()
140 * @brief checks if another instance describes the same file.
141 * @param rhs the other instance.
142 * @return @a true iff the other instance describes the same file.
144 * The "same file" means that the files are located on the same device and use the same inode.
145 * They might still have two different directory entries (different paths)!
147 bool Stat::isSameAs(const Stat& rhs)
149 return m_valid and rhs.m_valid
150 and ( m_device == rhs.m_device)
151 and ( m_inode == rhs.m_inode);
152 } // eo Stat::isSameAs(const Stat& rhs);
156 * @brief checks if this and the other instance describe the same device.
157 * @param rhs the other instance.
158 * @return @a true if we and the other instance describe a device and the same device.
160 * "Same device" means that the devices have the same type and the same major and minor id.
162 bool Stat::isSameDeviceAs(const Stat& rhs)
164 return isDevice() and rhs.isDevice()
165 and ( m_is_block_device == rhs.m_is_block_device )
166 and ( m_is_character_device == rhs.m_is_character_device )
167 and ( m_device_type == rhs.m_device_type);
168 } // eo Stat::isSameDeviceAs(const Stat&)
171 * @brief check existence of a path.
172 * @param path path which should be tested.
173 * @return @a true iff path exists.
175 bool path_exists(const std::string& path)
177 struct stat stat_info;
178 int res = ::stat(path.c_str(), &stat_info);
179 if (res) return false;
181 } // eo path_exists(const std::string&)
184 * @brief check existence of a regular file.
185 * @param path path which should be tested.
186 * @return @a true if path exists and is a regular file (or a link pointing to a regular file).
187 * @note this checks for regular files; not for the pure exitsnace of a path; use pathExists() for that.
190 bool file_exists(const std::string& path)
192 struct stat stat_info;
193 int res = ::stat(path.c_str(), &stat_info);
194 if (res) return false;
195 return S_ISREG(stat_info.st_mode);
196 } // eo file_exists(const std::string&)
198 // TODO: Use Stat class
201 * @param name filename to get size for
202 * @return file size or -1 if file does not exist
204 long file_size (const string &name)
208 struct stat statbuff;
210 if (lstat(name.c_str(), &statbuff) < 0)
213 if (!S_ISREG(statbuff.st_mode))
216 iReturn=statbuff.st_size;
222 * @brief tests the last modification time stamp of a path.
223 * @param path path which should be tested.
224 * @return the last modification time or 0 if the path doen't exist.
226 time_t fileMTime(const std::string& path)
228 struct stat stat_info;
229 int res = ::stat(path.c_str(), &stat_info);
231 return stat_info.st_mtime;
232 } // eo fileMTime(const std::string&)
236 * @brief reads the contents of a directory.
237 * @param path the path to the directory whose contents should be read.
238 * @param[out] result the resulting list of names.
239 * @param include_dot_names determines if dot-files should be included in the list.
240 * @return @a true if reading the directory was succesful, @a false on error.
243 const std::string& path,
244 std::vector< std::string >& result,
245 bool include_dot_names )
247 DIR* dir = ::opendir( path.c_str());
252 struct dirent *entry;
253 while ( NULL != (entry = ::readdir(dir)) )
255 std::string name( entry->d_name );
256 if (! include_dot_names && (name[0] == '.') )
260 result.push_back( name );
264 } // eo getDir(const std::string&,std::vector< std::string >&,bool)
268 * @brief reads the contents of a directory
269 * @param path the path to the directory whose contents should be read.
270 * @param include_dot_names determines if dot-files should be included in the list.
271 * @return the list of names (empty on error).
273 std::vector< std::string > getDir(const std::string& path, bool include_dot_names )
275 std::vector< std::string > result;
276 getDir(path,result,include_dot_names);
278 } // eo getDir(const std::string&,bool)
283 * @brief removes a file from a filesystem.
284 * @param path path to the file.
285 * @return @a true iff the unlink was successful.
287 bool unlink( const std::string& path )
289 int res = ::unlink( path.c_str() );
291 } // eo unlink(const std::string&)
296 * @brief creates a symbolic link named @a link_name to @a target.
297 * @param target the target the link should point to.
298 * @param link_name the name of the link.
299 * @param force if @a true, the (file or link) @a link_name is removed if it exists.
300 * @return @a true iff the symlink was successfully created.
302 bool symlink(const std::string& target, const std::string& link_name, bool force)
305 if (target.empty() or link_name.empty() or target == link_name)
307 // no, we don't do this!
310 std::string n_target;
311 if (target[0] == '/') // absolute target?
315 else // relative target
317 // for stat'ing: prepend dir of link_name:
318 n_target= dirname(link_name)+"/"+ target;
320 Stat target_stat(n_target, false);
321 Stat link_name_stat(link_name, false);
322 if (target_stat.exists() && link_name_stat.exists())
324 if (link_name_stat.isSameAs(target_stat)
325 or link_name_stat.isSameDeviceAs(target_stat) )
329 //TODO: more consistency checks?!
331 if (link_name_stat.exists())
333 // the link name already exists.
336 // "force" as given, so try to remove the link_name:
339 link_name_stat.recheck();
342 if (link_name_stat.exists())
344 // well, if the link_name still exists; we cannot create that link:
348 res= ::symlink(target.c_str(), link_name.c_str());
350 } // eo symlink(const std::string&,const std::string&,bool)
355 * @brief reads the target of a symbolic link.
356 * @param path path to the symbolic link
357 * @return the target of the link or an empty string on error.
359 std::string readLink(const std::string& path)
362 Stat stat(path,false);
363 if (!stat || !stat.isLink())
365 return std::string();
367 int buffer_size= PATH_MAX+1 + 128;
368 boost::scoped_array<char> buffer_ptr( new char[buffer_size] );
369 int res= ::readlink( path.c_str(), buffer_ptr.get(), buffer_size-1 );
372 return std::string( buffer_ptr.get(), res );
374 return std::string();
375 } // eo readLink(const std::string&)
380 * @brief returns content of a file as string.
382 * A simple (q'n'd) function for retrieving content of a file as string.<br>
383 * Also able to read special (but regular) files which don't provide a size when stat'ed
384 * (like files in the /proc filesystem).
386 * @param path path to the file.
387 * @return the content of the file as string (empty if file could be opened).
389 std::string readFile(const std::string& path)
394 return std::string();
396 std::ifstream f( path.c_str(), std::ios::in | std::ios::binary );
400 // NOTE: there are cases where we don't know the correct size (/proc files...)
401 // therefore we only use the size for reserving space if we know it, but don't
402 // use it when reading the file!
405 // if we know the size, we reserve enough space.
406 result.reserve( stat.size() );
411 f.read(buffer, sizeof(buffer));
412 result.append(buffer, f.gcount());
420 * @brief writes a string to a file.
421 * @param path path to the file
422 * @param data the data which should be written into the file
423 * @return @a true if the data was written to the file.
425 * A simple (q'n'd) function for writing a string to a file.
427 bool writeFile(const std::string& path, const std::string& data)
429 std::ofstream f( path.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
432 f.write( data.data(), data.size() );
443 * @brief returns the filename part of a path (last component)
444 * @param path the path.
445 * @return the last component of the path.
447 std::string basename(const std::string& path)
449 std::string::size_type pos= path.rfind('/');
450 if (pos != std::string::npos)
452 return path.substr(pos+1);
455 } // eo basename(const std::string&)
459 * @brief returns the directory part of a path.
460 * @param path the path.
461 * @return the directory part of the path.
463 std::string dirname(const std::string& path)
465 std::string::size_type pos= path.rfind('/');
466 if (pos != std::string::npos)
468 std::string result(path,0,pos);
476 } // eo dirname(const std::string&)
480 * @brief normalizes a path.
482 * This method removes empty and "." elements.
483 * It also resolves ".." parts by removing previous path elements if possible.
484 * Leading ".." elements are preserved when a relative path was given; else they are removed.
485 * Trailing slashes are removed.
487 * @param path the path which should be normalized.
488 * @return the normalized path.
491 std::string normalizePath(const std::string& path)
495 return std::string();
497 // remember if the given path was absolute since this information vanishes when
498 // we split the path (since we split with omitting empty parts...)
499 bool is_absolute= (path[0]=='/');
500 std::list< std::string > parts;
501 std::list< std::string > result_parts;
503 splitString(path,parts,"/",true);
505 for(std::list< std::string >::const_iterator it_parts= parts.begin();
506 it_parts != parts.end();
509 std::string part(*it_parts); //convenience..
510 if (part == std::string(".") )
512 // single dot is "current path"; ignore!
515 if (part == std::string("..") )
517 // double dot is "one part back"
518 if (result_parts.empty())
522 // ignore since we cannot move behind / on absolute paths...
526 // on relative path, we need to store the "..":
527 result_parts.push_back(part);
530 else if (result_parts.back() == std::string("..") )
532 // if last element was already "..", we need to store the new one again...
533 // (PS: no need for "absolute" check; this can only be the case on relative path)
534 result_parts.push_back(part);
538 // remove last element.
539 result_parts.pop_back();
543 result_parts.push_back(part);
550 result+= joinString(result_parts,"/");
552 } // eo normalizePath(const std::string&)
556 * @brief changes the file(/path) mode.
557 * @param path the path to change the mode for.
558 * @param mode the new file mode.
559 * @return @a true iff the file mode was sucessfully changed.
561 bool chmod(const std::string& path, int mode)
563 int res= ::chmod(path.c_str(), mode);
565 } // eo chmod(const std::string&,int)
569 * @brief changed the owner of a file(/path)
570 * @param path the path to change the owner for.
571 * @param user the new file owner.
572 * @param group the new file group.
573 * @return @a true iff the file owner was succesfully changed.
576 * the validity of user and group within the system is not checked.
577 * This is intentional since this way we can use id's which are not assigned.
579 bool chown(const std::string& path, const i2n::User& user, const i2n::Group& group)
582 if (uid<0) return false;
583 gid_t gid= group.gid;
584 if (gid<0) gid= user.gid;
585 if (gid<0) return false;
586 int res= ::chown( path.c_str(), uid, gid);
588 } // eo chown(const std::string&,const User&,const Group&)
591 * Recursive delete of files and directories
592 * @param path File or directory to delete
593 * @param error Will contain the error if the return value is false
594 * @return true on success, false otherwise
596 bool recursive_delete(const std::string &path, std::string *error)
602 if (stat(path.c_str(), &my_stat) != 0) {
603 throw runtime_error("can't stat " + path);
606 if (S_ISDIR(my_stat.st_mode)) {
607 DIR *dir = opendir(path.c_str());
609 throw runtime_error("can't open directory " + path);
612 struct dirent *entry;
613 while ((entry = readdir(dir))) {
614 string filename = entry->d_name;
615 if (filename == "." || filename == "..") {
619 // Delete subdir or file.
620 rtn = recursive_delete(path + "/" + filename, error);
627 if (rmdir(path.c_str()) != 0) {
628 throw runtime_error("can't remove directory " + path);
631 if (unlink(path.c_str()) != 0) {
632 throw runtime_error("can't unlink " + path);
635 } catch (exception &e) {
638 out << e.what() << " (" << strerror(errno) << ")";
645 out << "unknown error (" << strerror(errno) << ")";