diff --git a/src/opustags.c b/src/opustags.c deleted file mode 100644 index 18713bf..0000000 --- a/src/opustags.c +++ /dev/null @@ -1,505 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -typedef struct { - uint32_t vendor_length; - const char *vendor_string; - uint32_t count; - uint32_t *lengths; - const char **comment; -} opus_tags; - -int parse_tags(char *data, long len, opus_tags *tags){ - long pos; - if(len < 8+4+4) - return -1; - if(strncmp(data, "OpusTags", 8) != 0) - return -1; - // Vendor - pos = 8; - tags->vendor_length = le32toh(*((uint32_t*) (data + pos))); - tags->vendor_string = data + pos + 4; - pos += 4 + tags->vendor_length; - if(pos + 4 > len) - return -1; - // Count - tags->count = le32toh(*((uint32_t*) (data + pos))); - if(tags->count == 0) - return 0; - tags->lengths = calloc(tags->count, sizeof(uint32_t)); - if(tags->lengths == NULL) - return -1; - tags->comment = calloc(tags->count, sizeof(char*)); - if(tags->comment == NULL){ - free(tags->lengths); - return -1; - } - pos += 4; - // Comment - uint32_t i; - for(i=0; icount; i++){ - tags->lengths[i] = le32toh(*((uint32_t*) (data + pos))); - tags->comment[i] = data + pos + 4; - pos += 4 + tags->lengths[i]; - if(pos > len) - return -1; - } - if(pos != len) - return -1; - return 0; -} - -int render_tags(opus_tags *tags, ogg_packet *op){ - // Note: op->packet must be manually freed. - op->b_o_s = 0; - op->e_o_s = 0; - op->granulepos = 0; - op->packetno = 1; - long len = 8 + 4 + tags->vendor_length + 4; - uint32_t i; - for(i=0; icount; i++) - len += 4 + tags->lengths[i]; - op->bytes = len; - char *data = malloc(len); - if(!data) - return -1; - op->packet = (unsigned char*) data; - uint32_t n; - memcpy(data, "OpusTags", 8); - n = htole32(tags->vendor_length); - memcpy(data+8, &n, 4); - memcpy(data+12, tags->vendor_string, tags->vendor_length); - data += 12 + tags->vendor_length; - n = htole32(tags->count); - memcpy(data, &n, 4); - data += 4; - for(i=0; icount; i++){ - n = htole32(tags->lengths[i]); - memcpy(data, &n, 4); - memcpy(data+4, tags->comment[i], tags->lengths[i]); - data += 4 + tags->lengths[i]; - } - return 0; -} - -int match_field(const char *comment, uint32_t len, const char *field){ - size_t field_len; - for(field_len = 0; field[field_len] != '\0' && field[field_len] != '='; field_len++); - if(len <= field_len) - return 0; - if(comment[field_len] != '=') - return 0; - if(strncmp(comment, field, field_len) != 0) - return 0; - return 1; - -} - -void delete_tags(opus_tags *tags, const char *field){ - uint32_t i; - for(i=0; icount; i++){ - if(match_field(tags->comment[i], tags->lengths[i], field)){ - tags->count--; - tags->lengths[i] = tags->lengths[tags->count]; - tags->comment[i] = tags->comment[tags->count]; - // No need to resize the arrays. - } - } -} - -int add_tags(opus_tags *tags, const char **tags_to_add, uint32_t count){ - if(count == 0) - return 0; - uint32_t *lengths = realloc(tags->lengths, (tags->count + count) * sizeof(uint32_t)); - const char **comment = realloc(tags->comment, (tags->count + count) * sizeof(char*)); - if(lengths == NULL || comment == NULL) - return -1; - tags->lengths = lengths; - tags->comment = comment; - uint32_t i; - for(i=0; ilengths[tags->count + i] = strlen(tags_to_add[i]); - tags->comment[tags->count + i] = tags_to_add[i]; - } - tags->count += count; - return 0; -} - -void print_tags(opus_tags *tags){ - if(tags->count == 0) - puts("no tags"); - int i; - for(i=0; icount; i++){ - fwrite(tags->comment[i], 1, tags->lengths[i], stdout); - puts(""); - } -} - -void free_tags(opus_tags *tags){ - if(tags->count > 0){ - free(tags->lengths); - free(tags->comment); - } -} - -int write_page(ogg_page *og, FILE *stream){ - if(fwrite(og->header, 1, og->header_len, stream) < og->header_len) - return -1; - if(fwrite(og->body, 1, og->body_len, stream) < og->body_len) - return -1; - return 0; -} - -const char *version = "opustags version 1.1\n"; - -const char *usage = - "Usage: opustags --help\n" - " opustags [OPTIONS] FILE\n" - " opustags OPTIONS FILE -o FILE\n"; - -const char *help = - "Options:\n" - " -h, --help print this help\n" - " -o, --output write the modified tags to a file\n" - " -i, --in-place [SUFFIX] use a temporary file then replace the original file\n" - " -y, --overwrite overwrite the output file if it already exists\n" - " -d, --delete FIELD delete all the fields of a specified type\n" - " -a, --add FIELD=VALUE add a field\n" - " -s, --set FIELD=VALUE delete then add a field\n" - " -D, --delete-all delete all the fields!\n" - " -S, --set-all read the fields from stdin\n"; - -struct option options[] = { - {"help", no_argument, 0, 'h'}, - {"output", required_argument, 0, 'o'}, - {"in-place", optional_argument, 0, 'i'}, - {"overwrite", no_argument, 0, 'y'}, - {"delete", required_argument, 0, 'd'}, - {"add", required_argument, 0, 'a'}, - {"set", required_argument, 0, 's'}, - {"delete-all", no_argument, 0, 'D'}, - {"set-all", no_argument, 0, 'S'}, - {NULL, 0, 0, 0} -}; - -int main(int argc, char **argv){ - if(argc == 1){ - fputs(version, stdout); - fputs(usage, stdout); - return EXIT_SUCCESS; - } - char *path_in, *path_out = NULL, *inplace = NULL; - const char* to_add[argc]; - const char* to_delete[argc]; - int count_add = 0, count_delete = 0; - int delete_all = 0; - int set_all = 0; - int overwrite = 0; - int print_help = 0; - int c; - while((c = getopt_long(argc, argv, "ho:i::yd:a:s:DS", options, NULL)) != -1){ - switch(c){ - case 'h': - print_help = 1; - break; - case 'o': - path_out = optarg; - break; - case 'i': - inplace = optarg == NULL ? ".otmp" : optarg; - break; - case 'y': - overwrite = 1; - break; - case 'd': - if(strchr(optarg, '=') != NULL){ - fprintf(stderr, "invalid field: '%s'\n", optarg); - return EXIT_FAILURE; - } - to_delete[count_delete++] = optarg; - break; - case 'a': - case 's': - if(strchr(optarg, '=') == NULL){ - fprintf(stderr, "invalid comment: '%s'\n", optarg); - return EXIT_FAILURE; - } - to_add[count_add++] = optarg; - if(c == 's') - to_delete[count_delete++] = optarg; - break; - case 'S': - set_all = 1; - case 'D': - delete_all = 1; - break; - default: - return EXIT_FAILURE; - } - } - if(print_help){ - puts(version); - puts(usage); - puts(help); - puts("See the man page for extensive documentation."); - return EXIT_SUCCESS; - } - if(optind != argc - 1){ - fputs("invalid arguments\n", stderr); - return EXIT_FAILURE; - } - if(inplace && path_out){ - fputs("cannot combine --in-place and --output\n", stderr); - return EXIT_FAILURE; - } - path_in = argv[optind]; - if(path_out != NULL && strcmp(path_in, "-") != 0){ - char canon_in[PATH_MAX+1], canon_out[PATH_MAX+1]; - if(realpath(path_in, canon_in) && realpath(path_out, canon_out)){ - if(strcmp(canon_in, canon_out) == 0){ - fputs("error: the input and output files are the same\n", stderr); - return EXIT_FAILURE; - } - } - } - FILE *in; - if(strcmp(path_in, "-") == 0){ - if(set_all){ - fputs("can't open stdin for input when -S is specified\n", stderr); - return EXIT_FAILURE; - } - if(inplace){ - fputs("cannot modify stdin 'in-place'\n", stderr); - return EXIT_FAILURE; - } - in = stdin; - } - else - in = fopen(path_in, "r"); - if(!in){ - perror("fopen"); - return EXIT_FAILURE; - } - FILE *out = NULL; - if(inplace != NULL){ - path_out = malloc(strlen(path_in) + strlen(inplace) + 1); - if(path_out == NULL){ - fputs("failure to allocate memory\n", stderr); - fclose(in); - return EXIT_FAILURE; - } - strcpy(path_out, path_in); - strcat(path_out, inplace); - } - if(path_out != NULL){ - if(strcmp(path_out, "-") == 0) - out = stdout; - else{ - if(!overwrite && !inplace){ - if(access(path_out, F_OK) == 0){ - fprintf(stderr, "'%s' already exists (use -y to overwrite)\n", path_out); - fclose(in); - return EXIT_FAILURE; - } - } - out = fopen(path_out, "w"); - if(!out){ - perror("fopen"); - fclose(in); - if(inplace) - free(path_out); - return EXIT_FAILURE; - } - } - } - ogg_sync_state oy; - ogg_stream_state os, enc; - ogg_page og; - ogg_packet op; - opus_tags tags; - ogg_sync_init(&oy); - char *buf; - size_t len; - char *error = NULL; - int packet_count = -1; - while(error == NULL){ - // Read until we complete a page. - if(ogg_sync_pageout(&oy, &og) != 1){ - if(feof(in)) - break; - buf = ogg_sync_buffer(&oy, 65536); - if(buf == NULL){ - error = "ogg_sync_buffer: out of memory"; - break; - } - len = fread(buf, 1, 65536, in); - if(ferror(in)) - error = strerror(errno); - ogg_sync_wrote(&oy, len); - if(ogg_sync_check(&oy) != 0) - error = "ogg_sync_check: internal error"; - continue; - } - // We got a page. - // Short-circuit when the relevant packets have been read. - if(packet_count >= 2 && out){ - if(write_page(&og, out) == -1){ - error = "write_page: fwrite error"; - break; - } - continue; - } - // Initialize the streams from the first page. - if(packet_count == -1){ - if(ogg_stream_init(&os, ogg_page_serialno(&og)) == -1){ - error = "ogg_stream_init: couldn't create a decoder"; - break; - } - if(out){ - if(ogg_stream_init(&enc, ogg_page_serialno(&og)) == -1){ - error = "ogg_stream_init: couldn't create an encoder"; - break; - } - } - packet_count = 0; - } - if(ogg_stream_pagein(&os, &og) == -1){ - error = "ogg_stream_pagein: invalid page"; - break; - } - // Read all the packets. - while(ogg_stream_packetout(&os, &op) == 1){ - packet_count++; - if(packet_count == 1){ // Identification header - if(strncmp((char*) op.packet, "OpusHead", 8) != 0){ - error = "opustags: invalid identification header"; - break; - } - } - else if(packet_count == 2){ // Comment header - if(parse_tags((char*) op.packet, op.bytes, &tags) == -1){ - error = "opustags: invalid comment header"; - break; - } - if(delete_all) - tags.count = 0; - else{ - int i; - for(i=0; i