Home | History | Annotate | Line # | Download | only in fs
      1 // Copyright 2011 Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 // * Redistributions of source code must retain the above copyright
      9 //   notice, this list of conditions and the following disclaimer.
     10 // * Redistributions in binary form must reproduce the above copyright
     11 //   notice, this list of conditions and the following disclaimer in the
     12 //   documentation and/or other materials provided with the distribution.
     13 // * Neither the name of Google Inc. nor the names of its contributors
     14 //   may be used to endorse or promote products derived from this software
     15 //   without specific prior written permission.
     16 //
     17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 #include "utils/fs/lua_module.hpp"
     30 
     31 extern "C" {
     32 #include <dirent.h>
     33 }
     34 
     35 #include <cerrno>
     36 #include <cstring>
     37 #include <stdexcept>
     38 #include <string>
     39 
     40 #include <lutok/operations.hpp>
     41 #include <lutok/state.ipp>
     42 
     43 #include "utils/format/macros.hpp"
     44 #include "utils/fs/operations.hpp"
     45 #include "utils/fs/path.hpp"
     46 #include "utils/sanity.hpp"
     47 
     48 namespace fs = utils::fs;
     49 
     50 
     51 namespace {
     52 
     53 
     54 /// Lua binding for fs::path::basename.
     55 ///
     56 /// \pre stack(-1) The input path.
     57 /// \post stack(-1) The basename of the input path.
     58 ///
     59 /// \param state The Lua state.
     60 ///
     61 /// \return The number of result values, i.e. 1.
     62 static int
     63 lua_fs_basename(lutok::state& state)
     64 {
     65     if (!state.is_string())
     66         throw std::runtime_error("Need a string parameter");
     67     const fs::path path(state.to_string());
     68 
     69     state.push_string(path.leaf_name().c_str());
     70     return 1;
     71 }
     72 
     73 
     74 /// Lua binding for fs::path::dirname.
     75 ///
     76 /// \pre stack(-1) The input path.
     77 /// \post stack(-1) The directory part of the input path.
     78 ///
     79 /// \param state The Lua state.
     80 ///
     81 /// \return The number of result values, i.e. 1.
     82 static int
     83 lua_fs_dirname(lutok::state& state)
     84 {
     85     if (!state.is_string())
     86         throw std::runtime_error("Need a string parameter");
     87     const fs::path path(state.to_string());
     88 
     89     state.push_string(path.branch_path().c_str());
     90     return 1;
     91 }
     92 
     93 
     94 /// Lua binding for fs::path::exists.
     95 ///
     96 /// \pre stack(-1) The input path.
     97 /// \post stack(-1) Whether the input path exists or not.
     98 ///
     99 /// \param state The Lua state.
    100 ///
    101 /// \return The number of result values, i.e. 1.
    102 static int
    103 lua_fs_exists(lutok::state& state)
    104 {
    105     if (!state.is_string())
    106         throw std::runtime_error("Need a string parameter");
    107     const fs::path path(state.to_string());
    108 
    109     state.push_boolean(fs::exists(path));
    110     return 1;
    111 }
    112 
    113 
    114 /// Lua binding for the files iterator.
    115 ///
    116 /// This function takes an open directory from the closure of the iterator and
    117 /// returns the next entry.  See lua_fs_files() for the iterator generator
    118 /// function.
    119 ///
    120 /// \pre upvalue(1) The userdata containing an open DIR* object.
    121 ///
    122 /// \param state The lua state.
    123 ///
    124 /// \return The number of result values, i.e. 0 if there are no more entries or
    125 /// 1 if an entry has been read.
    126 static int
    127 files_iterator(lutok::state& state)
    128 {
    129     DIR** dirp = state.to_userdata< DIR* >(state.upvalue_index(1));
    130     const struct dirent* entry = ::readdir(*dirp);
    131     if (entry == NULL)
    132         return 0;
    133     else {
    134         state.push_string(entry->d_name);
    135         return 1;
    136     }
    137 }
    138 
    139 
    140 /// Lua binding for the destruction of the files iterator.
    141 ///
    142 /// This function takes an open directory and closes it.  See lua_fs_files() for
    143 /// the iterator generator function.
    144 ///
    145 /// \pre stack(-1) The userdata containing an open DIR* object.
    146 /// \post The DIR* object is closed.
    147 ///
    148 /// \param state The lua state.
    149 ///
    150 /// \return The number of result values, i.e. 0.
    151 static int
    152 files_gc(lutok::state& state)
    153 {
    154     PRE(state.is_userdata());
    155 
    156     DIR** dirp = state.to_userdata< DIR* >();
    157     // For some reason, this may be called more than once.  I don't know why
    158     // this happens, but we must protect against it.
    159     if (*dirp != NULL) {
    160         ::closedir(*dirp);
    161         *dirp = NULL;
    162     }
    163 
    164     return 0;
    165 }
    166 
    167 
    168 /// Lua binding to create an iterator to scan the contents of a directory.
    169 ///
    170 /// \pre stack(-1) The input path.
    171 /// \post stack(-1) The iterator function.
    172 ///
    173 /// \param state The Lua state.
    174 ///
    175 /// \return The number of result values, i.e. 1.
    176 static int
    177 lua_fs_files(lutok::state& state)
    178 {
    179     if (!state.is_string())
    180         throw std::runtime_error("Need a string parameter");
    181     const fs::path path(state.to_string());
    182 
    183     DIR** dirp = state.new_userdata< DIR* >();
    184 
    185     state.new_table();
    186     state.push_string("__gc");
    187     state.push_cxx_function(files_gc);
    188     state.set_table();
    189 
    190     state.set_metatable();
    191 
    192     *dirp = ::opendir(path.c_str());
    193     if (*dirp == NULL) {
    194         const int original_errno = errno;
    195         throw std::runtime_error(F("Failed to open directory: %s") %
    196                                  std::strerror(original_errno));
    197     }
    198 
    199     state.push_cxx_closure(files_iterator, 1);
    200 
    201     return 1;
    202 }
    203 
    204 
    205 /// Lua binding for fs::path::is_absolute.
    206 ///
    207 /// \pre stack(-1) The input path.
    208 /// \post stack(-1) Whether the input path is absolute or not.
    209 ///
    210 /// \param state The Lua state.
    211 ///
    212 /// \return The number of result values, i.e. 1.
    213 static int
    214 lua_fs_is_absolute(lutok::state& state)
    215 {
    216     if (!state.is_string())
    217         throw std::runtime_error("Need a string parameter");
    218     const fs::path path(state.to_string());
    219 
    220     state.push_boolean(path.is_absolute());
    221     return 1;
    222 }
    223 
    224 
    225 /// Lua binding for fs::path::operator/.
    226 ///
    227 /// \pre stack(-2) The first input path.
    228 /// \pre stack(-1) The second input path.
    229 /// \post stack(-1) The concatenation of the two paths.
    230 ///
    231 /// \param state The Lua state.
    232 ///
    233 /// \return The number of result values, i.e. 1.
    234 static int
    235 lua_fs_join(lutok::state& state)
    236 {
    237     if (!state.is_string(-2))
    238         throw std::runtime_error("Need a string parameter");
    239     const fs::path path1(state.to_string(-2));
    240 
    241     if (!state.is_string(-1))
    242         throw std::runtime_error("Need a string parameter");
    243     const fs::path path2(state.to_string(-1));
    244 
    245     state.push_string((path1 / path2).c_str());
    246     return 1;
    247 }
    248 
    249 
    250 }  // anonymous namespace
    251 
    252 
    253 /// Creates a Lua 'fs' module.
    254 ///
    255 /// \post The global 'fs' symbol is set to a table that contains functions to a
    256 /// variety of utilites from the fs C++ module.
    257 ///
    258 /// \param s The Lua state.
    259 void
    260 fs::open_fs(lutok::state& s)
    261 {
    262     std::map< std::string, lutok::cxx_function > members;
    263     members["basename"] = lua_fs_basename;
    264     members["dirname"] = lua_fs_dirname;
    265     members["exists"] = lua_fs_exists;
    266     members["files"] = lua_fs_files;
    267     members["is_absolute"] = lua_fs_is_absolute;
    268     members["join"] = lua_fs_join;
    269     lutok::create_module(s, "fs", members);
    270 }
    271