#include #include #include #include #include #include #include #include #include #include #include #include "md4c-html.h" #include "bindings.h" #include "logging.h" #ifdef __MINGW32__ // mingw has weird mkdir for some reason? (or maybe linux is just non-POSIX idk) #define MKDIR(pathname, mode) mkdir(pathname) #else #define MKDIR(pathname, mode) mkdir(pathname, mode) #endif struct concat_buffer { char *buf; size_t size; int index; bool ok; }; static void md_callback(const MD_CHAR *html, MD_SIZE size, void *data) { argent_log(TRACE, "begin md_callback()"); struct concat_buffer *d = data; if (!d->ok) return; if (d->index + size >= d->size) { char *new_buf = realloc(d->buf, d->size * 2); if (new_buf == NULL) { // failed to allocate memory, abort! argent_log(WARN, "failed to properly allocate memory for html buffer!"); d->ok = false; return; } d->buf = new_buf; d->size *= 2; } memcpy((d->buf) + d->index, html, size); d->index += size; argent_log(TRACE, "finish md_callback()"); } int markdown(lua_State *L) { argent_log(TRACE, "begin markdown parsing"); const char *markdown_buffer = luaL_checkstring(L, 1); size_t len = strlen(markdown_buffer); argent_log(TRACE, "markdown input (%ld bytes): %s", len, markdown_buffer); unsigned int md_flags = MD_FLAG_TABLES | MD_FLAG_STRIKETHROUGH | MD_FLAG_UNDERLINE; struct concat_buffer data; data.buf = malloc(2048 * sizeof(char)); data.size = 2048 * sizeof(char); data.index = 0; data.ok = true; argent_log(TRACE, "call md_html()"); // fill out the buffer int error = md_html(markdown_buffer, len, md_callback, &data, md_flags, 0); if (error) luaL_error(L, "markdown parsing failed!"); if (data.ok == false) luaL_error(L, "encountered error (re)allocating html buffer memory!"); data.buf[data.index] = 0; argent_log(TRACE, "finished HTML buffer (%d bytes, %d chars): %s", data.size, data.index, data.buf); lua_pushstring(L, data.buf); free(data.buf); argent_log(TRACE, "finish markdown parsing"); return 1; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int current_working_directory(lua_State *L) { char *cwd = getcwd(NULL, 0); lua_pushstring(L, cwd); free(cwd); return 1; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ static void throw_directory_error(lua_State *L, const char *dir_name); static bool is_directory(const char *path); static struct dirent *read_dir(lua_State *L, DIR *directory); int scan_directory(lua_State *L) { argent_log(TRACE, "begin scan_directory()"); const char *dir_name = luaL_checkstring(L, 1); DIR *directory = opendir(dir_name); if (directory == NULL) throw_directory_error(L, dir_name); lua_newtable(L); int directory_table = lua_gettop(L); int directory_index = 1; lua_newtable(L); int file_table = lua_gettop(L); int file_index = 1; struct dirent *entry; while ((entry = read_dir(L, directory)) != NULL) { lua_pushstring(L, entry->d_name); if (is_directory(entry->d_name)) { lua_rawseti(L, directory_table, directory_index); directory_index += 1; } else { lua_rawseti(L, file_table, file_index); file_index += 1; } } closedir(directory); argent_log(TRACE, "end scan_directory()"); return 2; } static bool is_directory(const char *path) { struct stat sb; if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { return true; } return false; } static void throw_directory_error(lua_State *L, const char *dir_name) { argent_log(ERROR, "failed to open directory: %d\n", errno); switch(errno) { case EACCES: luaL_error(L, "read %s: permission denied", dir_name); break; case EMFILE: luaL_error(L, "read %s: this process has too many open files", dir_name); break; case ENFILE: luaL_error(L, "read %s: this file system cannot support any more open files", dir_name); break; case ENOMEM: luaL_error(L, "read %s: Not enough memory", dir_name); break; default: luaL_error(L, "read %s: unknown error", dir_name); break; } } static struct dirent *read_dir(lua_State *L, DIR *directory) { // can't tell if a NULL was EOF or error without this errno = 0; struct dirent *entry = readdir(directory); if (entry == NULL && errno != 0) { argent_log(ERROR, "attempted to read invalid dirstream"); closedir(directory); luaL_error(L, "attempted to read invalid dirstream"); } return entry; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ static const char* mkdir_error_string() { switch(errno) { case EACCES: return "Permission denied"; case EEXIST: return "A file with that name already exists"; case EMLINK: return "The parent directory is full"; case ENOSPC: return "The file system is full"; case EROFS: return "The parent directory is read-only"; default: return "unknown error"; } } int create_directory(lua_State *L) { const char *dir_name = luaL_checkstring(L, 1); mode_t mode = S_IRWXU | S_IRWXG | (S_IROTH | S_IXOTH); // u+rwx, g+rwx, a+rx int error = MKDIR(dir_name, mode); if (error) { argent_log(ERROR, "failed to create directory '%s': %s", dir_name, mkdir_error_string()); luaL_error(L, "failed to create directory '%s': %s", dir_name, mkdir_error_string()); } return 0; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ static const char* open_error_string() { switch (errno) { case EACCES: return "Permission denied"; case EBUSY: return "Device busy"; case EEXIST: return "File exists"; case EFAULT: return "Pathname points outside your accessible address space"; case EINTR: return "Operation interrupted"; case EINVAL: return "Attempted an invalid operation"; case ELOOP: return "Too many symbolic links encountered in resolving the pathname"; case EMFILE: return "The process has too many open files"; case ENAMETOOLONG: return "The pathname was too long"; case ENOMEM: return "Insufficient kernel memory available"; case ENOSPC: return "No space available on the device"; case EOVERFLOW: return "File is too large to open"; case ETXTBSY: return "File is busy"; default: return "Other error"; } } int copy_file(lua_State *L) { const char *source_name = luaL_checkstring(L, 1); const char *dest_name = luaL_checkstring(L, 2); int source_fd, dest_fd; source_fd = open(source_name, 0); if (source_fd == -1) { argent_log(ERROR, "failed to open file '%s': %s", source_name, open_error_string()); luaL_error(L, "failed to open file '%s': %s", source_name, open_error_string()); } struct stat source_stat; int error = fstat(source_fd, &source_stat); if (error) { close(source_fd); argent_log(ERROR, "failed to stat '%s': %s", source_name, open_error_string()); luaL_error(L, "failed to stat '%s': %s", source_name, open_error_string()); } mode_t dest_mode = source_stat.st_mode; dest_fd = creat(dest_name, dest_mode); if (dest_fd == -1) { close(source_fd); argent_log(ERROR, "failed to open file '%s': %s", dest_name, open_error_string()); luaL_error(L, "failed to open file '%s': %s", dest_name, open_error_string()); } unsigned char buffer[256]; size_t buffer_size = sizeof(unsigned char) * 256; ssize_t bytes_read; while ( (bytes_read = read(source_fd, buffer, buffer_size)) == buffer_size ) { write(dest_fd, buffer, buffer_size); } if (bytes_read == -1) { argent_log(ERROR, "failed to write to file '%s'", dest_name); close(source_fd); close(dest_fd); luaL_error(L, "failed to write to file '%s'", dest_name); } else { write(dest_fd, buffer, bytes_read); } close(source_fd); close(dest_fd); return 0; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int argent_log_lua(lua_State *L) { const char *level_string = luaL_checkstring(L, 1); const char *message = luaL_checkstring(L, 2); int level; if(strcmp(level_string, "fatal") == 0) level = FATAL; else if (strcmp(level_string, "error") == 0) level = ERROR; else if (strcmp(level_string, "warn") == 0) level = WARN; else if (strcmp(level_string, "info") == 0) level = INFO; else if (strcmp(level_string, "debug") == 0) level = DEBUG; else if (strcmp(level_string, "trace") == 0) level = TRACE; else { argent_log(WARN, "level '%s' is invalid; defaulting to 'info'", level_string); level = INFO; } argent_log(level, message); return 0; }