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