#include #include #include #include #include #include #include #include #include #include "honeysuckle.h" #include "md4c-html.h" #include "bindings.h" #include "logging.h" 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"); char *markdown_buffer; hs_parse_args(L, hs_str(markdown_buffer)); 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(128 * sizeof(char)); data.size = 128 * 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) hs_throw_error(L, "markdown parsing failed!"); if (data.ok == false) hs_throw_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, char *dir_name); static struct dirent *read_dir(lua_State *L, DIR *directory); int scan_directory(lua_State *L) { argent_log(TRACE, "begin scan_directory()"); char *dir_name; hs_parse_args(L, hs_str(dir_name)); 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 (entry->d_type == DT_DIR) { 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 void throw_directory_error(lua_State *L, char *dir_name) { argent_log(ERROR, "failed to open directory: %d\n", errno); switch(errno) { case EACCES: hs_throw_error(L, "read %s: permission denied", dir_name); break; case EMFILE: hs_throw_error(L, "read %s: this process has too many open files", dir_name); break; case ENFILE: hs_throw_error(L, "read %s: this file system cannot support any more open files", dir_name); break; case ENOMEM: hs_throw_error(L, "read %s: Not enough memory", dir_name); break; default: hs_throw_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); hs_throw_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) { char *dir_name; hs_parse_args(L, hs_str(dir_name)); 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()); hs_throw_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 EDQUOT: return "User cannot create new inodes"; 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) { char *source_name, *dest_name; hs_parse_args(L, hs_str(source_name), hs_str(dest_name)); 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()); hs_throw_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()); hs_throw_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()); hs_throw_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); hs_throw_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) { char *level_string, *message; hs_parse_args(L, hs_str(level_string), hs_str(message)); 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; }