Mercurial > irccd
view lib/irccd/mod-file.cpp @ 207:6635b9187d71
Irccd: switch to 4 spaces indent, #518
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 21 Jun 2016 20:52:17 +0200 |
parents | 1099568ef450 |
children |
line wrap: on
line source
/* * js-file.cpp -- Irccd.File API * * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <algorithm> #include <array> #include <cassert> #include <iterator> #include <vector> #include "sysconfig.hpp" #if defined(HAVE_STAT) # include <sys/types.h> # include <sys/stat.h> #endif #include "fs.hpp" #include "mod-file.hpp" #include "mod-irccd.hpp" #include "plugin-js.hpp" namespace irccd { namespace { const char *Signature("\xff""\xff""irccd-file-ptr"); const char *Prototype("\xff""\xff""irccd-file-prototype"); #if defined(HAVE_STAT) /* * pushStat * ------------------------------------------------------------------ */ void pushStat(duk_context *ctx, const struct stat &st) { StackAssert sa(ctx, 1); duk_push_object(ctx); #if defined(HAVE_STAT_ST_ATIME) duk_push_int(ctx, st.st_atime); duk_put_prop_string(ctx, -2, "atime"); #endif #if defined(HAVE_STAT_ST_BLKSIZE) duk_push_int(ctx, st.st_blksize); duk_put_prop_string(ctx, -2, "blksize"); #endif #if defined(HAVE_STAT_ST_BLOCKS) duk_push_int(ctx, st.st_blocks); duk_put_prop_string(ctx, -2, "blocks"); #endif #if defined(HAVE_STAT_ST_CTIME) duk_push_int(ctx, st.st_ctime); duk_put_prop_string(ctx, -2, "ctime"); #endif #if defined(HAVE_STAT_ST_DEV) duk_push_int(ctx, st.st_dev); duk_put_prop_string(ctx, -2, "dev"); #endif #if defined(HAVE_STAT_ST_GID) duk_push_int(ctx, st.st_gid); duk_put_prop_string(ctx, -2, "gid"); #endif #if defined(HAVE_STAT_ST_INO) duk_push_int(ctx, st.st_ino); duk_put_prop_string(ctx, -2, "ino"); #endif #if defined(HAVE_STAT_ST_MODE) duk_push_int(ctx, st.st_mode); duk_put_prop_string(ctx, -2, "mode"); #endif #if defined(HAVE_STAT_ST_MTIME) duk_push_int(ctx, st.st_mtime); duk_put_prop_string(ctx, -2, "mtime"); #endif #if defined(HAVE_STAT_ST_NLINK) duk_push_int(ctx, st.st_nlink); duk_put_prop_string(ctx, -2, "nlink"); #endif #if defined(HAVE_STAT_ST_RDEV) duk_push_int(ctx, st.st_rdev); duk_put_prop_string(ctx, -2, "rdev"); #endif #if defined(HAVE_STAT_ST_SIZE) duk_push_int(ctx, st.st_size); duk_put_prop_string(ctx, -2, "size"); #endif #if defined(HAVE_STAT_ST_UID) duk_push_int(ctx, st.st_uid); duk_put_prop_string(ctx, -2, "uid"); #endif } #endif // !HAVE_STAT // Remove trailing \r for CRLF line style. inline std::string clearCr(std::string input) { if (input.length() > 0 && input.back() == '\r') input.pop_back(); return input; } File *self(duk_context *ctx) { StackAssert sa(ctx); duk_push_this(ctx); duk_get_prop_string(ctx, -1, Signature); auto ptr = static_cast<File *>(duk_to_pointer(ctx, -1)); duk_pop_2(ctx); if (!ptr) duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); return ptr; } /* * File methods. * ------------------------------------------------------------------ */ /* * Method: File.basename() * -------------------------------------------------------- * * Synonym of `Irccd.File.basename(path)` but with the path from the file. * * duk_ret_turns: * The base name. */ duk_ret_t methodBasename(duk_context *ctx) { dukx_push_std_string(ctx, fs::baseName(self(ctx)->path())); return 1; } /* * Method: File.close() * -------------------------------------------------------- * * Force close of the file, automatically called when object is collected. */ duk_ret_t methodClose(duk_context *ctx) { self(ctx)->close(); return 0; } /* * Method: File.dirname() * -------------------------------------------------------- * * Synonym of `Irccd.File.dirname(path)` but with the path from the file. * * duk_ret_turns: * The directory name. */ duk_ret_t methodDirname(duk_context *ctx) { dukx_push_std_string(ctx, fs::dirName(self(ctx)->path())); return 1; } /* * Method: File.lines() * -------------------------------------------------------- * * Read all lines and return an array. * * duk_ret_turns: * An array with all lines. * Throws * - Any exception on error. */ duk_ret_t methodLines(duk_context *ctx) { duk_push_array(ctx); std::FILE *fp = self(ctx)->handle(); std::string buffer; std::array<char, 128> data; std::int32_t i = 0; while (std::fgets(&data[0], data.size(), fp) != nullptr) { buffer += data.data(); auto pos = buffer.find('\n'); if (pos != std::string::npos) { dukx_push_std_string(ctx, clearCr(buffer.substr(0, pos))); duk_put_prop_index(ctx, -2, i++); buffer.erase(0, pos + 1); } } // Maybe an error in the stream. if (std::ferror(fp)) dukx_throw(ctx, SystemError()); // Missing '\n' in end of file. if (!buffer.empty()) { dukx_push_std_string(ctx, clearCr(buffer)); duk_put_prop_index(ctx, -2, i++); } return 1; } /* * Method: File.read(amount) * -------------------------------------------------------- * * Read the specified amount of characters or the whole file. * * Arguments: * - amount, the amount of characters or -1 to read all (Optional, default: -1). * duk_ret_turns: * The string. * Throws: * - Any exception on error. */ duk_ret_t methodRead(duk_context *ctx) { auto file = self(ctx); auto amount = duk_is_number(ctx, 0) ? duk_get_int(ctx, 0) : -1; if (amount == 0 || file->handle() == nullptr) return 0; try { std::string data; std::size_t total = 0; if (amount < 0) { std::array<char, 128> buffer; std::size_t nread; while ((nread = std::fread(&buffer[0], sizeof (buffer[0]), buffer.size(), file->handle())) > 0) { if (std::ferror(file->handle())) dukx_throw(ctx, SystemError()); std::copy(buffer.begin(), buffer.begin() + nread, std::back_inserter(data)); total += nread; } } else { data.resize((std::size_t)amount); total = std::fread(&data[0], sizeof (data[0]), (std::size_t)amount, file->handle()); if (std::ferror(file->handle())) dukx_throw(ctx, SystemError()); data.resize(total); } dukx_push_std_string(ctx, data); } catch (const std::exception &) { dukx_throw(ctx, SystemError()); } return 1; } /* * Method: File.readline() * -------------------------------------------------------- * * Read the next line available. * * duk_ret_turns: * The next line or undefined if eof. * Throws: * - Any exception on error. */ duk_ret_t methodReadline(duk_context *ctx) { std::FILE *fp = self(ctx)->handle(); std::string result; if (fp == nullptr || std::feof(fp)) return 0; for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; ) result += (char)ch; if (std::ferror(fp)) dukx_throw(ctx, SystemError()); dukx_push_std_string(ctx, clearCr(result)); return 1; } /* * Method: File.remove() * -------------------------------------------------------- * * Synonym of File.remove(path) but with the path from the file. * * Throws: * - Any exception on error. */ duk_ret_t methodRemove(duk_context *ctx) { if (::remove(self(ctx)->path().c_str()) < 0) dukx_throw(ctx, SystemError()); return 0; } /* * Method: File.seek(type, amount) * -------------------------------------------------------- * * Sets the position in the file. * * Arguments: * - type, the type of setting (File.SeekSet, File.SeekCur, File.SeekSet), * - amount, the new offset. * Throws: * - Any exception on error. */ duk_ret_t methodSeek(duk_context *ctx) { auto fp = self(ctx)->handle(); auto type = duk_require_int(ctx, 0); auto amount = duk_require_int(ctx, 1); if (fp != nullptr && std::fseek(fp, amount, type) != 0) dukx_throw(ctx, SystemError()); return 0; } #if defined(HAVE_STAT) /* * Method: File.stat() [optional] * -------------------------------------------------------- * * Synonym of File.stat(path) but with the path from the file. * * duk_ret_turns: * The stat information. * Throws: * - Any exception on error. */ duk_ret_t methodStat(duk_context *ctx) { auto file = self(ctx); struct stat st; if (file->handle() == nullptr && ::stat(file->path().c_str(), &st) < 0) dukx_throw(ctx, SystemError()); else pushStat(ctx, st); return 1; } #endif // !HAVE_STAT /* * Method: File.tell() * -------------------------------------------------------- * * Get the actual position in the file. * * duk_ret_turns: * The position. * Throws: * - Any exception on error. */ duk_ret_t methodTell(duk_context *ctx) { auto fp = self(ctx)->handle(); long pos; if (fp == nullptr) return 0; if ((pos = std::ftell(fp)) == -1L) dukx_throw(ctx, SystemError()); else duk_push_int(ctx, pos); return 1; } /* * Method: File.write(data) * -------------------------------------------------------- * * Write some characters to the file. * * Arguments: * - data, the character to write. * duk_ret_turns: * The number of bytes written. * Throws: * - Any exception on error. */ duk_ret_t methodWrite(duk_context *ctx) { std::FILE *fp = self(ctx)->handle(); std::string data = duk_require_string(ctx, 0); if (fp == nullptr) return 0; std::size_t nwritten = std::fwrite(data.c_str(), 1, data.length(), fp); if (std::ferror(fp)) dukx_throw(ctx, SystemError()); duk_push_uint(ctx, nwritten); return 1; } const duk_function_list_entry methods[] = { { "basename", methodBasename, 0 }, { "close", methodClose, 0 }, { "dirname", methodDirname, 0 }, { "lines", methodLines, 0 }, { "read", methodRead, 1 }, { "readline", methodReadline, 0 }, { "remove", methodRemove, 0 }, { "seek", methodSeek, 2 }, #if defined(HAVE_STAT) { "stat", methodStat, 0 }, #endif { "tell", methodTell, 0 }, { "write", methodWrite, 1 }, { nullptr, nullptr, 0 } }; /* * File "static" functions * ------------------------------------------------------------------ */ /* * Function: Irccd.File(path, mode) [constructor] * -------------------------------------------------------- * * Open a file specified by path with the specified mode. * * Arguments: * - path, the path to the file, * - mode, the mode string. * Throws: * - Any exception on error. */ duk_ret_t constructor(duk_context *ctx) { if (!duk_is_constructor_call(ctx)) return 0; try { dukx_new_file(ctx, new File(duk_require_string(ctx, 0), duk_require_string(ctx, 1))); } catch (const std::exception &) { dukx_throw(ctx, SystemError()); } return 0; } /* * Function: Irccd.File() [destructor] * ------------------------------------------------------------------ * * Delete the property. */ duk_ret_t destructor(duk_context *ctx) { duk_get_prop_string(ctx, 0, Signature); delete static_cast<File *>(duk_to_pointer(ctx, -1)); duk_pop(ctx); duk_del_prop_string(ctx, 0, Signature); return 0; } /* * Function: Irccd.File.basename(path) * -------------------------------------------------------- * * duk_ret_turn the file basename as specified in `basename(3)` C function. * * Arguments: * - path, the path to the file. * duk_ret_turns: * The base name. */ duk_ret_t functionBasename(duk_context *ctx) { dukx_push_std_string(ctx, fs::baseName(duk_require_string(ctx, 0))); return 1; } /* * Function: Irccd.File.dirname(path) * -------------------------------------------------------- * * duk_ret_turn the file directory name as specified in `dirname(3)` C function. * * Arguments: * - path, the path to the file. * duk_ret_turns: * The directory name. */ duk_ret_t functionDirname(duk_context *ctx) { dukx_push_std_string(ctx, fs::dirName(duk_require_string(ctx, 0))); return 1; } /* * Function: Irccd.File.exists(path) * -------------------------------------------------------- * * Check if the file exists. * * Arguments: * - path, the path to the file. * duk_ret_turns: * True if exists. * Throws: * - Any exception if we don't have access. */ duk_ret_t functionExists(duk_context *ctx) { duk_push_boolean(ctx, fs::exists(duk_require_string(ctx, 0))); return 1; } /* * function Irccd.File.remove(path) * -------------------------------------------------------- * * Remove the file at the specified path. * * Arguments: * - path, the path to the file. * Throws: * - Any exception on error. */ duk_ret_t functionRemove(duk_context *ctx) { if (::remove(duk_require_string(ctx, 0)) < 0) dukx_throw(ctx, SystemError()); return 0; } #if defined(HAVE_STAT) /* * function Irccd.File.stat(path) [optional] * -------------------------------------------------------- * * Get file information at the specified path. * * Arguments: * - path, the path to the file. * duk_ret_turns: * The stat information. * Throws: * - Any exception on error. */ duk_ret_t functionStat(duk_context *ctx) { struct stat st; if (::stat(duk_require_string(ctx, 0), &st) < 0) dukx_throw(ctx, SystemError()); pushStat(ctx, st); return 1; } #endif // !HAVE_STAT const duk_function_list_entry functions[] = { { "basename", functionBasename, 1 }, { "dirname", functionDirname, 1 }, { "exists", functionExists, 1 }, { "remove", functionRemove, 1 }, #if defined(HAVE_STAT) { "stat", functionStat, 1 }, #endif { nullptr, nullptr, 0 } }; const duk_number_list_entry constants[] = { { "SeekCur", SEEK_CUR }, { "SeekEnd", SEEK_END }, { "SeekSet", SEEK_SET }, { nullptr, 0 } }; } // !namespace FileModule::FileModule() noexcept : Module("Irccd.File") { } void FileModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin) { StackAssert sa(plugin->context()); duk_get_global_string(plugin->context(), "Irccd"); duk_push_c_function(plugin->context(), constructor, 2); duk_put_number_list(plugin->context(), -1, constants); duk_put_function_list(plugin->context(), -1, functions); duk_push_object(plugin->context()); duk_put_function_list(plugin->context(), -1, methods); duk_push_c_function(plugin->context(), destructor, 1); duk_set_finalizer(plugin->context(), -2); duk_dup(plugin->context(), -1); duk_put_global_string(plugin->context(), Prototype); duk_put_prop_string(plugin->context(), -2, "prototype"); duk_put_prop_string(plugin->context(), -2, "File"); duk_pop(plugin->context()); } void dukx_new_file(duk_context *ctx, File *fp) { assert(ctx); assert(fp); StackAssert sa(ctx); duk_push_this(ctx); duk_push_pointer(ctx, fp); duk_put_prop_string(ctx, -2, Signature); duk_pop(ctx); } void dukx_push_file(duk_context *ctx, File *fp) { assert(ctx); assert(fp); StackAssert sa(ctx, 1); duk_push_object(ctx); duk_push_pointer(ctx, fp); duk_put_prop_string(ctx, -2, Signature); duk_get_global_string(ctx, Prototype); duk_set_prototype(ctx, -2); } File *dukx_require_file(duk_context *ctx, duk_idx_t index) { if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, Signature)) duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); duk_get_prop_string(ctx, index, Signature); File *file = static_cast<File *>(duk_to_pointer(ctx, -1)); duk_pop(ctx); return file; } } // !irccd