Mercurial > irccd
view irccd/jsapi-file.c @ 1005:ea9cf916330d
misc: fix permissions
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 16 Feb 2021 19:54:16 +0100 |
parents | e2a86096bc05 |
children | 8f8ce47aba8a |
line wrap: on
line source
/* * jsapi-file.c -- Irccd.File API * * Copyright (c) 2013-2021 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 <compat.h> #include <sys/stat.h> #include <assert.h> #include <errno.h> #include <limits.h> #include <stdio.h> #include <string.h> #include <duktape.h> #include <irccd/util.h> #include "jsapi-file.h" #include "jsapi-system.h" #define SIGNATURE DUK_HIDDEN_SYMBOL("Irccd.File") #define PROTOTYPE DUK_HIDDEN_SYMBOL("Irccd.File.prototype") struct file { char path[PATH_MAX]; FILE *fp; int (*finalizer)(FILE *); }; static int read_until_eof(duk_context *ctx, struct file *file) { char *ret = NULL, *newret, buf[BUFSIZ]; size_t retsz = 0, nread; /* * This function is shared with popen which can not be used with stat * so read the file by small piece of chunks and concat them until we * reach the end. */ while ((nread = fread(buf, 1, sizeof (buf), file->fp)) > 0) { if (!(newret = realloc(ret, retsz + nread))) { free(ret); jsapi_system_raise(ctx); } ret = newret; memcpy(ret + retsz, buf, nread); retsz += nread; } if (ferror(file->fp)) { free(ret); jsapi_system_raise(ctx); } duk_push_lstring(ctx, ret, retsz); free(ret); return 1; } static int read_amount(duk_context *ctx, struct file *file, unsigned int amount) { char *ret; size_t nread; if (!(ret = malloc(amount))) jsapi_system_raise(ctx); if ((nread = fread(ret, 1, amount, file->fp)) <= 0 || ferror(file->fp)) { free(ret); jsapi_system_raise(ctx); } duk_push_lstring(ctx, ret, nread); free(ret); return 1; } static void push_stat(duk_context *ctx, const struct stat *st) { duk_push_object(ctx); #if defined(COMPAT_HAVE_STAT_ST_ATIME) duk_push_int(ctx, st->st_atime); duk_put_prop_string(ctx, -2, "atime"); #endif #if defined(COMPAT_HAVE_STAT_ST_BLKSIZE) duk_push_int(ctx, st->st_blksize); duk_put_prop_string(ctx, -2, "blksize"); #endif #if defined(COMPAT_HAVE_STAT_ST_BLOCKS) duk_push_int(ctx, st->st_blocks); duk_put_prop_string(ctx, -2, "blocks"); #endif #if defined(COMPAT_HAVE_STAT_ST_CTIME) duk_push_int(ctx, st->st_ctime); duk_put_prop_string(ctx, -2, "ctime"); #endif #if defined(COMPAT_HAVE_STAT_ST_DEV) duk_push_int(ctx, st->st_dev); duk_put_prop_string(ctx, -2, "dev"); #endif #if defined(COMPAT_HAVE_STAT_ST_GID) duk_push_int(ctx, st->st_gid); duk_put_prop_string(ctx, -2, "gid"); #endif #if defined(COMPAT_HAVE_STAT_ST_INO) duk_push_int(ctx, st->st_ino); duk_put_prop_string(ctx, -2, "ino"); #endif #if defined(COMPAT_HAVE_STAT_ST_MODE) duk_push_int(ctx, st->st_mode); duk_put_prop_string(ctx, -2, "mode"); #endif #if defined(COMPAT_HAVE_STAT_ST_MTIME) duk_push_int(ctx, st->st_mtime); duk_put_prop_string(ctx, -2, "mtime"); #endif #if defined(COMPAT_HAVE_STAT_ST_NLINK) duk_push_int(ctx, st->st_nlink); duk_put_prop_string(ctx, -2, "nlink"); #endif #if defined(COMPAT_HAVE_STAT_ST_RDEV) duk_push_int(ctx, st->st_rdev); duk_put_prop_string(ctx, -2, "rdev"); #endif #if defined(COMPAT_HAVE_STAT_ST_SIZE) duk_push_int(ctx, st->st_size); duk_put_prop_string(ctx, -2, "size"); #endif #if defined(COMPAT_HAVE_STAT_ST_UID) duk_push_int(ctx, st->st_uid); duk_put_prop_string(ctx, -2, "uid"); #endif } static struct file * self(duk_context *ctx) { struct file *file; duk_push_this(ctx); duk_get_prop_string(ctx, -1, SIGNATURE); file = duk_to_pointer(ctx, -1); duk_pop_2(ctx); if (!file) (void)duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); return file; } static int File_prototype_basename(duk_context *ctx) { const struct file *file = self(ctx); duk_push_string(ctx, irc_util_basename(file->path)); return 1; } static int File_prototype_close(duk_context *ctx) { struct file *file = self(ctx); if (file->fp) { file->finalizer(file->fp); file->fp = NULL; } return 0; } static int File_prototype_dirname(duk_context *ctx) { const struct file *file = self(ctx); duk_push_string(ctx, irc_util_dirname(file->path)); return 1; } static int File_prototype_lines(duk_context *ctx) { struct file *file = self(ctx); char *line = NULL; size_t linesz = 0; if (!file->fp) { errno = EBADF; jsapi_system_raise(ctx); } duk_push_array(ctx); for (int i = 0; getline(&line, &linesz, file->fp) >= 0; ++i) { line[strcspn(line, "\r\n")] = 0; duk_push_string(ctx, line); duk_put_prop_index(ctx, -2, i); } free(line); if (ferror(file->fp)) jsapi_system_raise(ctx); return 1; } static int File_prototype_read(duk_context *ctx) { struct file *file = self(ctx); unsigned int amount = duk_opt_uint(ctx, 0, -1); if (!file->fp) { errno = EBADF; jsapi_system_raise(ctx); } return amount == -1U ? read_until_eof(ctx, file) : read_amount(ctx, file, amount); } static int File_prototype_readline(duk_context *ctx) { const struct file *file = self(ctx); char *line = NULL; size_t linesz = 0; if (!file->fp) { errno = EBADF; jsapi_system_raise(ctx); } if (getline(&line, &linesz, file->fp) < 0) { free(line); if (feof(file->fp)) return 0; jsapi_system_raise(ctx); } line[strcspn(line, "\r\n")] = 0; duk_push_string(ctx, line); free(line); return 1; } static int File_prototype_remove(duk_context *ctx) { if (remove(self(ctx)->path) < 0) jsapi_system_raise(ctx); return 0; } static int File_prototype_seek(duk_context *ctx) { const struct file *file = self(ctx); const int type = duk_require_int(ctx, 0); const long offset = duk_require_int(ctx, 1); if (!file->fp || fseek(file->fp, offset, type) < 0) jsapi_system_raise(ctx); return 0; } static int File_prototype_stat(duk_context *ctx) { const struct file *file = self(ctx); struct stat st; if (!file->fp || fstat(fileno(file->fp), &st) < 0) jsapi_system_raise(ctx); push_stat(ctx, &st); return 0; } static int File_prototype_tell(duk_context *ctx) { const struct file *file = self(ctx); long position; if (!file->fp || (position = ftell(file->fp)) < 0) return jsapi_system_raise(ctx), 0; duk_push_number(ctx, position); return 1; } static int File_prototype_write(duk_context *ctx) { const struct file *file = self(ctx); size_t datasz = 0, written; const char *data = duk_require_lstring(ctx, 0, &datasz); if (!file->fp) jsapi_system_raise(ctx); written = fwrite(data, 1, datasz, file->fp); if (ferror(file->fp)) jsapi_system_raise(ctx); duk_push_uint(ctx, written); return 1; } static int File_constructor(duk_context *ctx) { const char *path = duk_require_string(ctx, 0); const char *mode = duk_require_string(ctx, 1); FILE *fp; struct file *file; if (!duk_is_constructor_call(ctx)) return 0; if (!(fp = fopen(path, mode))) jsapi_system_raise(ctx); file = irc_util_calloc(1, sizeof (*file)); file->fp = fp; file->finalizer = fclose; strlcpy(file->path, path, sizeof (file->path)); duk_push_this(ctx); duk_push_pointer(ctx, file); duk_put_prop_string(ctx, -2, SIGNATURE); duk_pop(ctx); return 0; } static int File_destructor(duk_context *ctx) { struct file *file; duk_get_prop_string(ctx, 0, SIGNATURE); if ((file = duk_to_pointer(ctx, -1)) && file->fp) { file->finalizer(file->fp); file->fp = NULL; free(file); } duk_pop(ctx); duk_del_prop_string(ctx, 0, SIGNATURE); return 0; } static int File_basename(duk_context *ctx) { const char *path = duk_require_string(ctx, 0); duk_push_string(ctx, irc_util_basename(path)); return 1; } static int File_dirname(duk_context *ctx) { const char *path = duk_require_string(ctx, 0); duk_push_string(ctx, irc_util_dirname(path)); return 1; } static int File_exists(duk_context *ctx) { const char *path = duk_require_string(ctx, 0); struct stat st; duk_push_boolean(ctx, stat(path, &st) == 0); return 1; } static int File_remove(duk_context *ctx) { const char *path = duk_require_string(ctx, 0); if (remove(path) < 0) jsapi_system_raise(ctx); return 0; } static int File_stat(duk_context *ctx) { const char *path = duk_require_string(ctx, 0); struct stat st; if (stat(path, &st) < 0) jsapi_system_raise(ctx); push_stat(ctx, &st); return 0; } static const duk_function_list_entry methods[] = { { "basename", File_prototype_basename, 0 }, { "close", File_prototype_close, 0 }, { "dirname", File_prototype_dirname, 0 }, { "lines", File_prototype_lines, 0 }, { "read", File_prototype_read, 1 }, { "readline", File_prototype_readline, 0 }, { "remove", File_prototype_remove, 0 }, { "seek", File_prototype_seek, 2 }, { "stat", File_prototype_stat, 0 }, { "tell", File_prototype_tell, 0 }, { "write", File_prototype_write, 1 }, { NULL, NULL, 0 } }; static const duk_function_list_entry functions[] = { { "basename", File_basename, 1 }, { "dirname", File_dirname, 1 }, { "exists", File_exists, 1 }, { "remove", File_remove, 1 }, { "stat", File_stat, 1 }, { NULL, NULL, 0 } }; static const duk_number_list_entry constants[] = { { "SeekCur", SEEK_CUR }, { "SeekEnd", SEEK_END }, { "SeekSet", SEEK_SET }, { NULL, 0 } }; void jsapi_file_load(duk_context *ctx) { assert(ctx); duk_get_global_string(ctx, "Irccd"); duk_push_c_function(ctx, File_constructor, 2); duk_put_number_list(ctx, -1, constants); duk_put_function_list(ctx, -1, functions); duk_push_object(ctx); duk_put_function_list(ctx, -1, methods); duk_push_c_function(ctx, File_destructor, 1); duk_set_finalizer(ctx, -2); duk_dup(ctx, -1); duk_put_global_string(ctx, PROTOTYPE); duk_put_prop_string(ctx, -2, "prototype"); duk_put_prop_string(ctx, -2, "File"); duk_pop(ctx); } void jsapi_file_push(duk_context *ctx, const char *path, FILE *fp, int (*finalizer)(FILE *)) { assert(ctx); assert(fp); assert(finalizer); struct file file = { .fp = fp, .finalizer = finalizer }; if (path) strlcpy(file.path, path, sizeof (file.path)); duk_push_object(ctx); duk_push_pointer(ctx, irc_util_memdup(&file, sizeof (file))); duk_put_prop_string(ctx, -2, SIGNATURE); duk_get_global_string(ctx, PROTOTYPE); duk_set_prototype(ctx, -2); }