From feed14ce8f8d78274d42a5bcd74b071a925bdc68 Mon Sep 17 00:00:00 2001 From: luming Date: Sat, 11 Jan 2020 10:53:22 +0800 Subject: [PATCH 1/9] Fix issuse of parameter position error. the newly compiler don't support that. --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 2f88542..0d1b3b8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -19,7 +19,7 @@ CC=c99 all: opuscomment ; opuscomment: $(OBJS) - $(CC) -o opuscomment $(DEFAULT_MACROS) $(CFLAGS) $(LDFLAGS) $(LIBS) $(OBJS) + $(CC) $(CFLAGS) $(DEFAULT_MACROS) $(LDFLAGS) $(OBJS) $(LIBS) -o opuscomment .SUFFIXES: .SUFFIXES: .c .o From a32d76af5a5b73f2a80899d5504d4399be1c492a Mon Sep 17 00:00:00 2001 From: zvezdochiot Date: Sat, 16 Jul 2022 16:10:13 +0300 Subject: [PATCH 2/9] 1.5.13: strip Makefile --- README.ja.md | 6 ++++++ README.md | 6 ++++++ src/Makefile | 17 ++++++++--------- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/README.ja.md b/README.ja.md index c1b2117..a306cd7 100644 --- a/README.ja.md +++ b/README.ja.md @@ -1,3 +1,9 @@ +![GitHub release (latest by date)](https://img.shields.io/github/v/release/hcmiya/opuscomment) +![GitHub Release Date](https://img.shields.io/github/release-date/hcmiya/opuscomment) +![GitHub repo size](https://img.shields.io/github/repo-size/hcmiya/opuscomment) +![GitHub all releases](https://img.shields.io/github/downloads/hcmiya/opuscomment/total) +![GitHub](https://img.shields.io/github/license/hcmiya/opuscomment) + # opuscomment ## 概要 diff --git a/README.md b/README.md index d2ffbbc..c8fc55e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +![GitHub release (latest by date)](https://img.shields.io/github/v/release/hcmiya/opuscomment) +![GitHub Release Date](https://img.shields.io/github/release-date/hcmiya/opuscomment) +![GitHub repo size](https://img.shields.io/github/repo-size/hcmiya/opuscomment) +![GitHub all releases](https://img.shields.io/github/downloads/hcmiya/opuscomment/total) +![GitHub](https://img.shields.io/github/license/hcmiya/opuscomment) + # opuscomment The formal README is written in [Japanese](./README.ja.md). diff --git a/src/Makefile b/src/Makefile index 0d1b3b8..3862b99 100644 --- a/src/Makefile +++ b/src/Makefile @@ -6,32 +6,31 @@ # NLSに対応していない、あるいは必要がない場合は、CFLAGSに-UNLSを追加することで無効に出来ます。 # `DEFAULT_NLS_PATH`を文字列でdefineをすると、NLSPATHが無い時に最初にcatopen(3)をする場所を指定することができます。 +CC=c99 +CFLAGS=-D_POSIX_C_SOURCE=200809L -DNLS +LDFLAGS=-logg -lm -lpthread SRCS=put-tags.c parse-tags.c read.c read-flac.c ocutil.c retrieve-tags.c select-codec.c CONFSRCS=endianness.c error.c main.c -OBJS=$(SRCS:.c=.o) $(CONFSRCS:.c=.o) HEADERS=global.h ocutil.h limit.h error.h iconv-impl.h ERRORDEFS=errordef/opus.tab errordef/main.tab -DEFAULT_MACROS=-D_POSIX_C_SOURCE=200809L -DNLS -#DEFAULT_MACROS=-D_XOPEN_SOURCE=600 -DNLS -LIBS=-logg -lm -lpthread -CC=c99 +OBJS=$(SRCS:.c=.o) $(CONFSRCS:.c=.o) -all: opuscomment ; +all: opuscomment opuscomment: $(OBJS) - $(CC) $(CFLAGS) $(DEFAULT_MACROS) $(LDFLAGS) $(OBJS) $(LIBS) -o opuscomment + $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ .SUFFIXES: .SUFFIXES: .c .o .c.o: - $(CC) $(DEFAULT_MACROS) $(CFLAGS) -c $< + $(CC) $(CFLAGS) -c $< $(SRCS): $(HEADERS) @touch $@ endianness.c: endianness-check.sh - ./endianness-check.sh >endianness.c + ./$< > $@ error.c: $(ERRORDEFS) $(HEADERS) @touch $@ From f5188577965040f81f5e852360518aab1b78e77c Mon Sep 17 00:00:00 2001 From: zvezdochiot Date: Sun, 24 Jul 2022 03:44:43 +0300 Subject: [PATCH 3/9] 1.5.14: fix #1: opuspic2tag --- doc/man/man1/opuschgain.1 | 2 +- doc/man/man1/opuscomment.1 | 2 +- doc/man/man1/opusmbptag.1 | 2 +- doc/man/man1/opuspic2tag.1 | 36 +++ src/Makefile | 10 +- src/opuspic2tag.c | 118 +++++++++ src/picture.c | 493 +++++++++++++++++++++++++++++++++++++ src/picture.h | 65 +++++ src/version.h | 2 +- 9 files changed, 724 insertions(+), 6 deletions(-) create mode 100644 doc/man/man1/opuspic2tag.1 create mode 100644 src/opuspic2tag.c create mode 100644 src/picture.c create mode 100644 src/picture.h diff --git a/doc/man/man1/opuschgain.1 b/doc/man/man1/opuschgain.1 index ed098aa..0389c0b 100644 --- a/doc/man/man1/opuschgain.1 +++ b/doc/man/man1/opuschgain.1 @@ -3,7 +3,7 @@ .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . -.TH "OPUSCHGAIN" "1" "2019-06-02" "1.5.8" "User Manual" +.TH "OPUSCHGAIN" "1" "2022-07-24" "1.5.14" "User Manual" .SH NAME opuschgain \- Updated Ogg Opus file output gain and R128 gain tag diff --git a/doc/man/man1/opuscomment.1 b/doc/man/man1/opuscomment.1 index a55f7b5..5fbdc10 100644 --- a/doc/man/man1/opuscomment.1 +++ b/doc/man/man1/opuscomment.1 @@ -3,7 +3,7 @@ .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . -.TH "OPUSCOMMENT" "1" "2019-06-02" "1.5.8" "User Manual" +.TH "OPUSCOMMENT" "1" "2022-07-24" "1.5.14" "User Manual" .SH NAME opuscomment \- Ogg Edit Opus file output gain and tag diff --git a/doc/man/man1/opusmbptag.1 b/doc/man/man1/opusmbptag.1 index b1c0e24..efadb0f 100644 --- a/doc/man/man1/opusmbptag.1 +++ b/doc/man/man1/opusmbptag.1 @@ -3,7 +3,7 @@ .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . -.TH "OPUSMBPTAG" "1" "2019-06-02" "1.5.8" "User Manual" +.TH "OPUSMBPTAG" "1" "2022-07-24" "1.5.14" "User Manual" .SH NAME opusmbptag \- Generate METADATA_BLOCK_PICTURE tag from image file diff --git a/doc/man/man1/opuspic2tag.1 b/doc/man/man1/opuspic2tag.1 new file mode 100644 index 0000000..292d5b8 --- /dev/null +++ b/doc/man/man1/opuspic2tag.1 @@ -0,0 +1,36 @@ +.TH "OpusPic2Tag" "1" "2022-07-24" "1.5.14" "User Manual" +.SH NAME +opuspic2tag \- Convert picture to opus tag +.SH SYNOPSIS +.B opuspic2tag +.RI [ OPTIONS ] +.I FILE{.jpg,.png,.gif} +.SH DESCRIPTION +.PP +\fBopuspic2tag\fP is part \fBopustags\fP for edit the comment header of an Opus file. +.SH OPTIONS +.TP +.B \-h, \-\-help +Display a brief description of the options. +.TP +.B \-d, \-\-desc \fISTRING\fI +description +.TP +.B \-t, \-\-type NUM +set image type by 0-20 or keywords. Default: 3 +.TP +.B \-o, \-\-output \fIFILE\fI +Output text file. +.SH EXAMPLE +opuspic2tag cover.jpg > cover.opustags +.PP +opuspic2tag -o cover.opustags cover.png +.PP +opuspic2tag cover.gif >> file.opustags +.SH SEE ALSO +.BR opuscomment (1), +.BR opusenc (1), +.BR vorbiscomment (1), +.BR sed (1) +.SH AUTHOR +Frédéric Mangano diff --git a/src/Makefile b/src/Makefile index 3862b99..bf3d50c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -10,16 +10,22 @@ CC=c99 CFLAGS=-D_POSIX_C_SOURCE=200809L -DNLS LDFLAGS=-logg -lm -lpthread SRCS=put-tags.c parse-tags.c read.c read-flac.c ocutil.c retrieve-tags.c select-codec.c +SRCP=opuspic2tag.c picture.c CONFSRCS=endianness.c error.c main.c HEADERS=global.h ocutil.h limit.h error.h iconv-impl.h ERRORDEFS=errordef/opus.tab errordef/main.tab OBJS=$(SRCS:.c=.o) $(CONFSRCS:.c=.o) +OBJP=$(SRCP:.c=.o) +RM = rm -f -all: opuscomment +all: opuscomment opuspic2tag opuscomment: $(OBJS) $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ +opuspic2tag: $(OBJP) + $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ + .SUFFIXES: .SUFFIXES: .c .o @@ -39,4 +45,4 @@ main.c: $(HEADERS) version.h @touch $@ clean: - rm endianness.c opuscomment $(OBJS) 2>/dev/null || : + $(RM) endianness.c opuscomment opuspic2tag $(OBJS) $(OBJP) 2>/dev/null || : diff --git a/src/opuspic2tag.c b/src/opuspic2tag.c new file mode 100644 index 0000000..c3b7795 --- /dev/null +++ b/src/opuspic2tag.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include +#include "picture.h" +#include "version.h" + +struct option options[] = { + {"help", no_argument, 0, 'h'}, + {"desc", required_argument, 0, 'd'}, + {"type", required_argument, 0, 't'}, + {"output", required_argument, 0, 'o'}, + {NULL, 0, 0, 0} +}; + +int main(int argc, char **argv) +{ + if(argc == 1) + { + fprintf(stdout, "opuspic2tag version %s\n", OPUSCOMMENT_VERSION); + fprintf(stdout, "Usage: %s [OPTIONS] FILE{.jpg,.png,.gif}\n", argv[0]); + return EXIT_SUCCESS; + } + char *path_picture = NULL, *picture_data = NULL, *picture_desc = NULL, *path_out = NULL; + const char *error_message; + int print_help = 0; + unsigned long picture_type = 3; + int c; + while((c = getopt_long(argc, argv, "hd:t:o:", options, NULL)) != -1) + { + switch(c){ + case 'h': + print_help = 1; + break; + case 'd': + picture_desc = optarg; + break; + case 't': + picture_type = atoi(optarg); + picture_type = (picture_type > 20) ? 3 : picture_type; + break; + case 'o': + path_out = optarg; + break; + default: + return EXIT_FAILURE; + } + } + if(print_help) + { + fprintf(stdout, "opuspic2tag version %s\n", OPUSCOMMENT_VERSION); + fprintf(stdout, "Usage: %s [OPTIONS] FILE{.jpg,.png,.gif}\n", argv[0]); + fprintf(stdout, "Options:\n"); + fprintf(stdout, " -h, --help print this help\n"); + fprintf(stdout, " -d, --desc STRING description\n"); + fprintf(stdout, " -t, --type NUM set image type by 0-20 or keywords. Default: 3\n"); + fprintf(stdout, " -o, --output FILE write tag to a file (default=stdout)\n"); + fprintf(stdout, "See the man page for extensive documentation."); + return EXIT_SUCCESS; + } + if(optind != argc - 1) + { + fputs("invalid arguments\n", stderr); + return EXIT_FAILURE; + } + path_picture = argv[optind]; + if(path_picture != NULL) + { + int seen_file_icons=0; + picture_data = opustags_picture_specification_parse(path_picture, &error_message, picture_desc, picture_type, &seen_file_icons); + if(picture_data == NULL) + { + fprintf(stderr,"Not read picture: %s\n", error_message); + } + } + FILE *out = NULL; + if(path_out != NULL) + { + if(strcmp(path_picture, path_out) == 0) + { + fputs("error: the input and output files are the same\n", stderr); + return EXIT_FAILURE; + } + out = fopen(path_out, "w"); + if(!out) + { + perror("fopen"); + return EXIT_FAILURE; + } + } else { + out = stdout; + } + if(picture_data != NULL) + { + char *picture_tag = "METADATA_BLOCK_PICTURE"; + char *picture_meta = NULL; + int picture_meta_len = strlen(picture_tag) + strlen(picture_data) + 1; + picture_meta = malloc(picture_meta_len); + if(picture_meta == NULL) + { + fprintf(stderr,"Bad picture size: %d\n", picture_meta_len); + free(picture_meta); + } else { + strcpy(picture_meta, picture_tag); + strcat(picture_meta, "="); + strcat(picture_meta, picture_data); + strcat(picture_meta, "\0"); + } + fwrite(picture_meta, 1, picture_meta_len, out); + free(picture_meta); + } + if(out) + fclose(out); + return EXIT_SUCCESS; +} diff --git a/src/picture.c b/src/picture.c new file mode 100644 index 0000000..b228d83 --- /dev/null +++ b/src/picture.c @@ -0,0 +1,493 @@ +// opus-tools 0.1.10: picture.c + +/* Copyright (C)2007-2013 Xiph.Org Foundation + File: picture.c + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include "picture.h" + +static const char BASE64_TABLE[64]={ + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' +}; + +/*Utility function for base64 encoding METADATA_BLOCK_PICTURE tags. + Stores BASE64_LENGTH(len)+1 bytes in dst (including a terminating NUL).*/ +void opustags_base64_encode(char *dst, const char *src, int len){ + unsigned s0; + unsigned s1; + unsigned s2; + int ngroups; + int i; + ngroups=len/3; + for(i=0;i>2]; + dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4]; + dst[4*i+2]=BASE64_TABLE[(s1&15)<<2|s2>>6]; + dst[4*i+3]=BASE64_TABLE[s2&63]; + } + len-=3*i; + if(len==1){ + s0=(unsigned char)src[3*i+0]; + dst[4*i+0]=BASE64_TABLE[s0>>2]; + dst[4*i+1]=BASE64_TABLE[(s0&3)<<4]; + dst[4*i+2]='='; + dst[4*i+3]='='; + i++; + } + else if(len==2){ + s0=(unsigned char)src[3*i+0]; + s1=(unsigned char)src[3*i+1]; + dst[4*i+0]=BASE64_TABLE[s0>>2]; + dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4]; + dst[4*i+2]=BASE64_TABLE[(s1&15)<<2]; + dst[4*i+3]='='; + i++; + } + dst[4*i]='\0'; +} + +/*A version of strncasecmp() that is guaranteed to only ignore the case of + ASCII characters.*/ +int opustags_oi_strncasecmp(const char *a, const char *b, int n){ + int i; + for(i=0;i='a'&&aval<='z') { + aval-='a'-'A'; + } + if(bval>='a'&&bval<='z'){ + bval-='a'-'A'; + } + diff=aval-bval; + if(diff){ + return diff; + } + } + return 0; +} + +int opustags_is_jpeg(const unsigned char *buf, size_t length){ + return length>=11&&memcmp(buf,"\xFF\xD8\xFF\xE0",4)==0 + &&(buf[4]<<8|buf[5])>=16&&memcmp(buf+6,"JFIF",5)==0; +} + +int opustags_is_png(const unsigned char *buf, size_t length){ + return length>=8&&memcmp(buf,"\x89PNG\x0D\x0A\x1A\x0A",8)==0; +} + +int opustags_is_gif(const unsigned char *buf, size_t length){ + return length>=6 + &&(memcmp(buf,"GIF87a",6)==0||memcmp(buf,"GIF89a",6)==0); +} + +/*Tries to extract the width, height, bits per pixel, and palette size of a + PNG. + On failure, simply leaves its outputs unmodified.*/ +void opustags_params_extract_png(const unsigned char *data, size_t data_length, + ogg_uint32_t *width, ogg_uint32_t *height, + ogg_uint32_t *depth, ogg_uint32_t *colors, + int *has_palette){ + if(opustags_is_png(data,data_length)){ + size_t offs; + offs=8; + while(data_length-offs>=12){ + ogg_uint32_t chunk_len; + chunk_len=READ_U32_BE(data+offs); + if(chunk_len>data_length-(offs+12))break; + else if(chunk_len==13&&memcmp(data+offs+4,"IHDR",4)==0){ + int color_type; + *width=READ_U32_BE(data+offs+8); + *height=READ_U32_BE(data+offs+12); + color_type=data[offs+17]; + if(color_type==3){ + *depth=24; + *has_palette=1; + } + else{ + int sample_depth; + sample_depth=data[offs+16]; + if(color_type==0)*depth=sample_depth; + else if(color_type==2)*depth=sample_depth*3; + else if(color_type==4)*depth=sample_depth*2; + else if(color_type==6)*depth=sample_depth*4; + *colors=0; + *has_palette=0; + break; + } + } + else if(*has_palette>0&&memcmp(data+offs+4,"PLTE",4)==0){ + *colors=chunk_len/3; + break; + } + offs+=12+chunk_len; + } + } +} + +/*Tries to extract the width, height, bits per pixel, and palette size of a + GIF. + On failure, simply leaves its outputs unmodified.*/ +void opustags_params_extract_gif(const unsigned char *data, size_t data_length, + ogg_uint32_t *width, ogg_uint32_t *height, + ogg_uint32_t *depth, ogg_uint32_t *colors, + int *has_palette){ + if(opustags_is_gif(data,data_length)&&data_length>=14){ + *width=data[6]|data[7]<<8; + *height=data[8]|data[9]<<8; + /*libFLAC hard-codes the depth to 24.*/ + *depth=24; + *colors=1<<((data[10]&7)+1); + *has_palette=1; + } +} + + +/*Tries to extract the width, height, bits per pixel, and palette size of a + JPEG. + On failure, simply leaves its outputs unmodified.*/ +void opustags_params_extract_jpeg(const unsigned char *data, size_t data_length, + ogg_uint32_t *width, ogg_uint32_t *height, + ogg_uint32_t *depth, ogg_uint32_t *colors, + int *has_palette){ + if(opustags_is_jpeg(data,data_length)){ + size_t offs; + offs=2; + for(;;){ + size_t segment_len; + int marker; + while(offs=data_length||(marker>=0xD8&&marker<=0xDA))break; + /*RST* (restart markers): skip (no segment length).*/ + else if(marker>=0xD0&&marker<=0xD7)continue; + /*Read the length of the marker segment.*/ + if(data_length-offs<2)break; + segment_len=data[offs]<<8|data[offs+1]; + if(segment_len<2||data_length-offs0xC0&&marker<0xD0&&(marker&3)!=0)){ + /*Found a SOFn (start of frame) marker segment:*/ + if(segment_len>=8){ + *height=data[offs+3]<<8|data[offs+4]; + *width=data[offs+5]<<8|data[offs+6]; + *depth=data[offs+2]*data[offs+7]; + *colors=0; + *has_palette=0; + } + break; + } + /*Other markers: skip the whole marker segment.*/ + offs+=segment_len; + } + } +} + +/*Parse a picture SPECIFICATION as given on the command-line. + spec: The specification. + error_message: Returns an error message on error. + seen_file_icons: Bit flags used to track if any pictures of type 1 or type 2 + have already been added, to ensure only one is allowed. + Return: A Base64-encoded string suitable for use in a METADATA_BLOCK_PICTURE + tag.*/ +char *opustags_picture_specification_parse(const char *spec, + const char **error_message, + const char *desc, + unsigned long picture_type, + int *seen_file_icons){ + FILE *picture_file; + unsigned long width; + unsigned long height; + unsigned long depth; + unsigned long colors; + const char *mime_type; + const char *mime_type_end; + const char *description; + const char *description_end; + const char *filename; + unsigned char *buf; + char *out; + size_t cbuf; + size_t nbuf; + size_t data_offset; + size_t data_length; + size_t b64_length; + int is_url; + /*If a filename has a '|' in it, there's no way we can distinguish it from a + full specification just from the spec string. + Instead, try to open the file. + If it exists, the user probably meant the file.*/ + width=height=depth=colors=0; + mime_type=mime_type_end=description=description_end=filename=spec; + is_url=0; + picture_file=fopen(filename,"rb"); + if (desc != NULL) description = desc; + if(picture_file==NULL&&strchr(spec,'|')){ + const char *p; + char *q; + /*We don't have a plain file, and there is a pipe character: assume it's + the full form of the specification.*/ + picture_type=strtoul(spec,&q,10); + if(*q!='|'||picture_type>20){ + *error_message="invalid picture type"; + return NULL; + } + if(picture_type>=1&&picture_type<=2&&(*seen_file_icons&picture_type)){ + *error_message=picture_type==1? + "only one picture of type 1 (32x32 icon) allowed": + "only one picture of type 2 (icon) allowed"; + return NULL; + } + /*An empty field implies a default of 'Cover (front)'.*/ + if(spec==q)picture_type=3; + mime_type=q+1; + mime_type_end=mime_type+strcspn(mime_type,"|"); + if(*mime_type_end!='|'){ + *error_message="invalid picture specification: not enough fields"; + return NULL; + } + /*The media type must be composed of ASCII printable characters 0x20-0x7E.*/ + for(p=mime_type;p0x7E){ + *error_message="invalid characters in media type"; + return NULL; + } + is_url=mime_type_end-mime_type==3 + &&strncmp("-->",mime_type,mime_type_end-mime_type)==0; + description=mime_type_end+1; + description_end=description+strcspn(description,"|"); + if(*description_end!='|'){ + *error_message="invalid picture specification: not enough fields"; + return NULL; + } + p=description_end+1; + if(*p!='|'){ + width=strtoul(p,&q,10); + if(*q!='x'){ + *error_message= + "invalid picture specification: can't parse resolution/color field"; + return NULL; + } + p=q+1; + height=strtoul(p,&q,10); + if(*q!='x'){ + *error_message= + "invalid picture specification: can't parse resolution/color field"; + return NULL; + } + p=q+1; + depth=strtoul(p,&q,10); + if(*q=='/'){ + p=q+1; + colors=strtoul(p,&q,10); + } + if(*q!='|'){ + *error_message= + "invalid picture specification: can't parse resolution/color field"; + return NULL; + } + p=q; + } + filename=p+1; + if(!is_url)picture_file=fopen(filename,"rb"); + } + /*Buffer size: 8 static 4-byte fields plus 2 dynamic fields, plus the + file/URL data. + We reserve at least 10 bytes for the media type, in case we still need to + extract it from the file.*/ + data_offset=32+(description_end-description)+IMAX(mime_type_end-mime_type,10); + buf=NULL; + if(is_url){ + /*Easy case: just stick the URL at the end. + We don't do anything to verify it's a valid URL.*/ + data_length=strlen(filename); + cbuf=nbuf=data_offset+data_length; + buf=(unsigned char *)malloc(cbuf); + memcpy(buf+data_offset,filename,data_length); + } + else{ + ogg_uint32_t file_width; + ogg_uint32_t file_height; + ogg_uint32_t file_depth; + ogg_uint32_t file_colors; + int has_palette; + /*Complicated case: we have a real file. + Read it in, attempt to parse the media type and image dimensions if + necessary, and validate what the user passed in.*/ + if(picture_file==NULL){ + *error_message="error opening picture file"; + return NULL; + } + nbuf=data_offset; + /*Add a reasonable starting image file size.*/ + cbuf=data_offset+65536; + for(;;){ + unsigned char *new_buf; + size_t nread; + new_buf=realloc(buf,cbuf); + if(new_buf==NULL){ + fclose(picture_file); + free(buf); + *error_message="insufficient memory"; + return NULL; + } + buf=new_buf; + nread=fread(buf+nbuf,1,cbuf-nbuf,picture_file); + nbuf+=nread; + if(nbuf0x7FFFFFFFU)cbuf=0xFFFFFFFFU; + else cbuf=cbuf<<1|1; + } + data_length=nbuf-data_offset; + /*If there was no media type, try to extract it from the file data.*/ + if(mime_type_end==mime_type){ + if(opustags_is_jpeg(buf+data_offset,data_length)){ + mime_type="image/jpeg"; + mime_type_end=mime_type+10; + } + else if(opustags_is_png(buf+data_offset,data_length)){ + mime_type="image/png"; + mime_type_end=mime_type+9; + } + else if(opustags_is_gif(buf+data_offset,data_length)){ + mime_type="image/gif"; + mime_type_end=mime_type+9; + } + else{ + free(buf); + *error_message="unable to guess media type from file, " + "must set it explicitly"; + return NULL; + } + } + /*Try to extract the image dimensions/color information from the file.*/ + file_width=file_height=file_depth=file_colors=0; + has_palette=-1; + if(mime_type_end-mime_type==9 + &&opustags_oi_strncasecmp("image/png",mime_type,mime_type_end-mime_type)==0){ + opustags_params_extract_png(buf+data_offset,data_length, + &file_width,&file_height,&file_depth,&file_colors,&has_palette); + } + else if(mime_type_end-mime_type==9 + &&opustags_oi_strncasecmp("image/gif",mime_type,mime_type_end-mime_type)==0){ + opustags_params_extract_gif(buf+data_offset,data_length, + &file_width,&file_height,&file_depth,&file_colors,&has_palette); + } + else if(mime_type_end-mime_type==10 + &&opustags_oi_strncasecmp("image/jpeg",mime_type,mime_type_end-mime_type)==0){ + opustags_params_extract_jpeg(buf+data_offset,data_length, + &file_width,&file_height,&file_depth,&file_colors,&has_palette); + } + if(!width)width=file_width; + if(!height)height=file_height; + if(!depth)depth=file_depth; + if(!colors)colors=file_colors; + if((file_width&&width!=file_width) + ||(file_height&&height!=file_height) + ||(file_depth&&depth!=file_depth) + /*We use has_palette to ensure we also reject non-0 user color counts for + images we've positively identified as non-paletted.*/ + ||(has_palette>=0&&colors!=file_colors)){ + free(buf); + *error_message="invalid picture specification: " + "resolution/color field does not match file"; + return NULL; + } + } + /*These fields MUST be set correctly OR all set to zero. + So if any of them (except colors, for which 0 is a valid value) are still + zero, clear the rest to zero.*/ + if(width==0||height==0||depth==0)width=height=depth=colors=0; + if(picture_type==1&&(width!=32||height!=32 + ||mime_type_end-mime_type!=9 + ||opustags_oi_strncasecmp("image/png",mime_type,mime_type_end-mime_type)!=0)){ + free(buf); + *error_message="pictures of type 1 MUST be 32x32 PNGs"; + return NULL; + } + /*Build the METADATA_BLOCK_PICTURE buffer. + We do this backwards from data_offset, because we didn't necessarily know + how big the media type string was before we read the data in.*/ + data_offset-=4; + WRITE_U32_BE(buf+data_offset,(unsigned long)data_length); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,colors); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,depth); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,height); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,width); + data_offset-=description_end-description; + memcpy(buf+data_offset,description,description_end-description); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,(unsigned long)(description_end-description)); + data_offset-=mime_type_end-mime_type; + memcpy(buf+data_offset,mime_type,mime_type_end-mime_type); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,(unsigned long)(mime_type_end-mime_type)); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,picture_type); + data_length=nbuf-data_offset; + b64_length=BASE64_LENGTH(data_length); + out=(char *)malloc(b64_length+1); + if(out!=NULL){ + opustags_base64_encode(out,(char *)buf+data_offset,data_length); + if(picture_type>=1&&picture_type<=2)*seen_file_icons|=picture_type; + } + free(buf); + return out; +} diff --git a/src/picture.h b/src/picture.h new file mode 100644 index 0000000..e162290 --- /dev/null +++ b/src/picture.h @@ -0,0 +1,65 @@ +#include + +#ifndef __OPUSTAGS_H +#define __OPUSTAGS_H + +#if defined(__cplusplus) +extern "C" { +#endif + +// opus-tools 0.1.10: picture.h + +typedef enum{ + PIC_FORMAT_JPEG, + PIC_FORMAT_PNG, + PIC_FORMAT_GIF +}opustags_picture_format; + +#define IMAX(a,b) ((a) > (b) ? (a) : (b)) +#define BASE64_LENGTH(len) (((len)+2)/3*4) + +/*Utility function for base64 encoding METADATA_BLOCK_PICTURE tags. + Stores BASE64_LENGTH(len)+1 bytes in dst (including a terminating NUL).*/ +void opustags_base64_encode(char *dst, const char *src, int len); + +int opustags_oi_strncasecmp(const char *a, const char *b, int n); + +int opustags_is_jpeg(const unsigned char *buf, size_t length); +int opustags_is_png(const unsigned char *buf, size_t length); +int opustags_is_gif(const unsigned char *buf, size_t length); + +void opustags_params_extract_png(const unsigned char *data, size_t data_length, + ogg_uint32_t *width, ogg_uint32_t *height, + ogg_uint32_t *depth, ogg_uint32_t *colors, + int *has_palette); +void opustags_params_extract_gif(const unsigned char *data, size_t data_length, + ogg_uint32_t *width, ogg_uint32_t *height, + ogg_uint32_t *depth, ogg_uint32_t *colors, + int *has_palette); +void opustags_params_extract_jpeg(const unsigned char *data, size_t data_length, + ogg_uint32_t *width, ogg_uint32_t *height, + ogg_uint32_t *depth, ogg_uint32_t *colors, + int *has_palette); + +char *opustags_picture_specification_parse(const char *spec, + const char **error_message, + const char *desc, + unsigned long picture_type, + int *seen_file_icons); + +#define READ_U32_BE(buf) \ + (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff)) +#define WRITE_U32_BE(buf, val) \ + do{ \ + (buf)[0]=(unsigned char)((val)>>24); \ + (buf)[1]=(unsigned char)((val)>>16); \ + (buf)[2]=(unsigned char)((val)>>8); \ + (buf)[3]=(unsigned char)(val); \ + } \ + while(0); + +#if defined(__cplusplus) +} +#endif + +#endif /* __OPUSTAGS_H */ diff --git a/src/version.h b/src/version.h index b4a5778..3ed0642 100644 --- a/src/version.h +++ b/src/version.h @@ -1,4 +1,4 @@ -#define OPUSCOMMENT_VERSION "1.5.12" +#define OPUSCOMMENT_VERSION "1.5.14" #define OPUSCOMMENT_REVISION_YEAR (2019 - 1900) #define OPUSCOMMENT_REVISION_MONTH (9 - 1) From caa7bf7e41ee7152f44e1aec4109ba28a030ccde Mon Sep 17 00:00:00 2001 From: zvezdochiot Date: Mon, 25 Jul 2022 01:08:41 +0300 Subject: [PATCH 4/9] 1.5.14: replace endianness-check.sh to endianness.c --- .gitignore | 1 - src/Makefile | 11 ++- src/endianness-check.sh | 52 -------------- src/endianness.c | 79 ++++++++++++++++++++++ src/error.c | 2 +- src/error.h | 2 +- src/errordef/{main.tab => opuscomment.tab} | 0 src/{main.c => opuscomment.c} | 0 8 files changed, 85 insertions(+), 62 deletions(-) delete mode 100755 src/endianness-check.sh create mode 100644 src/endianness.c rename src/errordef/{main.tab => opuscomment.tab} (100%) rename src/{main.c => opuscomment.c} (100%) diff --git a/.gitignore b/.gitignore index 8b4086a..b48052a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ opuscomment tests/ -endianness.c *.o nls/*/opuscomment.cat diff --git a/src/Makefile b/src/Makefile index bf3d50c..6392388 100644 --- a/src/Makefile +++ b/src/Makefile @@ -11,9 +11,9 @@ CFLAGS=-D_POSIX_C_SOURCE=200809L -DNLS LDFLAGS=-logg -lm -lpthread SRCS=put-tags.c parse-tags.c read.c read-flac.c ocutil.c retrieve-tags.c select-codec.c SRCP=opuspic2tag.c picture.c -CONFSRCS=endianness.c error.c main.c +CONFSRCS=endianness.c error.c opuscomment.c HEADERS=global.h ocutil.h limit.h error.h iconv-impl.h -ERRORDEFS=errordef/opus.tab errordef/main.tab +ERRORDEFS=errordef/opus.tab errordef/opuscomment.tab OBJS=$(SRCS:.c=.o) $(CONFSRCS:.c=.o) OBJP=$(SRCP:.c=.o) RM = rm -f @@ -35,14 +35,11 @@ opuspic2tag: $(OBJP) $(SRCS): $(HEADERS) @touch $@ -endianness.c: endianness-check.sh - ./$< > $@ - error.c: $(ERRORDEFS) $(HEADERS) @touch $@ -main.c: $(HEADERS) version.h +opuscomment.c: $(HEADERS) version.h @touch $@ clean: - $(RM) endianness.c opuscomment opuspic2tag $(OBJS) $(OBJP) 2>/dev/null || : + $(RM) opuscomment opuspic2tag $(OBJS) $(OBJP) 2>/dev/null || : diff --git a/src/endianness-check.sh b/src/endianness-check.sh deleted file mode 100755 index a4c96e3..0000000 --- a/src/endianness-check.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh -echo '#include ' -val=$(printf '\20\21\22\23\24\25\26\27' |od -v -An -tx8) -if [ $val = 1716151413121110 ] -then - #little endian - cat <> 8; -} -uint32_t oi32(uint32_t i) { - return i << 24 | (i & (M << 8)) << 8 | (i & (M << 16)) >> 8 | i >> 24; -} -uint64_t oi64(uint64_t i) { - return i << 56 | (i & (M << 8)) << 40 | (i & (M << 16)) >> 24 | (i & (M << 24)) >> 8 - |(i & (M << 32)) >> 8 | (i & (M << 40)) >> 24 | (i & (M << 48)) >> 40 | i >> 56; -} -#undef M -heredoc -else - terrible_endianness() { - cat < +int endian_test() +{ + uint32_t sample = {0x01020304}; + uint8_t *test = (uint8_t *)&sample; + if (test[0] == 4) + return -1; + else if (test[0] == 1) + return 1; + else + return 0; +} + +uint16_t oi16(uint16_t i) +{ + int et = endian_test(); + if (et == -1) + return i; + else if (et == 1) + return (i << 8 | i >> 8); + else + { + uint8_t *val = (uint8_t *)&i; + uint8_t out[2]; + out[0] = val[0]; + out[1] = val[1]; + return *(uint16_t*)out; + } +} +uint32_t oi32(uint32_t i) +{ + int et = endian_test(); + if (et == -1) + return i; + else if (et == 1) + return (i << 24 + | (i & (255ULL << 8)) << 8 + | (i & (255ULL << 16)) >> 8 + | i >> 24); + else + { + uint8_t *val = (uint8_t *)&i; + uint8_t out[4]; + out[0] = val[0]; + out[1] = val[1]; + out[2] = val[2]; + out[3] = val[3]; + return *(uint32_t*)out; + } +} +uint64_t oi64(uint64_t i) +{ + int et = endian_test(); + if (et == -1) + return i; + else if (et == 1) + return (i << 56 + | (i & (255ULL << 8)) << 40 + | (i & (255ULL << 16)) >> 24 + | (i & (255ULL << 24)) >> 8 + | (i & (255ULL << 32)) >> 8 + | (i & (255ULL << 40)) >> 24 + | (i & (255ULL << 48)) >> 40 + | i >> 56); + else + { + uint8_t *val = (uint8_t *)&i; + uint8_t out[8]; + out[0] = val[0]; + out[1] = val[1]; + out[2] = val[2]; + out[3] = val[3]; + out[4] = val[4]; + out[5] = val[5]; + out[6] = val[6]; + out[7] = val[7]; + return *(uint64_t*)out; + } +} diff --git a/src/error.c b/src/error.c index 32efe82..abe1998 100644 --- a/src/error.c +++ b/src/error.c @@ -14,7 +14,7 @@ noreturn void mainerror(int e, ...) { errorprefix(); char const *msg[] = { #define LIST(I, E, S) S, -#include "errordef/main.tab" +#include "errordef/opuscomment.tab" #undef LIST }; vfprintf(stderr, catgets(catd, 2, e, msg[e]), ap); diff --git a/src/error.h b/src/error.h index 942468a..389fa09 100644 --- a/src/error.h +++ b/src/error.h @@ -16,7 +16,7 @@ enum err_opus_ { enum err_main_ { #define LIST(I, E, S) err_main_##E = I, -#include "errordef/main.tab" +#include "errordef/opuscomment.tab" #undef LIST }; diff --git a/src/errordef/main.tab b/src/errordef/opuscomment.tab similarity index 100% rename from src/errordef/main.tab rename to src/errordef/opuscomment.tab diff --git a/src/main.c b/src/opuscomment.c similarity index 100% rename from src/main.c rename to src/opuscomment.c From 497166f165ba91ab6bad9ac787ecb173b090dea4 Mon Sep 17 00:00:00 2001 From: zvezdochiot Date: Mon, 25 Jul 2022 15:56:24 +0300 Subject: [PATCH 5/9] 1.5.14: fix #2: opuspic2tag: add end line --- README.md | 9 +++++++-- src/opuspic2tag.c | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c8fc55e..ab7dc51 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,13 @@ -![GitHub release (latest by date)](https://img.shields.io/github/v/release/hcmiya/opuscomment) +`Origin_:`![GitHub release (latest by date)](https://img.shields.io/github/v/release/hcmiya/opuscomment) ![GitHub Release Date](https://img.shields.io/github/release-date/hcmiya/opuscomment) ![GitHub repo size](https://img.shields.io/github/repo-size/hcmiya/opuscomment) ![GitHub all releases](https://img.shields.io/github/downloads/hcmiya/opuscomment/total) -![GitHub](https://img.shields.io/github/license/hcmiya/opuscomment) +![GitHub](https://img.shields.io/github/license/hcmiya/opuscomment) +`ORG.SLM:`![GitHub release (latest by date)](https://img.shields.io/github/v/release/Sound-Linux-More/opuscomment) +![GitHub Release Date](https://img.shields.io/github/release-date/Sound-Linux-More/opuscomment) +![GitHub repo size](https://img.shields.io/github/repo-size/Sound-Linux-More/opuscomment) +![GitHub all releases](https://img.shields.io/github/downloads/Sound-Linux-More/opuscomment/total) +![GitHub](https://img.shields.io/github/license/Sound-Linux-More/opuscomment) # opuscomment diff --git a/src/opuspic2tag.c b/src/opuspic2tag.c index c3b7795..b048375 100644 --- a/src/opuspic2tag.c +++ b/src/opuspic2tag.c @@ -111,6 +111,7 @@ int main(int argc, char **argv) } fwrite(picture_meta, 1, picture_meta_len, out); free(picture_meta); + fwrite("\n", 1, 1, out); } if(out) fclose(out); From d29fa7f2fa60f3ff7247ca35dfd8b6e7489aeb60 Mon Sep 17 00:00:00 2001 From: zvezdochiot Date: Tue, 26 Jul 2022 19:07:14 +0300 Subject: [PATCH 6/9] 1.5.15: fix #3: read-flac --- src/Makefile | 2 +- src/error.c | 94 ++-- src/global.h | 140 +++--- src/ocutil.c | 166 +++---- src/opuscomment.c | 636 +++++++++++++------------- src/opuscomment.h | 12 +- src/parse-tags.c | 1028 +++++++++++++++++++++---------------------- src/put-tags.c | 326 +++++++------- src/read-flac.c | 430 +++++++++--------- src/read.c | 868 ++++++++++++++++++------------------ src/retrieve-tags.c | 628 +++++++++++++------------- src/select-codec.c | 218 ++++----- src/version.h | 8 +- 13 files changed, 2279 insertions(+), 2277 deletions(-) diff --git a/src/Makefile b/src/Makefile index 6392388..68c3cdf 100644 --- a/src/Makefile +++ b/src/Makefile @@ -7,7 +7,7 @@ # `DEFAULT_NLS_PATH`を文字列でdefineをすると、NLSPATHが無い時に最初にcatopen(3)をする場所を指定することができます。 CC=c99 -CFLAGS=-D_POSIX_C_SOURCE=200809L -DNLS +CFLAGS=-D_POSIX_C_SOURCE=200809L -DNLS -g LDFLAGS=-logg -lm -lpthread SRCS=put-tags.c parse-tags.c read.c read-flac.c ocutil.c retrieve-tags.c select-codec.c SRCP=opuspic2tag.c picture.c diff --git a/src/error.c b/src/error.c index abe1998..8aaefd8 100644 --- a/src/error.c +++ b/src/error.c @@ -5,79 +5,79 @@ #include "opuscomment.h" void errorprefix(void) { - fprintf(stderr, catgets(catd, 1, 3, "%s: "), program_name); + fprintf(stderr, catgets(catd, 1, 3, "%s: "), program_name); } noreturn void mainerror(int e, ...) { - va_list ap; - va_start(ap, e); - errorprefix(); - char const *msg[] = { + va_list ap; + va_start(ap, e); + errorprefix(); + char const *msg[] = { #define LIST(I, E, S) S, #include "errordef/opuscomment.tab" #undef LIST - }; - vfprintf(stderr, catgets(catd, 2, e, msg[e]), ap); - fputc('\n', stderr); - exit(1); + }; + vfprintf(stderr, catgets(catd, 2, e, msg[e]), ap); + fputc('\n', stderr); + exit(1); } noreturn void opuserror(int e, ...) { - va_list ap; - va_start(ap, e); - errorprefix(); - - struct { - bool report_page; - char const *default_message; - } msg[] = { + va_list ap; + va_start(ap, e); + errorprefix(); + + struct { + bool report_page; + char const *default_message; + } msg[] = { #define LIST(I, B, E, S) {B, S}, #include "errordef/opus.tab" #undef LIST - }; - if (msg[e].report_page && codec->type != CODEC_FLAC) { - fprintf(stderr, catgets(catd, 1, 4, "%s format error: page %u: "), codec->name, opus_idx); - } - else { - fprintf(stderr, catgets(catd, 1, 8, "%s format error: "), codec->name); - } - vfprintf(stderr, catgets(catd, 3, e, msg[e].default_message), ap); - fputc('\n', stderr); - exit(2); + }; + if (msg[e].report_page && codec->type != CODEC_FLAC) { + fprintf(stderr, catgets(catd, 1, 4, "%s format error: page %u: "), codec->name, opus_idx); + } + else { + fprintf(stderr, catgets(catd, 1, 8, "%s format error: "), codec->name); + } + vfprintf(stderr, catgets(catd, 3, e, msg[e].default_message), ap); + fputc('\n', stderr); + exit(2); } noreturn void oserror(void) { - perror(program_name); - exit(3); + perror(program_name); + exit(3); } noreturn void oserror_fmt(char const *e, ...) { - va_list ap; - va_start(ap, e); - errorprefix(); - vfprintf(stderr, e, ap); - fputc('\n', stderr); - exit(3); + va_list ap; + va_start(ap, e); + errorprefix(); + vfprintf(stderr, e, ap); + fputc('\n', stderr); + exit(3); } noreturn void fileerror(char const *file) { - errorprefix(); - fprintf(stderr, catgets(catd, 1, 5, "%s: "), file); - perror(NULL); - exit(3); + errorprefix(); + fprintf(stderr, catgets(catd, 1, 5, "%s: "), file); + perror(NULL); + exit(3); } noreturn void opterror(int c, char const *e, ...) { - va_list ap; - va_start(ap, e); - errorprefix(); - fprintf(stderr, catgets(catd, 1, 7, "-%c: "), c); - vfprintf(stderr, e, ap); - fputc('\n', stderr); - exit(1); + va_list ap; + va_start(ap, e); + errorprefix(); + fprintf(stderr, catgets(catd, 1, 7, "-%c: "), c); + vfprintf(stderr, e, ap); + fputc('\n', stderr); + exit(1); } noreturn void exceed_output_limit(void) { - mainerror(err_main_output_limit, TAG_LENGTH_LIMIT__OUTPUT >> 20); + mainerror(err_main_output_limit, TAG_LENGTH_LIMIT__OUTPUT >> 20); } diff --git a/src/global.h b/src/global.h index a1e7804..b1a3596 100644 --- a/src/global.h +++ b/src/global.h @@ -13,89 +13,89 @@ #include GLOBAL struct { - enum { - EDIT_NONE, - EDIT_LIST, - EDIT_WRITE, - EDIT_APPEND, - } edit; - - bool gain_fix; - bool gain_relative; - bool gain_not_zero; - bool gain_q78; - int gain_val; - bool gain_val_sign; - bool gain_put; - - bool tag_ignore_picture; - enum { - TAG_ESCAPE_TAB, - TAG_ESCAPE_BACKSLASH, - TAG_ESCAPE_NUL, - } tag_escape; - bool tag_escape_tilde; - bool tag_raw; - bool tag_toupper; - char *tag_filename; - bool tag_deferred; - bool tag_verify; - bool tag_check_line_term; - - int target_idx; - - char *in, *out; + enum { + EDIT_NONE, + EDIT_LIST, + EDIT_WRITE, + EDIT_APPEND, + } edit; + + bool gain_fix; + bool gain_relative; + bool gain_not_zero; + bool gain_q78; + int gain_val; + bool gain_val_sign; + bool gain_put; + + bool tag_ignore_picture; + enum { + TAG_ESCAPE_TAB, + TAG_ESCAPE_BACKSLASH, + TAG_ESCAPE_NUL, + } tag_escape; + bool tag_escape_tilde; + bool tag_raw; + bool tag_toupper; + char *tag_filename; + bool tag_deferred; + bool tag_verify; + bool tag_check_line_term; + + int target_idx; + + char *in, *out; } O; GLOBAL enum { - PAGE_INFO, - PAGE_INFO_BORDER, - PAGE_COMMENT, - PAGE_OTHER_METADATA, - PAGE_SOUND, + PAGE_INFO, + PAGE_INFO_BORDER, + PAGE_COMMENT, + PAGE_OTHER_METADATA, + PAGE_SOUND, } opst; GLOBAL struct codec_parser { - enum { - CODEC_OPUS, - CODEC_COMMON, - CODEC_FLAC, - CODEC_VP8, - } type; - char const *prog, *name; - size_t headmagic_len; - char const *headmagic; - void (*parse)(ogg_page*); - size_t commagic_len; - char const *commagic; + enum { + CODEC_OPUS, + CODEC_COMMON, + CODEC_FLAC, + CODEC_VP8, + } type; + char const *prog, *name; + size_t headmagic_len; + char const *headmagic; + void (*parse)(ogg_page*); + size_t commagic_len; + char const *commagic; } *codec; GLOBAL char const *program_name; GLOBAL char const * const program_name_default GLOBAL_VAL("opuscomment"); GLOBAL uint8_t const * const MBPeq - // "METADATA_BLOCK_PICTURE=" in ASCII - GLOBAL_VAL( - "\x4d\x45\x54\x41\x44\x41\x54\x41\x5f\x42\x4c\x4f\x43\x4b\x5f\x50" - "\x49\x43\x54\x55\x52\x45\x3d" - ); + // "METADATA_BLOCK_PICTURE=" in ASCII + GLOBAL_VAL( + "\x4d\x45\x54\x41\x44\x41\x54\x41\x5f\x42\x4c\x4f\x43\x4b\x5f\x50" + "\x49\x43\x54\x55\x52\x45\x3d" + ); GLOBAL uint8_t const * const b64tab_ascii - GLOBAL_VAL( - "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" // A-O - "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a" // P-Z - "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" // a-o - "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a" // p-z - "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39" // 0-9 - "\x2b\x2f\x3d" // + / = - ); + GLOBAL_VAL( + "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" // A-O + "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a" // P-Z + "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" // a-o + "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a" // p-z + "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39" // 0-9 + "\x2b\x2f\x3d" // + / = + ); GLOBAL uint8_t const * const new_vendor_string_ascii - // 'Unknown (This string was generated by a metadata editor "opuscomment")' in ASCII - GLOBAL_VAL( - "\x55\x6e\x6b\x6e\x6f\x77\x6e\x20\x28\x54\x68\x69\x73\x20\x73\x74" - "\x72\x69\x6e\x67\x20\x77\x61\x73\x20\x67\x65\x6e\x65\x72\x61\x74" - "\x65\x64\x20\x62\x79\x20\x61\x20\x6d\x65\x74\x61\x64\x61\x74\x61" - "\x20\x65\x64\x69\x74\x6f\x72\x20\x22\x6f\x70\x75\x73\x63\x6f\x6d" - "\x6d\x65\x6e\x74\x22\x29" - ); + // 'Unknown (This string was generated by a metadata editor "opuscomment")' in ASCII + GLOBAL_VAL( + "\x55\x6e\x6b\x6e\x6f\x77\x6e\x20\x28\x54\x68\x69\x73\x20\x73\x74" + "\x72\x69\x6e\x67\x20\x77\x61\x73\x20\x67\x65\x6e\x65\x72\x61\x74" + "\x65\x64\x20\x62\x79\x20\x61\x20\x6d\x65\x74\x61\x64\x61\x74\x61" + "\x20\x65\x64\x69\x74\x6f\x72\x20\x22\x6f\x70\x75\x73\x63\x6f\x6d" + "\x6d\x65\x6e\x74\x22\x29" + ); GLOBAL uint32_t opus_idx, opus_sno, opus_idx_diff; GLOBAL bool leave_header_packets; diff --git a/src/ocutil.c b/src/ocutil.c index abf9768..5149f2f 100644 --- a/src/ocutil.c +++ b/src/ocutil.c @@ -7,109 +7,109 @@ #include "opuscomment.h" static bool test_tag_field_keepcase(uint8_t *line, size_t n, bool *on_field) { - size_t i; - bool valid = true; - for (i = 0; i < n && line[i] != 0x3d; i++) { - if (!(line[i] >= 0x20 && line[i] <= 0x7d)) { - valid = false; - } - } - if (i < n) *on_field = false; - return valid; + size_t i; + bool valid = true; + for (i = 0; i < n && line[i] != 0x3d; i++) { + if (!(line[i] >= 0x20 && line[i] <= 0x7d)) { + valid = false; + } + } + if (i < n) *on_field = false; + return valid; } bool test_tag_field(uint8_t *line, size_t n, bool upcase, bool *on_field, bool *upcase_applied_) { - // フィールドの使用文字チェック・大文字化 - if (!upcase) { - return test_tag_field_keepcase(line, n, on_field); - } - - bool dummy_, *upcase_applied; - upcase_applied = upcase_applied_ ? upcase_applied_ : &dummy_; - - size_t i; - bool valid = true; - for (i = 0; i < n && line[i] != 0x3d; i++) { - if (!(line[i] >= 0x20 && line[i] <= 0x7d)) { - valid = false; - } - if (line[i] >= 0x61 && line[i] <= 0x7a) { - line[i] -= 32; - *upcase_applied = true; - } - } - if (i < n) *on_field = false; - return valid; + // フィールドの使用文字チェック・大文字化 + if (!upcase) { + return test_tag_field_keepcase(line, n, on_field); + } + + bool dummy_, *upcase_applied; + upcase_applied = upcase_applied_ ? upcase_applied_ : &dummy_; + + size_t i; + bool valid = true; + for (i = 0; i < n && line[i] != 0x3d; i++) { + if (!(line[i] >= 0x20 && line[i] <= 0x7d)) { + valid = false; + } + if (line[i] >= 0x61 && line[i] <= 0x7a) { + line[i] -= 32; + *upcase_applied = true; + } + } + if (i < n) *on_field = false; + return valid; } size_t fill_buffer(void *buf, size_t left, size_t buflen, FILE *fp) { - size_t filllen = left > buflen ? buflen : left; - size_t readlen = fread(buf, 1, filllen, fp); - return filllen; + size_t filllen = left > buflen ? buflen : left; + size_t readlen = fread(buf, 1, filllen, fp); + return filllen; } bool test_non_opus(ogg_page *og) { - if (ogg_page_serialno(og) == opus_sno) { - if (ogg_page_bos(og)) { - opuserror(err_opus_bad_stream); - } - if (opst != PAGE_SOUND && ogg_page_pageno(og) != opus_idx) { - opuserror(err_opus_discontinuous, ogg_page_pageno(og), opus_idx); - } - return true; - } -// have_multi_streams = true; - - int pno = ogg_page_pageno(og); - if (pno == 0) { - if (leave_header_packets) { - opuserror(err_opus_bad_stream); - } - if (!ogg_page_bos(og) || ogg_page_eos(og) || ogg_page_granulepos(og) != 0) { - opuserror(err_opus_bad_stream); - } - } - else if (pno == 1) { - // 複数論理ストリームの先頭は全て0で始まるため、何かが1ページ目を始めたら今後0ページ目は来ないはずである。 - leave_header_packets = true; - } - else { - if (!leave_header_packets) { - // 他で1ページ目が始まってないのに2ページ目以上が来た場合 - opuserror(err_opus_bad_stream); - } - } - - if (pno && ogg_page_bos(og)) { - // Opusが続いているストリーム途中でBOSが来るはずがない - opuserror(err_opus_bad_stream); - } - return false; + if (ogg_page_serialno(og) == opus_sno) { + if (ogg_page_bos(og)) { + opuserror(err_opus_bad_stream); + } + if (opst != PAGE_SOUND && ogg_page_pageno(og) != opus_idx) { + opuserror(err_opus_discontinuous, ogg_page_pageno(og), opus_idx); + } + return true; + } +// have_multi_streams = true; + + int pno = ogg_page_pageno(og); + if (pno == 0) { + if (leave_header_packets) { + opuserror(err_opus_bad_stream); + } + if (!ogg_page_bos(og) || ogg_page_eos(og) || ogg_page_granulepos(og) != 0) { + opuserror(err_opus_bad_stream); + } + } + else if (pno == 1) { + // 複数論理ストリームの先頭は全て0で始まるため、何かが1ページ目を始めたら今後0ページ目は来ないはずである。 + leave_header_packets = true; + } + else { + if (!leave_header_packets) { + // 他で1ページ目が始まってないのに2ページ目以上が来た場合 + opuserror(err_opus_bad_stream); + } + } + + if (pno && ogg_page_bos(og)) { + // Opusが続いているストリーム途中でBOSが来るはずがない + opuserror(err_opus_bad_stream); + } + return false; } void write_page(ogg_page *og, FILE *fp) { - size_t ret; - ret = fwrite(og->header, 1, og->header_len, fp); - if (ret != og->header_len) { - oserror(); - } - ret = fwrite(og->body, 1, og->body_len, fp); - if (ret != og->body_len) { - oserror(); - } + size_t ret; + ret = fwrite(og->header, 1, og->header_len, fp); + if (ret != og->header_len) { + oserror(); + } + ret = fwrite(og->body, 1, og->body_len, fp); + if (ret != og->body_len) { + oserror(); + } } void set_pageno(ogg_page *og, int no) { - *(uint32_t*)&og->header[18] = oi32(no); + *(uint32_t*)&og->header[18] = oi32(no); } void set_granulepos(ogg_page *og, int64_t pos) { - *(int64_t*)&og->header[6] = oi64(pos); + *(int64_t*)&og->header[6] = oi64(pos); } #if _POSIX_C_SOURCE < 200809L size_t strnlen(char const *src, size_t n) { - char const *endp = src + n, *p = src; - while (p < endp && *p) p++; - return p - src; + char const *endp = src + n, *p = src; + while (p < endp && *p) p++; + return p - src; } #endif diff --git a/src/opuscomment.c b/src/opuscomment.c index 7d9286a..e4fd098 100644 --- a/src/opuscomment.c +++ b/src/opuscomment.c @@ -21,16 +21,16 @@ static struct codec_parser const *default_codec; static void usage(void) { - char revision[64]; - strftime(revision, 64, "%x", &(struct tm){ - .tm_year = OPUSCOMMENT_REVISION_YEAR, - .tm_mon = OPUSCOMMENT_REVISION_MONTH, - .tm_mday = OPUSCOMMENT_REVISION_DAY, - }); - fprintf(stderr, catgets(catd, 6, 3, "Version: %s (rev. %s)\n"), OPUSCOMMENT_VERSION, revision); - fputc('\n', stderr); - - fprintf(stderr, catgets(catd, 6, 4, + char revision[64]; + strftime(revision, 64, "%x", &(struct tm){ + .tm_year = OPUSCOMMENT_REVISION_YEAR, + .tm_mon = OPUSCOMMENT_REVISION_MONTH, + .tm_mday = OPUSCOMMENT_REVISION_DAY, + }); + fprintf(stderr, catgets(catd, 6, 3, "Version: %s (rev. %s)\n"), OPUSCOMMENT_VERSION, revision); + fputc('\n', stderr); + + fprintf(stderr, catgets(catd, 6, 4, "Synopsys:\n" " %1$s [-l] [-C codec] [-i idx] [-0DepQRUvV~] srcfile\n" " %1$s {-a|-w} [-C codec] [-i idx] [-g gain|-s scale] [-0e] [-c tagfile] [-t NAME=VALUE ...] [-d NAME[=VALUE] ...] [-1DQprRUv] srcfile [output]\n" @@ -66,9 +66,9 @@ static void usage(void) { " Implies -V.\n" " In append/write mode, return error when tag input is empty.\n" " Implies -T\n" - ), program_name, default_codec->name); - if (!default_codec->prog) { - fprintf(stderr, catgets(catd, 6, 5, + ), program_name, default_codec->name); + if (!default_codec->prog) { + fprintf(stderr, catgets(catd, 6, 5, " -g gain\n" " Specify output gain in dB\n" " -s scale\n" @@ -79,17 +79,17 @@ static void usage(void) { " set [+-]1/256 dB instead\n" " -Q Use Q7.8 format for editing output gain\n" " -v Put output gain to stderr\n" - )); - } - exit(1); + )); + } + exit(1); } static void fail_to_parse(int c) { - opterror(c, catgets(catd, 7, 1, "failed to parse value")); + opterror(c, catgets(catd, 7, 1, "failed to parse value")); } static void out_of_range(int c) { - opterror(c, catgets(catd, 7, 2, "the value is out of range")); + opterror(c, catgets(catd, 7, 2, "the value is out of range")); } void select_codec(void); @@ -97,320 +97,320 @@ bool select_codec_by_name(char const*); void set_parser_type(void); static void parse_args(int argc, char **argv) { - int c; - bool added_tag = false, del_tag = false; - int gainfmt; - double gv; - - while ((c = getopt(argc, argv, "01ac:C:d:Deg:hi:lpqQrRs:t:TUvVw~")) != -1) { - switch (c) { - case 'l': - O.edit = EDIT_LIST; - break; - - case 'w': - O.edit = EDIT_WRITE; - break; - - case 'a': - O.edit = EDIT_APPEND; - break; - - case 'h': - usage(); - break; - - case 'g': - case 's': - gainfmt = c; - O.gain_fix = true; - { - char *endp; - gv = strtod(optarg, &endp); - if (optarg == endp) { - fail_to_parse(c); - } - if (!isfinite(gv)) { - out_of_range(c); - } - if (gv > 65536 || gv < -65536) { - out_of_range(c); - } - } - break; - - case 'r': - O.gain_relative = true; - break; - - case 'Q': - O.gain_q78 = true; - break; - - case '1': - O.gain_not_zero = true; - break; - - case 'p': - O.tag_ignore_picture = true; - break; - - case 'R': - O.tag_raw = true; - break; - - case '~': - O.tag_escape_tilde = true; - break; - - case 'e': - O.tag_escape = TAG_ESCAPE_BACKSLASH; - break; - - case '0': - O.tag_escape = TAG_ESCAPE_NUL; - break; - - case 'U': - O.tag_toupper = true; - break; - - case 'v': - O.gain_put = true; - break; - - case 'c': - O.tag_filename = optarg; - break; - - case 't': - parse_opt_tag(c, optarg); - added_tag = true; - break; - - case 'd': - parse_opt_tag(c, optarg); - del_tag = true; - break; - - case 'D': - O.tag_deferred = true; - O.tag_verify = true; - O.tag_check_line_term = true; - break; - case 'T': - O.tag_check_line_term = true; - break; - case 'V': - O.tag_verify = true; - break; - case 'i': - { - char *endp; - unsigned long val = strtoul(optarg, &endp, 10); - if (endp == optarg || *endp != '\0') { - fail_to_parse(c); - } - if (errno == ERANGE || val == 0 || val > INT_MAX) { - out_of_range(c); - } - O.target_idx = (int)val; - } - break; - - case 'q': - // 何もしない - break; - - case 'C': - if (!select_codec_by_name(optarg)) { - opterror(c, catgets(catd, 7, 4, "unknown codec \"%s\""), optarg); - } - break; - - case '?': - exit(1); - break; - } - } - // オプションループ抜け - if (del_tag || added_tag) { - pticonv_close(); - } - if (del_tag) { - switch (O.edit) { - case EDIT_NONE: - O.edit = EDIT_APPEND; - break; - case EDIT_APPEND: - break; - default: - mainerror(err_main_del_without_a); - } - } - if (added_tag && O.edit == EDIT_LIST) { - mainerror(err_main_tag_with_l); - } - if (added_tag && !O.edit) { - mainerror(err_main_tag_without_aw); - } - if (!O.gain_fix && !O.edit) { - O.edit = EDIT_LIST; - } - else if (O.gain_fix) { - switch (gainfmt) { - case 'g': - if (O.gain_q78) { - O.gain_val = (int)trunc(gv); - } - else { - O.gain_val = (int)trunc(gv * 256); - } - break; - case 's': - if (gv <= 0) { - out_of_range(gainfmt); - } - O.gain_val = (int)trunc(20 * log10(gv) * 256); - break; - } - O.gain_val_sign = signbit(gv); - if (!O.gain_relative && (O.gain_val > 32767 || O.gain_val < -32768)) { - out_of_range(gainfmt); - } - - if (O.edit == EDIT_LIST) { - mainerror(err_main_gain_with_l); - } - else if (!O.edit) { - if (O.tag_filename/* || added_tag*/) { - mainerror(err_main_tag_without_aw); - } - } - } - if (O.edit == EDIT_APPEND) { - O.tag_ignore_picture = false; - } - if (codec->prog) { - O.gain_fix = false; - O.gain_put = false; - } + int c; + bool added_tag = false, del_tag = false; + int gainfmt; + double gv; + + while ((c = getopt(argc, argv, "01ac:C:d:Deg:hi:lpqQrRs:t:TUvVw~")) != -1) { + switch (c) { + case 'l': + O.edit = EDIT_LIST; + break; + + case 'w': + O.edit = EDIT_WRITE; + break; + + case 'a': + O.edit = EDIT_APPEND; + break; + + case 'h': + usage(); + break; + + case 'g': + case 's': + gainfmt = c; + O.gain_fix = true; + { + char *endp; + gv = strtod(optarg, &endp); + if (optarg == endp) { + fail_to_parse(c); + } + if (!isfinite(gv)) { + out_of_range(c); + } + if (gv > 65536 || gv < -65536) { + out_of_range(c); + } + } + break; + + case 'r': + O.gain_relative = true; + break; + + case 'Q': + O.gain_q78 = true; + break; + + case '1': + O.gain_not_zero = true; + break; + + case 'p': + O.tag_ignore_picture = true; + break; + + case 'R': + O.tag_raw = true; + break; + + case '~': + O.tag_escape_tilde = true; + break; + + case 'e': + O.tag_escape = TAG_ESCAPE_BACKSLASH; + break; + + case '0': + O.tag_escape = TAG_ESCAPE_NUL; + break; + + case 'U': + O.tag_toupper = true; + break; + + case 'v': + O.gain_put = true; + break; + + case 'c': + O.tag_filename = optarg; + break; + + case 't': + parse_opt_tag(c, optarg); + added_tag = true; + break; + + case 'd': + parse_opt_tag(c, optarg); + del_tag = true; + break; + + case 'D': + O.tag_deferred = true; + O.tag_verify = true; + O.tag_check_line_term = true; + break; + case 'T': + O.tag_check_line_term = true; + break; + case 'V': + O.tag_verify = true; + break; + case 'i': + { + char *endp; + unsigned long val = strtoul(optarg, &endp, 10); + if (endp == optarg || *endp != '\0') { + fail_to_parse(c); + } + if (errno == ERANGE || val == 0 || val > INT_MAX) { + out_of_range(c); + } + O.target_idx = (int)val; + } + break; + + case 'q': + // 何もしない + break; + + case 'C': + if (!select_codec_by_name(optarg)) { + opterror(c, catgets(catd, 7, 4, "unknown codec \"%s\""), optarg); + } + break; + + case '?': + exit(1); + break; + } + } + // オプションループ抜け + if (del_tag || added_tag) { + pticonv_close(); + } + if (del_tag) { + switch (O.edit) { + case EDIT_NONE: + O.edit = EDIT_APPEND; + break; + case EDIT_APPEND: + break; + default: + mainerror(err_main_del_without_a); + } + } + if (added_tag && O.edit == EDIT_LIST) { + mainerror(err_main_tag_with_l); + } + if (added_tag && !O.edit) { + mainerror(err_main_tag_without_aw); + } + if (!O.gain_fix && !O.edit) { + O.edit = EDIT_LIST; + } + else if (O.gain_fix) { + switch (gainfmt) { + case 'g': + if (O.gain_q78) { + O.gain_val = (int)trunc(gv); + } + else { + O.gain_val = (int)trunc(gv * 256); + } + break; + case 's': + if (gv <= 0) { + out_of_range(gainfmt); + } + O.gain_val = (int)trunc(20 * log10(gv) * 256); + break; + } + O.gain_val_sign = signbit(gv); + if (!O.gain_relative && (O.gain_val > 32767 || O.gain_val < -32768)) { + out_of_range(gainfmt); + } + + if (O.edit == EDIT_LIST) { + mainerror(err_main_gain_with_l); + } + else if (!O.edit) { + if (O.tag_filename/* || added_tag*/) { + mainerror(err_main_tag_without_aw); + } + } + } + if (O.edit == EDIT_APPEND) { + O.tag_ignore_picture = false; + } + if (codec->prog) { + O.gain_fix = false; + O.gain_put = false; + } } static void interrupted(int sig) { - exit(1); + exit(1); } static void read_ogg(void) { - ogg_sync_state oy; - ogg_sync_init(&oy); - - size_t buflen = 1 << 17; - uint8_t *buf = ogg_sync_buffer(&oy, buflen); - size_t len = fread(buf, 1, buflen, stream_input); - if (len == (size_t)-1) oserror(); - if (len < 4) { - opuserror(err_opus_non_ogg); - } - if (memcmp(buf, "\x4f\x67\x67\x53", 4) != 0) { - opuserror(err_opus_non_ogg); - } - ogg_sync_wrote(&oy, len); - read_page(&oy); - - for (;;) { - buf = ogg_sync_buffer(&oy, buflen); - len = fread(buf, 1, buflen, stream_input); - if (!len) { - break; - } - ogg_sync_wrote(&oy, len); - read_page(&oy); - } + ogg_sync_state oy; + ogg_sync_init(&oy); + + size_t buflen = 1 << 17; + uint8_t *buf = ogg_sync_buffer(&oy, buflen); + size_t len = fread(buf, 1, buflen, stream_input); + if (len == (size_t)-1) oserror(); + if (len < 4) { + opuserror(err_opus_non_ogg); + } + if (memcmp(buf, "\x4f\x67\x67\x53", 4) != 0) { + opuserror(err_opus_non_ogg); + } + ogg_sync_wrote(&oy, len); + read_page(&oy); + + for (;;) { + buf = ogg_sync_buffer(&oy, buflen); + len = fread(buf, 1, buflen, stream_input); + if (!len) { + break; + } + ogg_sync_wrote(&oy, len); + read_page(&oy); + } } int main(int argc, char **argv) { - struct sigaction sa; - sa.sa_handler = interrupted; - sigemptyset(&sa.sa_mask); - sigaddset(&sa.sa_mask, SIGINT); - sigaddset(&sa.sa_mask, SIGQUIT); - sigaddset(&sa.sa_mask, SIGTERM); - sigaddset(&sa.sa_mask, SIGPIPE); - sa.sa_flags = 0; - sigaction(SIGINT, &sa, NULL); - sigaction(SIGQUIT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - - setlocale(LC_ALL, ""); - if (*argv[0]) { - char *p = strrchr(argv[0], '/'); - program_name = p ? p + 1 : argv[0]; - } - else { - program_name = program_name_default; - } - select_codec(); - default_codec = codec; - + struct sigaction sa; + sa.sa_handler = interrupted; + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGINT); + sigaddset(&sa.sa_mask, SIGQUIT); + sigaddset(&sa.sa_mask, SIGTERM); + sigaddset(&sa.sa_mask, SIGPIPE); + sa.sa_flags = 0; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + setlocale(LC_ALL, ""); + if (*argv[0]) { + char *p = strrchr(argv[0], '/'); + program_name = p ? p + 1 : argv[0]; + } + else { + program_name = program_name_default; + } + select_codec(); + default_codec = codec; + #ifdef NLS #define co catd = catopen("opuscomment", NL_CAT_LOCALE) #ifdef DEFAULT_NLS_PATH - char const *nlspath = getenv("NLSPATH"); - if (nlspath && *nlspath) { - co; - } - else { - setenv("NLSPATH", DEFAULT_NLS_PATH, true); - co; - if (catd == (nl_catd)-1) { - unsetenv("NLSPATH"); - co; - } - } + char const *nlspath = getenv("NLSPATH"); + if (nlspath && *nlspath) { + co; + } + else { + setenv("NLSPATH", DEFAULT_NLS_PATH, true); + co; + if (catd == (nl_catd)-1) { + unsetenv("NLSPATH"); + co; + } + } #else - co; + co; #endif #undef co #endif - if (argc == 1) usage(); - - parse_args(argc, argv); - if (!argv[optind]) { - mainerror(err_main_no_file); - } - if (argv[optind + 1]) { - if (O.edit == EDIT_LIST) { - mainerror(err_main_many_args); - } - if (argv[optind + 2]) { - mainerror(err_main_many_args); - } - O.out = argv[optind + 1]; - } - - O.in = argv[optind]; - if (strcmp(O.in, "-")) { - stream_input = fopen(O.in, "r"); - if (!stream_input) { - fileerror(O.in); - } - struct stat sb; - fstat(fileno(stream_input), &sb); - input_is_regular_file = S_ISREG(sb.st_mode); - } - else { - stream_input = stdin; - } - - open_output_file(); - if (codec->type == CODEC_FLAC) read_flac(); - else read_ogg(); - - if (opst < PAGE_SOUND) { - opuserror(err_opus_interrupted); - } - - move_file(); - return 0; + if (argc == 1) usage(); + + parse_args(argc, argv); + if (!argv[optind]) { + mainerror(err_main_no_file); + } + if (argv[optind + 1]) { + if (O.edit == EDIT_LIST) { + mainerror(err_main_many_args); + } + if (argv[optind + 2]) { + mainerror(err_main_many_args); + } + O.out = argv[optind + 1]; + } + + O.in = argv[optind]; + if (strcmp(O.in, "-")) { + stream_input = fopen(O.in, "r"); + if (!stream_input) { + fileerror(O.in); + } + struct stat sb; + fstat(fileno(stream_input), &sb); + input_is_regular_file = S_ISREG(sb.st_mode); + } + else { + stream_input = stdin; + } + + open_output_file(); + if (codec->type == CODEC_FLAC) read_flac(); + else read_ogg(); + + if (opst < PAGE_SOUND) { + opuserror(err_opus_interrupted); + } + + move_file(); + return 0; } diff --git a/src/opuscomment.h b/src/opuscomment.h index c3e7d8f..e6f6658 100644 --- a/src/opuscomment.h +++ b/src/opuscomment.h @@ -23,14 +23,14 @@ #include "iconv-impl.h" struct rettag_st { - FILE *tag, *padding; - long tagbegin; - size_t num, del; - bool upcase, part_of_comment; + FILE *tag, *padding; + long tagbegin; + size_t num, del; + bool upcase, part_of_comment; }; struct edit_st { - FILE *str, *len, *pict; - size_t num; + FILE *str, *len, *pict; + size_t num; }; // parse-tags.c diff --git a/src/parse-tags.c b/src/parse-tags.c index 1cb6698..8572617 100644 --- a/src/parse-tags.c +++ b/src/parse-tags.c @@ -16,102 +16,102 @@ static bool from_file; static FILE *edit_input; static void readerror(void) { - if (from_file) { - fileerror(O.tag_filename); - } - else { - oserror(); - } + if (from_file) { + fileerror(O.tag_filename); + } + else { + oserror(); + } } static size_t tagnum; static noreturn void tagerror(char *e) { - errorprefix(); - fprintf(stderr, catgets(catd, 1, 6, "editing input #%zu: "), tagnum + 1); - fputs(e, stderr); - fputc('\n', stderr); - exit(1); + errorprefix(); + fprintf(stderr, catgets(catd, 1, 6, "editing input #%zu: "), tagnum + 1); + fputs(e, stderr); + fputc('\n', stderr); + exit(1); } static void err_nosep(void) { - tagerror(catgets(catd, 5, 1, "no field separator")); + tagerror(catgets(catd, 5, 1, "no field separator")); } static void err_name(void) { - tagerror(catgets(catd, 5, 2, "invalid field name")); + tagerror(catgets(catd, 5, 2, "invalid field name")); } static void err_empty(void) { - tagerror(catgets(catd, 5, 3, "empty field name")); + tagerror(catgets(catd, 5, 3, "empty field name")); } static void err_bin(void) { - tagerror(catgets(catd, 5, 4, "binary file")); + tagerror(catgets(catd, 5, 4, "binary file")); } static void err_esc(void) { - tagerror(catgets(catd, 5, 5, "invalid escape sequence")); + tagerror(catgets(catd, 5, 5, "invalid escape sequence")); } static void err_utf8(void) { - tagerror(catgets(catd, 5, 6, "invalid UTF-8 sequence")); + tagerror(catgets(catd, 5, 6, "invalid UTF-8 sequence")); } static void err_base64(void) { - tagerror(catgets(catd, 5, 7, "invalid BASE64 sequence")); + tagerror(catgets(catd, 5, 7, "invalid BASE64 sequence")); } static void err_noterm(void) { - mainerror(err_main_no_term); + mainerror(err_main_no_term); } static void toutf8(int fdu8) { - size_t const buflen = STACK_BUF_LEN / 2; - char ubuf[STACK_BUF_LEN]; - char *lbuf = ubuf + buflen; - - iconv_t cd = iconv_new("UTF-8", O.tag_raw ? "UTF-8" : nl_langinfo(CODESET)); - size_t readlen, remain, total; - remain = 0; total = 0; - while ((readlen = fread(&lbuf[remain], 1, buflen - remain, edit_input)) != 0) { - total += readlen; - if (total > TAG_LENGTH_LIMIT__INPUT) { - mainerror(err_main_long_input); - } - if (O.tag_escape != TAG_ESCAPE_NUL && strnlen(&lbuf[remain], readlen) != readlen) { - err_bin(); - } - size_t llen = readlen + remain; - remain = llen; - for (;;) { - size_t ulen = buflen; - char *lp = lbuf, *up = ubuf; - size_t iconvret = iconv(cd, &lp, &remain, &up, &ulen); - int ie = errno; - if (iconvret == (size_t)-1) { - switch (ie) { - case EILSEQ: - oserror(); - break; - case EINVAL: - case E2BIG: - break; - } - memcpy(lbuf, lp, remain); - } - write(fdu8, ubuf, up - ubuf); - if (iconvret != (size_t)-1 || ie == EINVAL) break; - } - } - if (fclose(edit_input) == EOF) { - readerror(); - } - if (remain) { - errno = EILSEQ; - readerror(); - } - char *up = ubuf; readlen = buflen; - remain = iconv(cd, NULL, NULL, &up, &readlen); - if (remain == (size_t)-1) oserror(); - iconv_close(cd); - write(fdu8, ubuf, up - ubuf); - close(fdu8); - if (O.edit == EDIT_WRITE && !total && O.tag_deferred) { - mainerror(err_main_no_input); - } + size_t const buflen = STACK_BUF_LEN / 2; + char ubuf[STACK_BUF_LEN]; + char *lbuf = ubuf + buflen; + + iconv_t cd = iconv_new("UTF-8", O.tag_raw ? "UTF-8" : nl_langinfo(CODESET)); + size_t readlen, remain, total; + remain = 0; total = 0; + while ((readlen = fread(&lbuf[remain], 1, buflen - remain, edit_input)) != 0) { + total += readlen; + if (total > TAG_LENGTH_LIMIT__INPUT) { + mainerror(err_main_long_input); + } + if (O.tag_escape != TAG_ESCAPE_NUL && strnlen(&lbuf[remain], readlen) != readlen) { + err_bin(); + } + size_t llen = readlen + remain; + remain = llen; + for (;;) { + size_t ulen = buflen; + char *lp = lbuf, *up = ubuf; + size_t iconvret = iconv(cd, &lp, &remain, &up, &ulen); + int ie = errno; + if (iconvret == (size_t)-1) { + switch (ie) { + case EILSEQ: + oserror(); + break; + case EINVAL: + case E2BIG: + break; + } + memcpy(lbuf, lp, remain); + } + write(fdu8, ubuf, up - ubuf); + if (iconvret != (size_t)-1 || ie == EINVAL) break; + } + } + if (fclose(edit_input) == EOF) { + readerror(); + } + if (remain) { + errno = EILSEQ; + readerror(); + } + char *up = ubuf; readlen = buflen; + remain = iconv(cd, NULL, NULL, &up, &readlen); + if (remain == (size_t)-1) oserror(); + iconv_close(cd); + write(fdu8, ubuf, up - ubuf); + close(fdu8); + if (O.edit == EDIT_WRITE && !total && O.tag_deferred) { + mainerror(err_main_no_input); + } } static FILE *strstore, *strcount, *flacpict; @@ -124,495 +124,495 @@ static uint8_t b64[4]; static int_fast8_t b64pos, b64rawlen; static bool b64term, store_picture; static void finalize_record(void) { - if (store_picture) { - if (b64pos != 0) err_base64(); - long picend = ftell(flacpict); - uint32_t piclength = picend - picture_top - 4; - piclength = ntohl(0x06000000 | piclength); - fseek(flacpict, picture_top, SEEK_SET); - fwrite(&piclength, 4, 1, flacpict); - fseek(flacpict, 0, SEEK_END); - picture_top = picend; - } - else { - fwrite(&recordlen, 4, 1, strcount); - tagnum++; - } - first_call = true; + if (store_picture) { + if (b64pos != 0) err_base64(); + long picend = ftell(flacpict); + uint32_t piclength = picend - picture_top - 4; + piclength = ntohl(0x06000000 | piclength); + fseek(flacpict, picture_top, SEEK_SET); + fwrite(&piclength, 4, 1, flacpict); + fseek(flacpict, 0, SEEK_END); + picture_top = picend; + } + else { + fwrite(&recordlen, 4, 1, strcount); + tagnum++; + } + first_call = true; } static size_t wsplen, fieldlen; static bool on_field, keep_blank; static uint8_t field_pending[22]; static bool test_blank(uint8_t *line, size_t n, bool lf) { - if (first_call) { - // = で始まってたらすぐエラー(575) - if (*line == 0x3d) err_empty(); - first_call = false; - on_field = true; - fieldlen = 0; - keep_blank = true; - wsplen = 0; - recordlen = 0; - store_picture = false; - b64term = false; - b64pos = 0; - } - if (keep_blank) { - size_t i; - bool blank_with_ctrl = false; - for (i = 0; i < n; i++) { - switch (line[i]) { - case 0x9: - case 0xa: - case 0xd: - blank_with_ctrl = true; - case 0x20: - break; - default: - keep_blank = false; - } - if (!keep_blank) break; - } - if (keep_blank) { - // まだ先頭から空白が続いている時 - if (lf) { - // 行が終わったが全て空白だったので飛ばして次の行へ - first_call = true; - } - else { - // 行が続いている - wsplen += n; - } - return true; - } - // 空白状態を抜けてフィールド判別状態にある - if (blank_with_ctrl) { - //空白がwsp以外を含んでいたらエラー - err_name(); - } - editlen += 4 + wsplen; - if (editlen > TAG_LENGTH_LIMIT__OUTPUT) { - exceed_output_limit(); - } - fieldlen = recordlen = wsplen; - if (wsplen <= 22) { - memset(field_pending, 0x20, wsplen); - } - else { - // 空白と見做していた分を書き込み - uint8_t buf[STACK_BUF_LEN]; - memset(buf, 0x20, STACK_BUF_LEN); - while (wsplen) { - size_t wlen = wsplen > STACK_BUF_LEN ? STACK_BUF_LEN : wsplen; - fwrite(buf, 1, wlen, strstore); - wsplen -= wlen; - } - } - } - return false; + if (first_call) { + // = で始まってたらすぐエラー(575) + if (*line == 0x3d) err_empty(); + first_call = false; + on_field = true; + fieldlen = 0; + keep_blank = true; + wsplen = 0; + recordlen = 0; + store_picture = false; + b64term = false; + b64pos = 0; + } + if (keep_blank) { + size_t i; + bool blank_with_ctrl = false; + for (i = 0; i < n; i++) { + switch (line[i]) { + case 0x9: + case 0xa: + case 0xd: + blank_with_ctrl = true; + case 0x20: + break; + default: + keep_blank = false; + } + if (!keep_blank) break; + } + if (keep_blank) { + // まだ先頭から空白が続いている時 + if (lf) { + // 行が終わったが全て空白だったので飛ばして次の行へ + first_call = true; + } + else { + // 行が続いている + wsplen += n; + } + return true; + } + // 空白状態を抜けてフィールド判別状態にある + if (blank_with_ctrl) { + //空白がwsp以外を含んでいたらエラー + err_name(); + } + editlen += 4 + wsplen; + if (editlen > TAG_LENGTH_LIMIT__OUTPUT) { + exceed_output_limit(); + } + fieldlen = recordlen = wsplen; + if (wsplen <= 22) { + memset(field_pending, 0x20, wsplen); + } + else { + // 空白と見做していた分を書き込み + uint8_t buf[STACK_BUF_LEN]; + memset(buf, 0x20, STACK_BUF_LEN); + while (wsplen) { + size_t wlen = wsplen > STACK_BUF_LEN ? STACK_BUF_LEN : wsplen; + fwrite(buf, 1, wlen, strstore); + wsplen -= wlen; + } + } + } + return false; } static void decode_base64(uint8_t *line, size_t n) { - uint8_t const *end = line + n; - while (line < end) { - uint8_t const *b64char = strchr(b64tab_ascii, *line++); - if (!b64char) err_base64(); - uint_fast8_t b64idx = b64char - b64tab_ascii; - if (b64term && b64pos == 0) err_base64(); - if (b64idx == 64 && b64pos < 2) err_base64(); - if (b64pos == 2 && b64idx == 64) { - b64term = true; - b64idx = 0; - b64rawlen = 1; - } - if (b64pos == 3) { - if (b64term) { - if (b64idx != 64) err_base64(); - else b64idx = 0; - } - else if (b64idx == 64) { - b64idx = 0; - b64term = true; - b64rawlen = 2; - } - else b64rawlen = 3; - } - b64[b64pos++] = b64idx; - if (b64pos == 4) { - uint8_t raw[3]; - // 000000 11|1111 2222|22 333333 - raw[0] = b64[0] << 2 | b64[1] >> 4; - raw[1] = b64[1] << 4 | b64[2] >> 2; - raw[2] = b64[2] << 6 | b64[3]; - fwrite(raw, 1, b64rawlen, flacpict); - b64pos = 0; - } - } + uint8_t const *end = line + n; + while (line < end) { + uint8_t const *b64char = strchr(b64tab_ascii, *line++); + if (!b64char) err_base64(); + uint_fast8_t b64idx = b64char - b64tab_ascii; + if (b64term && b64pos == 0) err_base64(); + if (b64idx == 64 && b64pos < 2) err_base64(); + if (b64pos == 2 && b64idx == 64) { + b64term = true; + b64idx = 0; + b64rawlen = 1; + } + if (b64pos == 3) { + if (b64term) { + if (b64idx != 64) err_base64(); + else b64idx = 0; + } + else if (b64idx == 64) { + b64idx = 0; + b64term = true; + b64rawlen = 2; + } + else b64rawlen = 3; + } + b64[b64pos++] = b64idx; + if (b64pos == 4) { + uint8_t raw[3]; + // 000000 11|1111 2222|22 333333 + raw[0] = b64[0] << 2 | b64[1] >> 4; + raw[1] = b64[1] << 4 | b64[2] >> 2; + raw[2] = b64[2] << 6 | b64[3]; + fwrite(raw, 1, b64rawlen, flacpict); + b64pos = 0; + } + } } static void append_buffer(uint8_t *line, size_t n) { - if (store_picture) { - decode_base64(line, n); - return; - } - editlen += n; - if (editlen > TAG_LENGTH_LIMIT__OUTPUT) { - exceed_output_limit(); - } - recordlen += n; - fwrite(line, 1, n, strstore); + if (store_picture) { + decode_base64(line, n); + return; + } + editlen += n; + if (editlen > TAG_LENGTH_LIMIT__OUTPUT) { + exceed_output_limit(); + } + recordlen += n; + fwrite(line, 1, n, strstore); } static bool count_field_len(uint8_t *line, size_t n) { - size_t add; - bool filled; - if (on_field) { - add = n - fieldlen; - } - else { - add = (uint8_t*)memchr(line, 0x3d, n) - line; - } - if (fieldlen + add <= 22) { - memcpy(&field_pending[fieldlen], line, add); - filled = !on_field; - } - else { - memcpy(&field_pending[fieldlen], line, 22 - fieldlen); - filled = true; - } - fieldlen += add; - return filled; + size_t add; + bool filled; + if (on_field) { + add = n - fieldlen; + } + else { + add = (uint8_t*)memchr(line, 0x3d, n) - line; + } + if (fieldlen + add <= 22) { + memcpy(&field_pending[fieldlen], line, add); + filled = !on_field; + } + else { + memcpy(&field_pending[fieldlen], line, 22 - fieldlen); + filled = true; + } + fieldlen += add; + return filled; } static void test_mbp(uint8_t **line, size_t *n) { - if (fieldlen > 22) { - append_buffer(*line, *n); - } - else { - bool filled; - size_t before = fieldlen; - filled = count_field_len(*line, *n); - size_t add = fieldlen - before; - if (filled) { - size_t w; - // M_B_Pを比較する分のバッファが埋まったか項目名が決まった場合 - if (fieldlen > 22) { - if (add + before > 22) { - add = 22 - before; - } - w = 22; - } - else if (fieldlen == 22 && !on_field) { - if (codec->type == CODEC_FLAC && - memcmp(field_pending, MBPeq, 22) == 0) { - store_picture = true; - fwrite((uint32_t[]){0}, 4, 1, flacpict); - editlen -= 4; - uint8_t *databegin = strchr(*line, 0x3d) + 1; - append_buffer(databegin, *n - (databegin - *line)); - return; - } - else { - w = 22; - } - } - else { - w = fieldlen; - } - append_buffer(field_pending, w); - *line += add; - *n -= add; - append_buffer(*line, *n); - } - } + if (fieldlen > 22) { + append_buffer(*line, *n); + } + else { + bool filled; + size_t before = fieldlen; + filled = count_field_len(*line, *n); + size_t add = fieldlen - before; + if (filled) { + size_t w; + // M_B_Pを比較する分のバッファが埋まったか項目名が決まった場合 + if (fieldlen > 22) { + if (add + before > 22) { + add = 22 - before; + } + w = 22; + } + else if (fieldlen == 22 && !on_field) { + if (codec->type == CODEC_FLAC && + memcmp(field_pending, MBPeq, 22) == 0) { + store_picture = true; + fwrite((uint32_t[]){0}, 4, 1, flacpict); + editlen -= 4; + uint8_t *databegin = strchr(*line, 0x3d) + 1; + append_buffer(databegin, *n - (databegin - *line)); + return; + } + else { + w = 22; + } + } + else { + w = fieldlen; + } + append_buffer(field_pending, w); + *line += add; + *n -= add; + append_buffer(*line, *n); + } + } } static void line_oc(uint8_t *line, size_t n, bool lf) { - static bool afterlf = false; - - if (!line) { - if (!first_call) { - if (O.tag_check_line_term && !afterlf) { - err_noterm(); - } - if (!keep_blank) { - if (on_field) err_nosep(); - finalize_record(); - } - first_call = true; - afterlf = false; - } - return; - } - - if (afterlf) { - if (*line == 0x9 || *line == 0x7e) { - *line = 0x0a; - if (lf) n--; - append_buffer(line, n); - afterlf = lf; - return; - } - else { - finalize_record(); - } - } - if (test_blank(line, n, lf)) return; - - if (lf) n--; - if (on_field) { - if(!test_tag_field(line, n, true, &on_field, NULL)) err_name(); - if (on_field && lf) err_name(); - test_mbp(&line, &n); - } - else { - append_buffer(line, n); - } - afterlf = lf; + static bool afterlf = false; + + if (!line) { + if (!first_call) { + if (O.tag_check_line_term && !afterlf) { + err_noterm(); + } + if (!keep_blank) { + if (on_field) err_nosep(); + finalize_record(); + } + first_call = true; + afterlf = false; + } + return; + } + + if (afterlf) { + if (*line == 0x9 || *line == 0x7e) { + *line = 0x0a; + if (lf) n--; + append_buffer(line, n); + afterlf = lf; + return; + } + else { + finalize_record(); + } + } + if (test_blank(line, n, lf)) return; + + if (lf) n--; + if (on_field) { + if(!test_tag_field(line, n, true, &on_field, NULL)) err_name(); + if (on_field && lf) err_name(); + test_mbp(&line, &n); + } + else { + append_buffer(line, n); + } + afterlf = lf; } static int esctab(int c) { - switch (c) { - case 0x30: // '0' - c = '\0'; - break; - case 0x5c: // '\' - c = 0x5c; - break; - case 0x6e: // 'n' - c = 0x0a; - break; - case 0x72: // 'r' - c = 0x0d; - break; - default: - err_esc(); - break; - } - return c; + switch (c) { + case 0x30: // '0' + c = '\0'; + break; + case 0x5c: // '\' + c = 0x5c; + break; + case 0x6e: // 'n' + c = 0x0a; + break; + case 0x72: // 'r' + c = 0x0d; + break; + default: + err_esc(); + break; + } + return c; } static void line_vc(uint8_t *line, size_t n, bool lf) { - static bool escape_pending = false; - - if (!line) { - if (!first_call) { - if (O.tag_check_line_term) { - err_noterm(); - } - if (!keep_blank) { - if (escape_pending) err_esc(); - if (on_field) err_nosep(); - finalize_record(); - } - first_call = true; - } - return; - } - - if(test_blank(line, n, lf)) return; - - if (escape_pending) { - *line = esctab(*line); - } - - if (lf) n--; - uint8_t *bs = memchr(line + escape_pending, 0x5c, n - escape_pending); - escape_pending = false; - if (bs) { - uint8_t *unesc = bs, *end = line + n; - while (bs < end) { - uint8_t c = *bs++; - if (c == 0x5c) { - if (bs == end) { - if (lf) err_esc(); - escape_pending = true; - n--; - break; - } - c = esctab(*bs++); - n--; - } - *unesc++ = c; - } - } - if (on_field) { - if(!test_tag_field(line, n, true, &on_field, NULL)) err_name(); - if (on_field && lf) err_nosep(); - test_mbp(&line, &n); - } - else { - append_buffer(line, n); - } - if (lf) { - finalize_record(); - } + static bool escape_pending = false; + + if (!line) { + if (!first_call) { + if (O.tag_check_line_term) { + err_noterm(); + } + if (!keep_blank) { + if (escape_pending) err_esc(); + if (on_field) err_nosep(); + finalize_record(); + } + first_call = true; + } + return; + } + + if(test_blank(line, n, lf)) return; + + if (escape_pending) { + *line = esctab(*line); + } + + if (lf) n--; + uint8_t *bs = memchr(line + escape_pending, 0x5c, n - escape_pending); + escape_pending = false; + if (bs) { + uint8_t *unesc = bs, *end = line + n; + while (bs < end) { + uint8_t c = *bs++; + if (c == 0x5c) { + if (bs == end) { + if (lf) err_esc(); + escape_pending = true; + n--; + break; + } + c = esctab(*bs++); + n--; + } + *unesc++ = c; + } + } + if (on_field) { + if(!test_tag_field(line, n, true, &on_field, NULL)) err_name(); + if (on_field && lf) err_nosep(); + test_mbp(&line, &n); + } + else { + append_buffer(line, n); + } + if (lf) { + finalize_record(); + } } static void line_nul(uint8_t *line, size_t n, bool term) { - if (!line) { - if (!first_call) { - if (!keep_blank) { - if (on_field) err_nosep(); - finalize_record(); - } - first_call = true; - } - return; - } - - if(test_blank(line, n, term)) return; - - if (on_field) { - if(!test_tag_field(line, n, true, &on_field, NULL)) err_name(); - if (on_field && term) err_nosep(); - test_mbp(&line, &n); - } - else { - append_buffer(line, n); - } - - if (term) finalize_record(); + if (!line) { + if (!first_call) { + if (!keep_blank) { + if (on_field) err_nosep(); + finalize_record(); + } + first_call = true; + } + return; + } + + if(test_blank(line, n, term)) return; + + if (on_field) { + if(!test_tag_field(line, n, true, &on_field, NULL)) err_name(); + if (on_field && term) err_nosep(); + test_mbp(&line, &n); + } + else { + append_buffer(line, n); + } + + if (term) finalize_record(); } void prepare_record(void) { - strstore = strstore ? strstore : tmpfile(); - strcount = strcount ? strcount : tmpfile(); - flacpict = flacpict ? flacpict : codec->type == CODEC_FLAC ? tmpfile() : NULL; + strstore = strstore ? strstore : tmpfile(); + strcount = strcount ? strcount : tmpfile(); + flacpict = flacpict ? flacpict : codec->type == CODEC_FLAC ? tmpfile() : NULL; } void *split_text(void *fp_) { - FILE *fp = fp_; - prepare_record(); - void (*line)(uint8_t *, size_t, bool) = O.tag_escape ? line_vc : line_oc; - - uint8_t tagbuf[STACK_BUF_LEN]; - size_t readlen; - while ((readlen = fread(tagbuf, 1, STACK_BUF_LEN, fp)) != 0) { - uint8_t *p1, *p2; - if (memchr(tagbuf, 0, readlen) != NULL) { - err_bin(); - } - p1 = tagbuf; - while ((p2 = memchr(p1, 0x0a, readlen - (p1 - tagbuf))) != NULL) { - p2++; - line(p1, p2 - p1, true); - p1 = p2; - } - size_t left = readlen - (p1 - tagbuf); - if (left) line(p1, left, false); - } - fclose(fp); - line(NULL, 0, false); - return NULL; + FILE *fp = fp_; + prepare_record(); + void (*line)(uint8_t *, size_t, bool) = O.tag_escape ? line_vc : line_oc; + + uint8_t tagbuf[STACK_BUF_LEN]; + size_t readlen; + while ((readlen = fread(tagbuf, 1, STACK_BUF_LEN, fp)) != 0) { + uint8_t *p1, *p2; + if (memchr(tagbuf, 0, readlen) != NULL) { + err_bin(); + } + p1 = tagbuf; + while ((p2 = memchr(p1, 0x0a, readlen - (p1 - tagbuf))) != NULL) { + p2++; + line(p1, p2 - p1, true); + p1 = p2; + } + size_t left = readlen - (p1 - tagbuf); + if (left) line(p1, left, false); + } + fclose(fp); + line(NULL, 0, false); + return NULL; } void *split_binary(void *fp_) { - FILE *fp = fp_; - prepare_record(); - - uint8_t tagbuf[STACK_BUF_LEN]; - size_t readlen; - while ((readlen = fread(tagbuf, 1, STACK_BUF_LEN, fp)) != 0) { - uint8_t *p1, *p2; - p1 = tagbuf; - while ((p2 = memchr(p1, 0, readlen - (p1 - tagbuf))) != NULL) { - line_nul(p1, p2 - p1, true); - p1 = p2 + 1; - } - size_t left = readlen - (p1 - tagbuf); - if (left) line_nul(p1, left, false); - } - fclose(fp); - line_nul(NULL, 0, false); - return NULL; + FILE *fp = fp_; + prepare_record(); + + uint8_t tagbuf[STACK_BUF_LEN]; + size_t readlen; + while ((readlen = fread(tagbuf, 1, STACK_BUF_LEN, fp)) != 0) { + uint8_t *p1, *p2; + p1 = tagbuf; + while ((p2 = memchr(p1, 0, readlen - (p1 - tagbuf))) != NULL) { + line_nul(p1, p2 - p1, true); + p1 = p2 + 1; + } + size_t left = readlen - (p1 - tagbuf); + if (left) line_nul(p1, left, false); + } + fclose(fp); + line_nul(NULL, 0, false); + return NULL; } void *parse_tags(void* nouse_) { - bool do_read = true; - if (tagnum) { - // -tでタグを追加した場合、-cが無ければstdinを使わない - if (!O.tag_filename) { - do_read = false; - } - } - if (O.tag_filename && strcmp(O.tag_filename, "-") != 0) { - from_file = true; - edit_input = fopen(O.tag_filename, "r"); - if (!edit_input) { - fileerror(O.tag_filename); - } - } - else { - edit_input = stdin; - } - - if (do_read) { - // UTF-8化された文字列をチャンク化する処理をスレッド化(化が多い) - int pfd[2]; - pipe(pfd); - FILE *fpu8 = fdopen(pfd[0], "r"); - pthread_t split_thread; - void *(*split)(void*) = O.tag_escape == TAG_ESCAPE_NUL ? split_binary : split_text; - pthread_create(&split_thread, NULL, split, fpu8); - - // 本スレッドはstdinをUTF-8化する - toutf8(pfd[1]); - pthread_join(split_thread, NULL); - } - - struct edit_st *rtn = calloc(1, sizeof(*rtn)); - rtn->str = strstore; - rtn->len = strcount; - rtn->pict = flacpict; - rtn->num = tagnum; - return rtn; + bool do_read = true; + if (tagnum) { + // -tでタグを追加した場合、-cが無ければstdinを使わない + if (!O.tag_filename) { + do_read = false; + } + } + if (O.tag_filename && strcmp(O.tag_filename, "-") != 0) { + from_file = true; + edit_input = fopen(O.tag_filename, "r"); + if (!edit_input) { + fileerror(O.tag_filename); + } + } + else { + edit_input = stdin; + } + + if (do_read) { + // UTF-8化された文字列をチャンク化する処理をスレッド化(化が多い) + int pfd[2]; + pipe(pfd); + FILE *fpu8 = fdopen(pfd[0], "r"); + pthread_t split_thread; + void *(*split)(void*) = O.tag_escape == TAG_ESCAPE_NUL ? split_binary : split_text; + pthread_create(&split_thread, NULL, split, fpu8); + + // 本スレッドはstdinをUTF-8化する + toutf8(pfd[1]); + pthread_join(split_thread, NULL); + } + + struct edit_st *rtn = calloc(1, sizeof(*rtn)); + rtn->str = strstore; + rtn->len = strcount; + rtn->pict = flacpict; + rtn->num = tagnum; + return rtn; } static iconv_t optcd = (iconv_t)-1; void parse_opt_tag(int opt, char const *arg) { - char *ls = (char*)arg; - size_t l = strlen(ls); - - if (optcd == (iconv_t)-1) { - optcd = iconv_new("UTF-8", nl_langinfo(CODESET)); - } - void (*addbuf)(uint8_t *, size_t, bool); - switch (opt) { - case 't': - prepare_record(); - addbuf = line_nul; - break; - case 'd': - addbuf = rt_del_args; - break; - } - char u8buf[STACK_BUF_LEN]; - size_t u8left; - char *u8; - while (l) { - u8left = STACK_BUF_LEN; - u8 = u8buf; - size_t ret = iconv(optcd, &ls, &l, &u8, &u8left); - - if (ret == (size_t)-1) { - // 引数処理なのでEINVAL時のバッファ持ち越しは考慮しない - if (errno != E2BIG) { - oserror(); - } - } - addbuf(u8buf, u8 - u8buf, false); - } - u8 = u8buf; - u8left = STACK_BUF_LEN; - if (iconv(optcd, NULL, NULL, &u8, &u8left) == (size_t)-1) { - oserror(); - } - addbuf(u8buf, u8 - u8buf, true); - addbuf(NULL, 0, false); + char *ls = (char*)arg; + size_t l = strlen(ls); + + if (optcd == (iconv_t)-1) { + optcd = iconv_new("UTF-8", nl_langinfo(CODESET)); + } + void (*addbuf)(uint8_t *, size_t, bool); + switch (opt) { + case 't': + prepare_record(); + addbuf = line_nul; + break; + case 'd': + addbuf = rt_del_args; + break; + } + char u8buf[STACK_BUF_LEN]; + size_t u8left; + char *u8; + while (l) { + u8left = STACK_BUF_LEN; + u8 = u8buf; + size_t ret = iconv(optcd, &ls, &l, &u8, &u8left); + + if (ret == (size_t)-1) { + // 引数処理なのでEINVAL時のバッファ持ち越しは考慮しない + if (errno != E2BIG) { + oserror(); + } + } + addbuf(u8buf, u8 - u8buf, false); + } + u8 = u8buf; + u8left = STACK_BUF_LEN; + if (iconv(optcd, NULL, NULL, &u8, &u8left) == (size_t)-1) { + oserror(); + } + addbuf(u8buf, u8 - u8buf, true); + addbuf(NULL, 0, false); } void pticonv_close(void) { - iconv_close(optcd); + iconv_close(optcd); } diff --git a/src/put-tags.c b/src/put-tags.c index 528f244..74ebfd1 100644 --- a/src/put-tags.c +++ b/src/put-tags.c @@ -10,206 +10,206 @@ #include "opuscomment.h" static void puterror(void) { - if (tag_output_to_file) { - fileerror(O.tag_filename); - } - else { - oserror(); - } + if (tag_output_to_file) { + fileerror(O.tag_filename); + } + else { + oserror(); + } } static int nth = 1; static void u8error(void) { - opuserror(err_opus_utf8, nth); + opuserror(err_opus_utf8, nth); } static void put_bin(uint8_t const *buf, size_t len) { - size_t ret = fwrite(buf, 1, len, tag_output); - if (ret != len) { - puterror(); - } + size_t ret = fwrite(buf, 1, len, tag_output); + if (ret != len) { + puterror(); + } } static void put_ls(char const *ls) { - put_bin(ls, strlen(ls)); + put_bin(ls, strlen(ls)); } static uint8_t *esc_oc(uint8_t *src, uint8_t *dest, size_t len) { - uint8_t *end = src + len; - while(src < end) { - *dest++ = *src++; - if (src[-1] == 0xa) *dest++ = O.tag_escape_tilde ? 0x7e : 0x9; - } - return dest; + uint8_t *end = src + len; + while(src < end) { + *dest++ = *src++; + if (src[-1] == 0xa) *dest++ = O.tag_escape_tilde ? 0x7e : 0x9; + } + return dest; } static uint8_t *esc_vc(uint8_t *src, uint8_t *dest, size_t len) { - uint8_t *end = src + len; - for (; src < end; src++) { - switch (*src) { - case 0x00: - *dest++ = 0x5c; - *dest++ = 0x30; - break; - - case 0x0a: - *dest++ = 0x5c; - *dest++ = 0x6e; - break; - case 0x0d: - *dest++ = 0x5c; - *dest++ = 0x72; - break; - - case 0x5c: - *dest++ = 0x5c; - *dest++ = 0x5c; - break; - - default: - *dest++ = *src; - break; - } - } - return dest; + uint8_t *end = src + len; + for (; src < end; src++) { + switch (*src) { + case 0x00: + *dest++ = 0x5c; + *dest++ = 0x30; + break; + + case 0x0a: + *dest++ = 0x5c; + *dest++ = 0x6e; + break; + case 0x0d: + *dest++ = 0x5c; + *dest++ = 0x72; + break; + + case 0x5c: + *dest++ = 0x5c; + *dest++ = 0x5c; + break; + + default: + *dest++ = *src; + break; + } + } + return dest; } static uint8_t *esc_nul(uint8_t *src, uint8_t *dest, size_t len) { - return memcpy(dest, src, len) + len; + return memcpy(dest, src, len) + len; } typedef uint8_t* (*esc_func_)(uint8_t*, uint8_t*, size_t); static esc_func_ esc_func[] = { - esc_oc, esc_vc, esc_nul + esc_oc, esc_vc, esc_nul }; static size_t conv_none(iconv_t cd, uint8_t *buf, size_t tagleft) { - put_bin(buf, tagleft); - return 0; + put_bin(buf, tagleft); + return 0; } static size_t conv_ls(iconv_t cd, uint8_t *buf, size_t tagleft) { - uint8_t *u8 = buf; - char *ls = buf + tagleft; - size_t remain = 0; - for (;;) { - char *lsend = ls; - size_t bufleft = STACK_BUF_LEN - ((uint8_t*)ls - buf) - 1; - size_t iconvret = iconv(cd, (char**)&u8, &tagleft, &lsend, &bufleft); - int ie = errno; - errno = 0; - if (iconvret == (size_t)-1 && ie == EILSEQ) { - u8error(); - } - *lsend = '\0'; - // WONTFIX - // ロケール文字列に \0 が含まれていてもそのままそこで途切れさせる - put_ls(ls); - if (iconvret != (size_t)-1 || ie == EINVAL) { - remain = tagleft; - if (remain) { - memcpy(buf, u8, remain); - } - break; - } - } - return remain; + uint8_t *u8 = buf; + char *ls = buf + tagleft; + size_t remain = 0; + for (;;) { + char *lsend = ls; + size_t bufleft = STACK_BUF_LEN - ((uint8_t*)ls - buf) - 1; + size_t iconvret = iconv(cd, (char**)&u8, &tagleft, &lsend, &bufleft); + int ie = errno; + errno = 0; + if (iconvret == (size_t)-1 && ie == EILSEQ) { + u8error(); + } + *lsend = '\0'; + // WONTFIX + // ロケール文字列に \0 が含まれていてもそのままそこで途切れさせる + put_ls(ls); + if (iconvret != (size_t)-1 || ie == EINVAL) { + remain = tagleft; + if (remain) { + memcpy(buf, u8, remain); + } + break; + } + } + return remain; } void tag_output_close(void) { - if (O.tag_deferred) { - FILE *deferred = tag_output; - tag_output = stdout; - rewind(deferred); - size_t readlen; - uint8_t buf[STACK_BUF_LEN]; - while(readlen = fread(buf, 1, STACK_BUF_LEN, deferred)) { - put_bin(buf, readlen); - } - fclose(deferred); - } - - if (fclose(stdout) == EOF) { - puterror(); - } + if (O.tag_deferred) { + FILE *deferred = tag_output; + tag_output = stdout; + rewind(deferred); + size_t readlen; + uint8_t buf[STACK_BUF_LEN]; + while(readlen = fread(buf, 1, STACK_BUF_LEN, deferred)) { + put_bin(buf, readlen); + } + fclose(deferred); + } + + if (fclose(stdout) == EOF) { + puterror(); + } } void *put_tags(void *fp_) { - // retrieve_tag() からスレッド化された - // fpはチャンク化されたタグ - // [4バイト: タグ長(ホストエンディアン)][任意長: UTF-8タグ] - FILE *fp = fp_; - - iconv_t cd; - - if (!O.tag_raw) { - cd = iconv_new(nl_langinfo(CODESET), "UTF-8"); - } - bool nulsep = O.tag_escape == TAG_ESCAPE_NUL; - size_t const buflenunit = STACK_BUF_LEN / (3 - nulsep); - uint8_t buf[STACK_BUF_LEN]; - uint8_t *raw = buf + buflenunit * (2 - nulsep); - uint8_t *(*esc)(uint8_t*, uint8_t*, size_t) = esc_func[O.tag_escape]; - size_t (*conv)(iconv_t cd, uint8_t*, size_t) = O.tag_raw ? conv_none : conv_ls; - - for (;;) { - uint32_t len; - if (!fread(&len, 4, 1, fp)) break; - if (nulsep && nth > 1) put_bin("", 1); - size_t left = len, remain = 0; - while (left) { - size_t readlen = fill_buffer(raw, left, buflenunit - remain, fp); - left -= readlen; - - size_t tagleft = esc(raw, buf + remain, readlen) - buf; - remain = conv(cd, buf, tagleft); - } - if (remain) { - u8error(); - } - if (O.tag_raw) { - if (!nulsep) { - put_bin("\x0a", 1); - } - } - else { - uint8_t *u8; - char *ls; - if (nulsep) { - *buf = '\0'; - left = 1, remain = 128; - } - else { - memcpy(buf, "\x0a", 2); - left = 2, remain = 128; - } - u8 = buf, ls = buf + left; - if (iconv(cd, (char**)&u8, &left, &ls, &remain) == (size_t)-1) { - oserror(); - } - // u8はここでiconv()前のlsを指していることに注意 - put_ls(u8); - } - nth++; - } - fclose(fp); - - return NULL; + // retrieve_tag() からスレッド化された + // fpはチャンク化されたタグ + // [4バイト: タグ長(ホストエンディアン)][任意長: UTF-8タグ] + FILE *fp = fp_; + + iconv_t cd; + + if (!O.tag_raw) { + cd = iconv_new(nl_langinfo(CODESET), "UTF-8"); + } + bool nulsep = O.tag_escape == TAG_ESCAPE_NUL; + size_t const buflenunit = STACK_BUF_LEN / (3 - nulsep); + uint8_t buf[STACK_BUF_LEN]; + uint8_t *raw = buf + buflenunit * (2 - nulsep); + uint8_t *(*esc)(uint8_t*, uint8_t*, size_t) = esc_func[O.tag_escape]; + size_t (*conv)(iconv_t cd, uint8_t*, size_t) = O.tag_raw ? conv_none : conv_ls; + + for (;;) { + uint32_t len; + if (!fread(&len, 4, 1, fp)) break; + if (nulsep && nth > 1) put_bin("", 1); + size_t left = len, remain = 0; + while (left) { + size_t readlen = fill_buffer(raw, left, buflenunit - remain, fp); + left -= readlen; + + size_t tagleft = esc(raw, buf + remain, readlen) - buf; + remain = conv(cd, buf, tagleft); + } + if (remain) { + u8error(); + } + if (O.tag_raw) { + if (!nulsep) { + put_bin("\x0a", 1); + } + } + else { + uint8_t *u8; + char *ls; + if (nulsep) { + *buf = '\0'; + left = 1, remain = 128; + } + else { + memcpy(buf, "\x0a", 2); + left = 2, remain = 128; + } + u8 = buf, ls = buf + left; + if (iconv(cd, (char**)&u8, &left, &ls, &remain) == (size_t)-1) { + oserror(); + } + // u8はここでiconv()前のlsを指していることに注意 + put_ls(u8); + } + nth++; + } + fclose(fp); + + return NULL; } iconv_t iconv_new(char const *to, char const *from) { #if defined __GLIBC__ || defined _LIBICONV_VERSION - char to2[64]; - strcat(strcpy(to2, to), "//TRANSLIT"); + char to2[64]; + strcat(strcpy(to2, to), "//TRANSLIT"); #else - char const *to2 = to; + char const *to2 = to; #endif - iconv_t cd = iconv_open(to2, from); - if (cd == (iconv_t)-1) { - if (errno == EINVAL) { - oserror_fmt(catgets(catd, 4, 3, "iconv doesn't support converting %s -> %s"), from, to); - } - else { - oserror(); - } - } - return cd; + iconv_t cd = iconv_open(to2, from); + if (cd == (iconv_t)-1) { + if (errno == EINVAL) { + oserror_fmt(catgets(catd, 4, 3, "iconv doesn't support converting %s -> %s"), from, to); + } + else { + oserror(); + } + } + return cd; } diff --git a/src/read-flac.c b/src/read-flac.c index cc0f171..62d729b 100644 --- a/src/read-flac.c +++ b/src/read-flac.c @@ -16,237 +16,239 @@ static size_t const gbuflen = 1 << 16; static uint8_t gbuf[1 << 16]; bool get_metadata_header(uint8_t *type, size_t *len) { - size_t readlen = fread(gbuf, 1, 4, stream_input); - if (readlen == (size_t)-1) oserror(); - if (readlen != 4) { - opuserror(err_opus_non_flac); - } - - bool last = 0x80 & gbuf[0]; - *type = 0x7f & gbuf[0]; - gbuf[0] = 0; - *len = htonl(*(uint32_t*)gbuf); - gbuf[0] = *type; // 「最後のヘッダ」標識を削除 - return last; + size_t readlen = fread(gbuf, 1, 4, stream_input); + if (readlen == (size_t)-1) oserror(); + if (readlen != 4) { + opuserror(err_opus_non_flac); + } + + bool last = 0x80 & gbuf[0]; + *type = 0x7f & gbuf[0]; + gbuf[0] = 0; + *len = htonl(*(uint32_t*)gbuf); + gbuf[0] = *type; // 「最後のヘッダ」標識を削除 + return last; } static void write_buffer(void const *buf, size_t len, FILE *fp) { - size_t wlen = fwrite(buf, 1, len, fp); - if (wlen != len) oserror(); + size_t wlen = fwrite(buf, 1, len, fp); + if (wlen != len) oserror(); } void store_tags_flac(struct rettag_st *rst, struct edit_st *est) { - size_t readlen; - if (rst) { - store_tags(NULL, rst, est, false); - uint32_t left = ftell(rst->tag); - *(uint32_t*)gbuf = ntohl(left); - gbuf[0] = 4; - write_buffer(gbuf, 4, built_stream); - rewind(rst->tag); - while (readlen = fread(gbuf, 1, gbuflen, rst->tag)) { - write_buffer(gbuf, readlen, built_stream); - } - } - rewind(est->pict); - while (readlen = fread(gbuf, 1, gbuflen, est->pict)) { - write_buffer(gbuf, readlen, built_stream); - } + size_t readlen; + if (rst) + { + store_tags(NULL, rst, est, false); + uint32_t left = ftell(rst->tag); + *(uint32_t*)gbuf = ntohl(left); + gbuf[0] = 4; + write_buffer(gbuf, 4, built_stream); + rewind(rst->tag); + while (readlen = fread(gbuf, 1, gbuflen, rst->tag)) + write_buffer(gbuf, readlen, built_stream); + } + if (est->pict) + { + rewind(est->pict); + while (readlen = fread(gbuf, 1, gbuflen, est->pict)) + write_buffer(gbuf, readlen, built_stream); + } } static void read_comment(size_t left) { - // ここから read.c の parse_info_border() と大体一緒 - pthread_t retriever_thread; - - // タグヘッダパースを別スレッド化 retrieve_tags.c へ - int pfd[2]; - pipe(pfd); - FILE *retriever = fdopen(pfd[0], "r"); - pthread_create(&retriever_thread, NULL, retrieve_tags, retriever); - - // 本スレッドはタグヘッダを送り続ける - while (left) { - size_t readlen = left > gbuflen ? gbuflen : left; - size_t readret = fread(gbuf, 1, readlen, stream_input); - if (readlen != readret) oserror(); - write(pfd[1], gbuf, readlen); - left -= readlen; - } - - // ここから read.c の parse_comment_term() と似たようなこと - struct rettag_st *rst; - struct edit_st *est; - close(pfd[1]); - pthread_join(retriever_thread, (void**)&rst); - if (O.edit != EDIT_LIST) { - // 編集入力タグパースのスレッドを合流 - pthread_join(parser_thread, (void **)&est); - store_tags_flac(rst, est); - } + // ここから read.c の parse_info_border() と大体一緒 + pthread_t retriever_thread; + + // タグヘッダパースを別スレッド化 retrieve_tags.c へ + int pfd[2]; + pipe(pfd); + FILE *retriever = fdopen(pfd[0], "r"); + pthread_create(&retriever_thread, NULL, retrieve_tags, retriever); + + // 本スレッドはタグヘッダを送り続ける + while (left) { + size_t readlen = left > gbuflen ? gbuflen : left; + size_t readret = fread(gbuf, 1, readlen, stream_input); + if (readlen != readret) oserror(); + write(pfd[1], gbuf, readlen); + left -= readlen; + } + + // ここから read.c の parse_comment_term() と似たようなこと + struct rettag_st *rst; + struct edit_st *est; + close(pfd[1]); + pthread_join(retriever_thread, (void**)&rst); + if (O.edit != EDIT_LIST) { + // 編集入力タグパースのスレッドを合流 + pthread_join(parser_thread, (void **)&est); + store_tags_flac(rst, est); + } } static void *put_base64_locale(void *tagin_) { - FILE *tagin = tagin_; - iconv_t cd = iconv_open(nl_langinfo(CODESET), "us-ascii"); - size_t readlen; - char buf[128]; - while (readlen = fread(buf, 1, 64, tagin)) { - size_t asciileft = readlen, locleft = 128 - readlen; - char *ascii = buf, *loc = buf + readlen; - iconv(cd, &ascii, &asciileft, &loc, &locleft); - // このiconv()はPCS範囲内でシフトも発生しないはずなのでバッファ持ち越しがない - // asciiは元のlocの位置に移った - write_buffer(ascii, loc - ascii, tag_output); - } - iconv_close(cd); - fclose(tagin); - return NULL; + FILE *tagin = tagin_; + iconv_t cd = iconv_open(nl_langinfo(CODESET), "us-ascii"); + size_t readlen; + char buf[128]; + while (readlen = fread(buf, 1, 64, tagin)) { + size_t asciileft = readlen, locleft = 128 - readlen; + char *ascii = buf, *loc = buf + readlen; + iconv(cd, &ascii, &asciileft, &loc, &locleft); + // このiconv()はPCS範囲内でシフトも発生しないはずなのでバッファ持ち越しがない + // asciiは元のlocの位置に移った + write_buffer(ascii, loc - ascii, tag_output); + } + iconv_close(cd); + fclose(tagin); + return NULL; } static bool met_comment, met_picture; static void read_picture_list(size_t left) { - pthread_t loctr_th; - FILE *ascii_out; - if (!O.tag_raw) { - int pfd[2]; - pipe(pfd); - ascii_out = fdopen(pfd[1], "w"); - FILE *tagin = fdopen(pfd[0], "r"); - pthread_create(&loctr_th, NULL, put_base64_locale, tagin); - } - else { - ascii_out = tag_output; - } - - if (O.tag_escape == TAG_ESCAPE_NUL && (met_comment || met_picture)) { - write_buffer("", 1, ascii_out); - } - write_buffer(MBPeq, strlen(MBPeq), ascii_out); - - while (left) { - uint8_t raw[3] = {0}; - uint8_t b64[4]; - size_t readlen = left > 3 ? 3 : left; - size_t readret = fread(raw, 1, readlen, stream_input); - if (readret != readlen) { - if (ferror(stream_input)) oserror(); - else opuserror(err_opus_lost_tag); - } - // 00000011 11112222 22333333 - b64[0] = raw[0] >> 2; - b64[1] = (raw[0] << 4 | raw[1] >> 4) & 0x3f; - b64[2] = (raw[1] << 2 | raw[2] >> 6) & 0x3f; - b64[3] = raw[2] & 0x3f; - switch (readlen) { - case 1: - b64[2] = 64; - // FALLTHROUGH - case 2: - b64[3] = 64; - break; - } - for (int_fast8_t i = 0; i < 4; i++) { - b64[i] = b64tab_ascii[b64[i]]; - } - write_buffer(b64, 4, ascii_out); - left -= readlen; - } - - if (O.tag_escape != TAG_ESCAPE_NUL) { - write_buffer((uint8_t[]){ 0xa }, 1, ascii_out); - } - - if (!O.tag_raw) { - fclose(ascii_out); - pthread_join(loctr_th, NULL); - } - - met_picture = true; + pthread_t loctr_th; + FILE *ascii_out; + if (!O.tag_raw) { + int pfd[2]; + pipe(pfd); + ascii_out = fdopen(pfd[1], "w"); + FILE *tagin = fdopen(pfd[0], "r"); + pthread_create(&loctr_th, NULL, put_base64_locale, tagin); + } + else { + ascii_out = tag_output; + } + + if (O.tag_escape == TAG_ESCAPE_NUL && (met_comment || met_picture)) { + write_buffer("", 1, ascii_out); + } + write_buffer(MBPeq, strlen(MBPeq), ascii_out); + + while (left) { + uint8_t raw[3] = {0}; + uint8_t b64[4]; + size_t readlen = left > 3 ? 3 : left; + size_t readret = fread(raw, 1, readlen, stream_input); + if (readret != readlen) { + if (ferror(stream_input)) oserror(); + else opuserror(err_opus_lost_tag); + } + // 00000011 11112222 22333333 + b64[0] = raw[0] >> 2; + b64[1] = (raw[0] << 4 | raw[1] >> 4) & 0x3f; + b64[2] = (raw[1] << 2 | raw[2] >> 6) & 0x3f; + b64[3] = raw[2] & 0x3f; + switch (readlen) { + case 1: + b64[2] = 64; + // FALLTHROUGH + case 2: + b64[3] = 64; + break; + } + for (int_fast8_t i = 0; i < 4; i++) { + b64[i] = b64tab_ascii[b64[i]]; + } + write_buffer(b64, 4, ascii_out); + left -= readlen; + } + + if (O.tag_escape != TAG_ESCAPE_NUL) { + write_buffer((uint8_t[]){ 0xa }, 1, ascii_out); + } + + if (!O.tag_raw) { + fclose(ascii_out); + pthread_join(loctr_th, NULL); + } + + met_picture = true; } void read_flac(void) { - size_t readlen = fread(gbuf, 1, 4, stream_input); - if (readlen == (size_t)-1) oserror(); - if (readlen != 4) { - opuserror(err_opus_non_flac); - } - if (memcmp(gbuf, "\x66\x4C\x61\x43", 4) != 0) { // fLaC - opuserror(err_opus_non_flac); - } - write_buffer(gbuf, 4, built_stream); - - bool last_metadata = false; - size_t metadata_num = 0; - while (!last_metadata) { - uint8_t type; - size_t left; - last_metadata = get_metadata_header(&type, &left); - if (type == 0 && metadata_num != 0 - || type == 0 && left != 34 - || type == 127 - || met_comment && type == 4) { - opuserror(err_opus_bad_content); - } - bool delete_header = O.edit == EDIT_LIST; - if (type == 1) delete_header = true; - switch (type) { - case 4: - if (O.edit == EDIT_LIST && O.tag_escape == TAG_ESCAPE_NUL && met_picture) { - write_buffer("", 1, tag_output); - } - read_comment(left); - met_comment = true; - break; - case 6: - if (O.edit == EDIT_LIST) { - if (!O.tag_ignore_picture) { - read_picture_list(left); - break; - } - } - else if (O.edit == EDIT_WRITE) { - delete_header = !O.tag_ignore_picture; - } - // FALLTHROUGH - default: - if (!delete_header) write_buffer(gbuf, 4, built_stream); - while (left) { - size_t readlen = left > gbuflen ? gbuflen : left; - size_t readret = fread(gbuf, 1, readlen, stream_input); - if (readlen != readret) oserror(); - if (!delete_header) write_buffer(gbuf, readlen, built_stream); - left -= readlen; - } - break; - } - metadata_num++; - } - if (O.edit == EDIT_LIST) { - tag_output_close(); - exit(0); - } - if (!met_comment) { - struct edit_st *est; - pthread_join(parser_thread, (void **)&est); - struct rettag_st *rst = NULL; - if (est->num) { - rst = calloc(1, sizeof(*rst)); - rst->tag = tmpfile(); - rst->part_of_comment = true; - size_t vendorlen = strlen(new_vendor_string_ascii); - fwrite((uint32_t[]){oi32(vendorlen)}, 4, 1, rst->tag); - fwrite(new_vendor_string_ascii, 1, vendorlen, rst->tag); - rst->tagbegin = vendorlen + 4; - fwrite((uint32_t[]){0}, 4, 1, rst->tag); - } - store_tags_flac(rst, est); - } - write_buffer("\x81\0\0", 4, built_stream); // 「最後のヘッダ」標識を立てたパディングで〆 - - opst = PAGE_SOUND; - while (readlen = fread(gbuf, 1, gbuflen, stream_input)) { - size_t writelen = fwrite(gbuf, 1, readlen, built_stream); - if (writelen != readlen) oserror(); - } - if (ferror(stream_input)) oserror(); + size_t readlen = fread(gbuf, 1, 4, stream_input); + if (readlen == (size_t)-1) oserror(); + if (readlen != 4) { + opuserror(err_opus_non_flac); + } + if (memcmp(gbuf, "\x66\x4C\x61\x43", 4) != 0) { // fLaC + opuserror(err_opus_non_flac); + } + write_buffer(gbuf, 4, built_stream); + + bool last_metadata = false; + size_t metadata_num = 0; + while (!last_metadata) { + uint8_t type; + size_t left; + last_metadata = get_metadata_header(&type, &left); + if (type == 0 && metadata_num != 0 + || type == 0 && left != 34 + || type == 127 + || met_comment && type == 4) { + opuserror(err_opus_bad_content); + } + bool delete_header = O.edit == EDIT_LIST; + if (type == 1) delete_header = true; + switch (type) { + case 4: + if (O.edit == EDIT_LIST && O.tag_escape == TAG_ESCAPE_NUL && met_picture) { + write_buffer("", 1, tag_output); + } + read_comment(left); + met_comment = true; + break; + case 6: + if (O.edit == EDIT_LIST) { + if (!O.tag_ignore_picture) { + read_picture_list(left); + break; + } + } + else if (O.edit == EDIT_WRITE) { + delete_header = !O.tag_ignore_picture; + } + // FALLTHROUGH + default: + if (!delete_header) write_buffer(gbuf, 4, built_stream); + while (left) { + size_t readlen = left > gbuflen ? gbuflen : left; + size_t readret = fread(gbuf, 1, readlen, stream_input); + if (readlen != readret) oserror(); + if (!delete_header) write_buffer(gbuf, readlen, built_stream); + left -= readlen; + } + break; + } + metadata_num++; + } + if (O.edit == EDIT_LIST) { + tag_output_close(); + exit(0); + } + if (!met_comment) { + struct edit_st *est; + pthread_join(parser_thread, (void **)&est); + struct rettag_st *rst = NULL; + if (est->num) { + rst = calloc(1, sizeof(*rst)); + rst->tag = tmpfile(); + rst->part_of_comment = true; + size_t vendorlen = strlen(new_vendor_string_ascii); + fwrite((uint32_t[]){oi32(vendorlen)}, 4, 1, rst->tag); + fwrite(new_vendor_string_ascii, 1, vendorlen, rst->tag); + rst->tagbegin = vendorlen + 4; + fwrite((uint32_t[]){0}, 4, 1, rst->tag); + } + store_tags_flac(rst, est); + } + write_buffer("\x81\0\0", 4, built_stream); // 「最後のヘッダ」標識を立てたパディングで〆 + + opst = PAGE_SOUND; + while (readlen = fread(gbuf, 1, gbuflen, stream_input)) { + size_t writelen = fwrite(gbuf, 1, readlen, built_stream); + if (writelen != readlen) oserror(); + } + if (ferror(stream_input)) oserror(); } diff --git a/src/read.c b/src/read.c index 3db52c6..0222922 100644 --- a/src/read.c +++ b/src/read.c @@ -22,487 +22,487 @@ static bool remove_tmp; static FILE *non_opus_stream; void move_file(void) { - if (fclose(built_stream) == EOF) { - fileerror(O.out ? O.out : outtmp); - } - if (O.out || !input_is_regular_file) { - } - else { - int fd = fileno(stream_input); - struct stat st; - fstat(fd, &st); - if (!rename(outtmp, O.in)) { - // rename(2)は移動先が存在していてもunlink(2)の必要はない - remove_tmp = false; - if (!chmod(O.in, st.st_mode)) { - return; - } - } - oserror(); - } + if (fclose(built_stream) == EOF) { + fileerror(O.out ? O.out : outtmp); + } + if (O.out || !input_is_regular_file) { + } + else { + int fd = fileno(stream_input); + struct stat st; + fstat(fd, &st); + if (!rename(outtmp, O.in)) { + // rename(2)は移動先が存在していてもunlink(2)の必要はない + remove_tmp = false; + if (!chmod(O.in, st.st_mode)) { + return; + } + } + oserror(); + } } static noreturn void put_left(long rew) { - clearerr(stream_input); - if (fseek(stream_input, seeked_len - rew, SEEK_SET)) { - oserror(); - } - - size_t l; - uint8_t buf[STACK_BUF_LEN]; - while (l = fread(buf, 1, STACK_BUF_LEN, stream_input)) { - size_t ret = fwrite(buf, 1, l, built_stream); - if (ret != l) { - oserror(); - } - } - if (ferror(stream_input)) { - oserror(); - } - move_file(); - exit(0); + clearerr(stream_input); + if (fseek(stream_input, seeked_len - rew, SEEK_SET)) { + oserror(); + } + + size_t l; + uint8_t buf[STACK_BUF_LEN]; + while (l = fread(buf, 1, STACK_BUF_LEN, stream_input)) { + size_t ret = fwrite(buf, 1, l, built_stream); + if (ret != l) { + oserror(); + } + } + if (ferror(stream_input)) { + oserror(); + } + move_file(); + exit(0); } static void put_non_opus_stream() { - rewind(non_opus_stream); - uint8_t buf[STACK_BUF_LEN]; - size_t readlen; - while (readlen = fread(buf, 1, STACK_BUF_LEN, non_opus_stream)) { - if (fwrite(buf, 1, readlen, built_stream) != readlen) { - oserror(); - } - } - fclose(non_opus_stream); - non_opus_stream = NULL; + rewind(non_opus_stream); + uint8_t buf[STACK_BUF_LEN]; + size_t readlen; + while (readlen = fread(buf, 1, STACK_BUF_LEN, non_opus_stream)) { + if (fwrite(buf, 1, readlen, built_stream) != readlen) { + oserror(); + } + } + fclose(non_opus_stream); + non_opus_stream = NULL; } void store_tags(ogg_page *np, struct rettag_st *rst, struct edit_st *est, bool packet_break_in_page) { - FILE *fptag = rst->tag; - size_t const pagebuflen = 65536; // oggページの最大長 = 65307 - uint8_t pagebuf[pagebuflen]; - - // タグ個数書き込み - fseek(fptag, rst->tagbegin, SEEK_SET); - *(uint32_t*)pagebuf = oi32(rst->num + est->num); - fwrite(pagebuf, 4, 1, fptag); - fseek(fptag, 0, SEEK_END); - - // 編集入力書き込み - rewind(est->str); rewind(est->len); - while (fread(pagebuf, 4, 1, est->len)) { - // ここでpagebufはOgg構造関係なく単なる移し替え用のバッファ - size_t reclen = *(uint32_t*)pagebuf; - *(uint32_t*)pagebuf = oi32(reclen); - fwrite(pagebuf, 4, 1, fptag); - while (reclen) { - size_t readlen = fill_buffer(pagebuf, reclen, pagebuflen, est->str); - fwrite(pagebuf, 1, readlen, fptag); - reclen -= readlen; - } - } - fclose(est->str); fclose(est->len); - - if (rst->padding && rst->part_of_comment) { - // パディングがある時はfptagの後ろに移す - rewind(rst->padding); - size_t readlen; - while (readlen = fread(pagebuf, 1, pagebuflen, rst->padding)) { - fwrite(pagebuf, 1, readlen, fptag); - } - fclose(rst->padding); - rst->padding = NULL; - } - long commentlen = ftell(fptag); - if (commentlen > TAG_LENGTH_LIMIT__OUTPUT) { - exceed_output_limit(); - } - if (codec->type == CODEC_FLAC) return; - - rewind(fptag); - ogg_page og; - og.header_len = 282; - og.header = pagebuf; - og.body_len = 255 * 255; - og.body = pagebuf + 282; - memcpy(og.header, "\x4f\x67\x67\x53\x0\x0", 6); - set_granulepos(&og, -1); - *(uint32_t*)&og.header[14] = oi32(opus_sno); - memset(og.header + 26, 0xff, 256); - - uint32_t idx = 1; - while (commentlen >= 255 * 255) { - fread(og.body, 1, 255 * 255, fptag); - set_pageno(&og, idx++); - ogg_page_checksum_set(&og); - write_page(&og, built_stream); - commentlen -= 255 * 255; - if (!og.header[5]) og.header[5] = 1; - } - - // パケットの最後のページ生成 - og.header[5] = idx != 1; - set_granulepos(&og, 0); - set_pageno(&og, idx++); - og.header[26] = commentlen / 255 + 1; - og.header[26 + og.header[26]] = commentlen % 255; - fread(og.body, 1, commentlen, fptag); - fclose(fptag); - og.header_len = 27 + og.header[26]; - og.body_len = commentlen; - ogg_page_checksum_set(&og); - write_page(&og, built_stream); - - if (input_is_regular_file) { - // 出力するタグ部分のページ番号が入力の音声開始部分のページ番号に満たない場合、 - // 空のページを生成して開始ページ番号を合わせる - og.header_len = 27; - og.header[5] = 0; - set_granulepos(&og, -1); - og.header[26] = 0; - og.body_len = 0; - while (idx < opus_idx - packet_break_in_page) { - set_pageno(&og, idx++); - ogg_page_checksum_set(&og); - write_page(&og, built_stream); - } - } - - if (packet_break_in_page) { - uint8_t *lace_tag = &np->header[27]; - while (*lace_tag == 255) { - lace_tag++; - np->body_len -= 255; - np->body += 255; - } - np->body_len -= *lace_tag; - np->body += *lace_tag; - lace_tag++; - np->header_len -= lace_tag - &np->header[27]; - np->header[5] = 0; - np->header[26] -= lace_tag - &np->header[27]; - memmove(&np->header[27], lace_tag, np->header[26]); - set_pageno(np, idx++); - ogg_page_checksum_set(np); - write_page(np, built_stream); - } - - put_non_opus_stream(); - - if (input_is_regular_file && idx == opus_idx) { - put_left(0); - /* NOTREACHED */ - } - opus_idx_diff = idx - opus_idx; - opst = PAGE_SOUND; + FILE *fptag = rst->tag; + size_t const pagebuflen = 65536; // oggページの最大長 = 65307 + uint8_t pagebuf[pagebuflen]; + + // タグ個数書き込み + fseek(fptag, rst->tagbegin, SEEK_SET); + *(uint32_t*)pagebuf = oi32(rst->num + est->num); + fwrite(pagebuf, 4, 1, fptag); + fseek(fptag, 0, SEEK_END); + + // 編集入力書き込み + rewind(est->str); rewind(est->len); + while (fread(pagebuf, 4, 1, est->len)) { + // ここでpagebufはOgg構造関係なく単なる移し替え用のバッファ + size_t reclen = *(uint32_t*)pagebuf; + *(uint32_t*)pagebuf = oi32(reclen); + fwrite(pagebuf, 4, 1, fptag); + while (reclen) { + size_t readlen = fill_buffer(pagebuf, reclen, pagebuflen, est->str); + fwrite(pagebuf, 1, readlen, fptag); + reclen -= readlen; + } + } + fclose(est->str); fclose(est->len); + + if (rst->padding && rst->part_of_comment) { + // パディングがある時はfptagの後ろに移す + rewind(rst->padding); + size_t readlen; + while (readlen = fread(pagebuf, 1, pagebuflen, rst->padding)) { + fwrite(pagebuf, 1, readlen, fptag); + } + fclose(rst->padding); + rst->padding = NULL; + } + long commentlen = ftell(fptag); + if (commentlen > TAG_LENGTH_LIMIT__OUTPUT) { + exceed_output_limit(); + } + if (codec->type == CODEC_FLAC) return; + + rewind(fptag); + ogg_page og; + og.header_len = 282; + og.header = pagebuf; + og.body_len = 255 * 255; + og.body = pagebuf + 282; + memcpy(og.header, "\x4f\x67\x67\x53\x0\x0", 6); + set_granulepos(&og, -1); + *(uint32_t*)&og.header[14] = oi32(opus_sno); + memset(og.header + 26, 0xff, 256); + + uint32_t idx = 1; + while (commentlen >= 255 * 255) { + fread(og.body, 1, 255 * 255, fptag); + set_pageno(&og, idx++); + ogg_page_checksum_set(&og); + write_page(&og, built_stream); + commentlen -= 255 * 255; + if (!og.header[5]) og.header[5] = 1; + } + + // パケットの最後のページ生成 + og.header[5] = idx != 1; + set_granulepos(&og, 0); + set_pageno(&og, idx++); + og.header[26] = commentlen / 255 + 1; + og.header[26 + og.header[26]] = commentlen % 255; + fread(og.body, 1, commentlen, fptag); + fclose(fptag); + og.header_len = 27 + og.header[26]; + og.body_len = commentlen; + ogg_page_checksum_set(&og); + write_page(&og, built_stream); + + if (input_is_regular_file) { + // 出力するタグ部分のページ番号が入力の音声開始部分のページ番号に満たない場合、 + // 空のページを生成して開始ページ番号を合わせる + og.header_len = 27; + og.header[5] = 0; + set_granulepos(&og, -1); + og.header[26] = 0; + og.body_len = 0; + while (idx < opus_idx - packet_break_in_page) { + set_pageno(&og, idx++); + ogg_page_checksum_set(&og); + write_page(&og, built_stream); + } + } + + if (packet_break_in_page) { + uint8_t *lace_tag = &np->header[27]; + while (*lace_tag == 255) { + lace_tag++; + np->body_len -= 255; + np->body += 255; + } + np->body_len -= *lace_tag; + np->body += *lace_tag; + lace_tag++; + np->header_len -= lace_tag - &np->header[27]; + np->header[5] = 0; + np->header[26] -= lace_tag - &np->header[27]; + memmove(&np->header[27], lace_tag, np->header[26]); + set_pageno(np, idx++); + ogg_page_checksum_set(np); + write_page(np, built_stream); + } + + put_non_opus_stream(); + + if (input_is_regular_file && idx == opus_idx) { + put_left(0); + /* NOTREACHED */ + } + opus_idx_diff = idx - opus_idx; + opst = PAGE_SOUND; } static void cleanup(void) { - if (remove_tmp) { - unlink(outtmp); - } + if (remove_tmp) { + unlink(outtmp); + } } static void exit_without_sigpipe(void) { - signal(SIGPIPE, SIG_IGN); + signal(SIGPIPE, SIG_IGN); } void open_output_file(void) { - if (O.edit == EDIT_LIST) { - built_stream = fopen("/dev/null", "w"); - if (!built_stream) { - fileerror(O.out); - } - remove_tmp = false; - - tag_output_to_file = O.tag_filename && strcmp(O.tag_filename, "-") != 0; - if (tag_output_to_file) { - FILE *tmp = freopen(O.tag_filename, "w", stdout); - if (!tmp) { - fileerror(O.tag_filename); - } - } - - tag_output = O.tag_deferred ? tmpfile() : stdout; - } - else if (O.out) { - if (strcmp(O.out, "-") == 0) { - built_stream = stdout; - } - else { - built_stream = fopen(O.out, "w"); - if (!built_stream) { - fileerror(O.out); - } - } - remove_tmp = false; - } - else { - if (input_is_regular_file) { - char const *tmpl = "opuscomment.XXXXXX"; - outtmp = calloc(strlen(O.in) + strlen(tmpl) + 1, 1); - char *p = strrchr(O.in, '/'); - if (p) { - p++; - strncpy(outtmp, O.in, p - O.in); - strcpy(outtmp + (p - O.in), tmpl); - } - else { - strcpy(outtmp, tmpl); - } - int fd = mkstemp(outtmp); - if (fd == -1) { - oserror(); - } - remove_tmp = true; - atexit(cleanup); - built_stream = fdopen(fd, "w+"); - } - else { - built_stream = stdout; - } - } - non_opus_stream = tmpfile(); - - // スレッド間通信で使っているパイプがエラー後のexit内での始末にSIGPIPEを発するのでその対策 - atexit(exit_without_sigpipe); - switch (O.edit) { - case EDIT_WRITE: - case EDIT_APPEND: - // 編集入力タグパースを別スレッド化 parse_tags.c へ - pthread_create(&parser_thread, NULL, parse_tags, NULL); - } + if (O.edit == EDIT_LIST) { + built_stream = fopen("/dev/null", "w"); + if (!built_stream) { + fileerror(O.out); + } + remove_tmp = false; + + tag_output_to_file = O.tag_filename && strcmp(O.tag_filename, "-") != 0; + if (tag_output_to_file) { + FILE *tmp = freopen(O.tag_filename, "w", stdout); + if (!tmp) { + fileerror(O.tag_filename); + } + } + + tag_output = O.tag_deferred ? tmpfile() : stdout; + } + else if (O.out) { + if (strcmp(O.out, "-") == 0) { + built_stream = stdout; + } + else { + built_stream = fopen(O.out, "w"); + if (!built_stream) { + fileerror(O.out); + } + } + remove_tmp = false; + } + else { + if (input_is_regular_file) { + char const *tmpl = "opuscomment.XXXXXX"; + outtmp = calloc(strlen(O.in) + strlen(tmpl) + 1, 1); + char *p = strrchr(O.in, '/'); + if (p) { + p++; + strncpy(outtmp, O.in, p - O.in); + strcpy(outtmp + (p - O.in), tmpl); + } + else { + strcpy(outtmp, tmpl); + } + int fd = mkstemp(outtmp); + if (fd == -1) { + oserror(); + } + remove_tmp = true; + atexit(cleanup); + built_stream = fdopen(fd, "w+"); + } + else { + built_stream = stdout; + } + } + non_opus_stream = tmpfile(); + + // スレッド間通信で使っているパイプがエラー後のexit内での始末にSIGPIPEを発するのでその対策 + atexit(exit_without_sigpipe); + switch (O.edit) { + case EDIT_WRITE: + case EDIT_APPEND: + // 編集入力タグパースを別スレッド化 parse_tags.c へ + pthread_create(&parser_thread, NULL, parse_tags, NULL); + } } static void check_header_is_single_page(ogg_page *og) { - uint8_t lace_num = og->header[26]; - if (!lace_num) opuserror(err_opus_border); - uint8_t const *lace = &og->header[27]; - for (int_fast16_t i = 0; i < lace_num - 1; i++) { - if (*lace++ != 255) { - opuserror(err_opus_border); - } - } - if (*lace == 255) { - opuserror(err_opus_border); - } - // 最後のlacing valueはチェックしない + uint8_t lace_num = og->header[26]; + if (!lace_num) opuserror(err_opus_border); + uint8_t const *lace = &og->header[27]; + for (int_fast16_t i = 0; i < lace_num - 1; i++) { + if (*lace++ != 255) { + opuserror(err_opus_border); + } + } + if (*lace == 255) { + opuserror(err_opus_border); + } + // 最後のlacing valueはチェックしない } static size_t header_packet_pos, header_packet_len; static long built_header_pos; bool parse_info(ogg_page *og) { - static int osidx = 0; - if (!ogg_page_bos(og) || ogg_page_eos(og)) { - if (ogg_page_pageno(og) == 0) { - opuserror(err_opus_bad_stream); - } - opuserror(err_opus_no_target_codec); - } - if (ogg_page_pageno(og) != 0) { - // BOSがあるのにページが0でない - opuserror(err_opus_bad_stream); - } - if (ogg_page_granulepos(og) != 0) { - // ヘッダのgranuleposは必ず0 - opuserror(err_opus_bad_stream); - } - check_header_is_single_page(og); - - if (og->body_len < codec->headmagic_len || memcmp(og->body, codec->headmagic, codec->headmagic_len) != 0) { -// have_multi_streams = true; - return false; - } - osidx++; - if (O.target_idx && O.target_idx != osidx) { -// have_multi_streams = true; - return false; - } - opus_sno = ogg_page_serialno(og); - header_packet_len = og->header_len + og->body_len; - header_packet_pos = seeked_len - header_packet_len; - built_header_pos = ftell(built_stream); - codec->parse(og); - if (O.edit != EDIT_LIST && codec->type != CODEC_FLAC) { - write_page(og, built_stream); - } - opus_idx++; - opst = PAGE_INFO_BORDER; - return true; + static int osidx = 0; + if (!ogg_page_bos(og) || ogg_page_eos(og)) { + if (ogg_page_pageno(og) == 0) { + opuserror(err_opus_bad_stream); + } + opuserror(err_opus_no_target_codec); + } + if (ogg_page_pageno(og) != 0) { + // BOSがあるのにページが0でない + opuserror(err_opus_bad_stream); + } + if (ogg_page_granulepos(og) != 0) { + // ヘッダのgranuleposは必ず0 + opuserror(err_opus_bad_stream); + } + check_header_is_single_page(og); + + if (og->body_len < codec->headmagic_len || memcmp(og->body, codec->headmagic, codec->headmagic_len) != 0) { +// have_multi_streams = true; + return false; + } + osidx++; + if (O.target_idx && O.target_idx != osidx) { +// have_multi_streams = true; + return false; + } + opus_sno = ogg_page_serialno(og); + header_packet_len = og->header_len + og->body_len; + header_packet_pos = seeked_len - header_packet_len; + built_header_pos = ftell(built_stream); + codec->parse(og); + if (O.edit != EDIT_LIST && codec->type != CODEC_FLAC) { + write_page(og, built_stream); + } + opus_idx++; + opst = PAGE_INFO_BORDER; + return true; } static int retriever_fd; static bool copy_tag_packet(ogg_page *og, bool *packet_break_in_page) { - static unsigned int total = 0; - - int lace_num = og->header[26]; - if (!lace_num) return false; - - uint8_t const *bin = og->body; - - uint_fast8_t i; - for (i = 0; i < lace_num - 1 && og->header[27 + i] == 255; i++, bin += 255, total += 255) { - if (total > TAG_LENGTH_LIMIT__INPUT) { - opuserror(err_opus_long_tag, TAG_LENGTH_LIMIT__INPUT >> 20); - } - write(retriever_fd, bin, 255); - } - total += og->header[27 + i]; - if (total > TAG_LENGTH_LIMIT__INPUT) { - opuserror(err_opus_long_tag, TAG_LENGTH_LIMIT__INPUT >> 20); - } - write(retriever_fd, bin, og->header[27 + i]); - i++; - bool packet_term = false; - if (i != lace_num || og->header[27 + i - 1] != 255) { - packet_term = true; - *packet_break_in_page = i != lace_num; - } - return packet_term; + static unsigned int total = 0; + + int lace_num = og->header[26]; + if (!lace_num) return false; + + uint8_t const *bin = og->body; + + uint_fast8_t i; + for (i = 0; i < lace_num - 1 && og->header[27 + i] == 255; i++, bin += 255, total += 255) { + if (total > TAG_LENGTH_LIMIT__INPUT) { + opuserror(err_opus_long_tag, TAG_LENGTH_LIMIT__INPUT >> 20); + } + write(retriever_fd, bin, 255); + } + total += og->header[27 + i]; + if (total > TAG_LENGTH_LIMIT__INPUT) { + opuserror(err_opus_long_tag, TAG_LENGTH_LIMIT__INPUT >> 20); + } + write(retriever_fd, bin, og->header[27 + i]); + i++; + bool packet_term = false; + if (i != lace_num || og->header[27 + i - 1] != 255) { + packet_term = true; + *packet_break_in_page = i != lace_num; + } + return packet_term; } static pthread_t retriever_thread; bool parse_info_border(ogg_page *og) { - // ここにはページ番号1で来ているはず - if (ogg_page_continued(og)) { - opuserror(err_opus_border); - } - leave_header_packets = true; - - if (/*O.gain_fix && */O.edit == EDIT_NONE) { - // 出力ゲイン編集のみの場合 - if (input_is_regular_file) { - if (O.out) { - // 出力指定が別にある場合残りをコピー - put_left(og->header_len + og->body_len); - } - else { - // 上書きするなら最初のページのみを直接書き換えようとする - uint8_t b[header_packet_len]; - FILE *stream_overwrite = freopen(NULL, "r+", stream_input); - if (!stream_overwrite - || fseek(built_stream, built_header_pos, SEEK_SET) - || fseek(stream_overwrite, header_packet_pos, SEEK_SET) - || header_packet_len != fread(b, 1, header_packet_len, built_stream) - || header_packet_len != fwrite(b, 1, header_packet_len, stream_overwrite) - ) { - oserror(); - } - exit(0); - } - } - else { - // 入力がパイプの時 - put_non_opus_stream(); - opst = PAGE_SOUND; - return parse_page_sound(og); - } - /* NOTREACHED */ - } - - // OpusTagsパケットパースを別スレッド化 retrieve_tags.c へ - int pfd[2]; - pipe(pfd); - retriever_fd = pfd[1]; - FILE *retriever = fdopen(pfd[0], "r"); - pthread_create(&retriever_thread, NULL, retrieve_tags, retriever); - - // 本スレッドはOpusTagsのパケットを構築する - opst = PAGE_COMMENT; - return parse_comment(og); + // ここにはページ番号1で来ているはず + if (ogg_page_continued(og)) { + opuserror(err_opus_border); + } + leave_header_packets = true; + + if (/*O.gain_fix && */O.edit == EDIT_NONE) { + // 出力ゲイン編集のみの場合 + if (input_is_regular_file) { + if (O.out) { + // 出力指定が別にある場合残りをコピー + put_left(og->header_len + og->body_len); + } + else { + // 上書きするなら最初のページのみを直接書き換えようとする + uint8_t b[header_packet_len]; + FILE *stream_overwrite = freopen(NULL, "r+", stream_input); + if (!stream_overwrite + || fseek(built_stream, built_header_pos, SEEK_SET) + || fseek(stream_overwrite, header_packet_pos, SEEK_SET) + || header_packet_len != fread(b, 1, header_packet_len, built_stream) + || header_packet_len != fwrite(b, 1, header_packet_len, stream_overwrite) + ) { + oserror(); + } + exit(0); + } + } + else { + // 入力がパイプの時 + put_non_opus_stream(); + opst = PAGE_SOUND; + return parse_page_sound(og); + } + /* NOTREACHED */ + } + + // OpusTagsパケットパースを別スレッド化 retrieve_tags.c へ + int pfd[2]; + pipe(pfd); + retriever_fd = pfd[1]; + FILE *retriever = fdopen(pfd[0], "r"); + pthread_create(&retriever_thread, NULL, retrieve_tags, retriever); + + // 本スレッドはOpusTagsのパケットを構築する + opst = PAGE_COMMENT; + return parse_comment(og); } bool parse_comment(ogg_page *og) { - if (ogg_page_eos(og) || opus_idx > 1 && !ogg_page_continued(og)) { - opuserror(err_opus_bad_stream); - } - opus_idx++; - bool packet_break_in_page; - if (copy_tag_packet(og, &packet_break_in_page)) { - return parse_comment_term(og, packet_break_in_page); - } - - return true; + if (ogg_page_eos(og) || opus_idx > 1 && !ogg_page_continued(og)) { + opuserror(err_opus_bad_stream); + } + opus_idx++; + bool packet_break_in_page; + if (copy_tag_packet(og, &packet_break_in_page)) { + return parse_comment_term(og, packet_break_in_page); + } + + return true; } static bool parse_comment_term(ogg_page *og, bool packet_break_in_page) { - // タグパケットのパース処理のスレッドを合流 - struct rettag_st *rst; - struct edit_st *est; - close(retriever_fd); - pthread_join(retriever_thread, (void**)&rst); - if (O.edit != EDIT_LIST) { - // 編集入力タグパースのスレッドを合流 - pthread_join(parser_thread, (void **)&est); - } - - if (O.edit == EDIT_APPEND && !rst->del && !est->num && !O.out && !O.gain_fix && !rst->upcase) { - // タグ追記モードで出力が上書き且つ - // タグ入力、ゲイン調整、タグ削除、大文字化適用が全て無い場合はすぐ終了する - exit(0); - } -// 来ない -// else if (O.edit == EDIT_LIST) { -// exit(0); -// } - store_tags(og, rst, est, packet_break_in_page); - free(rst); - free(est); - - return true; + // タグパケットのパース処理のスレッドを合流 + struct rettag_st *rst; + struct edit_st *est; + close(retriever_fd); + pthread_join(retriever_thread, (void**)&rst); + if (O.edit != EDIT_LIST) { + // 編集入力タグパースのスレッドを合流 + pthread_join(parser_thread, (void **)&est); + } + + if (O.edit == EDIT_APPEND && !rst->del && !est->num && !O.out && !O.gain_fix && !rst->upcase) { + // タグ追記モードで出力が上書き且つ + // タグ入力、ゲイン調整、タグ削除、大文字化適用が全て無い場合はすぐ終了する + exit(0); + } +// 来ない +// else if (O.edit == EDIT_LIST) { +// exit(0); +// } + store_tags(og, rst, est, packet_break_in_page); + free(rst); + free(est); + + return true; } bool parse_page_sound(ogg_page *og) { - set_pageno(og, ogg_page_pageno(og) + opus_idx_diff); - ogg_page_checksum_set(og); - write_page(og, built_stream); - if (ogg_page_eos(og)) { - if (input_is_regular_file) { - put_left(0); - /* NOTREACHED */ - } - else { - opus_idx_diff = 0; - } - } - return true; + set_pageno(og, ogg_page_pageno(og) + opus_idx_diff); + ogg_page_checksum_set(og); + write_page(og, built_stream); + if (ogg_page_eos(og)) { + if (input_is_regular_file) { + put_left(0); + /* NOTREACHED */ + } + else { + opus_idx_diff = 0; + } + } + return true; } static void parse_page(ogg_page *og) { - bool isopus; - if (opst == PAGE_INFO) { - isopus = parse_info(og); - } - else if ((isopus = test_non_opus(og))) { - switch (opst) { - case PAGE_INFO_BORDER: - parse_info_border(og); - break; - - case PAGE_COMMENT: - parse_comment(og); - break; - - case PAGE_SOUND: - parse_page_sound(og); - break; - } - } - if (isopus && opst < PAGE_SOUND && ogg_page_eos(og)) { - opuserror(err_opus_bad_stream); - } - if (!isopus) { - write_page(og, ogg_page_pageno(og) && non_opus_stream ? non_opus_stream : built_stream); - } + bool isopus; + if (opst == PAGE_INFO) { + isopus = parse_info(og); + } + else if ((isopus = test_non_opus(og))) { + switch (opst) { + case PAGE_INFO_BORDER: + parse_info_border(og); + break; + + case PAGE_COMMENT: + parse_comment(og); + break; + + case PAGE_SOUND: + parse_page_sound(og); + break; + } + } + if (isopus && opst < PAGE_SOUND && ogg_page_eos(og)) { + opuserror(err_opus_bad_stream); + } + if (!isopus) { + write_page(og, ogg_page_pageno(og) && non_opus_stream ? non_opus_stream : built_stream); + } } void read_page(ogg_sync_state *oy) { - int seeklen; - ogg_page og; - while ((seeklen = ogg_sync_pageseek(oy, &og)) != 0) { - if (seeklen > 0) { - seeked_len += seeklen; - parse_page(&og); - } - else seeked_len += -seeklen; - } + int seeklen; + ogg_page og; + while ((seeklen = ogg_sync_pageseek(oy, &og)) != 0) { + if (seeklen > 0) { + seeked_len += seeklen; + parse_page(&og); + } + else seeked_len += -seeklen; + } } diff --git a/src/retrieve-tags.c b/src/retrieve-tags.c index 924ba44..9e53ee7 100644 --- a/src/retrieve-tags.c +++ b/src/retrieve-tags.c @@ -10,351 +10,351 @@ #include "opuscomment.h" static void rtread(void *p, size_t len, FILE *fp) { - size_t readlen = fread(p, 1, len, fp); - if (readlen != len) opuserror(err_opus_lost_tag); + size_t readlen = fread(p, 1, len, fp); + if (readlen != len) opuserror(err_opus_lost_tag); } static uint32_t rtchunk(FILE *fp) { - uint32_t rtn; - rtread(&rtn, 4, fp); - return oi32(rtn); + uint32_t rtn; + rtread(&rtn, 4, fp); + return oi32(rtn); } static size_t rtfill(void *buf, size_t left, size_t buflen, FILE *fp) { - size_t rl = left > buflen ? buflen : left; - rtread(buf, rl, fp); - return rl; + size_t rl = left > buflen ? buflen : left; + rtread(buf, rl, fp); + return rl; } static bool test_mbp(uint8_t *buf, size_t len) { - uint8_t const *mbp = MBPeq; - size_t const mbplen = 23; - if (len < mbplen) { - return false; - } - while (*mbp) { - uint8_t c = *buf++; - if (c >= 0x61 && c <= 0x7a) { // a-z in ASCII - c -= 32; - } - if (*mbp != c) { - break; - } - mbp++; - } - return !*mbp; + uint8_t const *mbp = MBPeq; + size_t const mbplen = 23; + if (len < mbplen) { + return false; + } + while (*mbp) { + uint8_t c = *buf++; + if (c >= 0x61 && c <= 0x7a) { // a-z in ASCII + c -= 32; + } + if (*mbp != c) { + break; + } + mbp++; + } + return !*mbp; } static size_t tagpacket_total = 0; void check_tagpacket_length(size_t len) { - tagpacket_total += len; - if (tagpacket_total > TAG_LENGTH_LIMIT__OUTPUT) { - exceed_output_limit(); - } + tagpacket_total += len; + if (tagpacket_total > TAG_LENGTH_LIMIT__OUTPUT) { + exceed_output_limit(); + } } static bool rtcopy_write(FILE *packet_input, void *fptag_) { - FILE *fptag = fptag_; - uint32_t len = rtchunk(packet_input); - uint8_t buf[STACK_BUF_LEN]; - bool first = true; - bool field = true; - bool copy; - while (len) { - size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); - if (first) { - first = false; - - switch (O.edit) { - case EDIT_WRITE: - copy = O.tag_ignore_picture && test_mbp(buf, len); - break; - case EDIT_APPEND: - copy = true; - break; - } - if (copy) { - uint8_t chunk[4]; - *(uint32_t*)chunk = oi32(len); - fwrite(chunk, 4, 1, fptag); - check_tagpacket_length(4); - } - } - if (copy) { - if (O.tag_toupper && field) { - for (uint8_t *p = buf, *endp = buf + rl; p < endp; p++) { - if (*p >= 0x61 && *p <= 0x7a) { - *p -= 32; - } - if (*p == 0x3d) { - field = false; - break; - } - } - } - fwrite(buf, 1, rl, fptag); - check_tagpacket_length(rl); - } - len -= rl; - } - return copy; + FILE *fptag = fptag_; + uint32_t len = rtchunk(packet_input); + uint8_t buf[STACK_BUF_LEN]; + bool first = true; + bool field = true; + bool copy; + while (len) { + size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); + if (first) { + first = false; + + switch (O.edit) { + case EDIT_WRITE: + copy = O.tag_ignore_picture && test_mbp(buf, len); + break; + case EDIT_APPEND: + copy = true; + break; + } + if (copy) { + uint8_t chunk[4]; + *(uint32_t*)chunk = oi32(len); + fwrite(chunk, 4, 1, fptag); + check_tagpacket_length(4); + } + } + if (copy) { + if (O.tag_toupper && field) { + for (uint8_t *p = buf, *endp = buf + rl; p < endp; p++) { + if (*p >= 0x61 && *p <= 0x7a) { + *p -= 32; + } + if (*p == 0x3d) { + field = false; + break; + } + } + } + fwrite(buf, 1, rl, fptag); + check_tagpacket_length(rl); + } + len -= rl; + } + return copy; } static FILE *rtcd_src, *dellist_str, *dellist_len; static bool upcase_applied; static bool rtcopy_delete(FILE *packet_input, void *fptag_) { - static int idx = 1; - FILE *fptag = fptag_; - - // -dの引数と一つずつ比較していくための準備として、レコード1つを一時ファイルrtcd_srcに書き出す - rewind(rtcd_src); - uint32_t srclen = rtchunk(packet_input); - uint8_t buf[STACK_BUF_LEN]; - size_t len = srclen; - while (len) { - // フィールド名を大文字化する - bool field = true; - size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); - test_tag_field(buf, rl, true, &field, &upcase_applied); - fwrite(buf, 1, rl, rtcd_src); - len -= rl; - if (!field) break; - } - while (len) { - // フィールド名を抜けた後のループ - size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); - fwrite(buf, 1, rl, rtcd_src); - len -= rl; - } - - // 削除リストのループ - rewind(dellist_str); rewind(dellist_len); - bool matched = false; - while (fread(buf, 1, 5, dellist_len)) { - uint32_t cmplen = *(uint32_t*)buf; - bool field_only = buf[4]; - - size_t const bufhalf = STACK_BUF_LEN / 2; - uint8_t *cmp = buf + bufhalf; - rewind(rtcd_src); - // 比較 - if (field_only && srclen > cmplen || cmplen == srclen) { - // フィールド名のみ比較でソースが削除指定より長い または - // 全比較でソースと削除の長さが一致する時 - matched = true; - while (cmplen) { - size_t rl = fill_buffer(buf, cmplen, bufhalf, rtcd_src); - fread(cmp, 1, rl, dellist_str); - cmplen -= rl; - if (memcmp(buf, cmp, rl) != 0) { - matched = false; - break; - } - } - if (matched) { - if (field_only) { - fread(buf, 1, 1, rtcd_src); - if (*buf == 0x3d) { - goto MATCHED; - } - matched = false; - } - else { - goto MATCHED; - } - } - } - // 次の削除チャンクに進む - while (cmplen) { - cmplen -= fill_buffer(buf, cmplen, STACK_BUF_LEN, dellist_str); - } - } - // 削除するものと一致しなかったらfptagにコピー - rewind(rtcd_src); - *(uint32_t*)buf = oi32(srclen); - fwrite(buf, 4, 1, fptag); - check_tagpacket_length(4); - while (srclen) { - size_t rl = fill_buffer(buf, srclen, STACK_BUF_LEN, rtcd_src); - fwrite(buf, 1, rl, fptag); - srclen -= rl; - check_tagpacket_length(rl); - } - idx++; + static int idx = 1; + FILE *fptag = fptag_; + + // -dの引数と一つずつ比較していくための準備として、レコード1つを一時ファイルrtcd_srcに書き出す + rewind(rtcd_src); + uint32_t srclen = rtchunk(packet_input); + uint8_t buf[STACK_BUF_LEN]; + size_t len = srclen; + while (len) { + // フィールド名を大文字化する + bool field = true; + size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); + test_tag_field(buf, rl, true, &field, &upcase_applied); + fwrite(buf, 1, rl, rtcd_src); + len -= rl; + if (!field) break; + } + while (len) { + // フィールド名を抜けた後のループ + size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); + fwrite(buf, 1, rl, rtcd_src); + len -= rl; + } + + // 削除リストのループ + rewind(dellist_str); rewind(dellist_len); + bool matched = false; + while (fread(buf, 1, 5, dellist_len)) { + uint32_t cmplen = *(uint32_t*)buf; + bool field_only = buf[4]; + + size_t const bufhalf = STACK_BUF_LEN / 2; + uint8_t *cmp = buf + bufhalf; + rewind(rtcd_src); + // 比較 + if (field_only && srclen > cmplen || cmplen == srclen) { + // フィールド名のみ比較でソースが削除指定より長い または + // 全比較でソースと削除の長さが一致する時 + matched = true; + while (cmplen) { + size_t rl = fill_buffer(buf, cmplen, bufhalf, rtcd_src); + fread(cmp, 1, rl, dellist_str); + cmplen -= rl; + if (memcmp(buf, cmp, rl) != 0) { + matched = false; + break; + } + } + if (matched) { + if (field_only) { + fread(buf, 1, 1, rtcd_src); + if (*buf == 0x3d) { + goto MATCHED; + } + matched = false; + } + else { + goto MATCHED; + } + } + } + // 次の削除チャンクに進む + while (cmplen) { + cmplen -= fill_buffer(buf, cmplen, STACK_BUF_LEN, dellist_str); + } + } + // 削除するものと一致しなかったらfptagにコピー + rewind(rtcd_src); + *(uint32_t*)buf = oi32(srclen); + fwrite(buf, 4, 1, fptag); + check_tagpacket_length(4); + while (srclen) { + size_t rl = fill_buffer(buf, srclen, STACK_BUF_LEN, rtcd_src); + fwrite(buf, 1, rl, fptag); + srclen -= rl; + check_tagpacket_length(rl); + } + idx++; MATCHED: - return !matched; + return !matched; } static bool rtcopy_list(FILE *packet_input, void *listfd_) { - static size_t idx = 1; - int listfd = *(int*)listfd_; - uint32_t len = rtchunk(packet_input); - uint8_t buf[STACK_BUF_LEN]; - bool first = true; - bool field = true; - bool copy; - while (len) { - size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); - if (first) { - if (*buf == 0x3d) opuserror(err_opus_bad_tag, idx); - first = false; - copy = !O.tag_ignore_picture || !test_mbp(buf, len); - if (copy) { - write(listfd, &len, 4); - } - } - if (copy) { - if (field) { - if(!test_tag_field(buf, rl, O.tag_toupper, &field, &upcase_applied) && O.tag_verify) { - opuserror(err_opus_bad_tag, idx); - } - } - write(listfd, buf, rl); - } - len -= rl; - } - if (O.tag_verify && field) { - opuserror(err_opus_bad_tag, idx); - } - idx++; - return copy; + static size_t idx = 1; + int listfd = *(int*)listfd_; + uint32_t len = rtchunk(packet_input); + uint8_t buf[STACK_BUF_LEN]; + bool first = true; + bool field = true; + bool copy; + while (len) { + size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); + if (first) { + if (*buf == 0x3d) opuserror(err_opus_bad_tag, idx); + first = false; + copy = !O.tag_ignore_picture || !test_mbp(buf, len); + if (copy) { + write(listfd, &len, 4); + } + } + if (copy) { + if (field) { + if(!test_tag_field(buf, rl, O.tag_toupper, &field, &upcase_applied) && O.tag_verify) { + opuserror(err_opus_bad_tag, idx); + } + } + write(listfd, buf, rl); + } + len -= rl; + } + if (O.tag_verify && field) { + opuserror(err_opus_bad_tag, idx); + } + idx++; + return copy; } void *retrieve_tags(void *packet_input_) { - // parse_header_border() からスレッド化された - FILE *packet_input = packet_input_; - uint8_t buf[STACK_BUF_LEN]; - - struct rettag_st *rtn = calloc(1, sizeof(*rtn)); - - FILE *fptag = rtn->tag = tmpfile(); - rtn->part_of_comment = true; - uint32_t len; - rtread(buf, codec->commagic_len, packet_input); - if (memcmp(buf, codec->commagic, codec->commagic_len) != 0) { - if (codec->type == CODEC_VP8 && buf[5] != 2) { - len = codec->commagic_len; - rtn->part_of_comment = false; - } - else opuserror(err_opus_bad_content); - } - fwrite(buf, 1, codec->commagic_len, fptag); - if (!rtn->part_of_comment) { - // コメントパケットが無かった時用 - if (O.edit == EDIT_LIST) exit(0); - size_t vendorlen = strlen(new_vendor_string_ascii); - fwrite((uint32_t[]){oi32(vendorlen)}, 4, 1, fptag); - fwrite(new_vendor_string_ascii, 1, vendorlen, fptag); - rtn->tagbegin = codec->commagic_len + vendorlen + 4; - goto NOTCOMMENT; - } - - // ベンダ文字列 - len = rtchunk(packet_input); - *(uint32_t*)buf = oi32(len); - fwrite(buf, 4, 1, fptag); - check_tagpacket_length(codec->commagic_len + 4); - while (len) { - size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); - fwrite(buf, 1, rl, fptag); - check_tagpacket_length(rl); - len -= rl; - } - rtn->tagbegin = tagpacket_total; - - // レコード数 - size_t recordnum = rtn->del = rtchunk(packet_input); - fwrite(buf, 4, 1, fptag); // レコード数埋め(後でstore_tags()で書き換え) - check_tagpacket_length(4); - bool (*rtcopy)(FILE*, void*); - int pfd[2]; - pthread_t putth; - void *wh; - if (O.edit == EDIT_LIST) { - // タグ出力をスレッド化 put-tags.c へ - pipe(pfd); - FILE *fpput = fdopen(pfd[0], "r"); - pthread_create(&putth, NULL, put_tags, fpput); - rtcopy = rtcopy_list; - wh = pfd + 1; - } - else if (dellist_str) { - rtcopy = rtcopy_delete; - wh = fptag; - rtcd_src = tmpfile(); - } - else { - rtcopy = rtcopy_write; - wh = fptag; - } - - while (recordnum) { - rtn->num += rtcopy(packet_input, wh); - recordnum--; - } - rtn->del -= rtn->num; - - if (O.edit == EDIT_LIST) { - // タグ出力スレッド合流 - close(pfd[1]); - pthread_join(putth, NULL); - if (codec->type == CODEC_FLAC) return NULL; - tag_output_close(); - exit(0); - } - - len = fread(buf, 1, 1, packet_input); - if (len && (codec->prog || (*buf & 1))) { - // codec->prog ← opuscomment 以外は全部パディングを保存 - NOTCOMMENT: // ←VP8用 - rtn->padding = tmpfile(); - fwrite(buf, 1, len, rtn->padding); - check_tagpacket_length(len); - size_t n; - while ((n = fread(buf, 1, STACK_BUF_LEN, packet_input))) { - fwrite(buf, 1, n, rtn->padding); - check_tagpacket_length(n); - } - } - else { - size_t n; - while ((n = fread(buf, 1, STACK_BUF_LEN, packet_input))) {} - } - - if (rtcd_src) { - fclose(rtcd_src); - } - if (dellist_str) { - fclose(dellist_str); fclose(dellist_len); - } - fclose(packet_input); - rtn->upcase = upcase_applied; - return rtn; + // parse_header_border() からスレッド化された + FILE *packet_input = packet_input_; + uint8_t buf[STACK_BUF_LEN]; + + struct rettag_st *rtn = calloc(1, sizeof(*rtn)); + + FILE *fptag = rtn->tag = tmpfile(); + rtn->part_of_comment = true; + uint32_t len; + rtread(buf, codec->commagic_len, packet_input); + if (memcmp(buf, codec->commagic, codec->commagic_len) != 0) { + if (codec->type == CODEC_VP8 && buf[5] != 2) { + len = codec->commagic_len; + rtn->part_of_comment = false; + } + else opuserror(err_opus_bad_content); + } + fwrite(buf, 1, codec->commagic_len, fptag); + if (!rtn->part_of_comment) { + // コメントパケットが無かった時用 + if (O.edit == EDIT_LIST) exit(0); + size_t vendorlen = strlen(new_vendor_string_ascii); + fwrite((uint32_t[]){oi32(vendorlen)}, 4, 1, fptag); + fwrite(new_vendor_string_ascii, 1, vendorlen, fptag); + rtn->tagbegin = codec->commagic_len + vendorlen + 4; + goto NOTCOMMENT; + } + + // ベンダ文字列 + len = rtchunk(packet_input); + *(uint32_t*)buf = oi32(len); + fwrite(buf, 4, 1, fptag); + check_tagpacket_length(codec->commagic_len + 4); + while (len) { + size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); + fwrite(buf, 1, rl, fptag); + check_tagpacket_length(rl); + len -= rl; + } + rtn->tagbegin = tagpacket_total; + + // レコード数 + size_t recordnum = rtn->del = rtchunk(packet_input); + fwrite(buf, 4, 1, fptag); // レコード数埋め(後でstore_tags()で書き換え) + check_tagpacket_length(4); + bool (*rtcopy)(FILE*, void*); + int pfd[2]; + pthread_t putth; + void *wh; + if (O.edit == EDIT_LIST) { + // タグ出力をスレッド化 put-tags.c へ + pipe(pfd); + FILE *fpput = fdopen(pfd[0], "r"); + pthread_create(&putth, NULL, put_tags, fpput); + rtcopy = rtcopy_list; + wh = pfd + 1; + } + else if (dellist_str) { + rtcopy = rtcopy_delete; + wh = fptag; + rtcd_src = tmpfile(); + } + else { + rtcopy = rtcopy_write; + wh = fptag; + } + + while (recordnum) { + rtn->num += rtcopy(packet_input, wh); + recordnum--; + } + rtn->del -= rtn->num; + + if (O.edit == EDIT_LIST) { + // タグ出力スレッド合流 + close(pfd[1]); + pthread_join(putth, NULL); + if (codec->type == CODEC_FLAC) return NULL; + tag_output_close(); + exit(0); + } + + len = fread(buf, 1, 1, packet_input); + if (len && (codec->prog || (*buf & 1))) { + // codec->prog ← opuscomment 以外は全部パディングを保存 + NOTCOMMENT: // ←VP8用 + rtn->padding = tmpfile(); + fwrite(buf, 1, len, rtn->padding); + check_tagpacket_length(len); + size_t n; + while ((n = fread(buf, 1, STACK_BUF_LEN, packet_input))) { + fwrite(buf, 1, n, rtn->padding); + check_tagpacket_length(n); + } + } + else { + size_t n; + while ((n = fread(buf, 1, STACK_BUF_LEN, packet_input))) {} + } + + if (rtcd_src) { + fclose(rtcd_src); + } + if (dellist_str) { + fclose(dellist_str); fclose(dellist_len); + } + fclose(packet_input); + rtn->upcase = upcase_applied; + return rtn; } void rt_del_args(uint8_t *buf, size_t len, bool term) { - static uint32_t recordlen = 0; - static bool field = true; - if (!buf) { - if (!recordlen) { - opterror('d', catgets(catd, 7, 3, "invalid tag format")); - } - recordlen = oi32(recordlen); - fwrite(&recordlen, 4, 1, dellist_len); - uint8_t f = field; - fwrite(&f, 1, 1, dellist_len); - recordlen = 0; - field = true; - return; - } - dellist_len = dellist_len ? dellist_len : tmpfile(); - dellist_str = dellist_str ? dellist_str : tmpfile(); - - recordlen += len; - if (field && !test_tag_field(buf, len, true, &field, &upcase_applied)) { - opterror('d', catgets(catd, 7, 3, "invalid tag format")); - } - fwrite(buf, 1, len, dellist_str); + static uint32_t recordlen = 0; + static bool field = true; + if (!buf) { + if (!recordlen) { + opterror('d', catgets(catd, 7, 3, "invalid tag format")); + } + recordlen = oi32(recordlen); + fwrite(&recordlen, 4, 1, dellist_len); + uint8_t f = field; + fwrite(&f, 1, 1, dellist_len); + recordlen = 0; + field = true; + return; + } + dellist_len = dellist_len ? dellist_len : tmpfile(); + dellist_str = dellist_str ? dellist_str : tmpfile(); + + recordlen += len; + if (field && !test_tag_field(buf, len, true, &field, &upcase_applied)) { + opterror('d', catgets(catd, 7, 3, "invalid tag format")); + } + fwrite(buf, 1, len, dellist_str); } diff --git a/src/select-codec.c b/src/select-codec.c index 4ea732c..a40db86 100644 --- a/src/select-codec.c +++ b/src/select-codec.c @@ -14,79 +14,79 @@ #include "opuscomment.h" static void check_opus(ogg_page *og) { - if (og->body_len < 19) { - opuserror(err_opus_bad_content); - } - if ((og->body[8] & 0xf0) != 0) { - opuserror(err_opus_version); - } - if (O.gain_put) { - if (O.gain_q78) { - fprintf(stderr, "%d", (int16_t)oi16(*(int16_t*)(&og->body[16]))); - } - else { - fprintf(stderr, "%.8g", (int16_t)oi16(*(int16_t*)(&og->body[16])) / 256.0); - } - } - if (O.gain_fix) { - int16_t gi; - if (O.gain_relative) { - int gain = (int16_t)oi16(*(int16_t*)(&og->body[16])); - gain += O.gain_val; - if (gain > 32767) { - gain = 32767; - } - else if (gain < -32768) { - gain = -32768; - } - gi = gain; - } - else { - gi = O.gain_val; - } - if (O.gain_not_zero && gi == 0) { - gi = O.gain_val_sign ? -1 : 1; - } - - *(int16_t*)(&og->body[16]) = oi16(gi); - ogg_page_checksum_set(og); - - if (O.gain_put) { - if (O.gain_q78) { - fprintf(stderr, "\t%d", (int)gi); - } - else { - fprintf(stderr, "\t%.8g", gi / 256.0); - } - } - } - if (O.gain_put) { - fputc('\n', stderr); - } + if (og->body_len < 19) { + opuserror(err_opus_bad_content); + } + if ((og->body[8] & 0xf0) != 0) { + opuserror(err_opus_version); + } + if (O.gain_put) { + if (O.gain_q78) { + fprintf(stderr, "%d", (int16_t)oi16(*(int16_t*)(&og->body[16]))); + } + else { + fprintf(stderr, "%.8g", (int16_t)oi16(*(int16_t*)(&og->body[16])) / 256.0); + } + } + if (O.gain_fix) { + int16_t gi; + if (O.gain_relative) { + int gain = (int16_t)oi16(*(int16_t*)(&og->body[16])); + gain += O.gain_val; + if (gain > 32767) { + gain = 32767; + } + else if (gain < -32768) { + gain = -32768; + } + gi = gain; + } + else { + gi = O.gain_val; + } + if (O.gain_not_zero && gi == 0) { + gi = O.gain_val_sign ? -1 : 1; + } + + *(int16_t*)(&og->body[16]) = oi16(gi); + ogg_page_checksum_set(og); + + if (O.gain_put) { + if (O.gain_q78) { + fprintf(stderr, "\t%d", (int)gi); + } + else { + fprintf(stderr, "\t%.8g", gi / 256.0); + } + } + } + if (O.gain_put) { + fputc('\n', stderr); + } } static void check_theora(ogg_page *og) { - if (og->body_len != 42) { - opuserror(err_opus_bad_content); - } - // major 3, minor 2 - if (og->body[7] != 3 || og->body[8] != 2) { - opuserror(err_opus_version); - } + if (og->body_len != 42) { + opuserror(err_opus_bad_content); + } + // major 3, minor 2 + if (og->body[7] != 3 || og->body[8] != 2) { + opuserror(err_opus_version); + } } static void check_vp8(ogg_page *og) { - // https://people.freedesktop.org/~slomo/ogg-vp8/ogg-vp8.pdf - // ヘッダ長、ヘッダ種類 - if (og->body_len < 26 || og->body[5] != 1) opuserror(err_opus_bad_content); - // major 1 - if (og->body[6] != 1) opuserror(err_opus_version); - // minor 0。0ならば必ずサイズは26 - if (og->body[7] == 0 && og->body_len != 26) opuserror(err_opus_bad_content); + // https://people.freedesktop.org/~slomo/ogg-vp8/ogg-vp8.pdf + // ヘッダ長、ヘッダ種類 + if (og->body_len < 26 || og->body[5] != 1) opuserror(err_opus_bad_content); + // major 1 + if (og->body[6] != 1) opuserror(err_opus_version); + // minor 0。0ならば必ずサイズは26 + if (og->body[7] == 0 && og->body_len != 26) opuserror(err_opus_bad_content); } static void check_vorbis(ogg_page *og) { -// 0-6 "\x1" "\x76\x6f\x72\x62\x69\x73" +// 0-6 "\x1" "\x76\x6f\x72\x62\x69\x73" /*7-10 1) [vorbis_version] = read 32 bits as unsigned integer 11 2) [audio_channels] = read 8 bit integer as unsigned 12-15 3) [audio_sample_rate] = read 32 bits as unsigned integer @@ -97,11 +97,11 @@ static void check_vorbis(ogg_page *og) { 28.4-7 8) [blocksize_1] = 2 exponent (read 4 bits as unsigned integer) 29 9) [framing_flag] = read one bit */ - if (og->body_len != 30) opuserror(err_opus_bad_content); - if (oi32(*(uint32_t*)&og->body[7]) != 0) opuserror(err_opus_version); - if (!og->body[11]) opuserror(err_opus_bad_content); - if (oi32(*(uint32_t*)&og->body[12]) == 0) opuserror(err_opus_bad_content); - if (!og->body[29]) opuserror(err_opus_bad_content); + if (og->body_len != 30) opuserror(err_opus_bad_content); + if (oi32(*(uint32_t*)&og->body[7]) != 0) opuserror(err_opus_version); + if (!og->body[11]) opuserror(err_opus_bad_content); + if (oi32(*(uint32_t*)&og->body[12]) == 0) opuserror(err_opus_bad_content); + if (!og->body[29]) opuserror(err_opus_bad_content); } static void check_daala(ogg_page *og) { @@ -110,61 +110,61 @@ static void check_daala(ogg_page *og) { } static void check_speex(ogg_page *og) { - if (og->body_len != 80) opuserror(err_opus_bad_content); - if (oi32(*(uint32_t*)&og->body[28]) != 1) opuserror(err_opus_version); + if (og->body_len != 80) opuserror(err_opus_bad_content); + if (oi32(*(uint32_t*)&og->body[28]) != 1) opuserror(err_opus_version); } static void check_pcm(ogg_page *og) { - if (og->body_len != 28) opuserror(err_opus_bad_content); - if (htons(*(uint16_t*)&og->body[8]) != 0 || htons(*(uint16_t*)&og->body[10]) != 0) { - opuserror(err_opus_version); - } + if (og->body_len != 28) opuserror(err_opus_bad_content); + if (htons(*(uint16_t*)&og->body[8]) != 0 || htons(*(uint16_t*)&og->body[10]) != 0) { + opuserror(err_opus_version); + } } static void check_uvs(ogg_page *og) { - if (og->body_len != 48) opuserror(err_opus_bad_content); - if (htons(*(uint16_t*)&og->body[8]) != 0 || htons(*(uint16_t*)&og->body[10]) != 0) { - opuserror(err_opus_version); - } + if (og->body_len != 48) opuserror(err_opus_bad_content); + if (htons(*(uint16_t*)&og->body[8]) != 0 || htons(*(uint16_t*)&og->body[10]) != 0) { + opuserror(err_opus_version); + } } void check_flac(ogg_page *og); static struct codec_parser set[] = { - // "OpusHead", "OpusTags" - {CODEC_OPUS, NULL, "Opus", 8, "\x4f\x70\x75\x73\x48\x65\x61\x64", check_opus, 8, "\x4f\x70\x75\x73\x54\x61\x67\x73"}, - // "\x80" "theora", "\x81" "theora" - {CODEC_COMMON, "theoracomment", "Theora", 7, "\x80" "\x74\x68\x65\x6F\x72\x61", check_theora, 7, "\x81" "\x74\x68\x65\x6F\x72\x61"}, - // "\x1" "vorbis", "\x3" "vorbis" - {CODEC_COMMON, "vorbiscomment", "Vorbis", 7, "\x1" "\x76\x6f\x72\x62\x69\x73", check_vorbis, 7, "\x3" "\x76\x6f\x72\x62\x69\x73"}, - // "\x80" "daala", "\x81" "daala" - {CODEC_COMMON, "daalacomment", "Daala", 6, "\x80" "\x64\x61\x61\x6c\x61", check_daala, 6, "\x81" "\x64\x61\x61\x6c\x61"}, - // "Speex ", NULL - {CODEC_COMMON, "speexcomment", "Speex", 8, "\x53\x70\x65\x65\x78\x20\x20\x20", check_speex, 0, NULL}, - // "PCM ", NULL - {CODEC_COMMON, "oggpcmcomment", "PCM", 8, "\x50\x43\x4d\x20\x20\x20\x20\x20", check_pcm, 0, NULL}, - // "UVS ", NULL - {CODEC_COMMON, "ogguvscomment", "UVS", 8, "\x55\x56\x53\x20\x20\x20\x20\x20", check_uvs, 0, NULL}, - // FLAC (non Ogg) - {CODEC_FLAC, "flaccomment", "FLAC", 0, NULL, NULL, 0, NULL}, - // "\x4f" "VP80", "\x4f" "VP80" "\x2 " (optional) - {CODEC_VP8, "vp8comment", "VP8", 5, "\x4f" "\x56\x50\x38\x30", check_vp8, 7, "\x4f" "\x56\x50\x38\x30" "\x2\x20"}, - {0, NULL, NULL, 0, NULL, NULL, 0, NULL}, + // "OpusHead", "OpusTags" + {CODEC_OPUS, NULL, "Opus", 8, "\x4f\x70\x75\x73\x48\x65\x61\x64", check_opus, 8, "\x4f\x70\x75\x73\x54\x61\x67\x73"}, + // "\x80" "theora", "\x81" "theora" + {CODEC_COMMON, "theoracomment", "Theora", 7, "\x80" "\x74\x68\x65\x6F\x72\x61", check_theora, 7, "\x81" "\x74\x68\x65\x6F\x72\x61"}, + // "\x1" "vorbis", "\x3" "vorbis" + {CODEC_COMMON, "vorbiscomment", "Vorbis", 7, "\x1" "\x76\x6f\x72\x62\x69\x73", check_vorbis, 7, "\x3" "\x76\x6f\x72\x62\x69\x73"}, + // "\x80" "daala", "\x81" "daala" + {CODEC_COMMON, "daalacomment", "Daala", 6, "\x80" "\x64\x61\x61\x6c\x61", check_daala, 6, "\x81" "\x64\x61\x61\x6c\x61"}, + // "Speex ", NULL + {CODEC_COMMON, "speexcomment", "Speex", 8, "\x53\x70\x65\x65\x78\x20\x20\x20", check_speex, 0, NULL}, + // "PCM ", NULL + {CODEC_COMMON, "oggpcmcomment", "PCM", 8, "\x50\x43\x4d\x20\x20\x20\x20\x20", check_pcm, 0, NULL}, + // "UVS ", NULL + {CODEC_COMMON, "ogguvscomment", "UVS", 8, "\x55\x56\x53\x20\x20\x20\x20\x20", check_uvs, 0, NULL}, + // FLAC (non Ogg) + {CODEC_FLAC, "flaccomment", "FLAC", 0, NULL, NULL, 0, NULL}, + // "\x4f" "VP80", "\x4f" "VP80" "\x2 " (optional) + {CODEC_VP8, "vp8comment", "VP8", 5, "\x4f" "\x56\x50\x38\x30", check_vp8, 7, "\x4f" "\x56\x50\x38\x30" "\x2\x20"}, + {0, NULL, NULL, 0, NULL, NULL, 0, NULL}, }; void select_codec(void) { - for (codec = set + 1; codec->prog != NULL && strcmp(program_name, codec->prog) != 0; codec++) {} - if (!codec->prog) codec = set; + for (codec = set + 1; codec->prog != NULL && strcmp(program_name, codec->prog) != 0; codec++) {} + if (!codec->prog) codec = set; }; bool select_codec_by_name(char const *name) { - static locale_t clocale = (locale_t)0; - if (!clocale) { - clocale = newlocale(LC_ALL, "C", (locale_t)0); - if (!clocale) { - oserror(); - } - } - for (codec = set; codec->name != NULL && strcasecmp_l(name, codec->name, clocale) != 0; codec++) {} - return !!codec->name; + static locale_t clocale = (locale_t)0; + if (!clocale) { + clocale = newlocale(LC_ALL, "C", (locale_t)0); + if (!clocale) { + oserror(); + } + } + for (codec = set; codec->name != NULL && strcasecmp_l(name, codec->name, clocale) != 0; codec++) {} + return !!codec->name; } diff --git a/src/version.h b/src/version.h index 3ed0642..888875f 100644 --- a/src/version.h +++ b/src/version.h @@ -1,5 +1,5 @@ -#define OPUSCOMMENT_VERSION "1.5.14" +#define OPUSCOMMENT_VERSION "1.5.15" -#define OPUSCOMMENT_REVISION_YEAR (2019 - 1900) -#define OPUSCOMMENT_REVISION_MONTH (9 - 1) -#define OPUSCOMMENT_REVISION_DAY 7 +#define OPUSCOMMENT_REVISION_YEAR (2022 - 1900) +#define OPUSCOMMENT_REVISION_MONTH (7 - 1) +#define OPUSCOMMENT_REVISION_DAY 26 From 341e556f96406fb4ad0d254a0a74796814daf77b Mon Sep 17 00:00:00 2001 From: zvezdochiot Date: Wed, 27 Jul 2022 19:03:13 +0300 Subject: [PATCH 7/9] 1.5.15: astyle --style=allman --- src/endianness.c | 20 +- src/error.c | 39 +- src/error.h | 6 +- src/global.h | 60 ++-- src/ocutil.c | 78 ++-- src/opuscomment.c | 263 ++++++++------ src/opuscomment.h | 6 +- src/opuspic2tag.c | 44 ++- src/parse-tags.c | 362 ++++++++++++------- src/picture.c | 860 +++++++++++++++++++++++--------------------- src/picture.h | 37 +- src/put-tags.c | 117 ++++-- src/read-flac.c | 125 ++++--- src/read.c | 281 ++++++++++----- src/retrieve-tags.c | 193 ++++++---- src/select-codec.c | 116 +++--- 16 files changed, 1582 insertions(+), 1025 deletions(-) diff --git a/src/endianness.c b/src/endianness.c index bb855e4..0aabc63 100644 --- a/src/endianness.c +++ b/src/endianness.c @@ -34,9 +34,9 @@ uint32_t oi32(uint32_t i) return i; else if (et == 1) return (i << 24 - | (i & (255ULL << 8)) << 8 - | (i & (255ULL << 16)) >> 8 - | i >> 24); + | (i & (255ULL << 8)) << 8 + | (i & (255ULL << 16)) >> 8 + | i >> 24); else { uint8_t *val = (uint8_t *)&i; @@ -55,13 +55,13 @@ uint64_t oi64(uint64_t i) return i; else if (et == 1) return (i << 56 - | (i & (255ULL << 8)) << 40 - | (i & (255ULL << 16)) >> 24 - | (i & (255ULL << 24)) >> 8 - | (i & (255ULL << 32)) >> 8 - | (i & (255ULL << 40)) >> 24 - | (i & (255ULL << 48)) >> 40 - | i >> 56); + | (i & (255ULL << 8)) << 40 + | (i & (255ULL << 16)) >> 24 + | (i & (255ULL << 24)) >> 8 + | (i & (255ULL << 32)) >> 8 + | (i & (255ULL << 40)) >> 24 + | (i & (255ULL << 48)) >> 40 + | i >> 56); else { uint8_t *val = (uint8_t *)&i; diff --git a/src/error.c b/src/error.c index 8aaefd8..0c8c842 100644 --- a/src/error.c +++ b/src/error.c @@ -4,15 +4,18 @@ #include "opuscomment.h" -void errorprefix(void) { +void errorprefix(void) +{ fprintf(stderr, catgets(catd, 1, 3, "%s: "), program_name); } -noreturn void mainerror(int e, ...) { +noreturn void mainerror(int e, ...) +{ va_list ap; va_start(ap, e); errorprefix(); - char const *msg[] = { + char const *msg[] = + { #define LIST(I, E, S) S, #include "errordef/opuscomment.tab" #undef LIST @@ -22,23 +25,28 @@ noreturn void mainerror(int e, ...) { exit(1); } -noreturn void opuserror(int e, ...) { +noreturn void opuserror(int e, ...) +{ va_list ap; va_start(ap, e); errorprefix(); - struct { + struct + { bool report_page; char const *default_message; - } msg[] = { + } msg[] = + { #define LIST(I, B, E, S) {B, S}, #include "errordef/opus.tab" #undef LIST }; - if (msg[e].report_page && codec->type != CODEC_FLAC) { + if (msg[e].report_page && codec->type != CODEC_FLAC) + { fprintf(stderr, catgets(catd, 1, 4, "%s format error: page %u: "), codec->name, opus_idx); } - else { + else + { fprintf(stderr, catgets(catd, 1, 8, "%s format error: "), codec->name); } vfprintf(stderr, catgets(catd, 3, e, msg[e].default_message), ap); @@ -46,12 +54,14 @@ noreturn void opuserror(int e, ...) { exit(2); } -noreturn void oserror(void) { +noreturn void oserror(void) +{ perror(program_name); exit(3); } -noreturn void oserror_fmt(char const *e, ...) { +noreturn void oserror_fmt(char const *e, ...) +{ va_list ap; va_start(ap, e); errorprefix(); @@ -60,14 +70,16 @@ noreturn void oserror_fmt(char const *e, ...) { exit(3); } -noreturn void fileerror(char const *file) { +noreturn void fileerror(char const *file) +{ errorprefix(); fprintf(stderr, catgets(catd, 1, 5, "%s: "), file); perror(NULL); exit(3); } -noreturn void opterror(int c, char const *e, ...) { +noreturn void opterror(int c, char const *e, ...) +{ va_list ap; va_start(ap, e); errorprefix(); @@ -78,6 +90,7 @@ noreturn void opterror(int c, char const *e, ...) { } -noreturn void exceed_output_limit(void) { +noreturn void exceed_output_limit(void) +{ mainerror(err_main_output_limit, TAG_LENGTH_LIMIT__OUTPUT >> 20); } diff --git a/src/error.h b/src/error.h index 389fa09..999d140 100644 --- a/src/error.h +++ b/src/error.h @@ -8,13 +8,15 @@ noreturn void oserror_fmt(char const*, ...); noreturn void fileerror(char const*); noreturn void opterror(int, char const*, ...); -enum err_opus_ { +enum err_opus_ +{ #define LIST(I, B, E, S) err_opus_##E = I, #include "errordef/opus.tab" #undef LIST }; -enum err_main_ { +enum err_main_ +{ #define LIST(I, E, S) err_main_##E = I, #include "errordef/opuscomment.tab" #undef LIST diff --git a/src/global.h b/src/global.h index b1a3596..04a4ffe 100644 --- a/src/global.h +++ b/src/global.h @@ -12,8 +12,10 @@ #include #include -GLOBAL struct { - enum { +GLOBAL struct +{ + enum + { EDIT_NONE, EDIT_LIST, EDIT_WRITE, @@ -29,7 +31,8 @@ GLOBAL struct { bool gain_put; bool tag_ignore_picture; - enum { + enum + { TAG_ESCAPE_TAB, TAG_ESCAPE_BACKSLASH, TAG_ESCAPE_NUL, @@ -47,7 +50,8 @@ GLOBAL struct { char *in, *out; } O; -GLOBAL enum { +GLOBAL enum +{ PAGE_INFO, PAGE_INFO_BORDER, PAGE_COMMENT, @@ -55,8 +59,10 @@ GLOBAL enum { PAGE_SOUND, } opst; -GLOBAL struct codec_parser { - enum { +GLOBAL struct codec_parser +{ + enum + { CODEC_OPUS, CODEC_COMMON, CODEC_FLAC, @@ -73,29 +79,29 @@ GLOBAL struct codec_parser { GLOBAL char const *program_name; GLOBAL char const * const program_name_default GLOBAL_VAL("opuscomment"); GLOBAL uint8_t const * const MBPeq - // "METADATA_BLOCK_PICTURE=" in ASCII - GLOBAL_VAL( - "\x4d\x45\x54\x41\x44\x41\x54\x41\x5f\x42\x4c\x4f\x43\x4b\x5f\x50" - "\x49\x43\x54\x55\x52\x45\x3d" - ); +// "METADATA_BLOCK_PICTURE=" in ASCII +GLOBAL_VAL( + "\x4d\x45\x54\x41\x44\x41\x54\x41\x5f\x42\x4c\x4f\x43\x4b\x5f\x50" + "\x49\x43\x54\x55\x52\x45\x3d" +); GLOBAL uint8_t const * const b64tab_ascii - GLOBAL_VAL( - "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" // A-O - "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a" // P-Z - "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" // a-o - "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a" // p-z - "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39" // 0-9 - "\x2b\x2f\x3d" // + / = - ); +GLOBAL_VAL( + "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" // A-O + "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a" // P-Z + "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" // a-o + "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a" // p-z + "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39" // 0-9 + "\x2b\x2f\x3d" // + / = +); GLOBAL uint8_t const * const new_vendor_string_ascii - // 'Unknown (This string was generated by a metadata editor "opuscomment")' in ASCII - GLOBAL_VAL( - "\x55\x6e\x6b\x6e\x6f\x77\x6e\x20\x28\x54\x68\x69\x73\x20\x73\x74" - "\x72\x69\x6e\x67\x20\x77\x61\x73\x20\x67\x65\x6e\x65\x72\x61\x74" - "\x65\x64\x20\x62\x79\x20\x61\x20\x6d\x65\x74\x61\x64\x61\x74\x61" - "\x20\x65\x64\x69\x74\x6f\x72\x20\x22\x6f\x70\x75\x73\x63\x6f\x6d" - "\x6d\x65\x6e\x74\x22\x29" - ); +// 'Unknown (This string was generated by a metadata editor "opuscomment")' in ASCII +GLOBAL_VAL( + "\x55\x6e\x6b\x6e\x6f\x77\x6e\x20\x28\x54\x68\x69\x73\x20\x73\x74" + "\x72\x69\x6e\x67\x20\x77\x61\x73\x20\x67\x65\x6e\x65\x72\x61\x74" + "\x65\x64\x20\x62\x79\x20\x61\x20\x6d\x65\x74\x61\x64\x61\x74\x61" + "\x20\x65\x64\x69\x74\x6f\x72\x20\x22\x6f\x70\x75\x73\x63\x6f\x6d" + "\x6d\x65\x6e\x74\x22\x29" +); GLOBAL uint32_t opus_idx, opus_sno, opus_idx_diff; GLOBAL bool leave_header_packets; diff --git a/src/ocutil.c b/src/ocutil.c index 5149f2f..5f2f195 100644 --- a/src/ocutil.c +++ b/src/ocutil.c @@ -6,20 +6,25 @@ #include "opuscomment.h" -static bool test_tag_field_keepcase(uint8_t *line, size_t n, bool *on_field) { +static bool test_tag_field_keepcase(uint8_t *line, size_t n, bool *on_field) +{ size_t i; bool valid = true; - for (i = 0; i < n && line[i] != 0x3d; i++) { - if (!(line[i] >= 0x20 && line[i] <= 0x7d)) { + for (i = 0; i < n && line[i] != 0x3d; i++) + { + if (!(line[i] >= 0x20 && line[i] <= 0x7d)) + { valid = false; } } if (i < n) *on_field = false; return valid; } -bool test_tag_field(uint8_t *line, size_t n, bool upcase, bool *on_field, bool *upcase_applied_) { +bool test_tag_field(uint8_t *line, size_t n, bool upcase, bool *on_field, bool *upcase_applied_) +{ // フィールドの使用文字チェック・大文字化 - if (!upcase) { + if (!upcase) + { return test_tag_field_keepcase(line, n, on_field); } @@ -28,11 +33,14 @@ bool test_tag_field(uint8_t *line, size_t n, bool upcase, bool *on_field, bool * size_t i; bool valid = true; - for (i = 0; i < n && line[i] != 0x3d; i++) { - if (!(line[i] >= 0x20 && line[i] <= 0x7d)) { + for (i = 0; i < n && line[i] != 0x3d; i++) + { + if (!(line[i] >= 0x20 && line[i] <= 0x7d)) + { valid = false; } - if (line[i] >= 0x61 && line[i] <= 0x7a) { + if (line[i] >= 0x61 && line[i] <= 0x7a) + { line[i] -= 32; *upcase_applied = true; } @@ -41,18 +49,23 @@ bool test_tag_field(uint8_t *line, size_t n, bool upcase, bool *on_field, bool * return valid; } -size_t fill_buffer(void *buf, size_t left, size_t buflen, FILE *fp) { +size_t fill_buffer(void *buf, size_t left, size_t buflen, FILE *fp) +{ size_t filllen = left > buflen ? buflen : left; size_t readlen = fread(buf, 1, filllen, fp); return filllen; } -bool test_non_opus(ogg_page *og) { - if (ogg_page_serialno(og) == opus_sno) { - if (ogg_page_bos(og)) { +bool test_non_opus(ogg_page *og) +{ + if (ogg_page_serialno(og) == opus_sno) + { + if (ogg_page_bos(og)) + { opuserror(err_opus_bad_stream); } - if (opst != PAGE_SOUND && ogg_page_pageno(og) != opus_idx) { + if (opst != PAGE_SOUND && ogg_page_pageno(og) != opus_idx) + { opuserror(err_opus_discontinuous, ogg_page_pageno(og), opus_idx); } return true; @@ -60,54 +73,67 @@ bool test_non_opus(ogg_page *og) { // have_multi_streams = true; int pno = ogg_page_pageno(og); - if (pno == 0) { - if (leave_header_packets) { + if (pno == 0) + { + if (leave_header_packets) + { opuserror(err_opus_bad_stream); } - if (!ogg_page_bos(og) || ogg_page_eos(og) || ogg_page_granulepos(og) != 0) { + if (!ogg_page_bos(og) || ogg_page_eos(og) || ogg_page_granulepos(og) != 0) + { opuserror(err_opus_bad_stream); } } - else if (pno == 1) { + else if (pno == 1) + { // 複数論理ストリームの先頭は全て0で始まるため、何かが1ページ目を始めたら今後0ページ目は来ないはずである。 leave_header_packets = true; } - else { - if (!leave_header_packets) { + else + { + if (!leave_header_packets) + { // 他で1ページ目が始まってないのに2ページ目以上が来た場合 opuserror(err_opus_bad_stream); } } - if (pno && ogg_page_bos(og)) { + if (pno && ogg_page_bos(og)) + { // Opusが続いているストリーム途中でBOSが来るはずがない opuserror(err_opus_bad_stream); } return false; } -void write_page(ogg_page *og, FILE *fp) { +void write_page(ogg_page *og, FILE *fp) +{ size_t ret; ret = fwrite(og->header, 1, og->header_len, fp); - if (ret != og->header_len) { + if (ret != og->header_len) + { oserror(); } ret = fwrite(og->body, 1, og->body_len, fp); - if (ret != og->body_len) { + if (ret != og->body_len) + { oserror(); } } -void set_pageno(ogg_page *og, int no) { +void set_pageno(ogg_page *og, int no) +{ *(uint32_t*)&og->header[18] = oi32(no); } -void set_granulepos(ogg_page *og, int64_t pos) { +void set_granulepos(ogg_page *og, int64_t pos) +{ *(int64_t*)&og->header[6] = oi64(pos); } #if _POSIX_C_SOURCE < 200809L -size_t strnlen(char const *src, size_t n) { +size_t strnlen(char const *src, size_t n) +{ char const *endp = src + n, *p = src; while (p < endp && *p) p++; return p - src; diff --git a/src/opuscomment.c b/src/opuscomment.c index e4fd098..0c7d45f 100644 --- a/src/opuscomment.c +++ b/src/opuscomment.c @@ -20,9 +20,11 @@ #include "version.h" static struct codec_parser const *default_codec; -static void usage(void) { +static void usage(void) +{ char revision[64]; - strftime(revision, 64, "%x", &(struct tm){ + strftime(revision, 64, "%x", &(struct tm) + { .tm_year = OPUSCOMMENT_REVISION_YEAR, .tm_mon = OPUSCOMMENT_REVISION_MONTH, .tm_mday = OPUSCOMMENT_REVISION_DAY, @@ -31,64 +33,67 @@ static void usage(void) { fputc('\n', stderr); fprintf(stderr, catgets(catd, 6, 4, -"Synopsys:\n" -" %1$s [-l] [-C codec] [-i idx] [-0DepQRUvV~] srcfile\n" -" %1$s {-a|-w} [-C codec] [-i idx] [-g gain|-s scale] [-0e] [-c tagfile] [-t NAME=VALUE ...] [-d NAME[=VALUE] ...] [-1DQprRUv] srcfile [output]\n" -" %1$s [-h]\n" -"\n" -"Options:\n" -" -l List mode\n" -" -a Append mode\n" -" -w Write mode\n" -" -h Print this message and exit\n" -" -C codec\n" -" Select codec\n" -" -i idx\n" -" Specify %2$s index for editing in multiplexed Ogg stream\n" -" (1-origin, without non-%2$s stream)\n" -" -R Assume tag IO to be encoded in UTF-8\n" -" -~ Use '~', not for escaping line feed\n" -" -e Use escape sequence; \\\\, \\n, \\r and \\0\n" -" -0 Use '\\0' separation for tag IO\n" -" -t NAME=VALUE\n" -" Add the argument as a tag\n" -" -c tagfile\n" -" In list mode, write tags to tagfile.\n" -" In append/write mode, read tags from tagfile.\n" -" -d NAME[=VALUE]\n" -" Delete tags matched with the argument from srcfile.\n" -" When VALUE is omitted, All of NAME is removed. Implies -a, -U\n" -" -p Supress editing for METADATA_BLOCK_PICTURE\n" -" -U Convert field name stored in srcfile to uppercase\n" -" -V Verify tags stored in srcfile\n" -" -T Return error when tag input is not terminated by line feed\n" -" -D In list mode, defer tag output until comment packet is built.\n" -" Implies -V.\n" -" In append/write mode, return error when tag input is empty.\n" -" Implies -T\n" - ), program_name, default_codec->name); - if (!default_codec->prog) { + "Synopsys:\n" + " %1$s [-l] [-C codec] [-i idx] [-0DepQRUvV~] srcfile\n" + " %1$s {-a|-w} [-C codec] [-i idx] [-g gain|-s scale] [-0e] [-c tagfile] [-t NAME=VALUE ...] [-d NAME[=VALUE] ...] [-1DQprRUv] srcfile [output]\n" + " %1$s [-h]\n" + "\n" + "Options:\n" + " -l List mode\n" + " -a Append mode\n" + " -w Write mode\n" + " -h Print this message and exit\n" + " -C codec\n" + " Select codec\n" + " -i idx\n" + " Specify %2$s index for editing in multiplexed Ogg stream\n" + " (1-origin, without non-%2$s stream)\n" + " -R Assume tag IO to be encoded in UTF-8\n" + " -~ Use '~', not for escaping line feed\n" + " -e Use escape sequence; \\\\, \\n, \\r and \\0\n" + " -0 Use '\\0' separation for tag IO\n" + " -t NAME=VALUE\n" + " Add the argument as a tag\n" + " -c tagfile\n" + " In list mode, write tags to tagfile.\n" + " In append/write mode, read tags from tagfile.\n" + " -d NAME[=VALUE]\n" + " Delete tags matched with the argument from srcfile.\n" + " When VALUE is omitted, All of NAME is removed. Implies -a, -U\n" + " -p Supress editing for METADATA_BLOCK_PICTURE\n" + " -U Convert field name stored in srcfile to uppercase\n" + " -V Verify tags stored in srcfile\n" + " -T Return error when tag input is not terminated by line feed\n" + " -D In list mode, defer tag output until comment packet is built.\n" + " Implies -V.\n" + " In append/write mode, return error when tag input is empty.\n" + " Implies -T\n" + ), program_name, default_codec->name); + if (!default_codec->prog) + { fprintf(stderr, catgets(catd, 6, 5, -" -g gain\n" -" Specify output gain in dB\n" -" -s scale\n" -" Specify output gain in scale for PCM samples.\n" -" 1 for same scale. 0.5 for half.\n" -" -r Specify that the gain is relative to internal value\n" -" -1 When output gain becomes 0 by converting to internal representation,\n" -" set [+-]1/256 dB instead\n" -" -Q Use Q7.8 format for editing output gain\n" -" -v Put output gain to stderr\n" - )); + " -g gain\n" + " Specify output gain in dB\n" + " -s scale\n" + " Specify output gain in scale for PCM samples.\n" + " 1 for same scale. 0.5 for half.\n" + " -r Specify that the gain is relative to internal value\n" + " -1 When output gain becomes 0 by converting to internal representation,\n" + " set [+-]1/256 dB instead\n" + " -Q Use Q7.8 format for editing output gain\n" + " -v Put output gain to stderr\n" + )); } exit(1); } -static void fail_to_parse(int c) { +static void fail_to_parse(int c) +{ opterror(c, catgets(catd, 7, 1, "failed to parse value")); } -static void out_of_range(int c) { +static void out_of_range(int c) +{ opterror(c, catgets(catd, 7, 2, "the value is out of range")); } @@ -96,14 +101,17 @@ void select_codec(void); bool select_codec_by_name(char const*); void set_parser_type(void); -static void parse_args(int argc, char **argv) { +static void parse_args(int argc, char **argv) +{ int c; bool added_tag = false, del_tag = false; int gainfmt; double gv; - while ((c = getopt(argc, argv, "01ac:C:d:Deg:hi:lpqQrRs:t:TUvVw~")) != -1) { - switch (c) { + while ((c = getopt(argc, argv, "01ac:C:d:Deg:hi:lpqQrRs:t:TUvVw~")) != -1) + { + switch (c) + { case 'l': O.edit = EDIT_LIST; break; @@ -127,13 +135,16 @@ static void parse_args(int argc, char **argv) { { char *endp; gv = strtod(optarg, &endp); - if (optarg == endp) { + if (optarg == endp) + { fail_to_parse(c); } - if (!isfinite(gv)) { + if (!isfinite(gv)) + { out_of_range(c); } - if (gv > 65536 || gv < -65536) { + if (gv > 65536 || gv < -65536) + { out_of_range(c); } } @@ -205,25 +216,28 @@ static void parse_args(int argc, char **argv) { O.tag_verify = true; break; case 'i': + { + char *endp; + unsigned long val = strtoul(optarg, &endp, 10); + if (endp == optarg || *endp != '\0') { - char *endp; - unsigned long val = strtoul(optarg, &endp, 10); - if (endp == optarg || *endp != '\0') { - fail_to_parse(c); - } - if (errno == ERANGE || val == 0 || val > INT_MAX) { - out_of_range(c); - } - O.target_idx = (int)val; + fail_to_parse(c); } - break; + if (errno == ERANGE || val == 0 || val > INT_MAX) + { + out_of_range(c); + } + O.target_idx = (int)val; + } + break; case 'q': // 何もしない break; case 'C': - if (!select_codec_by_name(optarg)) { + if (!select_codec_by_name(optarg)) + { opterror(c, catgets(catd, 7, 4, "unknown codec \"%s\""), optarg); } break; @@ -234,11 +248,14 @@ static void parse_args(int argc, char **argv) { } } // オプションループ抜け - if (del_tag || added_tag) { + if (del_tag || added_tag) + { pticonv_close(); } - if (del_tag) { - switch (O.edit) { + if (del_tag) + { + switch (O.edit) + { case EDIT_NONE: O.edit = EDIT_APPEND; break; @@ -248,60 +265,76 @@ static void parse_args(int argc, char **argv) { mainerror(err_main_del_without_a); } } - if (added_tag && O.edit == EDIT_LIST) { + if (added_tag && O.edit == EDIT_LIST) + { mainerror(err_main_tag_with_l); } - if (added_tag && !O.edit) { + if (added_tag && !O.edit) + { mainerror(err_main_tag_without_aw); } - if (!O.gain_fix && !O.edit) { + if (!O.gain_fix && !O.edit) + { O.edit = EDIT_LIST; } - else if (O.gain_fix) { - switch (gainfmt) { + else if (O.gain_fix) + { + switch (gainfmt) + { case 'g': - if (O.gain_q78) { + if (O.gain_q78) + { O.gain_val = (int)trunc(gv); } - else { + else + { O.gain_val = (int)trunc(gv * 256); } break; case 's': - if (gv <= 0) { + if (gv <= 0) + { out_of_range(gainfmt); } O.gain_val = (int)trunc(20 * log10(gv) * 256); break; } O.gain_val_sign = signbit(gv); - if (!O.gain_relative && (O.gain_val > 32767 || O.gain_val < -32768)) { + if (!O.gain_relative && (O.gain_val > 32767 || O.gain_val < -32768)) + { out_of_range(gainfmt); } - if (O.edit == EDIT_LIST) { + if (O.edit == EDIT_LIST) + { mainerror(err_main_gain_with_l); } - else if (!O.edit) { - if (O.tag_filename/* || added_tag*/) { + else if (!O.edit) + { + if (O.tag_filename/* || added_tag*/) + { mainerror(err_main_tag_without_aw); } } } - if (O.edit == EDIT_APPEND) { + if (O.edit == EDIT_APPEND) + { O.tag_ignore_picture = false; } - if (codec->prog) { + if (codec->prog) + { O.gain_fix = false; O.gain_put = false; } } -static void interrupted(int sig) { +static void interrupted(int sig) +{ exit(1); } -static void read_ogg(void) { +static void read_ogg(void) +{ ogg_sync_state oy; ogg_sync_init(&oy); @@ -309,19 +342,23 @@ static void read_ogg(void) { uint8_t *buf = ogg_sync_buffer(&oy, buflen); size_t len = fread(buf, 1, buflen, stream_input); if (len == (size_t)-1) oserror(); - if (len < 4) { + if (len < 4) + { opuserror(err_opus_non_ogg); } - if (memcmp(buf, "\x4f\x67\x67\x53", 4) != 0) { + if (memcmp(buf, "\x4f\x67\x67\x53", 4) != 0) + { opuserror(err_opus_non_ogg); } ogg_sync_wrote(&oy, len); read_page(&oy); - for (;;) { + for (;;) + { buf = ogg_sync_buffer(&oy, buflen); len = fread(buf, 1, buflen, stream_input); - if (!len) { + if (!len) + { break; } ogg_sync_wrote(&oy, len); @@ -329,7 +366,8 @@ static void read_ogg(void) { } } -int main(int argc, char **argv) { +int main(int argc, char **argv) +{ struct sigaction sa; sa.sa_handler = interrupted; sigemptyset(&sa.sa_mask); @@ -343,11 +381,13 @@ int main(int argc, char **argv) { sigaction(SIGTERM, &sa, NULL); setlocale(LC_ALL, ""); - if (*argv[0]) { + if (*argv[0]) + { char *p = strrchr(argv[0], '/'); program_name = p ? p + 1 : argv[0]; } - else { + else + { program_name = program_name_default; } select_codec(); @@ -357,13 +397,16 @@ int main(int argc, char **argv) { #define co catd = catopen("opuscomment", NL_CAT_LOCALE) #ifdef DEFAULT_NLS_PATH char const *nlspath = getenv("NLSPATH"); - if (nlspath && *nlspath) { + if (nlspath && *nlspath) + { co; } - else { + else + { setenv("NLSPATH", DEFAULT_NLS_PATH, true); co; - if (catd == (nl_catd)-1) { + if (catd == (nl_catd)-1) + { unsetenv("NLSPATH"); co; } @@ -376,30 +419,37 @@ int main(int argc, char **argv) { if (argc == 1) usage(); parse_args(argc, argv); - if (!argv[optind]) { + if (!argv[optind]) + { mainerror(err_main_no_file); } - if (argv[optind + 1]) { - if (O.edit == EDIT_LIST) { + if (argv[optind + 1]) + { + if (O.edit == EDIT_LIST) + { mainerror(err_main_many_args); } - if (argv[optind + 2]) { + if (argv[optind + 2]) + { mainerror(err_main_many_args); } O.out = argv[optind + 1]; } O.in = argv[optind]; - if (strcmp(O.in, "-")) { + if (strcmp(O.in, "-")) + { stream_input = fopen(O.in, "r"); - if (!stream_input) { + if (!stream_input) + { fileerror(O.in); } struct stat sb; fstat(fileno(stream_input), &sb); input_is_regular_file = S_ISREG(sb.st_mode); } - else { + else + { stream_input = stdin; } @@ -407,7 +457,8 @@ int main(int argc, char **argv) { if (codec->type == CODEC_FLAC) read_flac(); else read_ogg(); - if (opst < PAGE_SOUND) { + if (opst < PAGE_SOUND) + { opuserror(err_opus_interrupted); } diff --git a/src/opuscomment.h b/src/opuscomment.h index e6f6658..a359476 100644 --- a/src/opuscomment.h +++ b/src/opuscomment.h @@ -22,13 +22,15 @@ #include "error.h" #include "iconv-impl.h" -struct rettag_st { +struct rettag_st +{ FILE *tag, *padding; long tagbegin; size_t num, del; bool upcase, part_of_comment; }; -struct edit_st { +struct edit_st +{ FILE *str, *len, *pict; size_t num; }; diff --git a/src/opuspic2tag.c b/src/opuspic2tag.c index b048375..47b9a95 100644 --- a/src/opuspic2tag.c +++ b/src/opuspic2tag.c @@ -8,7 +8,8 @@ #include "picture.h" #include "version.h" -struct option options[] = { +struct option options[] = +{ {"help", no_argument, 0, 'h'}, {"desc", required_argument, 0, 'd'}, {"type", required_argument, 0, 't'}, @@ -31,22 +32,23 @@ int main(int argc, char **argv) int c; while((c = getopt_long(argc, argv, "hd:t:o:", options, NULL)) != -1) { - switch(c){ - case 'h': - print_help = 1; - break; - case 'd': - picture_desc = optarg; - break; - case 't': - picture_type = atoi(optarg); - picture_type = (picture_type > 20) ? 3 : picture_type; - break; - case 'o': - path_out = optarg; - break; - default: - return EXIT_FAILURE; + switch(c) + { + case 'h': + print_help = 1; + break; + case 'd': + picture_desc = optarg; + break; + case 't': + picture_type = atoi(optarg); + picture_type = (picture_type > 20) ? 3 : picture_type; + break; + case 'o': + path_out = optarg; + break; + default: + return EXIT_FAILURE; } } if(print_help) @@ -90,7 +92,9 @@ int main(int argc, char **argv) perror("fopen"); return EXIT_FAILURE; } - } else { + } + else + { out = stdout; } if(picture_data != NULL) @@ -103,7 +107,9 @@ int main(int argc, char **argv) { fprintf(stderr,"Bad picture size: %d\n", picture_meta_len); free(picture_meta); - } else { + } + else + { strcpy(picture_meta, picture_tag); strcat(picture_meta, "="); strcat(picture_meta, picture_data); diff --git a/src/parse-tags.c b/src/parse-tags.c index 8572617..d56bccc 100644 --- a/src/parse-tags.c +++ b/src/parse-tags.c @@ -15,17 +15,21 @@ static bool from_file; static FILE *edit_input; -static void readerror(void) { - if (from_file) { +static void readerror(void) +{ + if (from_file) + { fileerror(O.tag_filename); } - else { + else + { oserror(); } } static size_t tagnum; -static noreturn void tagerror(char *e) { +static noreturn void tagerror(char *e) +{ errorprefix(); fprintf(stderr, catgets(catd, 1, 6, "editing input #%zu: "), tagnum + 1); fputs(e, stderr); @@ -33,56 +37,72 @@ static noreturn void tagerror(char *e) { exit(1); } -static void err_nosep(void) { +static void err_nosep(void) +{ tagerror(catgets(catd, 5, 1, "no field separator")); } -static void err_name(void) { +static void err_name(void) +{ tagerror(catgets(catd, 5, 2, "invalid field name")); } -static void err_empty(void) { +static void err_empty(void) +{ tagerror(catgets(catd, 5, 3, "empty field name")); } -static void err_bin(void) { +static void err_bin(void) +{ tagerror(catgets(catd, 5, 4, "binary file")); } -static void err_esc(void) { +static void err_esc(void) +{ tagerror(catgets(catd, 5, 5, "invalid escape sequence")); } -static void err_utf8(void) { +static void err_utf8(void) +{ tagerror(catgets(catd, 5, 6, "invalid UTF-8 sequence")); } -static void err_base64(void) { +static void err_base64(void) +{ tagerror(catgets(catd, 5, 7, "invalid BASE64 sequence")); } -static void err_noterm(void) { +static void err_noterm(void) +{ mainerror(err_main_no_term); } -static void toutf8(int fdu8) { +static void toutf8(int fdu8) +{ size_t const buflen = STACK_BUF_LEN / 2; char ubuf[STACK_BUF_LEN]; char *lbuf = ubuf + buflen; iconv_t cd = iconv_new("UTF-8", O.tag_raw ? "UTF-8" : nl_langinfo(CODESET)); size_t readlen, remain, total; - remain = 0; total = 0; - while ((readlen = fread(&lbuf[remain], 1, buflen - remain, edit_input)) != 0) { + remain = 0; + total = 0; + while ((readlen = fread(&lbuf[remain], 1, buflen - remain, edit_input)) != 0) + { total += readlen; - if (total > TAG_LENGTH_LIMIT__INPUT) { + if (total > TAG_LENGTH_LIMIT__INPUT) + { mainerror(err_main_long_input); } - if (O.tag_escape != TAG_ESCAPE_NUL && strnlen(&lbuf[remain], readlen) != readlen) { + if (O.tag_escape != TAG_ESCAPE_NUL && strnlen(&lbuf[remain], readlen) != readlen) + { err_bin(); } size_t llen = readlen + remain; remain = llen; - for (;;) { + for (;;) + { size_t ulen = buflen; char *lp = lbuf, *up = ubuf; size_t iconvret = iconv(cd, &lp, &remain, &up, &ulen); int ie = errno; - if (iconvret == (size_t)-1) { - switch (ie) { + if (iconvret == (size_t)-1) + { + switch (ie) + { case EILSEQ: oserror(); break; @@ -96,20 +116,24 @@ static void toutf8(int fdu8) { if (iconvret != (size_t)-1 || ie == EINVAL) break; } } - if (fclose(edit_input) == EOF) { + if (fclose(edit_input) == EOF) + { readerror(); } - if (remain) { + if (remain) + { errno = EILSEQ; readerror(); } - char *up = ubuf; readlen = buflen; + char *up = ubuf; + readlen = buflen; remain = iconv(cd, NULL, NULL, &up, &readlen); if (remain == (size_t)-1) oserror(); iconv_close(cd); write(fdu8, ubuf, up - ubuf); close(fdu8); - if (O.edit == EDIT_WRITE && !total && O.tag_deferred) { + if (O.edit == EDIT_WRITE && !total && O.tag_deferred) + { mainerror(err_main_no_input); } } @@ -123,8 +147,10 @@ static long picture_top; static uint8_t b64[4]; static int_fast8_t b64pos, b64rawlen; static bool b64term, store_picture; -static void finalize_record(void) { - if (store_picture) { +static void finalize_record(void) +{ + if (store_picture) + { if (b64pos != 0) err_base64(); long picend = ftell(flacpict); uint32_t piclength = picend - picture_top - 4; @@ -134,7 +160,8 @@ static void finalize_record(void) { fseek(flacpict, 0, SEEK_END); picture_top = picend; } - else { + else + { fwrite(&recordlen, 4, 1, strcount); tagnum++; } @@ -144,8 +171,10 @@ static void finalize_record(void) { static size_t wsplen, fieldlen; static bool on_field, keep_blank; static uint8_t field_pending[22]; -static bool test_blank(uint8_t *line, size_t n, bool lf) { - if (first_call) { +static bool test_blank(uint8_t *line, size_t n, bool lf) +{ + if (first_call) + { // = で始まってたらすぐエラー(575) if (*line == 0x3d) err_empty(); first_call = false; @@ -158,11 +187,14 @@ static bool test_blank(uint8_t *line, size_t n, bool lf) { b64term = false; b64pos = 0; } - if (keep_blank) { + if (keep_blank) + { size_t i; bool blank_with_ctrl = false; - for (i = 0; i < n; i++) { - switch (line[i]) { + for (i = 0; i < n; i++) + { + switch (line[i]) + { case 0x9: case 0xa: case 0xd: @@ -174,36 +206,44 @@ static bool test_blank(uint8_t *line, size_t n, bool lf) { } if (!keep_blank) break; } - if (keep_blank) { + if (keep_blank) + { // まだ先頭から空白が続いている時 - if (lf) { + if (lf) + { // 行が終わったが全て空白だったので飛ばして次の行へ first_call = true; } - else { + else + { // 行が続いている wsplen += n; } return true; } // 空白状態を抜けてフィールド判別状態にある - if (blank_with_ctrl) { + if (blank_with_ctrl) + { //空白がwsp以外を含んでいたらエラー err_name(); } editlen += 4 + wsplen; - if (editlen > TAG_LENGTH_LIMIT__OUTPUT) { + if (editlen > TAG_LENGTH_LIMIT__OUTPUT) + { exceed_output_limit(); } fieldlen = recordlen = wsplen; - if (wsplen <= 22) { + if (wsplen <= 22) + { memset(field_pending, 0x20, wsplen); } - else { + else + { // 空白と見做していた分を書き込み uint8_t buf[STACK_BUF_LEN]; memset(buf, 0x20, STACK_BUF_LEN); - while (wsplen) { + while (wsplen) + { size_t wlen = wsplen > STACK_BUF_LEN ? STACK_BUF_LEN : wsplen; fwrite(buf, 1, wlen, strstore); wsplen -= wlen; @@ -213,25 +253,31 @@ static bool test_blank(uint8_t *line, size_t n, bool lf) { return false; } -static void decode_base64(uint8_t *line, size_t n) { +static void decode_base64(uint8_t *line, size_t n) +{ uint8_t const *end = line + n; - while (line < end) { + while (line < end) + { uint8_t const *b64char = strchr(b64tab_ascii, *line++); if (!b64char) err_base64(); uint_fast8_t b64idx = b64char - b64tab_ascii; if (b64term && b64pos == 0) err_base64(); if (b64idx == 64 && b64pos < 2) err_base64(); - if (b64pos == 2 && b64idx == 64) { + if (b64pos == 2 && b64idx == 64) + { b64term = true; b64idx = 0; b64rawlen = 1; } - if (b64pos == 3) { - if (b64term) { + if (b64pos == 3) + { + if (b64term) + { if (b64idx != 64) err_base64(); else b64idx = 0; } - else if (b64idx == 64) { + else if (b64idx == 64) + { b64idx = 0; b64term = true; b64rawlen = 2; @@ -239,7 +285,8 @@ static void decode_base64(uint8_t *line, size_t n) { else b64rawlen = 3; } b64[b64pos++] = b64idx; - if (b64pos == 4) { + if (b64pos == 4) + { uint8_t raw[3]; // 000000 11|1111 2222|22 333333 raw[0] = b64[0] << 2 | b64[1] >> 4; @@ -251,33 +298,41 @@ static void decode_base64(uint8_t *line, size_t n) { } } -static void append_buffer(uint8_t *line, size_t n) { - if (store_picture) { +static void append_buffer(uint8_t *line, size_t n) +{ + if (store_picture) + { decode_base64(line, n); return; } editlen += n; - if (editlen > TAG_LENGTH_LIMIT__OUTPUT) { + if (editlen > TAG_LENGTH_LIMIT__OUTPUT) + { exceed_output_limit(); } recordlen += n; fwrite(line, 1, n, strstore); } -static bool count_field_len(uint8_t *line, size_t n) { +static bool count_field_len(uint8_t *line, size_t n) +{ size_t add; bool filled; - if (on_field) { + if (on_field) + { add = n - fieldlen; } - else { + else + { add = (uint8_t*)memchr(line, 0x3d, n) - line; } - if (fieldlen + add <= 22) { + if (fieldlen + add <= 22) + { memcpy(&field_pending[fieldlen], line, add); filled = !on_field; } - else { + else + { memcpy(&field_pending[fieldlen], line, 22 - fieldlen); filled = true; } @@ -285,39 +340,52 @@ static bool count_field_len(uint8_t *line, size_t n) { return filled; } -static void test_mbp(uint8_t **line, size_t *n) { - if (fieldlen > 22) { +static void test_mbp(uint8_t **line, size_t *n) +{ + if (fieldlen > 22) + { append_buffer(*line, *n); } - else { + else + { bool filled; size_t before = fieldlen; filled = count_field_len(*line, *n); size_t add = fieldlen - before; - if (filled) { + if (filled) + { size_t w; // M_B_Pを比較する分のバッファが埋まったか項目名が決まった場合 - if (fieldlen > 22) { - if (add + before > 22) { + if (fieldlen > 22) + { + if (add + before > 22) + { add = 22 - before; } w = 22; } - else if (fieldlen == 22 && !on_field) { + else if (fieldlen == 22 && !on_field) + { if (codec->type == CODEC_FLAC && - memcmp(field_pending, MBPeq, 22) == 0) { + memcmp(field_pending, MBPeq, 22) == 0) + { store_picture = true; - fwrite((uint32_t[]){0}, 4, 1, flacpict); + fwrite((uint32_t[]) + { + 0 + }, 4, 1, flacpict); editlen -= 4; uint8_t *databegin = strchr(*line, 0x3d) + 1; append_buffer(databegin, *n - (databegin - *line)); return; } - else { + else + { w = 22; } } - else { + else + { w = fieldlen; } append_buffer(field_pending, w); @@ -328,15 +396,20 @@ static void test_mbp(uint8_t **line, size_t *n) { } } -static void line_oc(uint8_t *line, size_t n, bool lf) { +static void line_oc(uint8_t *line, size_t n, bool lf) +{ static bool afterlf = false; - if (!line) { - if (!first_call) { - if (O.tag_check_line_term && !afterlf) { + if (!line) + { + if (!first_call) + { + if (O.tag_check_line_term && !afterlf) + { err_noterm(); } - if (!keep_blank) { + if (!keep_blank) + { if (on_field) err_nosep(); finalize_record(); } @@ -346,34 +419,41 @@ static void line_oc(uint8_t *line, size_t n, bool lf) { return; } - if (afterlf) { - if (*line == 0x9 || *line == 0x7e) { + if (afterlf) + { + if (*line == 0x9 || *line == 0x7e) + { *line = 0x0a; if (lf) n--; append_buffer(line, n); afterlf = lf; return; } - else { + else + { finalize_record(); } } if (test_blank(line, n, lf)) return; if (lf) n--; - if (on_field) { + if (on_field) + { if(!test_tag_field(line, n, true, &on_field, NULL)) err_name(); if (on_field && lf) err_name(); test_mbp(&line, &n); } - else { + else + { append_buffer(line, n); } afterlf = lf; } -static int esctab(int c) { - switch (c) { +static int esctab(int c) +{ + switch (c) + { case 0x30: // '0' c = '\0'; break; @@ -393,15 +473,20 @@ static int esctab(int c) { return c; } -static void line_vc(uint8_t *line, size_t n, bool lf) { +static void line_vc(uint8_t *line, size_t n, bool lf) +{ static bool escape_pending = false; - if (!line) { - if (!first_call) { - if (O.tag_check_line_term) { + if (!line) + { + if (!first_call) + { + if (O.tag_check_line_term) + { err_noterm(); } - if (!keep_blank) { + if (!keep_blank) + { if (escape_pending) err_esc(); if (on_field) err_nosep(); finalize_record(); @@ -413,19 +498,24 @@ static void line_vc(uint8_t *line, size_t n, bool lf) { if(test_blank(line, n, lf)) return; - if (escape_pending) { + if (escape_pending) + { *line = esctab(*line); } if (lf) n--; uint8_t *bs = memchr(line + escape_pending, 0x5c, n - escape_pending); escape_pending = false; - if (bs) { + if (bs) + { uint8_t *unesc = bs, *end = line + n; - while (bs < end) { + while (bs < end) + { uint8_t c = *bs++; - if (c == 0x5c) { - if (bs == end) { + if (c == 0x5c) + { + if (bs == end) + { if (lf) err_esc(); escape_pending = true; n--; @@ -437,23 +527,30 @@ static void line_vc(uint8_t *line, size_t n, bool lf) { *unesc++ = c; } } - if (on_field) { + if (on_field) + { if(!test_tag_field(line, n, true, &on_field, NULL)) err_name(); if (on_field && lf) err_nosep(); test_mbp(&line, &n); } - else { + else + { append_buffer(line, n); } - if (lf) { + if (lf) + { finalize_record(); } } -static void line_nul(uint8_t *line, size_t n, bool term) { - if (!line) { - if (!first_call) { - if (!keep_blank) { +static void line_nul(uint8_t *line, size_t n, bool term) +{ + if (!line) + { + if (!first_call) + { + if (!keep_blank) + { if (on_field) err_nosep(); finalize_record(); } @@ -464,38 +561,45 @@ static void line_nul(uint8_t *line, size_t n, bool term) { if(test_blank(line, n, term)) return; - if (on_field) { + if (on_field) + { if(!test_tag_field(line, n, true, &on_field, NULL)) err_name(); if (on_field && term) err_nosep(); test_mbp(&line, &n); } - else { + else + { append_buffer(line, n); } if (term) finalize_record(); } -void prepare_record(void) { +void prepare_record(void) +{ strstore = strstore ? strstore : tmpfile(); strcount = strcount ? strcount : tmpfile(); flacpict = flacpict ? flacpict : codec->type == CODEC_FLAC ? tmpfile() : NULL; } -void *split_text(void *fp_) { +void *split_text(void *fp_) +{ FILE *fp = fp_; prepare_record(); void (*line)(uint8_t *, size_t, bool) = O.tag_escape ? line_vc : line_oc; uint8_t tagbuf[STACK_BUF_LEN]; size_t readlen; - while ((readlen = fread(tagbuf, 1, STACK_BUF_LEN, fp)) != 0) { + while ((readlen = fread(tagbuf, 1, STACK_BUF_LEN, fp)) != 0) + { uint8_t *p1, *p2; - if (memchr(tagbuf, 0, readlen) != NULL) { + if (memchr(tagbuf, 0, readlen) != NULL) + { err_bin(); } p1 = tagbuf; - while ((p2 = memchr(p1, 0x0a, readlen - (p1 - tagbuf))) != NULL) { + while ((p2 = memchr(p1, 0x0a, readlen - (p1 - tagbuf))) != NULL) + { p2++; line(p1, p2 - p1, true); p1 = p2; @@ -508,16 +612,19 @@ void *split_text(void *fp_) { return NULL; } -void *split_binary(void *fp_) { +void *split_binary(void *fp_) +{ FILE *fp = fp_; prepare_record(); uint8_t tagbuf[STACK_BUF_LEN]; size_t readlen; - while ((readlen = fread(tagbuf, 1, STACK_BUF_LEN, fp)) != 0) { + while ((readlen = fread(tagbuf, 1, STACK_BUF_LEN, fp)) != 0) + { uint8_t *p1, *p2; p1 = tagbuf; - while ((p2 = memchr(p1, 0, readlen - (p1 - tagbuf))) != NULL) { + while ((p2 = memchr(p1, 0, readlen - (p1 - tagbuf))) != NULL) + { line_nul(p1, p2 - p1, true); p1 = p2 + 1; } @@ -529,26 +636,33 @@ void *split_binary(void *fp_) { return NULL; } -void *parse_tags(void* nouse_) { +void *parse_tags(void* nouse_) +{ bool do_read = true; - if (tagnum) { + if (tagnum) + { // -tでタグを追加した場合、-cが無ければstdinを使わない - if (!O.tag_filename) { + if (!O.tag_filename) + { do_read = false; } } - if (O.tag_filename && strcmp(O.tag_filename, "-") != 0) { + if (O.tag_filename && strcmp(O.tag_filename, "-") != 0) + { from_file = true; edit_input = fopen(O.tag_filename, "r"); - if (!edit_input) { + if (!edit_input) + { fileerror(O.tag_filename); } } - else { + else + { edit_input = stdin; } - if (do_read) { + if (do_read) + { // UTF-8化された文字列をチャンク化する処理をスレッド化(化が多い) int pfd[2]; pipe(pfd); @@ -571,15 +685,18 @@ void *parse_tags(void* nouse_) { } static iconv_t optcd = (iconv_t)-1; -void parse_opt_tag(int opt, char const *arg) { +void parse_opt_tag(int opt, char const *arg) +{ char *ls = (char*)arg; size_t l = strlen(ls); - if (optcd == (iconv_t)-1) { + if (optcd == (iconv_t)-1) + { optcd = iconv_new("UTF-8", nl_langinfo(CODESET)); } void (*addbuf)(uint8_t *, size_t, bool); - switch (opt) { + switch (opt) + { case 't': prepare_record(); addbuf = line_nul; @@ -591,14 +708,17 @@ void parse_opt_tag(int opt, char const *arg) { char u8buf[STACK_BUF_LEN]; size_t u8left; char *u8; - while (l) { + while (l) + { u8left = STACK_BUF_LEN; u8 = u8buf; size_t ret = iconv(optcd, &ls, &l, &u8, &u8left); - if (ret == (size_t)-1) { + if (ret == (size_t)-1) + { // 引数処理なのでEINVAL時のバッファ持ち越しは考慮しない - if (errno != E2BIG) { + if (errno != E2BIG) + { oserror(); } } @@ -606,13 +726,15 @@ void parse_opt_tag(int opt, char const *arg) { } u8 = u8buf; u8left = STACK_BUF_LEN; - if (iconv(optcd, NULL, NULL, &u8, &u8left) == (size_t)-1) { + if (iconv(optcd, NULL, NULL, &u8, &u8left) == (size_t)-1) + { oserror(); } addbuf(u8buf, u8 - u8buf, true); addbuf(NULL, 0, false); } -void pticonv_close(void) { +void pticonv_close(void) +{ iconv_close(optcd); } diff --git a/src/picture.c b/src/picture.c index b228d83..ffc1053 100644 --- a/src/picture.c +++ b/src/picture.c @@ -32,149 +32,171 @@ #include #include "picture.h" -static const char BASE64_TABLE[64]={ - 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', - 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', - 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', - 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' +static const char BASE64_TABLE[64]= +{ + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' }; /*Utility function for base64 encoding METADATA_BLOCK_PICTURE tags. Stores BASE64_LENGTH(len)+1 bytes in dst (including a terminating NUL).*/ -void opustags_base64_encode(char *dst, const char *src, int len){ - unsigned s0; - unsigned s1; - unsigned s2; - int ngroups; - int i; - ngroups=len/3; - for(i=0;i>2]; - dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4]; - dst[4*i+2]=BASE64_TABLE[(s1&15)<<2|s2>>6]; - dst[4*i+3]=BASE64_TABLE[s2&63]; - } - len-=3*i; - if(len==1){ - s0=(unsigned char)src[3*i+0]; - dst[4*i+0]=BASE64_TABLE[s0>>2]; - dst[4*i+1]=BASE64_TABLE[(s0&3)<<4]; - dst[4*i+2]='='; - dst[4*i+3]='='; - i++; - } - else if(len==2){ - s0=(unsigned char)src[3*i+0]; - s1=(unsigned char)src[3*i+1]; - dst[4*i+0]=BASE64_TABLE[s0>>2]; - dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4]; - dst[4*i+2]=BASE64_TABLE[(s1&15)<<2]; - dst[4*i+3]='='; - i++; - } - dst[4*i]='\0'; +void opustags_base64_encode(char *dst, const char *src, int len) +{ + unsigned s0; + unsigned s1; + unsigned s2; + int ngroups; + int i; + ngroups=len/3; + for(i=0; i>2]; + dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4]; + dst[4*i+2]=BASE64_TABLE[(s1&15)<<2|s2>>6]; + dst[4*i+3]=BASE64_TABLE[s2&63]; + } + len-=3*i; + if(len==1) + { + s0=(unsigned char)src[3*i+0]; + dst[4*i+0]=BASE64_TABLE[s0>>2]; + dst[4*i+1]=BASE64_TABLE[(s0&3)<<4]; + dst[4*i+2]='='; + dst[4*i+3]='='; + i++; + } + else if(len==2) + { + s0=(unsigned char)src[3*i+0]; + s1=(unsigned char)src[3*i+1]; + dst[4*i+0]=BASE64_TABLE[s0>>2]; + dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4]; + dst[4*i+2]=BASE64_TABLE[(s1&15)<<2]; + dst[4*i+3]='='; + i++; + } + dst[4*i]='\0'; } /*A version of strncasecmp() that is guaranteed to only ignore the case of ASCII characters.*/ -int opustags_oi_strncasecmp(const char *a, const char *b, int n){ - int i; - for(i=0;i='a'&&aval<='z') { - aval-='a'-'A'; - } - if(bval>='a'&&bval<='z'){ - bval-='a'-'A'; - } - diff=aval-bval; - if(diff){ - return diff; +int opustags_oi_strncasecmp(const char *a, const char *b, int n) +{ + int i; + for(i=0; i='a'&&aval<='z') + { + aval-='a'-'A'; + } + if(bval>='a'&&bval<='z') + { + bval-='a'-'A'; + } + diff=aval-bval; + if(diff) + { + return diff; + } } - } - return 0; + return 0; } -int opustags_is_jpeg(const unsigned char *buf, size_t length){ - return length>=11&&memcmp(buf,"\xFF\xD8\xFF\xE0",4)==0 - &&(buf[4]<<8|buf[5])>=16&&memcmp(buf+6,"JFIF",5)==0; +int opustags_is_jpeg(const unsigned char *buf, size_t length) +{ + return length>=11&&memcmp(buf,"\xFF\xD8\xFF\xE0",4)==0 + &&(buf[4]<<8|buf[5])>=16&&memcmp(buf+6,"JFIF",5)==0; } -int opustags_is_png(const unsigned char *buf, size_t length){ - return length>=8&&memcmp(buf,"\x89PNG\x0D\x0A\x1A\x0A",8)==0; +int opustags_is_png(const unsigned char *buf, size_t length) +{ + return length>=8&&memcmp(buf,"\x89PNG\x0D\x0A\x1A\x0A",8)==0; } -int opustags_is_gif(const unsigned char *buf, size_t length){ - return length>=6 - &&(memcmp(buf,"GIF87a",6)==0||memcmp(buf,"GIF89a",6)==0); +int opustags_is_gif(const unsigned char *buf, size_t length) +{ + return length>=6 + &&(memcmp(buf,"GIF87a",6)==0||memcmp(buf,"GIF89a",6)==0); } /*Tries to extract the width, height, bits per pixel, and palette size of a PNG. On failure, simply leaves its outputs unmodified.*/ void opustags_params_extract_png(const unsigned char *data, size_t data_length, - ogg_uint32_t *width, ogg_uint32_t *height, - ogg_uint32_t *depth, ogg_uint32_t *colors, - int *has_palette){ - if(opustags_is_png(data,data_length)){ - size_t offs; - offs=8; - while(data_length-offs>=12){ - ogg_uint32_t chunk_len; - chunk_len=READ_U32_BE(data+offs); - if(chunk_len>data_length-(offs+12))break; - else if(chunk_len==13&&memcmp(data+offs+4,"IHDR",4)==0){ - int color_type; - *width=READ_U32_BE(data+offs+8); - *height=READ_U32_BE(data+offs+12); - color_type=data[offs+17]; - if(color_type==3){ - *depth=24; - *has_palette=1; + ogg_uint32_t *width, ogg_uint32_t *height, + ogg_uint32_t *depth, ogg_uint32_t *colors, + int *has_palette) +{ + if(opustags_is_png(data,data_length)) + { + size_t offs; + offs=8; + while(data_length-offs>=12) + { + ogg_uint32_t chunk_len; + chunk_len=READ_U32_BE(data+offs); + if(chunk_len>data_length-(offs+12))break; + else if(chunk_len==13&&memcmp(data+offs+4,"IHDR",4)==0) + { + int color_type; + *width=READ_U32_BE(data+offs+8); + *height=READ_U32_BE(data+offs+12); + color_type=data[offs+17]; + if(color_type==3) + { + *depth=24; + *has_palette=1; + } + else + { + int sample_depth; + sample_depth=data[offs+16]; + if(color_type==0)*depth=sample_depth; + else if(color_type==2)*depth=sample_depth*3; + else if(color_type==4)*depth=sample_depth*2; + else if(color_type==6)*depth=sample_depth*4; + *colors=0; + *has_palette=0; + break; + } + } + else if(*has_palette>0&&memcmp(data+offs+4,"PLTE",4)==0) + { + *colors=chunk_len/3; + break; + } + offs+=12+chunk_len; } - else{ - int sample_depth; - sample_depth=data[offs+16]; - if(color_type==0)*depth=sample_depth; - else if(color_type==2)*depth=sample_depth*3; - else if(color_type==4)*depth=sample_depth*2; - else if(color_type==6)*depth=sample_depth*4; - *colors=0; - *has_palette=0; - break; - } - } - else if(*has_palette>0&&memcmp(data+offs+4,"PLTE",4)==0){ - *colors=chunk_len/3; - break; - } - offs+=12+chunk_len; } - } } /*Tries to extract the width, height, bits per pixel, and palette size of a GIF. On failure, simply leaves its outputs unmodified.*/ void opustags_params_extract_gif(const unsigned char *data, size_t data_length, - ogg_uint32_t *width, ogg_uint32_t *height, - ogg_uint32_t *depth, ogg_uint32_t *colors, - int *has_palette){ - if(opustags_is_gif(data,data_length)&&data_length>=14){ - *width=data[6]|data[7]<<8; - *height=data[8]|data[9]<<8; - /*libFLAC hard-codes the depth to 24.*/ - *depth=24; - *colors=1<<((data[10]&7)+1); - *has_palette=1; - } + ogg_uint32_t *width, ogg_uint32_t *height, + ogg_uint32_t *depth, ogg_uint32_t *colors, + int *has_palette) +{ + if(opustags_is_gif(data,data_length)&&data_length>=14) + { + *width=data[6]|data[7]<<8; + *height=data[8]|data[9]<<8; + /*libFLAC hard-codes the depth to 24.*/ + *depth=24; + *colors=1<<((data[10]&7)+1); + *has_palette=1; + } } @@ -182,43 +204,48 @@ void opustags_params_extract_gif(const unsigned char *data, size_t data_length, JPEG. On failure, simply leaves its outputs unmodified.*/ void opustags_params_extract_jpeg(const unsigned char *data, size_t data_length, - ogg_uint32_t *width, ogg_uint32_t *height, - ogg_uint32_t *depth, ogg_uint32_t *colors, - int *has_palette){ - if(opustags_is_jpeg(data,data_length)){ - size_t offs; - offs=2; - for(;;){ - size_t segment_len; - int marker; - while(offs=data_length||(marker>=0xD8&&marker<=0xDA))break; - /*RST* (restart markers): skip (no segment length).*/ - else if(marker>=0xD0&&marker<=0xD7)continue; - /*Read the length of the marker segment.*/ - if(data_length-offs<2)break; - segment_len=data[offs]<<8|data[offs+1]; - if(segment_len<2||data_length-offs0xC0&&marker<0xD0&&(marker&3)!=0)){ - /*Found a SOFn (start of frame) marker segment:*/ - if(segment_len>=8){ - *height=data[offs+3]<<8|data[offs+4]; - *width=data[offs+5]<<8|data[offs+6]; - *depth=data[offs+2]*data[offs+7]; - *colors=0; - *has_palette=0; + ogg_uint32_t *width, ogg_uint32_t *height, + ogg_uint32_t *depth, ogg_uint32_t *colors, + int *has_palette) +{ + if(opustags_is_jpeg(data,data_length)) + { + size_t offs; + offs=2; + for(;;) + { + size_t segment_len; + int marker; + while(offs=data_length||(marker>=0xD8&&marker<=0xDA))break; + /*RST* (restart markers): skip (no segment length).*/ + else if(marker>=0xD0&&marker<=0xD7)continue; + /*Read the length of the marker segment.*/ + if(data_length-offs<2)break; + segment_len=data[offs]<<8|data[offs+1]; + if(segment_len<2||data_length-offs0xC0&&marker<0xD0&&(marker&3)!=0)) + { + /*Found a SOFn (start of frame) marker segment:*/ + if(segment_len>=8) + { + *height=data[offs+3]<<8|data[offs+4]; + *width=data[offs+5]<<8|data[offs+6]; + *depth=data[offs+2]*data[offs+7]; + *colors=0; + *has_palette=0; + } + break; + } + /*Other markers: skip the whole marker segment.*/ + offs+=segment_len; } - break; - } - /*Other markers: skip the whole marker segment.*/ - offs+=segment_len; } - } } /*Parse a picture SPECIFICATION as given on the command-line. @@ -229,265 +256,296 @@ void opustags_params_extract_jpeg(const unsigned char *data, size_t data_length, Return: A Base64-encoded string suitable for use in a METADATA_BLOCK_PICTURE tag.*/ char *opustags_picture_specification_parse(const char *spec, - const char **error_message, - const char *desc, - unsigned long picture_type, - int *seen_file_icons){ - FILE *picture_file; - unsigned long width; - unsigned long height; - unsigned long depth; - unsigned long colors; - const char *mime_type; - const char *mime_type_end; - const char *description; - const char *description_end; - const char *filename; - unsigned char *buf; - char *out; - size_t cbuf; - size_t nbuf; - size_t data_offset; - size_t data_length; - size_t b64_length; - int is_url; - /*If a filename has a '|' in it, there's no way we can distinguish it from a - full specification just from the spec string. - Instead, try to open the file. - If it exists, the user probably meant the file.*/ - width=height=depth=colors=0; - mime_type=mime_type_end=description=description_end=filename=spec; - is_url=0; - picture_file=fopen(filename,"rb"); - if (desc != NULL) description = desc; - if(picture_file==NULL&&strchr(spec,'|')){ - const char *p; - char *q; - /*We don't have a plain file, and there is a pipe character: assume it's - the full form of the specification.*/ - picture_type=strtoul(spec,&q,10); - if(*q!='|'||picture_type>20){ - *error_message="invalid picture type"; - return NULL; - } - if(picture_type>=1&&picture_type<=2&&(*seen_file_icons&picture_type)){ - *error_message=picture_type==1? - "only one picture of type 1 (32x32 icon) allowed": - "only one picture of type 2 (icon) allowed"; - return NULL; - } - /*An empty field implies a default of 'Cover (front)'.*/ - if(spec==q)picture_type=3; - mime_type=q+1; - mime_type_end=mime_type+strcspn(mime_type,"|"); - if(*mime_type_end!='|'){ - *error_message="invalid picture specification: not enough fields"; - return NULL; - } - /*The media type must be composed of ASCII printable characters 0x20-0x7E.*/ - for(p=mime_type;p0x7E){ - *error_message="invalid characters in media type"; - return NULL; - } - is_url=mime_type_end-mime_type==3 - &&strncmp("-->",mime_type,mime_type_end-mime_type)==0; - description=mime_type_end+1; - description_end=description+strcspn(description,"|"); - if(*description_end!='|'){ - *error_message="invalid picture specification: not enough fields"; - return NULL; - } - p=description_end+1; - if(*p!='|'){ - width=strtoul(p,&q,10); - if(*q!='x'){ - *error_message= - "invalid picture specification: can't parse resolution/color field"; - return NULL; - } - p=q+1; - height=strtoul(p,&q,10); - if(*q!='x'){ - *error_message= - "invalid picture specification: can't parse resolution/color field"; - return NULL; - } - p=q+1; - depth=strtoul(p,&q,10); - if(*q=='/'){ - p=q+1; - colors=strtoul(p,&q,10); - } - if(*q!='|'){ - *error_message= - "invalid picture specification: can't parse resolution/color field"; - return NULL; - } - p=q; + const char **error_message, + const char *desc, + unsigned long picture_type, + int *seen_file_icons) +{ + FILE *picture_file; + unsigned long width; + unsigned long height; + unsigned long depth; + unsigned long colors; + const char *mime_type; + const char *mime_type_end; + const char *description; + const char *description_end; + const char *filename; + unsigned char *buf; + char *out; + size_t cbuf; + size_t nbuf; + size_t data_offset; + size_t data_length; + size_t b64_length; + int is_url; + /*If a filename has a '|' in it, there's no way we can distinguish it from a + full specification just from the spec string. + Instead, try to open the file. + If it exists, the user probably meant the file.*/ + width=height=depth=colors=0; + mime_type=mime_type_end=description=description_end=filename=spec; + is_url=0; + picture_file=fopen(filename,"rb"); + if (desc != NULL) description = desc; + if(picture_file==NULL&&strchr(spec,'|')) + { + const char *p; + char *q; + /*We don't have a plain file, and there is a pipe character: assume it's + the full form of the specification.*/ + picture_type=strtoul(spec,&q,10); + if(*q!='|'||picture_type>20) + { + *error_message="invalid picture type"; + return NULL; + } + if(picture_type>=1&&picture_type<=2&&(*seen_file_icons&picture_type)) + { + *error_message=picture_type==1? + "only one picture of type 1 (32x32 icon) allowed": + "only one picture of type 2 (icon) allowed"; + return NULL; + } + /*An empty field implies a default of 'Cover (front)'.*/ + if(spec==q)picture_type=3; + mime_type=q+1; + mime_type_end=mime_type+strcspn(mime_type,"|"); + if(*mime_type_end!='|') + { + *error_message="invalid picture specification: not enough fields"; + return NULL; + } + /*The media type must be composed of ASCII printable characters 0x20-0x7E.*/ + for(p=mime_type; p0x7E) + { + *error_message="invalid characters in media type"; + return NULL; + } + is_url=mime_type_end-mime_type==3 + &&strncmp("-->",mime_type,mime_type_end-mime_type)==0; + description=mime_type_end+1; + description_end=description+strcspn(description,"|"); + if(*description_end!='|') + { + *error_message="invalid picture specification: not enough fields"; + return NULL; + } + p=description_end+1; + if(*p!='|') + { + width=strtoul(p,&q,10); + if(*q!='x') + { + *error_message= + "invalid picture specification: can't parse resolution/color field"; + return NULL; + } + p=q+1; + height=strtoul(p,&q,10); + if(*q!='x') + { + *error_message= + "invalid picture specification: can't parse resolution/color field"; + return NULL; + } + p=q+1; + depth=strtoul(p,&q,10); + if(*q=='/') + { + p=q+1; + colors=strtoul(p,&q,10); + } + if(*q!='|') + { + *error_message= + "invalid picture specification: can't parse resolution/color field"; + return NULL; + } + p=q; + } + filename=p+1; + if(!is_url)picture_file=fopen(filename,"rb"); } - filename=p+1; - if(!is_url)picture_file=fopen(filename,"rb"); - } - /*Buffer size: 8 static 4-byte fields plus 2 dynamic fields, plus the - file/URL data. - We reserve at least 10 bytes for the media type, in case we still need to - extract it from the file.*/ - data_offset=32+(description_end-description)+IMAX(mime_type_end-mime_type,10); - buf=NULL; - if(is_url){ - /*Easy case: just stick the URL at the end. - We don't do anything to verify it's a valid URL.*/ - data_length=strlen(filename); - cbuf=nbuf=data_offset+data_length; - buf=(unsigned char *)malloc(cbuf); - memcpy(buf+data_offset,filename,data_length); - } - else{ - ogg_uint32_t file_width; - ogg_uint32_t file_height; - ogg_uint32_t file_depth; - ogg_uint32_t file_colors; - int has_palette; - /*Complicated case: we have a real file. - Read it in, attempt to parse the media type and image dimensions if - necessary, and validate what the user passed in.*/ - if(picture_file==NULL){ - *error_message="error opening picture file"; - return NULL; + /*Buffer size: 8 static 4-byte fields plus 2 dynamic fields, plus the + file/URL data. + We reserve at least 10 bytes for the media type, in case we still need to + extract it from the file.*/ + data_offset=32+(description_end-description)+IMAX(mime_type_end-mime_type,10); + buf=NULL; + if(is_url) + { + /*Easy case: just stick the URL at the end. + We don't do anything to verify it's a valid URL.*/ + data_length=strlen(filename); + cbuf=nbuf=data_offset+data_length; + buf=(unsigned char *)malloc(cbuf); + memcpy(buf+data_offset,filename,data_length); } - nbuf=data_offset; - /*Add a reasonable starting image file size.*/ - cbuf=data_offset+65536; - for(;;){ - unsigned char *new_buf; - size_t nread; - new_buf=realloc(buf,cbuf); - if(new_buf==NULL){ - fclose(picture_file); - free(buf); - *error_message="insufficient memory"; - return NULL; - } - buf=new_buf; - nread=fread(buf+nbuf,1,cbuf-nbuf,picture_file); - nbuf+=nread; - if(nbuf0x7FFFFFFFU)cbuf=0xFFFFFFFFU; + else cbuf=cbuf<<1|1; + } + data_length=nbuf-data_offset; + /*If there was no media type, try to extract it from the file data.*/ + if(mime_type_end==mime_type) + { + if(opustags_is_jpeg(buf+data_offset,data_length)) + { + mime_type="image/jpeg"; + mime_type_end=mime_type+10; + } + else if(opustags_is_png(buf+data_offset,data_length)) + { + mime_type="image/png"; + mime_type_end=mime_type+9; + } + else if(opustags_is_gif(buf+data_offset,data_length)) + { + mime_type="image/gif"; + mime_type_end=mime_type+9; + } + else + { + free(buf); + *error_message="unable to guess media type from file, " + "must set it explicitly"; + return NULL; + } + } + /*Try to extract the image dimensions/color information from the file.*/ + file_width=file_height=file_depth=file_colors=0; + has_palette=-1; + if(mime_type_end-mime_type==9 + &&opustags_oi_strncasecmp("image/png",mime_type,mime_type_end-mime_type)==0) + { + opustags_params_extract_png(buf+data_offset,data_length, + &file_width,&file_height,&file_depth,&file_colors,&has_palette); + } + else if(mime_type_end-mime_type==9 + &&opustags_oi_strncasecmp("image/gif",mime_type,mime_type_end-mime_type)==0) + { + opustags_params_extract_gif(buf+data_offset,data_length, + &file_width,&file_height,&file_depth,&file_colors,&has_palette); + } + else if(mime_type_end-mime_type==10 + &&opustags_oi_strncasecmp("image/jpeg",mime_type,mime_type_end-mime_type)==0) + { + opustags_params_extract_jpeg(buf+data_offset,data_length, + &file_width,&file_height,&file_depth,&file_colors,&has_palette); + } + if(!width)width=file_width; + if(!height)height=file_height; + if(!depth)depth=file_depth; + if(!colors)colors=file_colors; + if((file_width&&width!=file_width) + ||(file_height&&height!=file_height) + ||(file_depth&&depth!=file_depth) + /*We use has_palette to ensure we also reject non-0 user color counts for + images we've positively identified as non-paletted.*/ + ||(has_palette>=0&&colors!=file_colors)) + { + free(buf); + *error_message="invalid picture specification: " + "resolution/color field does not match file"; + return NULL; } - break; - } - if(cbuf==0xFFFFFFFF){ - fclose(picture_file); - free(buf); - *error_message="file too large"; - return NULL; - } - else if(cbuf>0x7FFFFFFFU)cbuf=0xFFFFFFFFU; - else cbuf=cbuf<<1|1; } - data_length=nbuf-data_offset; - /*If there was no media type, try to extract it from the file data.*/ - if(mime_type_end==mime_type){ - if(opustags_is_jpeg(buf+data_offset,data_length)){ - mime_type="image/jpeg"; - mime_type_end=mime_type+10; - } - else if(opustags_is_png(buf+data_offset,data_length)){ - mime_type="image/png"; - mime_type_end=mime_type+9; - } - else if(opustags_is_gif(buf+data_offset,data_length)){ - mime_type="image/gif"; - mime_type_end=mime_type+9; - } - else{ + /*These fields MUST be set correctly OR all set to zero. + So if any of them (except colors, for which 0 is a valid value) are still + zero, clear the rest to zero.*/ + if(width==0||height==0||depth==0)width=height=depth=colors=0; + if(picture_type==1&&(width!=32||height!=32 + ||mime_type_end-mime_type!=9 + ||opustags_oi_strncasecmp("image/png",mime_type,mime_type_end-mime_type)!=0)) + { free(buf); - *error_message="unable to guess media type from file, " - "must set it explicitly"; + *error_message="pictures of type 1 MUST be 32x32 PNGs"; return NULL; - } } - /*Try to extract the image dimensions/color information from the file.*/ - file_width=file_height=file_depth=file_colors=0; - has_palette=-1; - if(mime_type_end-mime_type==9 - &&opustags_oi_strncasecmp("image/png",mime_type,mime_type_end-mime_type)==0){ - opustags_params_extract_png(buf+data_offset,data_length, - &file_width,&file_height,&file_depth,&file_colors,&has_palette); - } - else if(mime_type_end-mime_type==9 - &&opustags_oi_strncasecmp("image/gif",mime_type,mime_type_end-mime_type)==0){ - opustags_params_extract_gif(buf+data_offset,data_length, - &file_width,&file_height,&file_depth,&file_colors,&has_palette); - } - else if(mime_type_end-mime_type==10 - &&opustags_oi_strncasecmp("image/jpeg",mime_type,mime_type_end-mime_type)==0){ - opustags_params_extract_jpeg(buf+data_offset,data_length, - &file_width,&file_height,&file_depth,&file_colors,&has_palette); - } - if(!width)width=file_width; - if(!height)height=file_height; - if(!depth)depth=file_depth; - if(!colors)colors=file_colors; - if((file_width&&width!=file_width) - ||(file_height&&height!=file_height) - ||(file_depth&&depth!=file_depth) - /*We use has_palette to ensure we also reject non-0 user color counts for - images we've positively identified as non-paletted.*/ - ||(has_palette>=0&&colors!=file_colors)){ - free(buf); - *error_message="invalid picture specification: " - "resolution/color field does not match file"; - return NULL; + /*Build the METADATA_BLOCK_PICTURE buffer. + We do this backwards from data_offset, because we didn't necessarily know + how big the media type string was before we read the data in.*/ + data_offset-=4; + WRITE_U32_BE(buf+data_offset,(unsigned long)data_length); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,colors); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,depth); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,height); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,width); + data_offset-=description_end-description; + memcpy(buf+data_offset,description,description_end-description); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,(unsigned long)(description_end-description)); + data_offset-=mime_type_end-mime_type; + memcpy(buf+data_offset,mime_type,mime_type_end-mime_type); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,(unsigned long)(mime_type_end-mime_type)); + data_offset-=4; + WRITE_U32_BE(buf+data_offset,picture_type); + data_length=nbuf-data_offset; + b64_length=BASE64_LENGTH(data_length); + out=(char *)malloc(b64_length+1); + if(out!=NULL) + { + opustags_base64_encode(out,(char *)buf+data_offset,data_length); + if(picture_type>=1&&picture_type<=2)*seen_file_icons|=picture_type; } - } - /*These fields MUST be set correctly OR all set to zero. - So if any of them (except colors, for which 0 is a valid value) are still - zero, clear the rest to zero.*/ - if(width==0||height==0||depth==0)width=height=depth=colors=0; - if(picture_type==1&&(width!=32||height!=32 - ||mime_type_end-mime_type!=9 - ||opustags_oi_strncasecmp("image/png",mime_type,mime_type_end-mime_type)!=0)){ free(buf); - *error_message="pictures of type 1 MUST be 32x32 PNGs"; - return NULL; - } - /*Build the METADATA_BLOCK_PICTURE buffer. - We do this backwards from data_offset, because we didn't necessarily know - how big the media type string was before we read the data in.*/ - data_offset-=4; - WRITE_U32_BE(buf+data_offset,(unsigned long)data_length); - data_offset-=4; - WRITE_U32_BE(buf+data_offset,colors); - data_offset-=4; - WRITE_U32_BE(buf+data_offset,depth); - data_offset-=4; - WRITE_U32_BE(buf+data_offset,height); - data_offset-=4; - WRITE_U32_BE(buf+data_offset,width); - data_offset-=description_end-description; - memcpy(buf+data_offset,description,description_end-description); - data_offset-=4; - WRITE_U32_BE(buf+data_offset,(unsigned long)(description_end-description)); - data_offset-=mime_type_end-mime_type; - memcpy(buf+data_offset,mime_type,mime_type_end-mime_type); - data_offset-=4; - WRITE_U32_BE(buf+data_offset,(unsigned long)(mime_type_end-mime_type)); - data_offset-=4; - WRITE_U32_BE(buf+data_offset,picture_type); - data_length=nbuf-data_offset; - b64_length=BASE64_LENGTH(data_length); - out=(char *)malloc(b64_length+1); - if(out!=NULL){ - opustags_base64_encode(out,(char *)buf+data_offset,data_length); - if(picture_type>=1&&picture_type<=2)*seen_file_icons|=picture_type; - } - free(buf); - return out; + return out; } diff --git a/src/picture.h b/src/picture.h index e162290..b988928 100644 --- a/src/picture.h +++ b/src/picture.h @@ -9,11 +9,12 @@ extern "C" { // opus-tools 0.1.10: picture.h -typedef enum{ - PIC_FORMAT_JPEG, - PIC_FORMAT_PNG, - PIC_FORMAT_GIF -}opustags_picture_format; +typedef enum +{ + PIC_FORMAT_JPEG, + PIC_FORMAT_PNG, + PIC_FORMAT_GIF +} opustags_picture_format; #define IMAX(a,b) ((a) > (b) ? (a) : (b)) #define BASE64_LENGTH(len) (((len)+2)/3*4) @@ -29,23 +30,23 @@ int opustags_is_png(const unsigned char *buf, size_t length); int opustags_is_gif(const unsigned char *buf, size_t length); void opustags_params_extract_png(const unsigned char *data, size_t data_length, - ogg_uint32_t *width, ogg_uint32_t *height, - ogg_uint32_t *depth, ogg_uint32_t *colors, - int *has_palette); + ogg_uint32_t *width, ogg_uint32_t *height, + ogg_uint32_t *depth, ogg_uint32_t *colors, + int *has_palette); void opustags_params_extract_gif(const unsigned char *data, size_t data_length, - ogg_uint32_t *width, ogg_uint32_t *height, - ogg_uint32_t *depth, ogg_uint32_t *colors, - int *has_palette); + ogg_uint32_t *width, ogg_uint32_t *height, + ogg_uint32_t *depth, ogg_uint32_t *colors, + int *has_palette); void opustags_params_extract_jpeg(const unsigned char *data, size_t data_length, - ogg_uint32_t *width, ogg_uint32_t *height, - ogg_uint32_t *depth, ogg_uint32_t *colors, - int *has_palette); + ogg_uint32_t *width, ogg_uint32_t *height, + ogg_uint32_t *depth, ogg_uint32_t *colors, + int *has_palette); char *opustags_picture_specification_parse(const char *spec, - const char **error_message, - const char *desc, - unsigned long picture_type, - int *seen_file_icons); + const char **error_message, + const char *desc, + unsigned long picture_type, + int *seen_file_icons); #define READ_U32_BE(buf) \ (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff)) diff --git a/src/put-tags.c b/src/put-tags.c index 74ebfd1..4cdc339 100644 --- a/src/put-tags.c +++ b/src/put-tags.c @@ -9,42 +9,54 @@ #include "opuscomment.h" -static void puterror(void) { - if (tag_output_to_file) { +static void puterror(void) +{ + if (tag_output_to_file) + { fileerror(O.tag_filename); } - else { + else + { oserror(); } } static int nth = 1; -static void u8error(void) { +static void u8error(void) +{ opuserror(err_opus_utf8, nth); } -static void put_bin(uint8_t const *buf, size_t len) { +static void put_bin(uint8_t const *buf, size_t len) +{ size_t ret = fwrite(buf, 1, len, tag_output); - if (ret != len) { + if (ret != len) + { puterror(); } } -static void put_ls(char const *ls) { +static void put_ls(char const *ls) +{ put_bin(ls, strlen(ls)); } -static uint8_t *esc_oc(uint8_t *src, uint8_t *dest, size_t len) { +static uint8_t *esc_oc(uint8_t *src, uint8_t *dest, size_t len) +{ uint8_t *end = src + len; - while(src < end) { + while(src < end) + { *dest++ = *src++; if (src[-1] == 0xa) *dest++ = O.tag_escape_tilde ? 0x7e : 0x9; } return dest; } -static uint8_t *esc_vc(uint8_t *src, uint8_t *dest, size_t len) { +static uint8_t *esc_vc(uint8_t *src, uint8_t *dest, size_t len) +{ uint8_t *end = src + len; - for (; src < end; src++) { - switch (*src) { + for (; src < end; src++) + { + switch (*src) + { case 0x00: *dest++ = 0x5c; *dest++ = 0x30; @@ -72,40 +84,48 @@ static uint8_t *esc_vc(uint8_t *src, uint8_t *dest, size_t len) { return dest; } -static uint8_t *esc_nul(uint8_t *src, uint8_t *dest, size_t len) { +static uint8_t *esc_nul(uint8_t *src, uint8_t *dest, size_t len) +{ return memcpy(dest, src, len) + len; } typedef uint8_t* (*esc_func_)(uint8_t*, uint8_t*, size_t); -static esc_func_ esc_func[] = { +static esc_func_ esc_func[] = +{ esc_oc, esc_vc, esc_nul }; -static size_t conv_none(iconv_t cd, uint8_t *buf, size_t tagleft) { +static size_t conv_none(iconv_t cd, uint8_t *buf, size_t tagleft) +{ put_bin(buf, tagleft); return 0; } -static size_t conv_ls(iconv_t cd, uint8_t *buf, size_t tagleft) { +static size_t conv_ls(iconv_t cd, uint8_t *buf, size_t tagleft) +{ uint8_t *u8 = buf; char *ls = buf + tagleft; size_t remain = 0; - for (;;) { + for (;;) + { char *lsend = ls; size_t bufleft = STACK_BUF_LEN - ((uint8_t*)ls - buf) - 1; size_t iconvret = iconv(cd, (char**)&u8, &tagleft, &lsend, &bufleft); int ie = errno; errno = 0; - if (iconvret == (size_t)-1 && ie == EILSEQ) { + if (iconvret == (size_t)-1 && ie == EILSEQ) + { u8error(); } *lsend = '\0'; // WONTFIX // ロケール文字列に \0 が含まれていてもそのままそこで途切れさせる put_ls(ls); - if (iconvret != (size_t)-1 || ie == EINVAL) { + if (iconvret != (size_t)-1 || ie == EINVAL) + { remain = tagleft; - if (remain) { + if (remain) + { memcpy(buf, u8, remain); } break; @@ -114,25 +134,30 @@ static size_t conv_ls(iconv_t cd, uint8_t *buf, size_t tagleft) { return remain; } -void tag_output_close(void) { - if (O.tag_deferred) { +void tag_output_close(void) +{ + if (O.tag_deferred) + { FILE *deferred = tag_output; tag_output = stdout; rewind(deferred); size_t readlen; uint8_t buf[STACK_BUF_LEN]; - while(readlen = fread(buf, 1, STACK_BUF_LEN, deferred)) { + while(readlen = fread(buf, 1, STACK_BUF_LEN, deferred)) + { put_bin(buf, readlen); } fclose(deferred); } - if (fclose(stdout) == EOF) { + if (fclose(stdout) == EOF) + { puterror(); } } -void *put_tags(void *fp_) { +void *put_tags(void *fp_) +{ // retrieve_tag() からスレッド化された // fpはチャンク化されたタグ // [4バイト: タグ長(ホストエンディアン)][任意長: UTF-8タグ] @@ -140,7 +165,8 @@ void *put_tags(void *fp_) { iconv_t cd; - if (!O.tag_raw) { + if (!O.tag_raw) + { cd = iconv_new(nl_langinfo(CODESET), "UTF-8"); } bool nulsep = O.tag_escape == TAG_ESCAPE_NUL; @@ -150,39 +176,48 @@ void *put_tags(void *fp_) { uint8_t *(*esc)(uint8_t*, uint8_t*, size_t) = esc_func[O.tag_escape]; size_t (*conv)(iconv_t cd, uint8_t*, size_t) = O.tag_raw ? conv_none : conv_ls; - for (;;) { + for (;;) + { uint32_t len; if (!fread(&len, 4, 1, fp)) break; if (nulsep && nth > 1) put_bin("", 1); size_t left = len, remain = 0; - while (left) { + while (left) + { size_t readlen = fill_buffer(raw, left, buflenunit - remain, fp); left -= readlen; size_t tagleft = esc(raw, buf + remain, readlen) - buf; remain = conv(cd, buf, tagleft); } - if (remain) { + if (remain) + { u8error(); } - if (O.tag_raw) { - if (!nulsep) { + if (O.tag_raw) + { + if (!nulsep) + { put_bin("\x0a", 1); } } - else { + else + { uint8_t *u8; char *ls; - if (nulsep) { + if (nulsep) + { *buf = '\0'; left = 1, remain = 128; } - else { + else + { memcpy(buf, "\x0a", 2); left = 2, remain = 128; } u8 = buf, ls = buf + left; - if (iconv(cd, (char**)&u8, &left, &ls, &remain) == (size_t)-1) { + if (iconv(cd, (char**)&u8, &left, &ls, &remain) == (size_t)-1) + { oserror(); } // u8はここでiconv()前のlsを指していることに注意 @@ -195,7 +230,8 @@ void *put_tags(void *fp_) { return NULL; } -iconv_t iconv_new(char const *to, char const *from) { +iconv_t iconv_new(char const *to, char const *from) +{ #if defined __GLIBC__ || defined _LIBICONV_VERSION char to2[64]; strcat(strcpy(to2, to), "//TRANSLIT"); @@ -203,11 +239,14 @@ iconv_t iconv_new(char const *to, char const *from) { char const *to2 = to; #endif iconv_t cd = iconv_open(to2, from); - if (cd == (iconv_t)-1) { - if (errno == EINVAL) { + if (cd == (iconv_t)-1) + { + if (errno == EINVAL) + { oserror_fmt(catgets(catd, 4, 3, "iconv doesn't support converting %s -> %s"), from, to); } - else { + else + { oserror(); } } diff --git a/src/read-flac.c b/src/read-flac.c index 62d729b..0ef4dfd 100644 --- a/src/read-flac.c +++ b/src/read-flac.c @@ -15,10 +15,12 @@ static size_t const gbuflen = 1 << 16; static uint8_t gbuf[1 << 16]; -bool get_metadata_header(uint8_t *type, size_t *len) { +bool get_metadata_header(uint8_t *type, size_t *len) +{ size_t readlen = fread(gbuf, 1, 4, stream_input); if (readlen == (size_t)-1) oserror(); - if (readlen != 4) { + if (readlen != 4) + { opuserror(err_opus_non_flac); } @@ -30,12 +32,14 @@ bool get_metadata_header(uint8_t *type, size_t *len) { return last; } -static void write_buffer(void const *buf, size_t len, FILE *fp) { +static void write_buffer(void const *buf, size_t len, FILE *fp) +{ size_t wlen = fwrite(buf, 1, len, fp); if (wlen != len) oserror(); } -void store_tags_flac(struct rettag_st *rst, struct edit_st *est) { +void store_tags_flac(struct rettag_st *rst, struct edit_st *est) +{ size_t readlen; if (rst) { @@ -56,7 +60,8 @@ void store_tags_flac(struct rettag_st *rst, struct edit_st *est) { } } -static void read_comment(size_t left) { +static void read_comment(size_t left) +{ // ここから read.c の parse_info_border() と大体一緒 pthread_t retriever_thread; @@ -67,7 +72,8 @@ static void read_comment(size_t left) { pthread_create(&retriever_thread, NULL, retrieve_tags, retriever); // 本スレッドはタグヘッダを送り続ける - while (left) { + while (left) + { size_t readlen = left > gbuflen ? gbuflen : left; size_t readret = fread(gbuf, 1, readlen, stream_input); if (readlen != readret) oserror(); @@ -80,19 +86,22 @@ static void read_comment(size_t left) { struct edit_st *est; close(pfd[1]); pthread_join(retriever_thread, (void**)&rst); - if (O.edit != EDIT_LIST) { + if (O.edit != EDIT_LIST) + { // 編集入力タグパースのスレッドを合流 pthread_join(parser_thread, (void **)&est); store_tags_flac(rst, est); } } -static void *put_base64_locale(void *tagin_) { +static void *put_base64_locale(void *tagin_) +{ FILE *tagin = tagin_; iconv_t cd = iconv_open(nl_langinfo(CODESET), "us-ascii"); size_t readlen; char buf[128]; - while (readlen = fread(buf, 1, 64, tagin)) { + while (readlen = fread(buf, 1, 64, tagin)) + { size_t asciileft = readlen, locleft = 128 - readlen; char *ascii = buf, *loc = buf + readlen; iconv(cd, &ascii, &asciileft, &loc, &locleft); @@ -106,31 +115,37 @@ static void *put_base64_locale(void *tagin_) { } static bool met_comment, met_picture; -static void read_picture_list(size_t left) { +static void read_picture_list(size_t left) +{ pthread_t loctr_th; FILE *ascii_out; - if (!O.tag_raw) { + if (!O.tag_raw) + { int pfd[2]; pipe(pfd); ascii_out = fdopen(pfd[1], "w"); FILE *tagin = fdopen(pfd[0], "r"); pthread_create(&loctr_th, NULL, put_base64_locale, tagin); } - else { + else + { ascii_out = tag_output; } - if (O.tag_escape == TAG_ESCAPE_NUL && (met_comment || met_picture)) { + if (O.tag_escape == TAG_ESCAPE_NUL && (met_comment || met_picture)) + { write_buffer("", 1, ascii_out); } write_buffer(MBPeq, strlen(MBPeq), ascii_out); - while (left) { + while (left) + { uint8_t raw[3] = {0}; uint8_t b64[4]; size_t readlen = left > 3 ? 3 : left; size_t readret = fread(raw, 1, readlen, stream_input); - if (readret != readlen) { + if (readret != readlen) + { if (ferror(stream_input)) oserror(); else opuserror(err_opus_lost_tag); } @@ -139,26 +154,33 @@ static void read_picture_list(size_t left) { b64[1] = (raw[0] << 4 | raw[1] >> 4) & 0x3f; b64[2] = (raw[1] << 2 | raw[2] >> 6) & 0x3f; b64[3] = raw[2] & 0x3f; - switch (readlen) { + switch (readlen) + { case 1: b64[2] = 64; - // FALLTHROUGH + // FALLTHROUGH case 2: b64[3] = 64; break; } - for (int_fast8_t i = 0; i < 4; i++) { + for (int_fast8_t i = 0; i < 4; i++) + { b64[i] = b64tab_ascii[b64[i]]; } write_buffer(b64, 4, ascii_out); left -= readlen; } - if (O.tag_escape != TAG_ESCAPE_NUL) { - write_buffer((uint8_t[]){ 0xa }, 1, ascii_out); + if (O.tag_escape != TAG_ESCAPE_NUL) + { + write_buffer((uint8_t[]) + { + 0xa + }, 1, ascii_out); } - if (!O.tag_raw) { + if (!O.tag_raw) + { fclose(ascii_out); pthread_join(loctr_th, NULL); } @@ -166,53 +188,64 @@ static void read_picture_list(size_t left) { met_picture = true; } -void read_flac(void) { +void read_flac(void) +{ size_t readlen = fread(gbuf, 1, 4, stream_input); if (readlen == (size_t)-1) oserror(); - if (readlen != 4) { + if (readlen != 4) + { opuserror(err_opus_non_flac); } - if (memcmp(gbuf, "\x66\x4C\x61\x43", 4) != 0) { // fLaC + if (memcmp(gbuf, "\x66\x4C\x61\x43", 4) != 0) // fLaC + { opuserror(err_opus_non_flac); } write_buffer(gbuf, 4, built_stream); bool last_metadata = false; size_t metadata_num = 0; - while (!last_metadata) { + while (!last_metadata) + { uint8_t type; size_t left; last_metadata = get_metadata_header(&type, &left); if (type == 0 && metadata_num != 0 - || type == 0 && left != 34 - || type == 127 - || met_comment && type == 4) { + || type == 0 && left != 34 + || type == 127 + || met_comment && type == 4) + { opuserror(err_opus_bad_content); } bool delete_header = O.edit == EDIT_LIST; if (type == 1) delete_header = true; - switch (type) { + switch (type) + { case 4: - if (O.edit == EDIT_LIST && O.tag_escape == TAG_ESCAPE_NUL && met_picture) { + if (O.edit == EDIT_LIST && O.tag_escape == TAG_ESCAPE_NUL && met_picture) + { write_buffer("", 1, tag_output); } read_comment(left); met_comment = true; break; case 6: - if (O.edit == EDIT_LIST) { - if (!O.tag_ignore_picture) { + if (O.edit == EDIT_LIST) + { + if (!O.tag_ignore_picture) + { read_picture_list(left); break; } } - else if (O.edit == EDIT_WRITE) { + else if (O.edit == EDIT_WRITE) + { delete_header = !O.tag_ignore_picture; } - // FALLTHROUGH + // FALLTHROUGH default: if (!delete_header) write_buffer(gbuf, 4, built_stream); - while (left) { + while (left) + { size_t readlen = left > gbuflen ? gbuflen : left; size_t readret = fread(gbuf, 1, readlen, stream_input); if (readlen != readret) oserror(); @@ -223,30 +256,40 @@ void read_flac(void) { } metadata_num++; } - if (O.edit == EDIT_LIST) { + if (O.edit == EDIT_LIST) + { tag_output_close(); exit(0); } - if (!met_comment) { + if (!met_comment) + { struct edit_st *est; pthread_join(parser_thread, (void **)&est); struct rettag_st *rst = NULL; - if (est->num) { + if (est->num) + { rst = calloc(1, sizeof(*rst)); rst->tag = tmpfile(); rst->part_of_comment = true; size_t vendorlen = strlen(new_vendor_string_ascii); - fwrite((uint32_t[]){oi32(vendorlen)}, 4, 1, rst->tag); + fwrite((uint32_t[]) + { + oi32(vendorlen) + }, 4, 1, rst->tag); fwrite(new_vendor_string_ascii, 1, vendorlen, rst->tag); rst->tagbegin = vendorlen + 4; - fwrite((uint32_t[]){0}, 4, 1, rst->tag); + fwrite((uint32_t[]) + { + 0 + }, 4, 1, rst->tag); } store_tags_flac(rst, est); } write_buffer("\x81\0\0", 4, built_stream); // 「最後のヘッダ」標識を立てたパディングで〆 opst = PAGE_SOUND; - while (readlen = fread(gbuf, 1, gbuflen, stream_input)) { + while (readlen = fread(gbuf, 1, gbuflen, stream_input)) + { size_t writelen = fwrite(gbuf, 1, readlen, built_stream); if (writelen != readlen) oserror(); } diff --git a/src/read.c b/src/read.c index 0222922..6eef938 100644 --- a/src/read.c +++ b/src/read.c @@ -21,20 +21,26 @@ static char *outtmp; static bool remove_tmp; static FILE *non_opus_stream; -void move_file(void) { - if (fclose(built_stream) == EOF) { +void move_file(void) +{ + if (fclose(built_stream) == EOF) + { fileerror(O.out ? O.out : outtmp); } - if (O.out || !input_is_regular_file) { + if (O.out || !input_is_regular_file) + { } - else { + else + { int fd = fileno(stream_input); struct stat st; fstat(fd, &st); - if (!rename(outtmp, O.in)) { + if (!rename(outtmp, O.in)) + { // rename(2)は移動先が存在していてもunlink(2)の必要はない remove_tmp = false; - if (!chmod(O.in, st.st_mode)) { + if (!chmod(O.in, st.st_mode)) + { return; } } @@ -42,33 +48,41 @@ void move_file(void) { } } -static noreturn void put_left(long rew) { +static noreturn void put_left(long rew) +{ clearerr(stream_input); - if (fseek(stream_input, seeked_len - rew, SEEK_SET)) { + if (fseek(stream_input, seeked_len - rew, SEEK_SET)) + { oserror(); } size_t l; uint8_t buf[STACK_BUF_LEN]; - while (l = fread(buf, 1, STACK_BUF_LEN, stream_input)) { + while (l = fread(buf, 1, STACK_BUF_LEN, stream_input)) + { size_t ret = fwrite(buf, 1, l, built_stream); - if (ret != l) { + if (ret != l) + { oserror(); } } - if (ferror(stream_input)) { + if (ferror(stream_input)) + { oserror(); } move_file(); exit(0); } -static void put_non_opus_stream() { +static void put_non_opus_stream() +{ rewind(non_opus_stream); uint8_t buf[STACK_BUF_LEN]; size_t readlen; - while (readlen = fread(buf, 1, STACK_BUF_LEN, non_opus_stream)) { - if (fwrite(buf, 1, readlen, built_stream) != readlen) { + while (readlen = fread(buf, 1, STACK_BUF_LEN, non_opus_stream)) + { + if (fwrite(buf, 1, readlen, built_stream) != readlen) + { oserror(); } } @@ -76,7 +90,8 @@ static void put_non_opus_stream() { non_opus_stream = NULL; } -void store_tags(ogg_page *np, struct rettag_st *rst, struct edit_st *est, bool packet_break_in_page) { +void store_tags(ogg_page *np, struct rettag_st *rst, struct edit_st *est, bool packet_break_in_page) +{ FILE *fptag = rst->tag; size_t const pagebuflen = 65536; // oggページの最大長 = 65307 uint8_t pagebuf[pagebuflen]; @@ -88,32 +103,39 @@ void store_tags(ogg_page *np, struct rettag_st *rst, struct edit_st *est, bool p fseek(fptag, 0, SEEK_END); // 編集入力書き込み - rewind(est->str); rewind(est->len); - while (fread(pagebuf, 4, 1, est->len)) { + rewind(est->str); + rewind(est->len); + while (fread(pagebuf, 4, 1, est->len)) + { // ここでpagebufはOgg構造関係なく単なる移し替え用のバッファ size_t reclen = *(uint32_t*)pagebuf; *(uint32_t*)pagebuf = oi32(reclen); fwrite(pagebuf, 4, 1, fptag); - while (reclen) { + while (reclen) + { size_t readlen = fill_buffer(pagebuf, reclen, pagebuflen, est->str); fwrite(pagebuf, 1, readlen, fptag); reclen -= readlen; } } - fclose(est->str); fclose(est->len); + fclose(est->str); + fclose(est->len); - if (rst->padding && rst->part_of_comment) { + if (rst->padding && rst->part_of_comment) + { // パディングがある時はfptagの後ろに移す rewind(rst->padding); size_t readlen; - while (readlen = fread(pagebuf, 1, pagebuflen, rst->padding)) { + while (readlen = fread(pagebuf, 1, pagebuflen, rst->padding)) + { fwrite(pagebuf, 1, readlen, fptag); } fclose(rst->padding); rst->padding = NULL; } long commentlen = ftell(fptag); - if (commentlen > TAG_LENGTH_LIMIT__OUTPUT) { + if (commentlen > TAG_LENGTH_LIMIT__OUTPUT) + { exceed_output_limit(); } if (codec->type == CODEC_FLAC) return; @@ -130,7 +152,8 @@ void store_tags(ogg_page *np, struct rettag_st *rst, struct edit_st *est, bool p memset(og.header + 26, 0xff, 256); uint32_t idx = 1; - while (commentlen >= 255 * 255) { + while (commentlen >= 255 * 255) + { fread(og.body, 1, 255 * 255, fptag); set_pageno(&og, idx++); ogg_page_checksum_set(&og); @@ -152,7 +175,8 @@ void store_tags(ogg_page *np, struct rettag_st *rst, struct edit_st *est, bool p ogg_page_checksum_set(&og); write_page(&og, built_stream); - if (input_is_regular_file) { + if (input_is_regular_file) + { // 出力するタグ部分のページ番号が入力の音声開始部分のページ番号に満たない場合、 // 空のページを生成して開始ページ番号を合わせる og.header_len = 27; @@ -160,16 +184,19 @@ void store_tags(ogg_page *np, struct rettag_st *rst, struct edit_st *est, bool p set_granulepos(&og, -1); og.header[26] = 0; og.body_len = 0; - while (idx < opus_idx - packet_break_in_page) { + while (idx < opus_idx - packet_break_in_page) + { set_pageno(&og, idx++); ogg_page_checksum_set(&og); write_page(&og, built_stream); } } - if (packet_break_in_page) { + if (packet_break_in_page) + { uint8_t *lace_tag = &np->header[27]; - while (*lace_tag == 255) { + while (*lace_tag == 255) + { lace_tag++; np->body_len -= 255; np->body += 255; @@ -188,7 +215,8 @@ void store_tags(ogg_page *np, struct rettag_st *rst, struct edit_st *est, bool p put_non_opus_stream(); - if (input_is_regular_file && idx == opus_idx) { + if (input_is_regular_file && idx == opus_idx) + { put_left(0); /* NOTREACHED */ } @@ -196,68 +224,86 @@ void store_tags(ogg_page *np, struct rettag_st *rst, struct edit_st *est, bool p opst = PAGE_SOUND; } -static void cleanup(void) { - if (remove_tmp) { +static void cleanup(void) +{ + if (remove_tmp) + { unlink(outtmp); } } -static void exit_without_sigpipe(void) { +static void exit_without_sigpipe(void) +{ signal(SIGPIPE, SIG_IGN); } -void open_output_file(void) { - if (O.edit == EDIT_LIST) { +void open_output_file(void) +{ + if (O.edit == EDIT_LIST) + { built_stream = fopen("/dev/null", "w"); - if (!built_stream) { + if (!built_stream) + { fileerror(O.out); } remove_tmp = false; tag_output_to_file = O.tag_filename && strcmp(O.tag_filename, "-") != 0; - if (tag_output_to_file) { + if (tag_output_to_file) + { FILE *tmp = freopen(O.tag_filename, "w", stdout); - if (!tmp) { + if (!tmp) + { fileerror(O.tag_filename); } } tag_output = O.tag_deferred ? tmpfile() : stdout; } - else if (O.out) { - if (strcmp(O.out, "-") == 0) { + else if (O.out) + { + if (strcmp(O.out, "-") == 0) + { built_stream = stdout; } - else { + else + { built_stream = fopen(O.out, "w"); - if (!built_stream) { + if (!built_stream) + { fileerror(O.out); } } remove_tmp = false; } - else { - if (input_is_regular_file) { + else + { + if (input_is_regular_file) + { char const *tmpl = "opuscomment.XXXXXX"; outtmp = calloc(strlen(O.in) + strlen(tmpl) + 1, 1); char *p = strrchr(O.in, '/'); - if (p) { + if (p) + { p++; strncpy(outtmp, O.in, p - O.in); strcpy(outtmp + (p - O.in), tmpl); } - else { + else + { strcpy(outtmp, tmpl); } int fd = mkstemp(outtmp); - if (fd == -1) { + if (fd == -1) + { oserror(); } remove_tmp = true; atexit(cleanup); built_stream = fdopen(fd, "w+"); } - else { + else + { built_stream = stdout; } } @@ -265,7 +311,8 @@ void open_output_file(void) { // スレッド間通信で使っているパイプがエラー後のexit内での始末にSIGPIPEを発するのでその対策 atexit(exit_without_sigpipe); - switch (O.edit) { + switch (O.edit) + { case EDIT_WRITE: case EDIT_APPEND: // 編集入力タグパースを別スレッド化 parse_tags.c へ @@ -273,16 +320,20 @@ void open_output_file(void) { } } -static void check_header_is_single_page(ogg_page *og) { +static void check_header_is_single_page(ogg_page *og) +{ uint8_t lace_num = og->header[26]; if (!lace_num) opuserror(err_opus_border); uint8_t const *lace = &og->header[27]; - for (int_fast16_t i = 0; i < lace_num - 1; i++) { - if (*lace++ != 255) { + for (int_fast16_t i = 0; i < lace_num - 1; i++) + { + if (*lace++ != 255) + { opuserror(err_opus_border); } } - if (*lace == 255) { + if (*lace == 255) + { opuserror(err_opus_border); } // 最後のlacing valueはチェックしない @@ -290,30 +341,37 @@ static void check_header_is_single_page(ogg_page *og) { static size_t header_packet_pos, header_packet_len; static long built_header_pos; -bool parse_info(ogg_page *og) { +bool parse_info(ogg_page *og) +{ static int osidx = 0; - if (!ogg_page_bos(og) || ogg_page_eos(og)) { - if (ogg_page_pageno(og) == 0) { + if (!ogg_page_bos(og) || ogg_page_eos(og)) + { + if (ogg_page_pageno(og) == 0) + { opuserror(err_opus_bad_stream); } opuserror(err_opus_no_target_codec); } - if (ogg_page_pageno(og) != 0) { + if (ogg_page_pageno(og) != 0) + { // BOSがあるのにページが0でない opuserror(err_opus_bad_stream); } - if (ogg_page_granulepos(og) != 0) { + if (ogg_page_granulepos(og) != 0) + { // ヘッダのgranuleposは必ず0 opuserror(err_opus_bad_stream); } check_header_is_single_page(og); - if (og->body_len < codec->headmagic_len || memcmp(og->body, codec->headmagic, codec->headmagic_len) != 0) { + if (og->body_len < codec->headmagic_len || memcmp(og->body, codec->headmagic, codec->headmagic_len) != 0) + { // have_multi_streams = true; return false; } osidx++; - if (O.target_idx && O.target_idx != osidx) { + if (O.target_idx && O.target_idx != osidx) + { // have_multi_streams = true; return false; } @@ -322,7 +380,8 @@ bool parse_info(ogg_page *og) { header_packet_pos = seeked_len - header_packet_len; built_header_pos = ftell(built_stream); codec->parse(og); - if (O.edit != EDIT_LIST && codec->type != CODEC_FLAC) { + if (O.edit != EDIT_LIST && codec->type != CODEC_FLAC) + { write_page(og, built_stream); } opus_idx++; @@ -331,7 +390,8 @@ bool parse_info(ogg_page *og) { } static int retriever_fd; -static bool copy_tag_packet(ogg_page *og, bool *packet_break_in_page) { +static bool copy_tag_packet(ogg_page *og, bool *packet_break_in_page) +{ static unsigned int total = 0; int lace_num = og->header[26]; @@ -340,20 +400,24 @@ static bool copy_tag_packet(ogg_page *og, bool *packet_break_in_page) { uint8_t const *bin = og->body; uint_fast8_t i; - for (i = 0; i < lace_num - 1 && og->header[27 + i] == 255; i++, bin += 255, total += 255) { - if (total > TAG_LENGTH_LIMIT__INPUT) { + for (i = 0; i < lace_num - 1 && og->header[27 + i] == 255; i++, bin += 255, total += 255) + { + if (total > TAG_LENGTH_LIMIT__INPUT) + { opuserror(err_opus_long_tag, TAG_LENGTH_LIMIT__INPUT >> 20); } write(retriever_fd, bin, 255); } total += og->header[27 + i]; - if (total > TAG_LENGTH_LIMIT__INPUT) { + if (total > TAG_LENGTH_LIMIT__INPUT) + { opuserror(err_opus_long_tag, TAG_LENGTH_LIMIT__INPUT >> 20); } write(retriever_fd, bin, og->header[27 + i]); i++; bool packet_term = false; - if (i != lace_num || og->header[27 + i - 1] != 255) { + if (i != lace_num || og->header[27 + i - 1] != 255) + { packet_term = true; *packet_break_in_page = i != lace_num; } @@ -361,36 +425,44 @@ static bool copy_tag_packet(ogg_page *og, bool *packet_break_in_page) { } static pthread_t retriever_thread; -bool parse_info_border(ogg_page *og) { +bool parse_info_border(ogg_page *og) +{ // ここにはページ番号1で来ているはず - if (ogg_page_continued(og)) { + if (ogg_page_continued(og)) + { opuserror(err_opus_border); } leave_header_packets = true; - if (/*O.gain_fix && */O.edit == EDIT_NONE) { + if (/*O.gain_fix && */O.edit == EDIT_NONE) + { // 出力ゲイン編集のみの場合 - if (input_is_regular_file) { - if (O.out) { + if (input_is_regular_file) + { + if (O.out) + { // 出力指定が別にある場合残りをコピー put_left(og->header_len + og->body_len); } - else { + else + { // 上書きするなら最初のページのみを直接書き換えようとする uint8_t b[header_packet_len]; FILE *stream_overwrite = freopen(NULL, "r+", stream_input); if (!stream_overwrite - || fseek(built_stream, built_header_pos, SEEK_SET) - || fseek(stream_overwrite, header_packet_pos, SEEK_SET) - || header_packet_len != fread(b, 1, header_packet_len, built_stream) - || header_packet_len != fwrite(b, 1, header_packet_len, stream_overwrite) - ) { + || fseek(built_stream, built_header_pos, SEEK_SET) + || fseek(stream_overwrite, header_packet_pos, SEEK_SET) + || header_packet_len != fread(b, 1, header_packet_len, built_stream) + || header_packet_len != fwrite(b, 1, header_packet_len, stream_overwrite) + ) + { oserror(); } exit(0); } } - else { + else + { // 入力がパイプの時 put_non_opus_stream(); opst = PAGE_SOUND; @@ -411,31 +483,37 @@ bool parse_info_border(ogg_page *og) { return parse_comment(og); } -bool parse_comment(ogg_page *og) { - if (ogg_page_eos(og) || opus_idx > 1 && !ogg_page_continued(og)) { +bool parse_comment(ogg_page *og) +{ + if (ogg_page_eos(og) || opus_idx > 1 && !ogg_page_continued(og)) + { opuserror(err_opus_bad_stream); } opus_idx++; bool packet_break_in_page; - if (copy_tag_packet(og, &packet_break_in_page)) { + if (copy_tag_packet(og, &packet_break_in_page)) + { return parse_comment_term(og, packet_break_in_page); } return true; } -static bool parse_comment_term(ogg_page *og, bool packet_break_in_page) { +static bool parse_comment_term(ogg_page *og, bool packet_break_in_page) +{ // タグパケットのパース処理のスレッドを合流 struct rettag_st *rst; struct edit_st *est; close(retriever_fd); pthread_join(retriever_thread, (void**)&rst); - if (O.edit != EDIT_LIST) { + if (O.edit != EDIT_LIST) + { // 編集入力タグパースのスレッドを合流 pthread_join(parser_thread, (void **)&est); } - if (O.edit == EDIT_APPEND && !rst->del && !est->num && !O.out && !O.gain_fix && !rst->upcase) { + if (O.edit == EDIT_APPEND && !rst->del && !est->num && !O.out && !O.gain_fix && !rst->upcase) + { // タグ追記モードで出力が上書き且つ // タグ入力、ゲイン調整、タグ削除、大文字化適用が全て無い場合はすぐ終了する exit(0); @@ -451,29 +529,37 @@ static bool parse_comment_term(ogg_page *og, bool packet_break_in_page) { return true; } -bool parse_page_sound(ogg_page *og) { +bool parse_page_sound(ogg_page *og) +{ set_pageno(og, ogg_page_pageno(og) + opus_idx_diff); ogg_page_checksum_set(og); write_page(og, built_stream); - if (ogg_page_eos(og)) { - if (input_is_regular_file) { + if (ogg_page_eos(og)) + { + if (input_is_regular_file) + { put_left(0); /* NOTREACHED */ } - else { + else + { opus_idx_diff = 0; } } return true; } -static void parse_page(ogg_page *og) { +static void parse_page(ogg_page *og) +{ bool isopus; - if (opst == PAGE_INFO) { + if (opst == PAGE_INFO) + { isopus = parse_info(og); } - else if ((isopus = test_non_opus(og))) { - switch (opst) { + else if ((isopus = test_non_opus(og))) + { + switch (opst) + { case PAGE_INFO_BORDER: parse_info_border(og); break; @@ -487,19 +573,24 @@ static void parse_page(ogg_page *og) { break; } } - if (isopus && opst < PAGE_SOUND && ogg_page_eos(og)) { + if (isopus && opst < PAGE_SOUND && ogg_page_eos(og)) + { opuserror(err_opus_bad_stream); } - if (!isopus) { + if (!isopus) + { write_page(og, ogg_page_pageno(og) && non_opus_stream ? non_opus_stream : built_stream); } } -void read_page(ogg_sync_state *oy) { +void read_page(ogg_sync_state *oy) +{ int seeklen; ogg_page og; - while ((seeklen = ogg_sync_pageseek(oy, &og)) != 0) { - if (seeklen > 0) { + while ((seeklen = ogg_sync_pageseek(oy, &og)) != 0) + { + if (seeklen > 0) + { seeked_len += seeklen; parse_page(&og); } diff --git a/src/retrieve-tags.c b/src/retrieve-tags.c index 9e53ee7..85f746f 100644 --- a/src/retrieve-tags.c +++ b/src/retrieve-tags.c @@ -9,35 +9,43 @@ #include "opuscomment.h" -static void rtread(void *p, size_t len, FILE *fp) { +static void rtread(void *p, size_t len, FILE *fp) +{ size_t readlen = fread(p, 1, len, fp); if (readlen != len) opuserror(err_opus_lost_tag); } -static uint32_t rtchunk(FILE *fp) { +static uint32_t rtchunk(FILE *fp) +{ uint32_t rtn; rtread(&rtn, 4, fp); return oi32(rtn); } -static size_t rtfill(void *buf, size_t left, size_t buflen, FILE *fp) { +static size_t rtfill(void *buf, size_t left, size_t buflen, FILE *fp) +{ size_t rl = left > buflen ? buflen : left; rtread(buf, rl, fp); return rl; } -static bool test_mbp(uint8_t *buf, size_t len) { +static bool test_mbp(uint8_t *buf, size_t len) +{ uint8_t const *mbp = MBPeq; size_t const mbplen = 23; - if (len < mbplen) { + if (len < mbplen) + { return false; } - while (*mbp) { + while (*mbp) + { uint8_t c = *buf++; - if (c >= 0x61 && c <= 0x7a) { // a-z in ASCII + if (c >= 0x61 && c <= 0x7a) // a-z in ASCII + { c -= 32; } - if (*mbp != c) { + if (*mbp != c) + { break; } mbp++; @@ -46,27 +54,33 @@ static bool test_mbp(uint8_t *buf, size_t len) { } static size_t tagpacket_total = 0; -void check_tagpacket_length(size_t len) { +void check_tagpacket_length(size_t len) +{ tagpacket_total += len; - if (tagpacket_total > TAG_LENGTH_LIMIT__OUTPUT) { + if (tagpacket_total > TAG_LENGTH_LIMIT__OUTPUT) + { exceed_output_limit(); } } -static bool rtcopy_write(FILE *packet_input, void *fptag_) { +static bool rtcopy_write(FILE *packet_input, void *fptag_) +{ FILE *fptag = fptag_; uint32_t len = rtchunk(packet_input); uint8_t buf[STACK_BUF_LEN]; bool first = true; bool field = true; bool copy; - while (len) { + while (len) + { size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); - if (first) { + if (first) + { first = false; - switch (O.edit) { + switch (O.edit) + { case EDIT_WRITE: copy = O.tag_ignore_picture && test_mbp(buf, len); break; @@ -74,20 +88,26 @@ static bool rtcopy_write(FILE *packet_input, void *fptag_) { copy = true; break; } - if (copy) { + if (copy) + { uint8_t chunk[4]; *(uint32_t*)chunk = oi32(len); fwrite(chunk, 4, 1, fptag); check_tagpacket_length(4); } } - if (copy) { - if (O.tag_toupper && field) { - for (uint8_t *p = buf, *endp = buf + rl; p < endp; p++) { - if (*p >= 0x61 && *p <= 0x7a) { + if (copy) + { + if (O.tag_toupper && field) + { + for (uint8_t *p = buf, *endp = buf + rl; p < endp; p++) + { + if (*p >= 0x61 && *p <= 0x7a) + { *p -= 32; } - if (*p == 0x3d) { + if (*p == 0x3d) + { field = false; break; } @@ -103,7 +123,8 @@ static bool rtcopy_write(FILE *packet_input, void *fptag_) { static FILE *rtcd_src, *dellist_str, *dellist_len; static bool upcase_applied; -static bool rtcopy_delete(FILE *packet_input, void *fptag_) { +static bool rtcopy_delete(FILE *packet_input, void *fptag_) +{ static int idx = 1; FILE *fptag = fptag_; @@ -112,7 +133,8 @@ static bool rtcopy_delete(FILE *packet_input, void *fptag_) { uint32_t srclen = rtchunk(packet_input); uint8_t buf[STACK_BUF_LEN]; size_t len = srclen; - while (len) { + while (len) + { // フィールド名を大文字化する bool field = true; size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); @@ -121,7 +143,8 @@ static bool rtcopy_delete(FILE *packet_input, void *fptag_) { len -= rl; if (!field) break; } - while (len) { + while (len) + { // フィールド名を抜けた後のループ size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); fwrite(buf, 1, rl, rtcd_src); @@ -129,9 +152,11 @@ static bool rtcopy_delete(FILE *packet_input, void *fptag_) { } // 削除リストのループ - rewind(dellist_str); rewind(dellist_len); + rewind(dellist_str); + rewind(dellist_len); bool matched = false; - while (fread(buf, 1, 5, dellist_len)) { + while (fread(buf, 1, 5, dellist_len)) + { uint32_t cmplen = *(uint32_t*)buf; bool field_only = buf[4]; @@ -139,34 +164,42 @@ static bool rtcopy_delete(FILE *packet_input, void *fptag_) { uint8_t *cmp = buf + bufhalf; rewind(rtcd_src); // 比較 - if (field_only && srclen > cmplen || cmplen == srclen) { + if (field_only && srclen > cmplen || cmplen == srclen) + { // フィールド名のみ比較でソースが削除指定より長い または // 全比較でソースと削除の長さが一致する時 matched = true; - while (cmplen) { + while (cmplen) + { size_t rl = fill_buffer(buf, cmplen, bufhalf, rtcd_src); fread(cmp, 1, rl, dellist_str); cmplen -= rl; - if (memcmp(buf, cmp, rl) != 0) { + if (memcmp(buf, cmp, rl) != 0) + { matched = false; break; } } - if (matched) { - if (field_only) { + if (matched) + { + if (field_only) + { fread(buf, 1, 1, rtcd_src); - if (*buf == 0x3d) { + if (*buf == 0x3d) + { goto MATCHED; } matched = false; } - else { + else + { goto MATCHED; } } } // 次の削除チャンクに進む - while (cmplen) { + while (cmplen) + { cmplen -= fill_buffer(buf, cmplen, STACK_BUF_LEN, dellist_str); } } @@ -175,7 +208,8 @@ static bool rtcopy_delete(FILE *packet_input, void *fptag_) { *(uint32_t*)buf = oi32(srclen); fwrite(buf, 4, 1, fptag); check_tagpacket_length(4); - while (srclen) { + while (srclen) + { size_t rl = fill_buffer(buf, srclen, STACK_BUF_LEN, rtcd_src); fwrite(buf, 1, rl, fptag); srclen -= rl; @@ -186,7 +220,8 @@ static bool rtcopy_delete(FILE *packet_input, void *fptag_) { return !matched; } -static bool rtcopy_list(FILE *packet_input, void *listfd_) { +static bool rtcopy_list(FILE *packet_input, void *listfd_) +{ static size_t idx = 1; int listfd = *(int*)listfd_; uint32_t len = rtchunk(packet_input); @@ -194,19 +229,25 @@ static bool rtcopy_list(FILE *packet_input, void *listfd_) { bool first = true; bool field = true; bool copy; - while (len) { + while (len) + { size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); - if (first) { + if (first) + { if (*buf == 0x3d) opuserror(err_opus_bad_tag, idx); first = false; copy = !O.tag_ignore_picture || !test_mbp(buf, len); - if (copy) { + if (copy) + { write(listfd, &len, 4); } } - if (copy) { - if (field) { - if(!test_tag_field(buf, rl, O.tag_toupper, &field, &upcase_applied) && O.tag_verify) { + if (copy) + { + if (field) + { + if(!test_tag_field(buf, rl, O.tag_toupper, &field, &upcase_applied) && O.tag_verify) + { opuserror(err_opus_bad_tag, idx); } } @@ -214,14 +255,16 @@ static bool rtcopy_list(FILE *packet_input, void *listfd_) { } len -= rl; } - if (O.tag_verify && field) { + if (O.tag_verify && field) + { opuserror(err_opus_bad_tag, idx); } idx++; return copy; } -void *retrieve_tags(void *packet_input_) { +void *retrieve_tags(void *packet_input_) +{ // parse_header_border() からスレッド化された FILE *packet_input = packet_input_; uint8_t buf[STACK_BUF_LEN]; @@ -232,19 +275,25 @@ void *retrieve_tags(void *packet_input_) { rtn->part_of_comment = true; uint32_t len; rtread(buf, codec->commagic_len, packet_input); - if (memcmp(buf, codec->commagic, codec->commagic_len) != 0) { - if (codec->type == CODEC_VP8 && buf[5] != 2) { + if (memcmp(buf, codec->commagic, codec->commagic_len) != 0) + { + if (codec->type == CODEC_VP8 && buf[5] != 2) + { len = codec->commagic_len; rtn->part_of_comment = false; } else opuserror(err_opus_bad_content); } fwrite(buf, 1, codec->commagic_len, fptag); - if (!rtn->part_of_comment) { + if (!rtn->part_of_comment) + { // コメントパケットが無かった時用 if (O.edit == EDIT_LIST) exit(0); size_t vendorlen = strlen(new_vendor_string_ascii); - fwrite((uint32_t[]){oi32(vendorlen)}, 4, 1, fptag); + fwrite((uint32_t[]) + { + oi32(vendorlen) + }, 4, 1, fptag); fwrite(new_vendor_string_ascii, 1, vendorlen, fptag); rtn->tagbegin = codec->commagic_len + vendorlen + 4; goto NOTCOMMENT; @@ -255,7 +304,8 @@ void *retrieve_tags(void *packet_input_) { *(uint32_t*)buf = oi32(len); fwrite(buf, 4, 1, fptag); check_tagpacket_length(codec->commagic_len + 4); - while (len) { + while (len) + { size_t rl = rtfill(buf, len, STACK_BUF_LEN, packet_input); fwrite(buf, 1, rl, fptag); check_tagpacket_length(rl); @@ -271,7 +321,8 @@ void *retrieve_tags(void *packet_input_) { int pfd[2]; pthread_t putth; void *wh; - if (O.edit == EDIT_LIST) { + if (O.edit == EDIT_LIST) + { // タグ出力をスレッド化 put-tags.c へ pipe(pfd); FILE *fpput = fdopen(pfd[0], "r"); @@ -279,23 +330,27 @@ void *retrieve_tags(void *packet_input_) { rtcopy = rtcopy_list; wh = pfd + 1; } - else if (dellist_str) { + else if (dellist_str) + { rtcopy = rtcopy_delete; wh = fptag; rtcd_src = tmpfile(); } - else { + else + { rtcopy = rtcopy_write; wh = fptag; } - while (recordnum) { + while (recordnum) + { rtn->num += rtcopy(packet_input, wh); recordnum--; } rtn->del -= rtn->num; - if (O.edit == EDIT_LIST) { + if (O.edit == EDIT_LIST) + { // タグ出力スレッド合流 close(pfd[1]); pthread_join(putth, NULL); @@ -305,28 +360,34 @@ void *retrieve_tags(void *packet_input_) { } len = fread(buf, 1, 1, packet_input); - if (len && (codec->prog || (*buf & 1))) { + if (len && (codec->prog || (*buf & 1))) + { // codec->prog ← opuscomment 以外は全部パディングを保存 - NOTCOMMENT: // ←VP8用 +NOTCOMMENT: // ←VP8用 rtn->padding = tmpfile(); fwrite(buf, 1, len, rtn->padding); check_tagpacket_length(len); size_t n; - while ((n = fread(buf, 1, STACK_BUF_LEN, packet_input))) { + while ((n = fread(buf, 1, STACK_BUF_LEN, packet_input))) + { fwrite(buf, 1, n, rtn->padding); check_tagpacket_length(n); } } - else { + else + { size_t n; while ((n = fread(buf, 1, STACK_BUF_LEN, packet_input))) {} } - if (rtcd_src) { + if (rtcd_src) + { fclose(rtcd_src); } - if (dellist_str) { - fclose(dellist_str); fclose(dellist_len); + if (dellist_str) + { + fclose(dellist_str); + fclose(dellist_len); } fclose(packet_input); rtn->upcase = upcase_applied; @@ -334,11 +395,14 @@ void *retrieve_tags(void *packet_input_) { } -void rt_del_args(uint8_t *buf, size_t len, bool term) { +void rt_del_args(uint8_t *buf, size_t len, bool term) +{ static uint32_t recordlen = 0; static bool field = true; - if (!buf) { - if (!recordlen) { + if (!buf) + { + if (!recordlen) + { opterror('d', catgets(catd, 7, 3, "invalid tag format")); } recordlen = oi32(recordlen); @@ -353,7 +417,8 @@ void rt_del_args(uint8_t *buf, size_t len, bool term) { dellist_str = dellist_str ? dellist_str : tmpfile(); recordlen += len; - if (field && !test_tag_field(buf, len, true, &field, &upcase_applied)) { + if (field && !test_tag_field(buf, len, true, &field, &upcase_applied)) + { opterror('d', catgets(catd, 7, 3, "invalid tag format")); } fwrite(buf, 1, len, dellist_str); diff --git a/src/select-codec.c b/src/select-codec.c index a40db86..726adb5 100644 --- a/src/select-codec.c +++ b/src/select-codec.c @@ -13,69 +13,89 @@ #include "opuscomment.h" -static void check_opus(ogg_page *og) { - if (og->body_len < 19) { +static void check_opus(ogg_page *og) +{ + if (og->body_len < 19) + { opuserror(err_opus_bad_content); } - if ((og->body[8] & 0xf0) != 0) { + if ((og->body[8] & 0xf0) != 0) + { opuserror(err_opus_version); } - if (O.gain_put) { - if (O.gain_q78) { + if (O.gain_put) + { + if (O.gain_q78) + { fprintf(stderr, "%d", (int16_t)oi16(*(int16_t*)(&og->body[16]))); } - else { + else + { fprintf(stderr, "%.8g", (int16_t)oi16(*(int16_t*)(&og->body[16])) / 256.0); } } - if (O.gain_fix) { + if (O.gain_fix) + { int16_t gi; - if (O.gain_relative) { + if (O.gain_relative) + { int gain = (int16_t)oi16(*(int16_t*)(&og->body[16])); gain += O.gain_val; - if (gain > 32767) { + if (gain > 32767) + { gain = 32767; } - else if (gain < -32768) { + else if (gain < -32768) + { gain = -32768; } gi = gain; } - else { + else + { gi = O.gain_val; } - if (O.gain_not_zero && gi == 0) { + if (O.gain_not_zero && gi == 0) + { gi = O.gain_val_sign ? -1 : 1; } *(int16_t*)(&og->body[16]) = oi16(gi); ogg_page_checksum_set(og); - if (O.gain_put) { - if (O.gain_q78) { + if (O.gain_put) + { + if (O.gain_q78) + { fprintf(stderr, "\t%d", (int)gi); } - else { + else + { fprintf(stderr, "\t%.8g", gi / 256.0); } } } - if (O.gain_put) { + if (O.gain_put) + { fputc('\n', stderr); } } -static void check_theora(ogg_page *og) { - if (og->body_len != 42) { +static void check_theora(ogg_page *og) +{ + if (og->body_len != 42) + { opuserror(err_opus_bad_content); } // major 3, minor 2 - if (og->body[7] != 3 || og->body[8] != 2) { + if (og->body[7] != 3 || og->body[8] != 2) + { opuserror(err_opus_version); } } -static void check_vp8(ogg_page *og) { +static void check_vp8(ogg_page *og) +{ // https://people.freedesktop.org/~slomo/ogg-vp8/ogg-vp8.pdf // ヘッダ長、ヘッダ種類 if (og->body_len < 26 || og->body[5] != 1) opuserror(err_opus_bad_content); @@ -85,18 +105,19 @@ static void check_vp8(ogg_page *og) { if (og->body[7] == 0 && og->body_len != 26) opuserror(err_opus_bad_content); } -static void check_vorbis(ogg_page *og) { +static void check_vorbis(ogg_page *og) +{ // 0-6 "\x1" "\x76\x6f\x72\x62\x69\x73" -/*7-10 1) [vorbis_version] = read 32 bits as unsigned integer -11 2) [audio_channels] = read 8 bit integer as unsigned -12-15 3) [audio_sample_rate] = read 32 bits as unsigned integer -16 4) [bitrate_maximum] = read 32 bits as signed integer -20 5) [bitrate_nominal] = read 32 bits as signed integer -24 6) [bitrate_minimum] = read 32 bits as signed integer -28.0-3 7) [blocksize_0] = 2 exponent (read 4 bits as unsigned integer) -28.4-7 8) [blocksize_1] = 2 exponent (read 4 bits as unsigned integer) -29 9) [framing_flag] = read one bit -*/ + /*7-10 1) [vorbis_version] = read 32 bits as unsigned integer + 11 2) [audio_channels] = read 8 bit integer as unsigned + 12-15 3) [audio_sample_rate] = read 32 bits as unsigned integer + 16 4) [bitrate_maximum] = read 32 bits as signed integer + 20 5) [bitrate_nominal] = read 32 bits as signed integer + 24 6) [bitrate_minimum] = read 32 bits as signed integer + 28.0-3 7) [blocksize_0] = 2 exponent (read 4 bits as unsigned integer) + 28.4-7 8) [blocksize_1] = 2 exponent (read 4 bits as unsigned integer) + 29 9) [framing_flag] = read one bit + */ if (og->body_len != 30) opuserror(err_opus_bad_content); if (oi32(*(uint32_t*)&og->body[7]) != 0) opuserror(err_opus_version); if (!og->body[11]) opuserror(err_opus_bad_content); @@ -104,33 +125,40 @@ static void check_vorbis(ogg_page *og) { if (!og->body[29]) opuserror(err_opus_bad_content); } -static void check_daala(ogg_page *og) { +static void check_daala(ogg_page *og) +{ // TODO // Daalaの仕様文書がないのでマジックナンバー読むぐらいしかしてない } -static void check_speex(ogg_page *og) { +static void check_speex(ogg_page *og) +{ if (og->body_len != 80) opuserror(err_opus_bad_content); if (oi32(*(uint32_t*)&og->body[28]) != 1) opuserror(err_opus_version); } -static void check_pcm(ogg_page *og) { +static void check_pcm(ogg_page *og) +{ if (og->body_len != 28) opuserror(err_opus_bad_content); - if (htons(*(uint16_t*)&og->body[8]) != 0 || htons(*(uint16_t*)&og->body[10]) != 0) { + if (htons(*(uint16_t*)&og->body[8]) != 0 || htons(*(uint16_t*)&og->body[10]) != 0) + { opuserror(err_opus_version); } } -static void check_uvs(ogg_page *og) { +static void check_uvs(ogg_page *og) +{ if (og->body_len != 48) opuserror(err_opus_bad_content); - if (htons(*(uint16_t*)&og->body[8]) != 0 || htons(*(uint16_t*)&og->body[10]) != 0) { + if (htons(*(uint16_t*)&og->body[8]) != 0 || htons(*(uint16_t*)&og->body[10]) != 0) + { opuserror(err_opus_version); } } void check_flac(ogg_page *og); -static struct codec_parser set[] = { +static struct codec_parser set[] = +{ // "OpusHead", "OpusTags" {CODEC_OPUS, NULL, "Opus", 8, "\x4f\x70\x75\x73\x48\x65\x61\x64", check_opus, 8, "\x4f\x70\x75\x73\x54\x61\x67\x73"}, // "\x80" "theora", "\x81" "theora" @@ -152,16 +180,20 @@ static struct codec_parser set[] = { {0, NULL, NULL, 0, NULL, NULL, 0, NULL}, }; -void select_codec(void) { +void select_codec(void) +{ for (codec = set + 1; codec->prog != NULL && strcmp(program_name, codec->prog) != 0; codec++) {} if (!codec->prog) codec = set; }; -bool select_codec_by_name(char const *name) { +bool select_codec_by_name(char const *name) +{ static locale_t clocale = (locale_t)0; - if (!clocale) { + if (!clocale) + { clocale = newlocale(LC_ALL, "C", (locale_t)0); - if (!clocale) { + if (!clocale) + { oserror(); } } From 1e54ac7ce2ee677bf7c9cc8dc9cad491e5ccbe6f Mon Sep 17 00:00:00 2001 From: zvezdochiot Date: Thu, 28 Jul 2022 08:39:22 +0300 Subject: [PATCH 8/9] 1.5.15: terrible endian --- src/endianness.c | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/endianness.c b/src/endianness.c index 0aabc63..14777f8 100644 --- a/src/endianness.c +++ b/src/endianness.c @@ -1,14 +1,10 @@ #include int endian_test() { - uint32_t sample = {0x01020304}; + int out[3] = {0, -1, 1}; // {terrible, little, big} endian + uint32_t sample = {0x02000001}; uint8_t *test = (uint8_t *)&sample; - if (test[0] == 4) - return -1; - else if (test[0] == 1) - return 1; - else - return 0; + return out[test[0]]; } uint16_t oi16(uint16_t i) @@ -20,10 +16,12 @@ uint16_t oi16(uint16_t i) return (i << 8 | i >> 8); else { - uint8_t *val = (uint8_t *)&i; + uint16_t sample = {0x0100}; uint8_t out[2]; - out[0] = val[0]; - out[1] = val[1]; + uint8_t *ind = (uint8_t *)&sample; + uint8_t *val = (uint8_t *)&i; + for (int j = 0; j < 2; j++) + out[ind[j]] = val[j]; return *(uint16_t*)out; } } @@ -39,12 +37,12 @@ uint32_t oi32(uint32_t i) | i >> 24); else { - uint8_t *val = (uint8_t *)&i; + uint32_t sample = {0x03020100}; uint8_t out[4]; - out[0] = val[0]; - out[1] = val[1]; - out[2] = val[2]; - out[3] = val[3]; + uint8_t *ind = (uint8_t *)&sample; + uint8_t *val = (uint8_t *)&i; + for (int j = 0; j < 4; j++) + out[ind[j]] = val[j]; return *(uint32_t*)out; } } @@ -64,16 +62,12 @@ uint64_t oi64(uint64_t i) | i >> 56); else { - uint8_t *val = (uint8_t *)&i; + uint64_t sample = {0x0706050403020100}; uint8_t out[8]; - out[0] = val[0]; - out[1] = val[1]; - out[2] = val[2]; - out[3] = val[3]; - out[4] = val[4]; - out[5] = val[5]; - out[6] = val[6]; - out[7] = val[7]; + uint8_t *ind = (uint8_t *)&sample; + uint8_t *val = (uint8_t *)&i; + for (int j = 0; j < 8; j++) + out[ind[j]] = val[j]; return *(uint64_t*)out; } } From 399928fd3c98335e047c1ae078f073476a3ef964 Mon Sep 17 00:00:00 2001 From: zvezdochiot Date: Thu, 28 Jul 2022 10:07:02 +0300 Subject: [PATCH 9/9] 1.5.16: fix big endian x64 --- src/endianness.c | 4 ++-- src/version.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/endianness.c b/src/endianness.c index 14777f8..8129ca7 100644 --- a/src/endianness.c +++ b/src/endianness.c @@ -54,8 +54,8 @@ uint64_t oi64(uint64_t i) else if (et == 1) return (i << 56 | (i & (255ULL << 8)) << 40 - | (i & (255ULL << 16)) >> 24 - | (i & (255ULL << 24)) >> 8 + | (i & (255ULL << 16)) << 24 + | (i & (255ULL << 24)) << 8 | (i & (255ULL << 32)) >> 8 | (i & (255ULL << 40)) >> 24 | (i & (255ULL << 48)) >> 40 diff --git a/src/version.h b/src/version.h index 888875f..c8ee581 100644 --- a/src/version.h +++ b/src/version.h @@ -1,5 +1,5 @@ -#define OPUSCOMMENT_VERSION "1.5.15" +#define OPUSCOMMENT_VERSION "1.5.16" #define OPUSCOMMENT_REVISION_YEAR (2022 - 1900) #define OPUSCOMMENT_REVISION_MONTH (7 - 1) -#define OPUSCOMMENT_REVISION_DAY 26 +#define OPUSCOMMENT_REVISION_DAY 28