aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--alloc.c20
-rw-r--r--alloc.h10
-rwxr-xr-xautomedia4
-rw-r--r--buffer.c54
-rw-r--r--buffer.h30
-rwxr-xr-xbuild.sh8
-rw-r--r--main.c60
-rw-r--r--program.c102
-rw-r--r--program.h13
10 files changed, 299 insertions, 5 deletions
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 <stdio.h>
+
+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 <stddef.h>
+#include <stdlib.h>
+
+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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+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 <stddef.h>
+
+/*
+ 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 <stdio.h>
+#include <stdlib.h>
+#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 <type> <url|filename> [--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 <download_dir>\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 <unistd.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#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