The vbs tools - vbs_ls, vbs_rm, vbs_fs - for listing, removing and mounting vbs and Mark6 format scattered VLBI recordings on FlexBuff and Mark6 systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

401 lines
14 KiB

  1. // function templates for mapping or filtering directory entries
  2. //
  3. // Note: all templated functions throw an int (errno) on error
  4. // in a system call
  5. //
  6. //
  7. // ===========================================================
  8. // direntries_type dir_filter(DIR/string, <predicate>)
  9. // ===========================================================
  10. //
  11. // use the dir_filter function template to filter directory
  12. // entries satisfying <predicate> (the template parameter).
  13. // An example predicate is found below.
  14. //
  15. // <predicate> is a unary boolean function:
  16. // bool <predicate>(std::string const&)
  17. //
  18. // Returns a std::set<std::string> [direntries_type] with matching
  19. // directory entries.
  20. //
  21. // Usage:
  22. // struct my_predicate {
  23. // bool operator()(std::string const& nm) {
  24. // return nm[0]!=".";
  25. // };
  26. //
  27. // ...
  28. // direntries_type f = dir_filter("/path/to/dir", my_predicate())
  29. //
  30. //
  31. // ===========================================================
  32. // struct dir_mapper<fun> { ... };
  33. // ===========================================================
  34. //
  35. // Use the templated struct dir_mapper<> to apply a function/functor
  36. // to each entry in the directory. The results are returned as
  37. //
  38. // map [STRING] => <operator>::value_type
  39. //
  40. // where the keys are the directory entry names and the mapped
  41. // values are the result of calling the <fun> with that
  42. // directory entry
  43. //
  44. // The struct overloads the function-call operator twice:
  45. // operator()( DIR* );
  46. // operator()( string const& dirname );
  47. //
  48. // Example:
  49. // // Simple fileSize functor - just to test dirMapper construction
  50. // struct fileSize {
  51. // // the result of calling this functor will be an 'off_t'
  52. // typedef off_t value_type;
  53. //
  54. // value_type operator()(std::string const& fnm) const {
  55. // int fd;
  56. // off_t fs;
  57. //
  58. // if( (fd=::open(fnm.c_str(), O_RDONLY))<0 )
  59. // return (off_t)-1;
  60. // fs = ::lseek(fd, 0, SEEK_END);
  61. // ::close(fd);
  62. // return fs;
  63. // }
  64. // };
  65. //
  66. // ...
  67. //
  68. // typedef dir_mapper<fileSize> filesizer_type;
  69. //
  70. // filesizer_type::value_type filesizes = filesizer_type()("/path/to/directory")
  71. //
  72. // It is possible to pass an instance of the functor, in case you want
  73. // the functor to use some dynamic value:
  74. //
  75. // struct newerThan {
  76. // // the result of calling this functor will be a boolean - wether
  77. // // or not the entry is newer than some time
  78. // typedef bool value_type;
  79. //
  80. // newerThan(time_t ts):
  81. // __m_timestamp(ts)
  82. // {}
  83. //
  84. // value_type operator()(std::string const& fnm) const {
  85. // struct stat status;
  86. //
  87. // ::lstat(....);
  88. // return status.st_mtime>__m_timestamp;
  89. // }
  90. //
  91. // time_t __m_timestamp;
  92. // };
  93. //
  94. // typedef dir_mapper<newerThan> changedsince_type;
  95. //
  96. // // newer than one minute ago:
  97. // newerThan oneMinuteAgo( time_t(0) - 60 );
  98. // changedsince_type::value_type entries = changedsince_type(oneMinuteAgo)( "/path/to/directory" )
  99. //
  100. // // this sucks about c++ ... [c++11 is better, with the 'auto' keyword]
  101. // for(changedsince_type::value_type::const_iterator ptr=entries.begin(); ptr!=entries.end(); ptr++)
  102. // // newerThan returned true for the entry if it was newer ...
  103. // if( ptr->second )
  104. // cout << ptr->first << " is younger than one minute" << endl;
  105. //
  106. #ifndef EVLBI5A_DIRECTORY_HELPER_TEMPLATES_H
  107. #define EVLBI5A_DIRECTORY_HELPER_TEMPLATES_H
  108. #include <auto_array.h>
  109. #include <set>
  110. #include <map>
  111. #include <string>
  112. #include <cstddef>
  113. #include <cerrno>
  114. #include <cstring>
  115. #include <unistd.h>
  116. #include <sys/types.h>
  117. #include <dirent.h>
  118. ///////////////////////////////////////////////
  119. // VBS_FS/libvbs are going to have to quite often
  120. // grovel over directories, sometimes filtering
  121. // entries and sometimes not.
  122. // This set of function templates allow for flexible
  123. // filtering (or not) of directory entries.
  124. //
  125. // Works on already opened DIR* or std::string (==path)
  126. // The template parameter is the predicate.
  127. // IF the predicate(d_entry) is true, then d_entry
  128. // will be added to the set of entries.
  129. //
  130. ///////////////////////////////////////////////
  131. typedef std::set<std::string> direntries_type;
  132. // provide a wrapper around ::strerror_r() such that
  133. // users don't have to allocate a buffer themselves each time
  134. // Note: of course this shouldn't /have/ to be a template
  135. // but making it one means that it can live safely
  136. // in a header-only "library" (like this one hopefully is :D)
  137. template <size_t Size>
  138. std::string errno_to_string(int e) {
  139. char errBuf[Size];
  140. ::strerror_r(e, errBuf, Size);
  141. return std::string(errBuf);
  142. }
  143. // A no-filter predicate - gets all entries in a directory
  144. // this isn't a template and therefore it's left in comment
  145. struct NoFilter {
  146. bool operator()(std::string const&) const {
  147. return true;
  148. }
  149. };
  150. // Returns true if there exists at least one entry in dir for which
  151. // Predicate return true. Stops as soon as it finds a match.
  152. template <typename Predicate>
  153. bool dir_exists(std::string const& dir, Predicate const& pred) {
  154. // readdir_r(3) is deprecated so we use readdir(3) in stead.
  155. // Because we use only stack variables in here we're thread safe by
  156. // definition; it is impossible for two threads to be using the same
  157. // DIR* being fed to readdir(3)
  158. DIR* dirp;
  159. bool rv = false;
  160. struct dirent* entryPtr;
  161. // This is a systemcall so can use ASSERT*() which will
  162. // capture the error message from errno
  163. if( (dirp=::opendir(dir.c_str()))==0 ) {
  164. std::cerr << "*** dir_exists/failed to open " << dir << " - " << errno_to_string<256>(errno) << std::endl;
  165. throw errno;
  166. }
  167. // Check each entry. Because readdir(3) does not return the error code,
  168. // we follow the notes from readdir(3):
  169. // set errno = 0 before the call, then check if NULL
  170. // and use errno to disambiguate between error or end-of-directory
  171. while( !rv ) {
  172. errno = 0;
  173. if( (entryPtr=::readdir(dirp))==0 )
  174. break;
  175. // skip . and ..
  176. const std::string enm( entryPtr->d_name );
  177. if( enm=="." || enm==".." )
  178. continue;
  179. // 'concatenate' the predicate's result to existing result
  180. rv = (rv || pred(dir +"/" + enm));
  181. }
  182. // Force succesfull loop ending
  183. const int oeno = errno;
  184. ::closedir(dirp);
  185. if( oeno!=0 ) {
  186. std::cerr << "*** dir_exists[" << dir << "] - " << errno_to_string<256>(oeno) << std::endl;
  187. throw oeno;
  188. }
  189. return rv;
  190. }
  191. // This version will call the predicate with "entry->d_name"
  192. // (i.e. just the name of the entry in the directory) as
  193. // parameter because we do not know the path leading to DIR*
  194. template <typename Predicate>
  195. direntries_type dir_filter(DIR* dirp, Predicate const& pred) {
  196. // readdir_r(3) is deprecated now so use readdir(3) in stead.
  197. // Because we are given DIR*, we let someone else [the caller]
  198. // worry about thread safety - i.e. the caller is responsible for
  199. // NOT calling this method on the same DIR* from different threads
  200. struct dirent* entryPtr;
  201. direntries_type rv;
  202. // Make sure it's rewound
  203. ::rewinddir( dirp );
  204. // Check each entry. Because readdir(3) does not return the error code,
  205. // we follow the notes from readdir(3):
  206. // set errno = 0 before the call, then check if NULL
  207. // and use errno to disambiguate between error or end-of-directory
  208. while( true ) {
  209. errno = 0;
  210. if( (entryPtr=::readdir(dirp))==0 )
  211. break;
  212. // If predicate returns true, add current entry to result
  213. if( pred(std::string(entryPtr->d_name)) )
  214. if( rv.insert(entryPtr->d_name).second==false )
  215. std::cerr << "dir_filter[DIR*]/duplicate insert - " << entryPtr->d_name << std::endl;
  216. }
  217. if( errno!=0 ) {
  218. std::cerr << "*** dir_filter: " << errno_to_string<256>(errno) << std::endl;
  219. throw errno;
  220. }
  221. return rv;
  222. }
  223. // This variation, which works on a given path will call the predicate with
  224. // the FULL path to the entry, not just the name from the directory listing!
  225. // So it is "pred( dir + "/" + entry->d_name )"
  226. template <typename Predicate>
  227. direntries_type dir_filter(std::string const& dir, Predicate const& pred) {
  228. // readdir_r(3) is deprecated so we use readdir(3) in stead.
  229. // Because we use only stack variables in here we're thread safe by
  230. // definition; it is impossible for two threads to be using the same
  231. // DIR* being fed to readdir(3)
  232. DIR* dirp;
  233. struct dirent* entryPtr;
  234. direntries_type rv;
  235. // This is a systemcall so can use ASSERT*() which will
  236. // capture the error message from errno
  237. if( (dirp=::opendir(dir.c_str()))==0 ) {
  238. std::cerr << "*** dir_filter/failed to open " << dir << " - " << errno_to_string<256>(errno) << std::endl;
  239. throw errno;
  240. }
  241. // Check each entry. Because readdir(3) does not return the error code,
  242. // we follow the notes from readdir(3):
  243. // set errno = 0 before the call, then check if NULL
  244. // and use errno to disambiguate between error or end-of-directory
  245. while( true ) {
  246. errno = 0;
  247. if( (entryPtr=::readdir(dirp))==0 )
  248. break;
  249. // If predicate returns true, add current entry to result
  250. if( pred(dir+"/"+entryPtr->d_name) )
  251. if( rv.insert(dir+"/"+entryPtr->d_name).second==false )
  252. std::cerr << "dir_filter[" << dir << "]/duplicate insert - " << entryPtr->d_name << std::endl;
  253. }
  254. // Force succesfull loop ending; save current value of errno
  255. // such that the result of closedir(3) does not clobber
  256. // the result of processing the entries
  257. const int oeno = errno;
  258. ::closedir(dirp);
  259. if( oeno!=0 ) {
  260. std::cerr << "*** dir_filter[" << dir << "] - " << errno_to_string<256>(oeno) << std::endl;
  261. throw oeno;
  262. }
  263. return rv;
  264. }
  265. ///////////////////////////////////////////////
  266. //
  267. // The dir_mapper
  268. //
  269. ///////////////////////////////////////////////
  270. template <typename Callback>
  271. struct dir_mapper {
  272. typedef std::map<std::string, typename Callback::value_type> value_type;
  273. dir_mapper():
  274. __m_cb( Callback() )
  275. {}
  276. dir_mapper( Callback const& cb ):
  277. __m_cb( cb )
  278. {}
  279. // readdir_r(3) is deprecated so we use readdir(3) in stead.
  280. // Because we use only stack variables in here we're thread safe by
  281. // definition; it is impossible for two threads to be using the same
  282. // DIR* being fed to readdir(3)
  283. value_type operator()(std::string const& dir) const {
  284. return (*this)(dir, NoFilter());
  285. }
  286. template <typename Predicate>
  287. value_type operator()(std::string const& dir, Predicate const& p) const {
  288. DIR* dirp;
  289. value_type rv;
  290. struct dirent* entryPtr;
  291. // This is a systemcall so can use ASSERT*() which will
  292. // capture the error message from errno
  293. if( (dirp=::opendir(dir.c_str()))==0 ) {
  294. std::cerr << "*** dir_mapper[" << dir << "] failed to opendir - " << errno_to_string<256>(errno) << std::endl;
  295. throw errno;
  296. }
  297. // Check each entry. Because readdir(3) does not return the error code,
  298. // we follow the notes from readdir(3):
  299. // set errno = 0 before the call, then check if NULL
  300. // and use errno to disambiguate between error or end-of-directory
  301. while( true ) {
  302. errno = 0;
  303. if( (entryPtr=::readdir(dirp))==0 )
  304. break;
  305. // skip . and .. and entries not fulfilling the predicate
  306. const std::string d_name( entryPtr->d_name );
  307. if( d_name=="." || d_name==".." || !p(d_name) )
  308. continue;
  309. // Now form complete name
  310. const std::string fnm = dir + "/" + d_name;
  311. // Request callback to yield a value for the current entry
  312. if( rv.insert ( make_pair(d_name, __m_cb(fnm))).second==false )
  313. std::cerr << "dir_mapper[" << dir << "]/duplicate insert - " << fnm << std::endl;
  314. }
  315. // Force succesfull loop ending; save current value of errno
  316. // such that the result of closedir(3) does not clobber
  317. // the result of processing the entries
  318. const int oeno = errno;
  319. ::closedir(dirp);
  320. if( oeno!=0 ) {
  321. std::cerr << "*** dir_mapper[" << dir << "] " << errno_to_string<256>(oeno) << std::endl;
  322. throw oeno;
  323. }
  324. return rv;
  325. }
  326. // readdir_r(3) is deprecated now so use readdir(3) in stead.
  327. // Because we are given DIR*, we let someone else [the caller]
  328. // worry about thread safety - i.e. the caller is responsible for
  329. // NOT calling this method on the same DIR* from different threads
  330. value_type operator()(DIR* dirp) const {
  331. return (*this)(dirp, NoFilter());
  332. }
  333. template <typename Predicate>
  334. value_type operator()(DIR* dirp, Predicate p) const {
  335. value_type rv;
  336. struct dirent* entryPtr;
  337. ::rewinddir(dirp);
  338. // Check each entry. Because readdir(3) does not return the error code,
  339. // we follow the notes from readdir(3):
  340. // set errno = 0 before the call, then check if NULL
  341. // and use errno to disambiguate between error or end-of-directory
  342. while( true ) {
  343. errno = 0;
  344. if( (entryPtr=::readdir(dirp))==0 )
  345. break;
  346. const std::string fnm( entryPtr->d_name );
  347. // Because we're working from DIR* we have no *idea* what the
  348. // actual path is so all we can give the predicate and callback
  349. // is the entry name.
  350. // If the predicate returns false we're skipping this one
  351. if( fnm=="." || fnm==".." || !p(fnm) )
  352. continue;
  353. // Request callback to yield a value for the current entry
  354. if( rv.insert ( make_pair(fnm, __m_cb(fnm))).second==false )
  355. std::cerr << "dir_mapper[DIR*]/duplicate insert - " << fnm << std::endl;
  356. }
  357. if( errno!=0 ) {
  358. std::cerr << "*** dir_mapper[DIR*] " << errno_to_string<256>(errno) << std::endl;
  359. throw errno;
  360. }
  361. return rv;
  362. }
  363. Callback __m_cb;
  364. };
  365. #endif // DIRECTORY_HELPER_TEMPLATES_H