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/README.ja.md b/README.ja.md
index c1b2117..a306cd7 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -1,3 +1,9 @@
+
+
+
+
+
+
# opuscomment
## 概要
diff --git a/README.md b/README.md
index d2ffbbc..ab7dc51 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,14 @@
+`Origin_:`
+
+
+
+
+`ORG.SLM:`
+
+
+
+
+
# opuscomment
The formal README is written in [Japanese](./README.ja.md).
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 2f88542..68c3cdf 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -6,38 +6,40 @@
# NLSに対応していない、あるいは必要がない場合は、CFLAGSに-UNLSを追加することで無効に出来ます。
# `DEFAULT_NLS_PATH`を文字列でdefineをすると、NLSPATHが無い時に最初にcatopen(3)をする場所を指定することができます。
+CC=c99
+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
-CONFSRCS=endianness.c error.c main.c
-OBJS=$(SRCS:.c=.o) $(CONFSRCS:.c=.o)
+SRCP=opuspic2tag.c picture.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
-DEFAULT_MACROS=-D_POSIX_C_SOURCE=200809L -DNLS
-#DEFAULT_MACROS=-D_XOPEN_SOURCE=600 -DNLS
-LIBS=-logg -lm -lpthread
-CC=c99
+ERRORDEFS=errordef/opus.tab errordef/opuscomment.tab
+OBJS=$(SRCS:.c=.o) $(CONFSRCS:.c=.o)
+OBJP=$(SRCP:.c=.o)
+RM = rm -f
-all: opuscomment ;
+all: opuscomment opuspic2tag
opuscomment: $(OBJS)
- $(CC) -o opuscomment $(DEFAULT_MACROS) $(CFLAGS) $(LDFLAGS) $(LIBS) $(OBJS)
+ $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
+
+opuspic2tag: $(OBJP)
+ $(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 $@
-main.c: $(HEADERS) version.h
+opuscomment.c: $(HEADERS) version.h
@touch $@
clean:
- rm endianness.c opuscomment $(OBJS) 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()
+{
+ int out[3] = {0, -1, 1}; // {terrible, little, big} endian
+ uint32_t sample = {0x02000001};
+ uint8_t *test = (uint8_t *)&sample;
+ return out[test[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
+ {
+ uint16_t sample = {0x0100};
+ uint8_t out[2];
+ 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;
+ }
+}
+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
+ {
+ uint32_t sample = {0x03020100};
+ uint8_t out[4];
+ 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;
+ }
+}
+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
+ {
+ uint64_t sample = {0x0706050403020100};
+ uint8_t out[8];
+ 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;
+ }
+}
diff --git a/src/error.c b/src/error.c
index 32efe82..0c8c842 100644
--- a/src/error.c
+++ b/src/error.c
@@ -4,80 +4,93 @@
#include "opuscomment.h"
-void errorprefix(void) {
- fprintf(stderr, catgets(catd, 1, 3, "%s: "), program_name);
+void errorprefix(void)
+{
+ 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[] = {
+noreturn void mainerror(int e, ...)
+{
+ va_list ap;
+ va_start(ap, 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);
- 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[] = {
+noreturn void opuserror(int e, ...)
+{
+ 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);
+noreturn void oserror(void)
+{
+ 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);
+noreturn void oserror_fmt(char const *e, ...)
+{
+ 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);
+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, ...) {
- 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 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);
}
-noreturn void exceed_output_limit(void) {
- mainerror(err_main_output_limit, TAG_LENGTH_LIMIT__OUTPUT >> 20);
+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 942468a..999d140 100644
--- a/src/error.h
+++ b/src/error.h
@@ -8,15 +8,17 @@ 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/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/global.h b/src/global.h
index a1e7804..04a4ffe 100644
--- a/src/global.h
+++ b/src/global.h
@@ -12,90 +12,96 @@
#include
#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;
+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;
} O;
-GLOBAL enum {
- PAGE_INFO,
- PAGE_INFO_BORDER,
- PAGE_COMMENT,
- PAGE_OTHER_METADATA,
- PAGE_SOUND,
+GLOBAL enum
+{
+ 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;
+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;
} *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/main.c b/src/main.c
deleted file mode 100644
index 7d9286a..0000000
--- a/src/main.c
+++ /dev/null
@@ -1,416 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-
-#define GLOBAL_MAIN
-#include "opuscomment.h"
-#include "version.h"
-
-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,
-"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"
- ));
- }
- exit(1);
-}
-
-static void fail_to_parse(int c) {
- 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"));
-}
-
-void select_codec(void);
-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;
- }
-}
-
-static void interrupted(int sig) {
- 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);
- }
-}
-
-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;
-
-#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;
- }
- }
-#else
- 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;
-}
diff --git a/src/ocutil.c b/src/ocutil.c
index abf9768..5f2f195 100644
--- a/src/ocutil.c
+++ b/src/ocutil.c
@@ -6,110 +6,136 @@
#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;
+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;
}
-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;
+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;
}
-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 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)) {
- 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;
+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;
}
-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();
- }
+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();
+ }
}
-void set_pageno(ogg_page *og, int no) {
- *(uint32_t*)&og->header[18] = oi32(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) {
- *(int64_t*)&og->header[6] = oi64(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) {
- char const *endp = src + n, *p = src;
- while (p < endp && *p) p++;
- return p - src;
+size_t strnlen(char const *src, size_t n)
+{
+ 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
new file mode 100644
index 0000000..0c7d45f
--- /dev/null
+++ b/src/opuscomment.c
@@ -0,0 +1,467 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#define GLOBAL_MAIN
+#include "opuscomment.h"
+#include "version.h"
+
+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,
+ "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"
+ ));
+ }
+ exit(1);
+}
+
+static void fail_to_parse(int c)
+{
+ 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"));
+}
+
+void select_codec(void);
+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;
+ }
+}
+
+static void interrupted(int sig)
+{
+ 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);
+ }
+}
+
+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;
+
+#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;
+ }
+ }
+#else
+ 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;
+}
diff --git a/src/opuscomment.h b/src/opuscomment.h
index c3e7d8f..a359476 100644
--- a/src/opuscomment.h
+++ b/src/opuscomment.h
@@ -22,15 +22,17 @@
#include "error.h"
#include "iconv-impl.h"
-struct rettag_st {
- FILE *tag, *padding;
- long tagbegin;
- size_t num, del;
- bool upcase, part_of_comment;
+struct rettag_st
+{
+ FILE *tag, *padding;
+ long tagbegin;
+ size_t num, del;
+ bool upcase, part_of_comment;
};
-struct edit_st {
- FILE *str, *len, *pict;
- size_t num;
+struct edit_st
+{
+ FILE *str, *len, *pict;
+ size_t num;
};
// parse-tags.c
diff --git a/src/opuspic2tag.c b/src/opuspic2tag.c
new file mode 100644
index 0000000..47b9a95
--- /dev/null
+++ b/src/opuspic2tag.c
@@ -0,0 +1,125 @@
+#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);
+ fwrite("\n", 1, 1, out);
+ }
+ if(out)
+ fclose(out);
+ return EXIT_SUCCESS;
+}
diff --git a/src/parse-tags.c b/src/parse-tags.c
index 1cb6698..d56bccc 100644
--- a/src/parse-tags.c
+++ b/src/parse-tags.c
@@ -15,103 +15,127 @@
static bool from_file;
static FILE *edit_input;
-static void readerror(void) {
- if (from_file) {
- fileerror(O.tag_filename);
- }
- else {
- oserror();
- }
+static void readerror(void)
+{
+ 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);
+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);
}
-static void err_nosep(void) {
- tagerror(catgets(catd, 5, 1, "no field separator"));
+static void err_nosep(void)
+{
+ tagerror(catgets(catd, 5, 1, "no field separator"));
}
-static void err_name(void) {
- tagerror(catgets(catd, 5, 2, "invalid field name"));
+static void err_name(void)
+{
+ tagerror(catgets(catd, 5, 2, "invalid field name"));
}
-static void err_empty(void) {
- tagerror(catgets(catd, 5, 3, "empty field name"));
+static void err_empty(void)
+{
+ tagerror(catgets(catd, 5, 3, "empty field name"));
}
-static void err_bin(void) {
- tagerror(catgets(catd, 5, 4, "binary file"));
+static void err_bin(void)
+{
+ tagerror(catgets(catd, 5, 4, "binary file"));
}
-static void err_esc(void) {
- tagerror(catgets(catd, 5, 5, "invalid escape sequence"));
+static void err_esc(void)
+{
+ tagerror(catgets(catd, 5, 5, "invalid escape sequence"));
}
-static void err_utf8(void) {
- tagerror(catgets(catd, 5, 6, "invalid UTF-8 sequence"));
+static void err_utf8(void)
+{
+ tagerror(catgets(catd, 5, 6, "invalid UTF-8 sequence"));
}
-static void err_base64(void) {
- tagerror(catgets(catd, 5, 7, "invalid BASE64 sequence"));
+static void err_base64(void)
+{
+ tagerror(catgets(catd, 5, 7, "invalid BASE64 sequence"));
}
-static void err_noterm(void) {
- mainerror(err_main_no_term);
+static void err_noterm(void)
+{
+ 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);
- }
+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);
+ }
}
static FILE *strstore, *strcount, *flacpict;
@@ -123,496 +147,594 @@ 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) {
- 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 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;
}
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;
+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;
}
-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;
- }
- }
+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;
+ }
+ }
}
-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);
+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);
}
-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;
+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;
}
-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);
- }
- }
+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);
+ }
+ }
}
-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 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 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;
+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;
}
-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 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 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();
+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();
}
-void prepare_record(void) {
- strstore = strstore ? strstore : tmpfile();
- strcount = strcount ? strcount : tmpfile();
- flacpict = flacpict ? flacpict : codec->type == CODEC_FLAC ? tmpfile() : NULL;
+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_) {
- 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_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;
}
-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;
+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;
}
-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;
+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;
}
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);
+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);
}
-void pticonv_close(void) {
- iconv_close(optcd);
+void pticonv_close(void)
+{
+ iconv_close(optcd);
}
diff --git a/src/picture.c b/src/picture.c
new file mode 100644
index 0000000..ffc1053
--- /dev/null
+++ b/src/picture.c
@@ -0,0 +1,551 @@
+// 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..b988928
--- /dev/null
+++ b/src/picture.h
@@ -0,0 +1,66 @@
+#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/put-tags.c b/src/put-tags.c
index 528f244..4cdc339 100644
--- a/src/put-tags.c
+++ b/src/put-tags.c
@@ -9,207 +9,246 @@
#include "opuscomment.h"
-static void puterror(void) {
- if (tag_output_to_file) {
- fileerror(O.tag_filename);
- }
- else {
- oserror();
- }
+static void puterror(void)
+{
+ if (tag_output_to_file)
+ {
+ fileerror(O.tag_filename);
+ }
+ else
+ {
+ oserror();
+ }
}
static int nth = 1;
-static void u8error(void) {
- opuserror(err_opus_utf8, nth);
+static void u8error(void)
+{
+ 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();
- }
+static void put_bin(uint8_t const *buf, size_t len)
+{
+ 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));
+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) {
- 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_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;
}
-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;
+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;
}
-static uint8_t *esc_nul(uint8_t *src, uint8_t *dest, size_t len) {
- return memcpy(dest, src, len) + 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[] = {
- esc_oc, esc_vc, esc_nul
+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) {
- put_bin(buf, tagleft);
- return 0;
+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) {
- 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;
+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;
}
-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();
- }
+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();
+ }
}
-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;
+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;
}
-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");
+ 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..0ef4dfd 100644
--- a/src/read-flac.c
+++ b/src/read-flac.c
@@ -15,238 +15,283 @@
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;
+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;
}
-static void write_buffer(void const *buf, size_t len, FILE *fp) {
- size_t wlen = fwrite(buf, 1, len, fp);
- if (wlen != len) oserror();
+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) {
- 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);
- }
+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);
+ }
+ 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);
- }
+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);
+ }
}
-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;
+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;
}
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;
+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;
}
-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();
+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();
}
diff --git a/src/read.c b/src/read.c
index 3db52c6..6eef938 100644
--- a/src/read.c
+++ b/src/read.c
@@ -21,488 +21,579 @@ static char *outtmp;
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();
- }
+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();
+ }
}
-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);
+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);
}
-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;
+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;
}
-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;
+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;
}
-static void cleanup(void) {
- if (remove_tmp) {
- unlink(outtmp);
- }
+static void cleanup(void)
+{
+ if (remove_tmp)
+ {
+ unlink(outtmp);
+ }
}
-static void exit_without_sigpipe(void) {
- signal(SIGPIPE, SIG_IGN);
+static void exit_without_sigpipe(void)
+{
+ 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);
- }
+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);
+ }
}
-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はチェックしない
+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はチェックしない
}
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;
+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 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 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 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);
+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);
}
-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;
+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;
}
-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;
+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;
}
-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;
+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;
}
-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);
- }
+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);
+ }
}
-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;
- }
+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;
+ }
}
diff --git a/src/retrieve-tags.c b/src/retrieve-tags.c
index 924ba44..85f746f 100644
--- a/src/retrieve-tags.c
+++ b/src/retrieve-tags.c
@@ -9,352 +9,417 @@
#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);
+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) {
- uint32_t rtn;
- rtread(&rtn, 4, fp);
- return oi32(rtn);
+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) {
- size_t rl = left > buflen ? buflen : left;
- rtread(buf, rl, fp);
- return rl;
+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) {
- 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 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;
}
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();
- }
+void check_tagpacket_length(size_t len)
+{
+ 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;
+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;
}
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 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++;
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 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;
}
-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;
+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;
}
-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);
+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);
}
diff --git a/src/select-codec.c b/src/select-codec.c
index 4ea732c..726adb5 100644
--- a/src/select-codec.c
+++ b/src/select-codec.c
@@ -13,158 +13,190 @@
#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);
- }
+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);
+ }
}
-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);
- }
+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);
+ }
}
-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);
+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);
}
-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
-*/
- 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_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
+ */
+ 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) {
+static void check_daala(ogg_page *og)
+{
// TODO
// Daalaの仕様文書がないのでマジックナンバー読むぐらいしかしてない
}
-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_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) {
- 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_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);
+ }
}
-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);
- }
+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);
+ }
}
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},
+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},
};
-void select_codec(void) {
- for (codec = set + 1; codec->prog != NULL && strcmp(program_name, codec->prog) != 0; codec++) {}
- if (!codec->prog) codec = set;
+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) {
- 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;
+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;
}
diff --git a/src/version.h b/src/version.h
index b4a5778..c8ee581 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,5 +1,5 @@
-#define OPUSCOMMENT_VERSION "1.5.12"
+#define OPUSCOMMENT_VERSION "1.5.16"
-#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 28