From dfad8a8016426d7fe198dc32973b01a1e075142e Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 12 Jul 2020 21:32:44 +0200 Subject: Starting on conversion to c. Program exec, buffers.. --- .gitignore | 3 +- alloc.c | 20 ++++++++++++ alloc.h | 10 ++++++ automedia | 4 --- buffer.c | 54 ++++++++++++++++++++++++++++++++ buffer.h | 30 ++++++++++++++++++ build.sh | 8 +++++ main.c | 60 ++++++++++++++++++++++++++++++++++++ program.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ program.h | 13 ++++++++ 10 files changed, 299 insertions(+), 5 deletions(-) create mode 100644 alloc.c create mode 100644 alloc.h delete mode 100755 automedia create mode 100644 buffer.c create mode 100644 buffer.h create mode 100755 build.sh create mode 100644 main.c create mode 100644 program.c create mode 100644 program.h diff --git a/.gitignore b/.gitignore index 0daa041..c65a4e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode/ -__pycache__ \ No newline at end of file +__pycache__ +automedia diff --git a/alloc.c b/alloc.c new file mode 100644 index 0000000..dca69b5 --- /dev/null +++ b/alloc.c @@ -0,0 +1,20 @@ +#include "alloc.h" +#include + +void* alloc_or_crash(size_t size) { + void *mem = malloc(size); + if(!mem) { + fprintf(stderr, "Error: failed to allocate %zu bytes\n", size); + abort(); + } + return mem; +} + +void* realloc_or_crash(void *mem, size_t new_size) { + void *new_mem = realloc(mem, new_size); + if(!new_mem) { + fprintf(stderr, "Error: failed to reallocate %p to size %zu\n", mem, new_size); + abort(); + } + return new_mem; +} diff --git a/alloc.h b/alloc.h new file mode 100644 index 0000000..e8c6c85 --- /dev/null +++ b/alloc.h @@ -0,0 +1,10 @@ +#ifndef ALLOC_H +#define ALLOC_H + +#include +#include + +void* alloc_or_crash(size_t size); +void* realloc_or_crash(void *mem, size_t new_size); + +#endif diff --git a/automedia b/automedia deleted file mode 100755 index e6e35a1..0000000 --- a/automedia +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -cd /usr/share/automedia -./automedia.py "$@" diff --git a/buffer.c b/buffer.c new file mode 100644 index 0000000..879a62b --- /dev/null +++ b/buffer.c @@ -0,0 +1,54 @@ +#include "buffer.h" +#include "alloc.h" +#include +#include +#include + +void buffer_init(Buffer *self) { + self->data = NULL; + self->size = 0; + self->capacity = 0; +} + +void buffer_deinit(Buffer *self) { + free(self->data); + self->size = 0; + self->capacity = 0; +} + +static void buffer_ensure_capacity(Buffer *self, size_t new_size) { + size_t new_capacity = self->capacity; + if(new_size <= self->capacity) + return; + + if(new_capacity == 0) + new_capacity = 8; + + while(new_capacity < new_size) { + /* new_capacity *= 1.5 */ + new_capacity += (new_capacity >> 1); + } + + self->data = realloc_or_crash(self->data, new_capacity); + self->capacity = new_capacity; +} + +void buffer_append(Buffer *self, const void *data, size_t size) { + buffer_ensure_capacity(self, self->size + size); + memcpy((char*)self->data + self->size, data, size); + self->size += size; +} + +void* buffer_pop(Buffer *self, size_t size) { + assert(self->size >= size); + self->size -= size; + return (char*)self->data + self->size; +} + +void* buffer_begin(Buffer *self) { + return self->data; +} + +void* buffer_end(Buffer *self) { + return (char*)self->data + self->size; +} diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..a8c1940 --- /dev/null +++ b/buffer.h @@ -0,0 +1,30 @@ +#ifndef BUFFER_H +#define BUFFER_H + +#include + +/* + TODO: Optimize small size buffers by using data and size members (16 bytes on x86) + instead of heap allocation +*/ +typedef struct { + void *data; + size_t size; + size_t capacity; +} Buffer; + +void buffer_init(Buffer *self); +void buffer_deinit(Buffer *self); + +void buffer_append(Buffer *self, const void *data, size_t size); +/* + This function changes the size of the buffer without changing the capacity + and returns the value at the back of the buffer, which is valid until @tsl_buffer_append or + @tsl_buffer_deinit is called. + The buffer size has to be larger or equal to @size. +*/ +void* buffer_pop(Buffer *self, size_t size); +void* buffer_begin(Buffer *self); +void* buffer_end(Buffer *self); + +#endif diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..575c08b --- /dev/null +++ b/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +CFLAGS="-O0 -g3 -Wall -Wextra -Werror" +#CFLAGS="-O3 -s" +#LIBS="-lcurl" +LIBS="" +#gcc +musl-gcc -static main.c program.c alloc.c buffer.c -o automedia $CFLAGS $LIBS diff --git a/main.c b/main.c new file mode 100644 index 0000000..04c8f29 --- /dev/null +++ b/main.c @@ -0,0 +1,60 @@ +#include +#include +#include "program.h" +#include "buffer.h" + +static int program_buffer_write_callback(char *data, int size, void *userdata) { + Buffer *buffer = userdata; + buffer_append(buffer, data, size); + return 0; +} + +static void usage() { + fprintf(stderr, "usage: automedia COMMAND\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "COMMANDS\n"); + fprintf(stderr, " add Add media to track\n"); + fprintf(stderr, " sync Start syncing tracked media\n"); + fprintf(stderr, " downloaded List downloaded media\n"); + exit(1); +} + +static void usage_add() { + fprintf(stderr, "usage: automedia add [--name name] [--start-after start_after]\n"); + fprintf(stderr, "OPTIONS\n"); + fprintf(stderr, " type The type should be either rss or html\n"); + fprintf(stderr, " url The url to the rss or html\n"); + fprintf(stderr, " filename The filename of an episode of an existing serie to start track. Currently only works with rss on https://nyaa.si\n"); + fprintf(stderr, " --name The display name to be used for the media. Optional for rss, in which case the name will be retries from rss TITLE, required for html\n"); + fprintf(stderr, " --start-after The sync should start downloading media after this item. This --start-after value should be the title of the episode/chapter (Optional, default is to start from the first item)\n"); + fprintf(stderr, "EXAMPLES\n"); + fprintf(stderr, " automedia.py add rss 'https://nyaa.si/?page=rss&q=Tejina-senpai+1080p&c=0_0&f=0&u=HorribleSubs'\n"); + fprintf(stderr, " automedia.py add html 'https://manganelo.com/manga/read_naruto_manga_online_free3' --name Naruto\n"); + fprintf(stderr, " automedia.py add rss '[Erai-raws] Saiki Kusuo no Psi Nan - Kanketsu-hen - 01 [1080p][Multiple Subtitle].mkv'\n"); + exit(1); +} + +static void usage_sync() { + fprintf(stderr, "usage: automedia sync \n"); + fprintf(stderr, "OPTIONS\n"); + fprintf(stderr, " download_dir The path where media should be downloaded to\n"); + fprintf(stderr, "EXAMPLES\n"); + fprintf(stderr, " automedia.py sync /home/adam/Downloads/automedia\n"); + exit(1); +} + +int main(int argc, char **argv) { + if(argc < 3) + usage(); + + Buffer buffer; + buffer_init(&buffer); + + const char *args[] = { "curl", "-s", "-L", "-f", "https://google.com", NULL }; + program_exec(args, program_buffer_write_callback, &buffer); + printf("program output: %.*s\n", (int)buffer.size, (char*)buffer.data); + + buffer_deinit(&buffer); + + return 0; +} diff --git a/program.c b/program.c new file mode 100644 index 0000000..6004ccb --- /dev/null +++ b/program.c @@ -0,0 +1,102 @@ +#include "program.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define READ_END 0 +#define WRITE_END 1 + +int program_exec(const char **args, ProgramOutputCallback output_callback, void *userdata) { + /* 1 arguments */ + if(args[0] == NULL) + return -1; + + int fd[2]; + if(pipe(fd) == -1) { + perror("Failed to open pipe"); + return -2; + } + + pid_t parent_pid = getpid(); + + pid_t pid = fork(); + if(pid == -1) { + perror("Failed to fork"); + return -3; + } else if(pid == 0) { /* child */ + if(prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) { + perror("prctl(PR_SET_PDEATHSIG, SIGTERM) failed"); + exit(127); + } + + /* Test if the parent died before the above call to prctl */ + if(getppid() != parent_pid) + exit(127); + + dup2(fd[WRITE_END], STDOUT_FILENO); + close(fd[READ_END]); + close(fd[WRITE_END]); + + execvp(args[0], (char* const*)args); + perror("execvp"); + exit(127); + } else { /* parent */ + close(fd[WRITE_END]); + + int result = 0; + int status; + + char buffer[4097]; + + for(;;) { + ssize_t bytes_read = read(fd[READ_END], buffer, sizeof(buffer) - 1); + if(bytes_read == 0) { + break; + } else if(bytes_read == -1) { + int err = errno; + fprintf(stderr, "Failed to read from pipe to program %s, error: %s\n", args[0], strerror(err)); + result = -err; + goto cleanup; + } + + buffer[bytes_read] = '\0'; + if(output_callback && output_callback(buffer, bytes_read, userdata) != 0) + break; + } + + if(waitpid(pid, &status, 0) == -1) { + perror("waitpid failed"); + result = -5; + goto cleanup; + } + + if(!WIFEXITED(status)) { + result = -4; + goto cleanup; + } + + int exit_status = WEXITSTATUS(status); + if(exit_status != 0) { + fprintf(stderr, "Failed to execute program ("); + const char **arg = args; + while(*arg) { + if(arg != args) + fputc(' ', stderr); + fprintf(stderr, "'%s'", *arg); + ++arg; + } + fprintf(stderr, "), exit status %d\n", exit_status); + result = -exit_status; + goto cleanup; + } + + cleanup: + close(fd[READ_END]); + return result; + } +} diff --git a/program.h b/program.h new file mode 100644 index 0000000..4c93066 --- /dev/null +++ b/program.h @@ -0,0 +1,13 @@ +#ifndef PROGRAM_H +#define PROGRAM_H + +/* Return 0 if you want to continue reading. @data is null-terminated */ +typedef int (*ProgramOutputCallback)(char *data, int size, void *userdata); + +/* + @args need to have at least 2 arguments. The first which is the program name + and the last which is NULL, which indicates end of args +*/ +int program_exec(const char **args, ProgramOutputCallback output_callback, void *userdata); + +#endif -- cgit v1.2.3