1 /*
2  * Copyright 2006-2008 The FLWOR Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 #ifndef ZORBA_FS_UTIL_H
18 #define ZORBA_FS_UTIL_H
19 
20 #include <zorba/config.h>
21 
22 #include <iostream>
23 #include <stdexcept>
24 #ifdef WIN32
25 # include <windows.h>
26 #else
27 # include <dirent.h>
28 # include <sys/types.h>                 /* for off_t */
29 #endif /* WIN32 */
30 
31 #include "ascii_util.h"
32 #include "cxx_util.h"
33 #include "error_util.h"
34 #include "string_util.h"
35 #include "zorbatypes/zstring.h"
36 
37 #ifndef MAX_PATH
38 /**
39  * Maximum path length.  This is defined under Windows to be 1024.  There is no
40  * equivalent constant/macro for *nix systems, so simply borrow Windows' value.
41  */
42 #define MAX_PATH 1024
43 #endif /* MAX_PATH */
44 
45 namespace zorba {
46 namespace fs {
47 
48 ////////// constants //////////////////////////////////////////////////////////
49 
50 #ifdef WIN32
51 char const dir_separator = '\\';
52 char const path_separator = ';';
53 #else
54 char const dir_separator = '/';
55 char const path_separator = ':';
56 #endif /* WIN32 */
57 
58 ////////// types //////////////////////////////////////////////////////////////
59 
60 /**
61  * File size type.
62  */
63 #ifdef WIN32
64 typedef __int64 size_type;
65 #else
66 typedef off_t size_type;
67 #endif /* WIN32 */
68 
69 /**
70  * File type.
71  */
72 enum type {
73   non_existent,
74   directory,
75   file,
76   link,
77   volume,
78   other   // named pipe, character/block special, socket, etc.
79 };
80 extern char const *const type_string[];
81 
82 /**
83  * Emits the string representation of a file type to the given ostream.
84  *
85  * @param o The ostream to emit to.
86  * @param t The file type to emit.
87  * @return Returns \a o.
88  */
89 inline std::ostream& operator<<( std::ostream &o, type t ) {
90   return o << type_string[ t ];
91 }
92 
93 ////////// Windows ////////////////////////////////////////////////////////////
94 
95 #ifdef WIN32
96 namespace win32 {
97 
98 // Do not use this function directly.
99 void make_absolute_impl( char const *path, char *abs_path );
100 
101 } // namespace win32
102 #endif /* WIN32 */
103 
104 ////////// Exceptions /////////////////////////////////////////////////////////
105 
106 typedef os_error::exception exception;
107 
108 ////////// Directory //////////////////////////////////////////////////////////
109 
110 #ifdef ZORBA_WITH_FILE_ACCESS
111 
112 /**
113  * Changes the current working directory.
114  *
115  * @param path The full path of the directory to change to.
116  */
117 void chdir( char const *path );
118 
119 /**
120  * Changes the current working directory.
121  *
122  * @tparam PathStringType The \a path string type.
123  * @param path The full path of the directory to change to.
124  */
125 template<class PathStringType> inline
126 typename std::enable_if<ztd::has_c_str<PathStringType,
127                           char const* (PathStringType::*)() const>::value,
128                         void>::type
chdir(PathStringType const & path)129 chdir( PathStringType const &path ) {
130   chdir( path.c_str() );
131 }
132 
133 #endif /* ZORBA_WITH_FILE_ACCESS */
134 
135 /**
136  * Gets the current directory.
137  *
138  * @return Returns said directory.
139  * @throws fs::exception if it fails.
140  */
141 zstring curdir();
142 
143 #ifdef ZORBA_WITH_FILE_ACCESS
144 
145 /**
146  * Creates a directory.
147  *
148  * @param path The full path of the directory to create.
149  * @throws fs::exception if the creation fails.
150  */
151 void mkdir( char const *path );
152 
153 /**
154  * Creates a directory.
155  *
156  * @tparam PathStringType The \a path string type.
157  * @param path The full path of the directory to create.
158  * @throws fs::exception if the creation fails.
159  */
160 template<class PathStringType> inline
161 typename std::enable_if<ztd::has_c_str<PathStringType,
162                           char const* (PathStringType::*)() const>::value,
163                         void>::type
mkdir(PathStringType const & path)164 mkdir( PathStringType const &path ) {
165   mkdir( path.c_str() );
166 }
167 
168 ////////// Directory iteration ////////////////////////////////////////////////
169 
170 /**
171  * An fs::iterator iterates over the entries in a directory.
172  */
173 class iterator {
174 public:
175   /**
176    * Constructs an %itertor.
177    *
178    * @throws fs::exception if the construction failed, e.g., path not found.
179    */
180   iterator( char const *path );
181 
182   /**
183    * Destroys this %iterator.
184    */
185   ~iterator();
186 
187   /**
188    * Attempts to get the next directory entry.
189    *
190    * @return Returns \c true only if there is a next directory.
191    */
192   bool next();
193 
194   /**
195    * Gets the name of the curent directory entry.
196    *
197    * @return Returns said name.
198    */
entry_name()199   char const* entry_name() const {
200 #   ifndef WIN32
201     return ent_->d_name;
202 #   else
203     return ent_name_;
204 #   endif /* WIN32 */
205   }
206 
207   /**
208    * Gets the type of the current directory entry.
209    *
210    * @return Returns said type.
211    */
entry_type()212   type entry_type() const {
213     return ent_type_;
214   }
215 
216   /**
217    * Gets the directory's path.
218    *
219    * @return Returns said path.
220    */
path()221   char const* path() const {
222     return dir_path_.c_str();
223   }
224 
225   /**
226    * Resets this iterator to the beginning.
227    */
228   void reset();
229 
230 private:
231   zstring dir_path_;
232   type ent_type_;
233 #ifndef WIN32
234   DIR *dir_;
235   struct dirent *ent_;
236 #else
237   HANDLE dir_;
238   bool dir_is_empty_;
239   WIN32_FIND_DATA ent_data_;
240   char ent_name_[ MAX_PATH ];
241   bool use_first_;
242 
243   void win32_opendir( char const *path );
244   void win32_closedir();
245 #endif /* WIN32 */
246 
247   // forbid
248   iterator( iterator const& );
249   iterator& operator=( iterator const& );
250 };
251 
252 #endif /* ZORBA_WITH_FILE_ACCESS */
253 
254 ////////// File creation //////////////////////////////////////////////////////
255 
256 #ifdef ZORBA_WITH_FILE_ACCESS
257 
258 /**
259  * Creates the given file.
260  *
261  * @param path The full path of the file to create.
262  * @throws fs::exception if the creation failed.
263  */
264 void create( char const *path );
265 
266 /**
267  * Creates the given file.
268  *
269  * @tparam PathStringType The \a path string type.
270  * @param path The full path of the file to create.
271  * @throws fs::exception if the creation failed.
272  */
273 template<class PathStringType> inline
274 typename std::enable_if<ztd::has_c_str<PathStringType,
275                           char const* (PathStringType::*)() const>::value,
276                         void>::type
create(PathStringType const & path)277 create( PathStringType const &path ) {
278   create( path.c_str() );
279 }
280 
281 #endif /* ZORBA_WITH_FILE_ACCESS */
282 
283 ////////// File deletion //////////////////////////////////////////////////////
284 
285 #ifdef ZORBA_WITH_FILE_ACCESS
286 
287 /**
288  * Removes the given file or directory.
289  *
290  * @param path The full path of the file or directory to remove.
291  * @return Returns \c true only if the file or directory was removed.
292  */
293 bool remove( char const *path );
294 
295 /**
296  * Removes the given file or directory.
297  *
298  * @tparam PathStringType The \a path string type.
299  * @param path The full path of the file or directory to remove.
300  * @return Returns \c true only if the file or directory was removed.
301  */
302 template<class PathStringType> inline
303 typename std::enable_if<ztd::has_c_str<PathStringType,
304                           char const* (PathStringType::*)() const>::value,
305                         bool>::type
remove(PathStringType const & path)306 remove( PathStringType const &path ) {
307   return remove( path.c_str() );
308 }
309 
310 /**
311  * An %auto_remover is a simple class to guarantee that a file or directory
312  * referred to by a path is deleted upon the %auto_remover getting destroyed.
313  *
314  * @tparam PathStringType The type of string to store and return the path as.
315  */
316 template<class PathStringType>
317 class auto_remover {
318 public:
319   typedef PathStringType string_type;
320 
321   /**
322    * Constructs an %auto_remover.
323    *
324    * @param The full path of the file or directory to delete automatically, if
325    * any.
326    */
path_(path)327   explicit auto_remover( char const *path = "" ) : path_( path ) {
328   }
329 
330   /**
331    * Constructs an %auto_remover.
332    *
333    * @tparam PathStringType The path's string type.
334    * @param The full path of the file or directory to delete automatically, if
335    * any.
336    */
auto_remover(PathStringType const & path)337   explicit auto_remover( PathStringType const &path ) : path_( path ) {
338   }
339 
340   /**
341    * Copy-constructs an %auto_remover from another.  This is a destructive
342    * construction: the other %auto_remover's path will be reset to the empty
343    * string.
344    *
345    * @param a The %auto_remover to copy from.
346    */
auto_remover(auto_remover & a)347   auto_remover( auto_remover &a ) : path_( a.release() ) {
348   }
349 
350   /**
351    * Copy-constructs an %auto_remover from another.  This is a destructive
352    * construction: the other %auto_remover's path will be reset to the empty
353    * string.
354    *
355    * @tparam PathStringType2 The other %auto_remover's path string type.
356    * @param a The %auto_remover to copy from.
357    */
358   template<class PathStringType2>
auto_remover(auto_remover<PathStringType2> & a)359   auto_remover( auto_remover<PathStringType2> &a ) : path_( a.release() ) {
360   }
361 
362   /**
363    * Destroys this %auto_remover.  If its path is not empty, the referred to
364    * file or directory is deleted.
365    */
~auto_remover()366   ~auto_remover() {
367     delete_path();
368   }
369 
370   /**
371    * Assigns the path of another %auto_remover to this %auto_remover.  This is
372    * a destructive assignment: the other %auto_remover's path will be reset to
373    * the empty string.
374    *
375    * @param a The %auto_remover to assign from.
376    * @return Returns \c *this.
377    */
378   auto_remover& operator=( auto_remover &a ) {
379     reset( a.release() );
380     return *this;
381   }
382 
383   /**
384    * Assigns the path of another %auto_remover to this %auto_remover.  This is
385    * a destructive assignment: the other %auto_remover's path will be reset to
386    * the empty string.
387    *
388    * @tparam PathStringType2 The other %auto_remover's path string type.
389    * @param a The %auto_remover to assign from.
390    * @return Returns \c *this.
391    */
392   template<class PathStringType2>
393   auto_remover& operator=( auto_remover<PathStringType2> &a ) {
394     reset( a.release() );
395     return *this;
396   }
397 
398   /**
399    * Gets the path of the file or directory this %auto_remover will delete upon
400    * destruction.
401    *
402    * @return Returns said path.
403    */
path()404   PathStringType const& path() const {
405     return path_;
406   }
407 
408   /**
409    * Releases the current file or directory from being deleted automatically.
410    */
release()411   PathStringType release() {
412     PathStringType const temp( path_ );
413     path_.clear();
414     return temp;
415   }
416 
417   /**
418    * Resets this %auto_remover to delete a different file or directory upon
419    * destruction.  The previous file or directory is deleted immediately.
420    * (If you don't want it to be deleted, call release() first.)
421    *
422    * @param The full path of the file or directory to delete automatically.
423    */
reset(char const * path)424   void reset( char const *path ) {
425     if ( path != path_ ) {
426       delete_path();
427       path_ = path;
428     }
429   }
430 
431   /**
432    * Resets this %auto_remover to delete a different file or directory upon
433    * destruction.  The previous file or directory is deleted immediately.
434    * (If you don't want it to be deleted, call release() first.)
435    *
436    * @param The full path of the file or directory to delete automatically.
437    */
reset(PathStringType const & path)438   void reset( PathStringType const &path ) {
439     reset( path.c_str() );
440   }
441 
442   /**
443    * Resets this %auto_remover to delete a different file or directory upon
444    * destruction.  The previous file or directory is deleted immediately.
445    * (If you don't want it to be deleted, call release() first.)
446    *
447    * @tparam PathStringType2 The path's string type.
448    * @param The full path of the file or directory to delete automatically.
449    */
450   template<class PathStringType2>
reset(PathStringType2 const & path)451   void reset( PathStringType2 const &path ) {
452     reset( path.c_str() );
453   }
454 
455 private:
delete_path()456   void delete_path() {
457     if ( !path_.empty() )
458       remove( path_ );
459   }
460 
461   PathStringType path_;
462 };
463 
464 #endif /* ZORBA_WITH_FILE_ACCESS */
465 
466 ////////// File information ///////////////////////////////////////////////////
467 
468 #ifdef ZORBA_WITH_FILE_ACCESS
469 
470 /**
471  * Gets the type of the given file.
472  *
473  * @param path The full path to check.
474  * @param size A pointer to a receive the size of the file in bytes.  The size
475  * is set only if it's not \c nullptr and the file's type is \c file.
476  * @return Returns said type.
477  */
478 type get_type( char const *path, size_type *size = nullptr );
479 
480 /**
481  * Gets the type of the given file.
482  *
483  * @tparam PathStringType The \a path string type.
484  * @param path The full path to check.
485  * @param size A pointer to a receive the size of the file in bytes.  The size
486  * is set only if it's not \c nullptr and the file's type is \c file.
487  * @return Returns said type.
488  */
489 template<class PathStringType> inline
490 typename std::enable_if<ztd::has_c_str<PathStringType,
491                           char const* (PathStringType::*)() const>::value,
492                         type>::type
493 get_type( PathStringType const &path, size_type *size = nullptr ) {
494   return get_type( path.c_str(), size );
495 }
496 
497 #endif /* ZORBA_WITH_FILE_ACCESS */
498 
499 /**
500  * Checks whether the given path is an absolute path.
501  *
502  * @param path The full path to check.
503  * @return Returns \c true only if the path is absolute.
504  */
is_absolute(char const * path)505 inline bool is_absolute( char const *path ) {
506 #ifndef WIN32
507   return path[0] == '/';
508 #else
509   //
510   // No, this should NOT also check for '/'.  The path should have been
511   // normalized for Windows first, i.e., have '/' replaced by '\'.
512   //
513   return ascii::is_alpha( path[0] ) && path[1] == ':' && path[2] == '\\';
514 #endif /* WIN32 */
515 }
516 
517 /**
518  * Checks whether the given path is an absolute path.
519  *
520  * @tparam PathStringType The \a path string type.
521  * @param path The full path to check.
522  * @return Returns \c true only if the path is absolute.
523  */
524 template<class PathStringType> inline
525 typename std::enable_if<ztd::has_c_str<PathStringType,
526                           char const* (PathStringType::*)() const>::value,
527                         bool>::type
is_absolute(PathStringType const & path)528 is_absolute( PathStringType const &path ) {
529   return is_absolute( path.c_str() );
530 }
531 
532 ////////// File renaming //////////////////////////////////////////////////////
533 
534 #ifdef ZORBA_WITH_FILE_ACCESS
535 
536 /**
537  * Renames a file or directory.
538  *
539  * @param from The full path of the existing file or directory to rename.
540  * @param to The full path of the new name for the file or directory.
541  * @throws fs::exception if the rename fails.
542  */
543 void rename( char const *from, char const *to );
544 
545 /**
546  * Renames a file or directory.
547  *
548  * @tparam FromStringType The \a from string type.
549  * @param from The full path of the existing file or directory to rename.
550  * @param to The full path of the new name for the file or directory.
551  * @throws fs::exception if the rename fails.
552  */
553 template<class FromStringType> inline
554 typename std::enable_if<ztd::has_c_str<FromStringType,
555                           char const* (FromStringType::*)() const>::value,
556                         void>::type
rename(FromStringType const & from,char const * to)557 rename( FromStringType const &from, char const *to ) {
558   rename( from.c_str(), to );
559 }
560 
561 /**
562  * Renames a file or directory.
563  *
564  * @tparam ToStringType The \a to string type.
565  * @param from The full path of the existing file or directory to rename.
566  * @param to The full path of the new name for the file or directory.
567  * @throws fs::exception if the rename fails.
568  */
569 template<class ToStringType> inline
570 typename std::enable_if<ztd::has_c_str<ToStringType,
571                           char const* (ToStringType::*)() const>::value,
572                         void>::type
rename(char const * from,ToStringType const & to)573 rename( char const *from, ToStringType const &to ) {
574   rename( from, to.c_str() );
575 }
576 
577 /**
578  * Renames a file or directory.
579  *
580  * @tparam FromStringType The \a from string type.
581  * @tparam ToStringType The \a to string type.
582  * @param from The full path of the existing file or directory to rename.
583  * @param to The full path of the new name for the file or directory.
584  * @throws fs::exception if the rename fails.
585  */
586 template<class FromStringType,class ToStringType> inline
587 typename std::enable_if<ztd::has_c_str<FromStringType,
588                           char const* (FromStringType::*)() const>::value
589                      && ztd::has_c_str<ToStringType,
590                           char const* (ToStringType::*)() const>::value,
591                         void>::type
rename(FromStringType const & from,ToStringType const & to)592 rename( FromStringType const &from, ToStringType const &to ) {
593   rename( from.c_str(), to.c_str() );
594 }
595 
596 #endif /* ZORBA_WITH_FILE_ACCESS */
597 
598 ////////// Path normalization /////////////////////////////////////////////////
599 
600 /**
601  * Gets the normalized path of the given path.
602  *
603  * @param path The path to normalize.
604  * @param base The base path, if any.
605  * @return Returns the normalized path.
606  * @throws XQueryException err::XPTY0004 for malformed paths.
607  */
608 zstring get_normalized_path( char const *path, char const *base = nullptr );
609 
610 /**
611  * Gets the normalized path of the given path.
612  *
613  * @tparam PathStringType The \a path string type.
614  * @param path The path to normalize.
615  * @param base The base path, if any.
616  * @return Returns the normalized path.
617  * @throws XQueryException err::XPTY0004 for malformed paths.
618  */
619 template<class PathStringType> inline
620 typename std::enable_if<ztd::has_c_str<PathStringType,
621                           char const* (PathStringType::*)() const>::value,
622                         zstring>::type
623 get_normalized_path( PathStringType const &path,
624                              PathStringType const &base = "" ) {
625   return get_normalized_path( path.c_str(), base.c_str() );
626 }
627 
628 /**
629  * Normalizes the given path.
630  *
631  * @tparam PathStringType The path string type.
632  * @param path The path to normalize.
633  * @param base The base path, if any.
634  * @return Returns the normalized path.
635  * @throws XQueryException err::XPTY0004 for malformed paths.
636  */
637 template<class PathStringType> inline
638 typename std::enable_if<ztd::has_c_str<PathStringType,
639                           char const* (PathStringType::*)() const>::value,
640                         void>::type
641 normalize_path( PathStringType &path, PathStringType const &base = "" ) {
642   path = get_normalized_path( path, base );
643 }
644 
645 ////////// Path manipulation //////////////////////////////////////////////////
646 
647 /**
648  * Appends a path component onto another path ensuring that exactly one
649  * separator is used.
650  *
651  * @tparam PathStringType1 The \a path1 string type.
652  * @param path1 The path to append to.
653  * @param path2 The path to append.
654  */
655 template<class PathStringType1> inline
656 typename std::enable_if<ztd::has_c_str<PathStringType1,
657                           char const* (PathStringType1::*)() const>::value,
658                         void>::type
append(PathStringType1 & path1,char const * path2)659 append( PathStringType1 &path1, char const *path2 ) {
660   if ( !ascii::ends_with( path1, dir_separator ) )
661     path1 += dir_separator;
662   path1 += path2;
663 }
664 
665 /**
666  * Appends a path component onto another path.
667  *
668  * @tparam PathStringType1 The \a path1 string type.
669  * @tparam PathStringType2 The \a path2 string type.
670  * @param path1 The path to append to.
671  * @param path2 The path to append.
672  */
673 template<class PathStringType1,class PathStringType2> inline
674 typename std::enable_if<ztd::has_c_str<PathStringType1,
675                           char const* (PathStringType1::*)() const>::value
676                      && ztd::has_c_str<PathStringType2,
677                           char const* (PathStringType2::*)() const>::value,
678                         void>::type
append(PathStringType1 & path1,PathStringType2 const & path2)679 append( PathStringType1 &path1, PathStringType2 const &path2 ) {
680   append( path1, path2.c_str() );
681 }
682 
683 /**
684  * Makes a relative path into an absolute path.
685  *
686  * @tparam PathStringType The \a path string type.
687  * @param path The path to make absolute.
688  */
689 template<class PathStringType> inline
690 typename std::enable_if<ztd::has_c_str<PathStringType,
691                           char const* (PathStringType::*)() const>::value,
692                         void>::type
make_absolute(PathStringType & path)693 make_absolute( PathStringType &path ) {
694   if ( !is_absolute( path ) ) {
695 #ifndef WIN32
696     typedef typename PathStringType::size_type size_type;
697     path.insert( static_cast<size_type>(0), static_cast<size_type>(1), '/' );
698     path.insert( 0, curdir().c_str() );
699 #else
700     char temp[ MAX_PATH ];
701     win32::make_absolute_impl( path.c_str(), temp );
702     path = temp;
703 #endif /* WIN32 */
704   }
705 }
706 
707 ////////// Temporary files ////////////////////////////////////////////////////
708 
709 #ifdef ZORBA_WITH_FILE_ACCESS
710 
711 /**
712  * Gets a path for a temporary file.
713  *
714  * @param path_buf A buffer to receive the path.  It must be at least
715  * \c MAX_PATH bytes long.
716  * @throws fs::exception if the operation fails.
717  */
718 void get_temp_file( char *path_buf );
719 
720 /**
721  * Gets a path for a temporary file.
722  *
723  * @tparam PathStringType The \a path string type.
724  * @param path The string to receive the path.
725  * @throws fs::exception if the operation fails.
726  */
727 template<class PathStringType> inline
728 typename std::enable_if<ztd::has_c_str<PathStringType,
729                           char const* (PathStringType::*)() const>::value,
730                         void>::type
get_temp_file(PathStringType * path)731 get_temp_file( PathStringType *path ) {
732   char path_buf[ MAX_PATH ];
733   get_temp_file( path_buf );
734   *path = path_buf;
735 }
736 
737 #endif /* ZORBA_WITH_FILE_ACCESS */
738 
739 ///////////////////////////////////////////////////////////////////////////////
740 
741 } // namespace fs
742 } // namespace zorba
743 
744 #endif /* ZORBA_FS_UTIL_H */
745 /*
746  * Local variables:
747  * mode: c++
748  * End:
749  */
750 /* vim:set et sw=2 ts=2: */
751