#define STRVIEW_IMPLEMENTATION #include "strview.h" #include "compiler.h" #include "parser.h" #include #include #include #include #include static char* read_file(const char* path) { FILE* file = fopen(path, "rb"); if (file == NULL) { return NULL; } if (fseek(file, 0, SEEK_END) != 0) { fclose(file); return NULL; } long length = ftell(file); if (length < 0 || fseek(file, 0, SEEK_SET) != 0) { fclose(file); return NULL; } char* text = malloc((size_t)length + 1); if (text == NULL) { fclose(file); return NULL; } size_t read_count = fread(text, 1, (size_t)length, file); bool ok = read_count == (size_t)length && !ferror(file); fclose(file); if (!ok) { free(text); return NULL; } text[length] = '\0'; return text; } static void print_error(const char* stage, const Error* error) { if (error->line > 0) { fprintf(stderr, "%s:%d: %s\n", stage, error->line, error->message); } else { fprintf(stderr, "%s: %s\n", stage, error->message); } } static void usage(FILE* stream, const char* name) { fprintf(stream, "usage: %s [-n] PROGRAM OUTPUT [SCRIPT_ARGUMENT ...]\n", name); fprintf(stream, " -n compile and print without running ffmpeg\n"); } int main(int argc, char** argv) { if (argc == 4 && sv_equal(sv_from_cstr(argv[1]), svlit("--internal-subtitle-clip"))) { char* end = NULL; errno = 0; double duration = strtod(argv[3], &end); Error error = {0}; if (errno != 0 || end == argv[3] || *end != '\0' || !isfinite(duration) || duration < 0 || !subtitle_clip_file(argv[2], duration, &error)) { if (error.message[0] == '\0') snprintf(error.message, sizeof(error.message), "invalid duration"); print_error("subtitle clip", &error); return 1; } return 0; } if (argc == 5 && sv_equal(sv_from_cstr(argv[1]), svlit("--internal-subtitle-concat-list"))) { Error error = {0}; if (!subtitle_write_concat_list(argv[2], argv[3], argv[4], &error)) { print_error("subtitle concat", &error); return 1; } return 0; } if (argc == 4 && sv_equal(sv_from_cstr(argv[1]), svlit("--internal-write-file"))) { Error error = {0}; if (!write_generated_file(argv[2], argv[3], &error)) { print_error("generated metadata", &error); return 1; } return 0; } if (argc == 4 && sv_equal(sv_from_cstr(argv[1]), svlit("--internal-write-file-hex"))) { Error error = {0}; if (!write_generated_hex_file(argv[2], argv[3], &error)) { print_error("generated metadata", &error); return 1; } return 0; } if (argc == 4 && sv_equal(sv_from_cstr(argv[1]), svlit("--internal-copy-file"))) { Error error = {0}; if (!copy_generated_file(argv[2], argv[3], &error)) { print_error("font attachment", &error); return 1; } return 0; } if (argc == 3 && sv_equal(sv_from_cstr(argv[1]), svlit("--internal-mkdir"))) { Error error = {0}; if (!ensure_generated_directory(argv[2], &error)) { print_error("font directory", &error); return 1; } return 0; } bool dry_run = false; int argument = 1; if (argument < argc && sv_equal(sv_from_cstr(argv[argument]), svlit("-n"))) { dry_run = true; argument++; } if (argc - argument < 2) { usage(stderr, argv[0]); return 2; } const char* program_path = argv[argument]; const char* output_path = argv[argument + 1]; size_t script_argument_count = (size_t)(argc - argument - 2); const char* const* script_arguments = (const char* const*)(argv + argument + 2); char* text = read_file(program_path); if (text == NULL) { fprintf(stderr, "%s: cannot read %s: %s\n", argv[0], program_path, strerror(errno)); return 1; } Error error = {0}; Program program; TypedProgram typed; CompiledCommand command; ProbeCache probes; probe_cache_init(&probes, NULL, NULL); if (!parse_program_with_args(text, script_argument_count, script_arguments, &program, &error)) { print_error(program_path, &error); free(text); probe_cache_destroy(&probes); return 1; } free(text); if (!typecheck_program(&program, &typed, &error)) { print_error(program_path, &error); program_destroy(&program); probe_cache_destroy(&probes); return 1; } if (!compile_program(&program, &typed, &probes, output_path, &command, &error)) { print_error(program_path, &error); typed_program_destroy(&typed); program_destroy(&program); probe_cache_destroy(&probes); return 1; } printf("%s\n", command.shell_command); int exit_code = 0; if (!dry_run && !execute_command(&command, &exit_code, &error)) { print_error("ffmpeg", &error); exit_code = 1; } compiled_command_destroy(&command); typed_program_destroy(&typed); program_destroy(&program); probe_cache_destroy(&probes); return exit_code; }