mirror of
https://github.com/fmang/opustags.git
synced 2025-07-05 17:17:50 +02:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
cd99ac50c7 | |||
82a5124f14 | |||
6c0d4fc297 | |||
c74e19922f | |||
5c1a7b3a99 | |||
b70e65f0d4 | |||
fc7e5e939e | |||
e8b66a6207 | |||
ba5c151b5d | |||
76afc0efd5 | |||
a54bac8f55 | |||
3293647e8f |
30
.github/workflows/ci.yaml
vendored
Normal file
30
.github/workflows/ci.yaml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
name: Continuous Integration
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
env:
|
||||
LC_CTYPE: C.UTF-8
|
||||
CMAKE_COLOR_DIAGNOSTICS: ON
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout git repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt install cmake g++ pkg-config libogg-dev ffmpeg libtest-harness-perl libtest-deep-perl liblist-moreutils-perl libtest-utf8-perl
|
||||
- name: Build
|
||||
env:
|
||||
CXX: g++
|
||||
CXXFLAGS: -D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS -D_GLIBCXX_DEBUG -O2 -flto=auto -g -Wall -Wextra -Werror=format-security -fstack-protector-strong -fstack-clash-protection -fcf-protection -fsanitize=address,undefined
|
||||
LDFLAGS: -fsanitize=address,undefined
|
||||
run: |
|
||||
cmake -B target -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON
|
||||
cmake --build target
|
||||
- name: Test
|
||||
run: |
|
||||
cmake --build target --target check
|
83
.github/workflows/release.yml
vendored
Normal file
83
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
extract-tag:
|
||||
name: Extract release tag
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
tag: ${{ env.RELEASE_TAG }}
|
||||
version: ${{ env.RELEASE_VERSION }}
|
||||
steps:
|
||||
- name: Parse refs
|
||||
run: |
|
||||
RELEASE_TAG=${GITHUB_REF#refs/*/}
|
||||
test -z $RELEASE_TAG && exit 1
|
||||
RELEASE_VERSION=$(echo "$RELEASE_TAG" | sed 's/^[^0-9]*//')
|
||||
test -z $RELEASE_VERSION && exit 1
|
||||
echo "Releasing version: $RELEASE_VERSION; Tag: $RELEASE_TAG"
|
||||
echo "RELEASE_VERSION=$RELEASE_VERSION" >> $GITHUB_ENV
|
||||
echo "RELEASE_TAG=$RELEASE_TAG" >> $GITHUB_ENV
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-22.04
|
||||
needs: ["extract-tag"]
|
||||
steps:
|
||||
- name: git checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt install -y g++ cmake pkg-config libogg-dev libogg0
|
||||
|
||||
- name: CMake build
|
||||
run: |
|
||||
mkdir build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr -S .
|
||||
make
|
||||
make install DESTDIR=./build
|
||||
|
||||
- name: Create control file
|
||||
run: |
|
||||
mkdir -p build/DEBIAN
|
||||
cat << EOF > build/DEBIAN/control
|
||||
Package: opustags
|
||||
Version: ${{ needs.extract-tag.outputs.version }}
|
||||
Architecture: amd64
|
||||
Maintainer: github.com/fmang
|
||||
Depends: libogg0 (>= 1.3.4) | libogg (>= 1.3.4)
|
||||
Priority: optional
|
||||
Description: Ogg Opus tags editor
|
||||
EOF
|
||||
|
||||
- name: Create package file
|
||||
run: |
|
||||
dpkg-deb -v --build ./build
|
||||
mv build.deb opustags-${{ needs.extract-tag.outputs.version }}-amd64.deb
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debian-pkg
|
||||
path: opustags-${{ needs.extract-tag.outputs.version }}-amd64.deb
|
||||
|
||||
upload-assets:
|
||||
name: Upload release assets
|
||||
needs: ["extract-tag", "build"]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download all workflow run artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
path: .
|
||||
|
||||
- name: Upload .deb package file
|
||||
run: |
|
||||
gh release upload ${{ needs.extract-tag.outputs.tag }} opustags-${{ needs.extract-tag.outputs.version }}-amd64.deb
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
49
opustags.1
49
opustags.1
@ -1,4 +1,4 @@
|
||||
.TH opustags 1 "April 2024" "@PROJECT_NAME@ @PROJECT_VERSION@"
|
||||
.TH opustags 1 "March 2025" "@PROJECT_NAME@ @PROJECT_VERSION@"
|
||||
.SH NAME
|
||||
opustags \- Ogg Opus tag editor
|
||||
.SH SYNOPSIS
|
||||
@ -43,13 +43,13 @@ to set new tags without being bothered by the old ones.
|
||||
If you want to replace all the tags, you can use the \fB--set-all\fP option which will cause
|
||||
\fBopustags\fP to read tags from standard input.
|
||||
The format is the same as the one used for output: newline-separated \fIFIELD=Value\fP assignment.
|
||||
All the previously existing tags as deleted.
|
||||
All the previously existing tags are deleted.
|
||||
.PP
|
||||
The Opus format specifications requires that tags are encoded in UTF-8, so that's the only encoding
|
||||
opustags supports. If your system encoding is different, the tags are automatically converted to and
|
||||
from your system locale. When you edit an Opus file whose tags contains characters unsupported by
|
||||
your system encoding, the original UTF-8 values will be preserved for the tags you don't explicitly
|
||||
modify.
|
||||
The Opus format specification requires that tags are encoded in UTF-8, so that’s the only encoding
|
||||
\fBopustags\fP supports. If your system encoding is different, the tags are automatically converted
|
||||
to and from your system locale. When you edit an Opus file whose tags contain characters unsupported
|
||||
by your system encoding, the original UTF-8 values will be preserved for the tags you don’t
|
||||
explicitly modify.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-h, \-\-help
|
||||
@ -67,7 +67,7 @@ setting \fB--output\fP to the same path as the input file and enabling \fB--over
|
||||
This option conflicts with \fB--output\fP.
|
||||
.TP
|
||||
.B \-y, \-\-overwrite
|
||||
By default, \fBopustags\fP refuses to overwrite an already-existent file.
|
||||
By default, \fBopustags\fP refuses to overwrite an already-existing file.
|
||||
Use \fB-y\fP to allow overwriting.
|
||||
Note that this option is not needed when the output is a special file like \fI/dev/null\fP.
|
||||
.TP
|
||||
@ -79,10 +79,10 @@ In both cases, the field names are case-insensitive, and expected to be ASCII.
|
||||
.B \-a, \-\-add \fIFIELD=VALUE\fP
|
||||
Add a tag. Note that multiple tags with the same field name are perfectly acceptable, so you can add
|
||||
multiple fields with the same name, and previously existing tags will also be preserved.
|
||||
When the \fB--delete\fP is used with the same \fIFIELD\fP, only the older tags are deleted.
|
||||
When \fB--delete\fP is used with the same \fIFIELD\fP, only the older tags are deleted.
|
||||
.TP
|
||||
.B \-s, \-\-set \fIFIELD=VALUE\fP
|
||||
This option is provided for convenience. It delete all the fields of the same
|
||||
This option is provided for convenience. It deletes all the fields of the same
|
||||
type that may already exist, then adds it with the wanted value.
|
||||
This is strictly equivalent to \fB--delete\fP \fIFIELD\fP \fB--add\fP
|
||||
\fIFIELD=VALUE\fP. You can combine it with \fB--add\fP to add tags of the same
|
||||
@ -93,33 +93,35 @@ added with \fB--add\fP.
|
||||
Delete all the previously existing tags.
|
||||
.TP
|
||||
.B \-S, \-\-set-all
|
||||
Sets the tags from scratch.
|
||||
Set the tags from scratch.
|
||||
All the original tags are deleted and new ones are read from standard input.
|
||||
Each line must specify a \fIFIELD=VALUE\fP pair and be separated with line feeds.
|
||||
Empty lines and lines starting with \fI#\fP are ignored.
|
||||
Multiline tags must have their continuation lines prefixed by a single tab (in other words, every
|
||||
Multi-line tags must have their continuation lines prefixed by a single tab (in other words, every
|
||||
\fI\\n\fP must be replaced by \fI\\n\\t\fP).
|
||||
.TP
|
||||
.B \-e, \-\-edit
|
||||
Edit tags interactively by spawning the program specified by the EDITOR
|
||||
environment variable. The allowed format is the same as \fB--set-all\fP.
|
||||
environment variable. The allowed format is the same as with \fB--set-all\fP.
|
||||
If TERM and VISUAL are set, VISUAL takes precedence over EDITOR.
|
||||
.TP
|
||||
.B \-\-output-cover \fIFILE\fP
|
||||
Save the cover art of the input Opus file to the specified location.
|
||||
Extract the cover art from the \fBMETADATA_BLOCK_PICTURE\fP tag into the specified location.
|
||||
If the input file does not contain any cover art, this option has no effect.
|
||||
To allow overwriting the target location, specify \fB--overwrite\fP.
|
||||
In the case of multiple pictures embedded in the Opus tags, only the first one is saved.
|
||||
Note that the since the image format is not fixed, you should consider an extension-less file name
|
||||
and rely on the magic number to deduce the type. opustags does not add or check the target file’s
|
||||
extension.
|
||||
Note that since the image format is not fixed, you should consider an extension-less file name and
|
||||
rely on the magic number to deduce the type.
|
||||
\fBopustags\fP does not add or check the target file’s extension.
|
||||
You can specify \fB-\fP for standard output, in which case the regular output will be suppressed.
|
||||
.TP
|
||||
.B \-\-set-cover \fIFILE\fP
|
||||
Replace or set the cover art to the specified picture.
|
||||
Set the cover art by embedding the specified picture into the \fBMETADATA_BLOCK_PICTURE\fP tag,
|
||||
replacing any existing values.
|
||||
Specify \fB-\fP to read the picture from standard input.
|
||||
In theory, an Opus file may contain multiple pictures with different roles, though in practice only
|
||||
the front cover really matters. opustags can currently only handle one front cover and nothing else.
|
||||
the front cover really matters.
|
||||
\fBopustags\fP can currently only handle one front cover and nothing else.
|
||||
.TP
|
||||
.B \-\-vendor
|
||||
Print the vendor string from the OpusTags packet and do nothing else. Standard tags operations are
|
||||
@ -140,9 +142,10 @@ character set even though your system cannot display it.
|
||||
When editing tags programmatically with line-based tools like grep or sed, tags containing newlines
|
||||
are likely to corrupt the result because these tools won’t interpret multi-line tags as a whole. To
|
||||
make automatic processing easier, \fB-z\fP delimits tags by a null byte (ASCII NUL) instead of line
|
||||
feeds. That same \fB-z\fP flag is also supported by GNU grep or GNU sed and, combined with opustags
|
||||
-z, would make them process the input tag-by-tag instead of line-by-line, thus supporting multi-line
|
||||
tags as well. This option also disables the TAB prefix for continuation lines after a line feed.
|
||||
feeds. That same \fB-z\fP flag is also supported by GNU grep or GNU sed and, combined with
|
||||
\fBopustags -z\fP, would make them process the input tag-by-tag instead of line-by-line, thus
|
||||
supporting multi-line tags as well.
|
||||
This option also disables the tab prefix for continuation lines after a line feed.
|
||||
.SH EXAMPLES
|
||||
.PP
|
||||
List all the tags in file foo.opus:
|
||||
@ -172,7 +175,7 @@ Use GNU grep to remove all the CHAPTER* tags, with -z to support multi-line tags
|
||||
.SH CAVEATS
|
||||
.PP
|
||||
\fBopustags\fP currently has the following limitations:
|
||||
.IP \[bu]
|
||||
.IP \[bu] 2n
|
||||
Multiplexed streams are not supported.
|
||||
.IP \[bu]
|
||||
Control characters inside tags are printed raw rather than being escaped.
|
||||
|
@ -27,7 +27,7 @@ std::u8string ot::encode_base64(ot::byte_string_view src)
|
||||
std::u8string out;
|
||||
out.resize(olen);
|
||||
|
||||
const uint8_t* in = src.data();
|
||||
const uint8_t* in = reinterpret_cast<const uint8_t*>(src.data());
|
||||
const uint8_t* end = in + len;
|
||||
char8_t* pos = out.data();
|
||||
while (end - in >= 3) {
|
||||
@ -56,7 +56,7 @@ std::u8string ot::encode_base64(ot::byte_string_view src)
|
||||
ot::byte_string ot::decode_base64(std::u8string_view src)
|
||||
{
|
||||
// Remove the padding and rely on the string length instead.
|
||||
while (src.back() == u8'=')
|
||||
while (!src.empty() && src.back() == u8'=')
|
||||
src.remove_suffix(1);
|
||||
|
||||
size_t olen = src.size() / 4 * 3; // Whole blocks;
|
||||
@ -68,7 +68,7 @@ ot::byte_string ot::decode_base64(std::u8string_view src)
|
||||
|
||||
ot::byte_string out;
|
||||
out.resize(olen);
|
||||
uint8_t* pos = out.data();
|
||||
uint8_t* pos = reinterpret_cast<uint8_t*>(out.data());
|
||||
|
||||
unsigned char dtable[256];
|
||||
memset(dtable, 0x80, 256);
|
||||
@ -84,11 +84,11 @@ ot::byte_string ot::decode_base64(std::u8string_view src)
|
||||
|
||||
block[count++] = tmp;
|
||||
if (count == 2) {
|
||||
*pos++ = (block[0] << 2) | (block[1] >> 4);
|
||||
*pos++ = 0xFF & (block[0] << 2) | (block[1] >> 4);
|
||||
} else if (count == 3) {
|
||||
*pos++ = (block[1] << 4) | (block[2] >> 2);
|
||||
*pos++ = 0xFF & (block[1] << 4) | (block[2] >> 2);
|
||||
} else if (count == 4) {
|
||||
*pos++ = (block[2] << 6) | block[3];
|
||||
*pos++ = 0xFF & (block[2] << 6) | block[3];
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
@ -503,7 +503,7 @@ static void process(ot::ogg_reader& reader, ot::ogg_writer* writer, const ot::op
|
||||
* output stream, we need to renumber all the succeeding pages. If the input stream
|
||||
* contains gaps, the offset will naively reproduce the gaps: page numbers 0 (1) 2 4 will
|
||||
* become 0 (1 2) 3 5, where (…) is the OpusTags packet, and not 0 (1 2) 3 4. */
|
||||
int pageno_offset = 0;
|
||||
long pageno_offset = 0;
|
||||
|
||||
while (reader.next_page()) {
|
||||
auto serialno = ogg_page_serialno(&reader.page);
|
||||
|
@ -28,8 +28,8 @@ bool ot::ogg_reader::next_page()
|
||||
while ((rc = ogg_sync_pageout(&sync, &page)) != 1) {
|
||||
if (rc == -1) {
|
||||
throw status {st::bad_stream,
|
||||
absolute_page_no == (size_t) -1 ? "Input is not a valid Ogg file."
|
||||
: "Unsynced data in stream."};
|
||||
absolute_page_no == -1 ? "Input is not a valid Ogg file."
|
||||
: "Unsynced data in stream."};
|
||||
}
|
||||
if (ogg_sync_check(&sync) != 0)
|
||||
throw status {st::libogg_error, "ogg_sync_check signalled an error."};
|
||||
|
29
src/opus.cc
29
src/opus.cc
@ -55,7 +55,9 @@ ot::opus_tags ot::parse_tags(const ogg_packet& packet)
|
||||
// Comment count
|
||||
if (pos + 4 > size)
|
||||
throw status {st::cut_comment_count, "Comment count did not fit the comment header"};
|
||||
uint32_t count = le32toh(*((uint32_t*) (data + pos)));
|
||||
uint32_t count;
|
||||
memcpy(&count, data + pos, sizeof(count));
|
||||
count = le32toh(count);
|
||||
pos += 4;
|
||||
|
||||
// Comments' data
|
||||
@ -63,7 +65,9 @@ ot::opus_tags ot::parse_tags(const ogg_packet& packet)
|
||||
if (pos + 4 > size)
|
||||
throw status {st::cut_comment_length,
|
||||
"Comment length did not fit the comment header"};
|
||||
uint32_t comment_length = le32toh(*((uint32_t*) (data + pos)));
|
||||
uint32_t comment_length;
|
||||
memcpy(&comment_length, data + pos, sizeof(comment_length));
|
||||
comment_length = le32toh(comment_length);
|
||||
if (pos + 4 + comment_length > size)
|
||||
throw status {st::cut_comment_data,
|
||||
"Comment string did not fit the comment header"};
|
||||
@ -73,7 +77,7 @@ ot::opus_tags ot::parse_tags(const ogg_packet& packet)
|
||||
}
|
||||
|
||||
// Extra data
|
||||
my_tags.extra_data = byte_string(data + pos, size - pos);
|
||||
my_tags.extra_data = byte_string(reinterpret_cast<const char*>(data + pos), size - pos);
|
||||
|
||||
return my_tags;
|
||||
}
|
||||
@ -134,12 +138,16 @@ ot::picture::picture(ot::byte_string block)
|
||||
size_t desc_offset = mime_offset + 4 + mime_size;
|
||||
if (storage.size() < desc_offset + 4)
|
||||
throw status { st::invalid_size, "missing description in picture block" };
|
||||
uint32_t desc_size = be32toh(*reinterpret_cast<const uint32_t*>(&storage[desc_offset]));
|
||||
uint32_t desc_size;
|
||||
memcpy(&desc_size, &storage[desc_offset], sizeof(desc_size));
|
||||
desc_size = be32toh(desc_size);
|
||||
|
||||
size_t pic_offset = desc_offset + 4 + desc_size + 16;
|
||||
if (storage.size() < pic_offset + 4)
|
||||
throw status { st::invalid_size, "missing picture data in picture block" };
|
||||
uint32_t pic_size = be32toh(*reinterpret_cast<const uint32_t*>(&storage[pic_offset]));
|
||||
uint32_t pic_size;
|
||||
memcpy(&pic_size, &storage[pic_offset], sizeof(pic_size));
|
||||
pic_size = be32toh(pic_size);
|
||||
|
||||
if (storage.size() != pic_offset + 4 + pic_size)
|
||||
throw status { st::invalid_size, "invalid picture block size" };
|
||||
@ -157,7 +165,8 @@ ot::byte_string ot::picture::serialize() const
|
||||
*reinterpret_cast<uint32_t*>(&bytes[0]) = htobe32(3); // Picture type: front cover.
|
||||
*reinterpret_cast<uint32_t*>(&bytes[mime_offset]) = htobe32(mime_type.size());
|
||||
std::copy(mime_type.begin(), mime_type.end(), std::next(bytes.begin(), mime_offset + 4));
|
||||
*reinterpret_cast<uint32_t*>(&bytes[pic_offset]) = htobe32(picture_data.size());
|
||||
uint32_t picture_data_size = htobe32(picture_data.size());
|
||||
memcpy(&bytes[pic_offset], &picture_data_size, sizeof(picture_data_size));
|
||||
std::copy(picture_data.begin(), picture_data.end(), std::next(bytes.begin(), pic_offset + 4));
|
||||
return bytes;
|
||||
}
|
||||
@ -191,16 +200,16 @@ std::optional<ot::picture> ot::extract_cover(const ot::opus_tags& tags)
|
||||
static ot::byte_string_view detect_mime_type(ot::byte_string_view data)
|
||||
{
|
||||
static std::initializer_list<std::pair<ot::byte_string_view, ot::byte_string_view>> magic_numbers = {
|
||||
{ "\xff\xd8\xff"_bsv, "image/jpeg"_bsv },
|
||||
{ "\x89PNG"_bsv, "image/png"_bsv },
|
||||
{ "GIF8"_bsv, "image/gif"_bsv },
|
||||
{ "\xff\xd8\xff"sv, "image/jpeg"sv },
|
||||
{ "\x89PNG"sv, "image/png"sv },
|
||||
{ "GIF8"sv, "image/gif"sv },
|
||||
};
|
||||
for (auto [magic, mime] : magic_numbers) {
|
||||
if (data.starts_with(magic))
|
||||
return mime;
|
||||
}
|
||||
fputs("warning: Could not identify the MIME type of the picture; defaulting to application/octet-stream.\n", stderr);
|
||||
return "application/octet-stream"_bsv;
|
||||
return "application/octet-stream"sv;
|
||||
}
|
||||
|
||||
std::u8string ot::make_cover(ot::byte_string_view picture_data)
|
||||
|
@ -111,21 +111,27 @@ struct status {
|
||||
std::string message;
|
||||
};
|
||||
|
||||
using byte_string = std::basic_string<uint8_t>;
|
||||
using byte_string_view = std::basic_string_view<uint8_t>;
|
||||
/**
|
||||
* Alias for binary data strings. Concretely the same as regular strings but reflect the intent.
|
||||
*/
|
||||
using byte_string = std::string;
|
||||
using byte_string_view = std::string_view;
|
||||
|
||||
/***********************************************************************************************//**
|
||||
* \defgroup system System
|
||||
* \{
|
||||
*/
|
||||
|
||||
/** fclose wrapper for std::unique_ptr’s deleter. */
|
||||
void close_file(FILE*);
|
||||
|
||||
/**
|
||||
* Smart auto-closing FILE* handle.
|
||||
*
|
||||
* It implictly converts from an already opened FILE*.
|
||||
*/
|
||||
struct file : std::unique_ptr<FILE, decltype(&fclose)> {
|
||||
file(FILE* f = nullptr) : std::unique_ptr<FILE, decltype(&fclose)>(f, &fclose) {}
|
||||
struct file : std::unique_ptr<FILE, decltype(&close_file)> {
|
||||
file(FILE* f = nullptr) : std::unique_ptr<FILE, decltype(&close_file)>(f, &close_file) {}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -260,10 +266,9 @@ struct ogg_reader {
|
||||
ogg_page page;
|
||||
/**
|
||||
* Page number in the physical stream of the last read page, disregarding multiplexed
|
||||
* streams. The first page number is 0. When no page has been read, its value is
|
||||
* (size_t) -1.
|
||||
* streams. The first page number is 0. When no page has been read, its value is -1.
|
||||
*/
|
||||
size_t absolute_page_no = -1;
|
||||
long absolute_page_no = -1;
|
||||
/**
|
||||
* The file is our source of binary data. It is not integrated to libogg, so we need to
|
||||
* handle it ourselves.
|
||||
@ -581,7 +586,3 @@ void run(const options& opt);
|
||||
/** \} */
|
||||
|
||||
}
|
||||
|
||||
/** Handy literal suffix for building byte strings. */
|
||||
ot::byte_string operator""_bs(const char* data, size_t size);
|
||||
ot::byte_string_view operator""_bsv(const char* data, size_t size);
|
||||
|
@ -19,14 +19,9 @@
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
ot::byte_string operator""_bs(const char* data, size_t size)
|
||||
void ot::close_file(FILE* file)
|
||||
{
|
||||
return ot::byte_string(reinterpret_cast<const uint8_t*>(data), size);
|
||||
}
|
||||
|
||||
ot::byte_string_view operator""_bsv(const char* data, size_t size)
|
||||
{
|
||||
return ot::byte_string_view(reinterpret_cast<const uint8_t*>(data), size);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void ot::partial_file::open(const char* destination)
|
||||
@ -122,9 +117,9 @@ ot::byte_string ot::slurp_binary_file(const char* filename)
|
||||
|
||||
byte_string content;
|
||||
long file_size = get_file_size(f.get());
|
||||
if (file_size == -1) {
|
||||
if (file_size < 0) {
|
||||
// Read the input stream block by block and resize the output byte string as needed.
|
||||
uint8_t buffer[4096];
|
||||
char buffer[4096];
|
||||
while (!feof(f.get())) {
|
||||
size_t read_len = fread(buffer, 1, sizeof(buffer), f.get());
|
||||
content.append(buffer, read_len);
|
||||
@ -135,7 +130,7 @@ ot::byte_string ot::slurp_binary_file(const char* filename)
|
||||
} else {
|
||||
// Lucky! We know the file size, so let’s slurp it at once.
|
||||
content.resize(file_size);
|
||||
if (fread(content.data(), 1, file_size, f.get()) < file_size)
|
||||
if (fread(content.data(), 1, file_size, f.get()) < size_t(file_size))
|
||||
throw status { st::standard_error,
|
||||
"Could not read '"s + filename + "': " + strerror(errno) + "." };
|
||||
}
|
||||
|
32
t/base64.cc
32
t/base64.cc
@ -3,26 +3,26 @@
|
||||
|
||||
static void check_encode_base64()
|
||||
{
|
||||
opaque_is(ot::encode_base64(""_bsv), u8"", "empty");
|
||||
opaque_is(ot::encode_base64("a"_bsv), u8"YQ==", "1 character");
|
||||
opaque_is(ot::encode_base64("aa"_bsv), u8"YWE=", "2 characters");
|
||||
opaque_is(ot::encode_base64("aaa"_bsv), u8"YWFh", "3 characters");
|
||||
opaque_is(ot::encode_base64("aaaa"_bsv), u8"YWFhYQ==", "4 characters");
|
||||
opaque_is(ot::encode_base64("\xFF\xFF\xFE"_bsv), u8"///+", "RFC alphabet");
|
||||
opaque_is(ot::encode_base64("\0x"_bsv), u8"AHg=", "embedded null bytes");
|
||||
opaque_is(ot::encode_base64(""sv), u8"", "empty");
|
||||
opaque_is(ot::encode_base64("a"sv), u8"YQ==", "1 character");
|
||||
opaque_is(ot::encode_base64("aa"sv), u8"YWE=", "2 characters");
|
||||
opaque_is(ot::encode_base64("aaa"sv), u8"YWFh", "3 characters");
|
||||
opaque_is(ot::encode_base64("aaaa"sv), u8"YWFhYQ==", "4 characters");
|
||||
opaque_is(ot::encode_base64("\xFF\xFF\xFE"sv), u8"///+", "RFC alphabet");
|
||||
opaque_is(ot::encode_base64("\0x"sv), u8"AHg=", "embedded null bytes");
|
||||
}
|
||||
|
||||
static void check_decode_base64()
|
||||
{
|
||||
opaque_is(ot::decode_base64(u8""), ""_bsv, "empty");
|
||||
opaque_is(ot::decode_base64(u8"YQ=="), "a"_bsv, "1 character");
|
||||
opaque_is(ot::decode_base64(u8"YWE="), "aa"_bsv, "2 characters");
|
||||
opaque_is(ot::decode_base64(u8"YQ"), "a"_bsv, "padless 1 character");
|
||||
opaque_is(ot::decode_base64(u8"YWE"), "aa"_bsv, "padless 2 characters");
|
||||
opaque_is(ot::decode_base64(u8"YWFh"), "aaa"_bsv, "3 characters");
|
||||
opaque_is(ot::decode_base64(u8"YWFhYQ=="), "aaaa"_bsv, "4 characters");
|
||||
opaque_is(ot::decode_base64(u8"///+"), "\xFF\xFF\xFE"_bsv, "RFC alphabet");
|
||||
opaque_is(ot::decode_base64(u8"AHg="), "\0x"_bsv, "embedded null bytes");
|
||||
opaque_is(ot::decode_base64(u8""), ""sv, "empty");
|
||||
opaque_is(ot::decode_base64(u8"YQ=="), "a"sv, "1 character");
|
||||
opaque_is(ot::decode_base64(u8"YWE="), "aa"sv, "2 characters");
|
||||
opaque_is(ot::decode_base64(u8"YQ"), "a"sv, "padless 1 character");
|
||||
opaque_is(ot::decode_base64(u8"YWE"), "aa"sv, "padless 2 characters");
|
||||
opaque_is(ot::decode_base64(u8"YWFh"), "aaa"sv, "3 characters");
|
||||
opaque_is(ot::decode_base64(u8"YWFhYQ=="), "aaaa"sv, "4 characters");
|
||||
opaque_is(ot::decode_base64(u8"///+"), "\xFF\xFF\xFE"sv, "RFC alphabet");
|
||||
opaque_is(ot::decode_base64(u8"AHg="), "\0x"sv, "embedded null bytes");
|
||||
|
||||
try {
|
||||
ot::decode_base64(u8"Y===");
|
||||
|
12
t/opus.cc
12
t/opus.cc
@ -123,7 +123,7 @@ static void recode_padding()
|
||||
op.packet = (unsigned char*) padded_OpusTags.data();
|
||||
|
||||
ot::opus_tags tags = ot::parse_tags(op);
|
||||
if (tags.extra_data != "\0hello"_bsv)
|
||||
if (tags.extra_data != "\0hello"sv)
|
||||
throw failure("corrupted extra data");
|
||||
// recode the packet and ensure it's exactly the same
|
||||
auto packet = ot::render_tags(tags);
|
||||
@ -137,7 +137,7 @@ static void recode_padding()
|
||||
|
||||
static void extract_cover()
|
||||
{
|
||||
ot::byte_string_view picture_data = ""_bsv
|
||||
ot::byte_string_view picture_data = ""sv
|
||||
"\x00\x00\x00\x03" // Picture type 3.
|
||||
"\x00\x00\x00\x09" "image/foo" // MIME type.
|
||||
"\x00\x00\x00\x00" "" // Description.
|
||||
@ -152,9 +152,9 @@ static void extract_cover()
|
||||
std::optional<ot::picture> cover = ot::extract_cover(tags);
|
||||
if (!cover)
|
||||
throw failure("could not extract the cover");
|
||||
if (cover->mime_type != "image/foo"_bsv)
|
||||
if (cover->mime_type != "image/foo"sv)
|
||||
throw failure("bad extracted MIME type");
|
||||
if (cover->picture_data != "Picture data"_bsv)
|
||||
if (cover->picture_data != "Picture data"sv)
|
||||
throw failure("bad extracted picture data");
|
||||
|
||||
ot::byte_string_view truncated_data = picture_data.substr(0, picture_data.size() - 1);
|
||||
@ -167,7 +167,7 @@ static void extract_cover()
|
||||
|
||||
static void make_cover()
|
||||
{
|
||||
ot::byte_string_view picture_block = ""_bsv
|
||||
ot::byte_string_view picture_block = ""sv
|
||||
"\x00\x00\x00\x03" // Picture type 3.
|
||||
"\x00\x00\x00\x09" "image/png" // MIME type.
|
||||
"\x00\x00\x00\x00" "" // Description.
|
||||
@ -178,7 +178,7 @@ static void make_cover()
|
||||
"\x00\x00\x00\x11" "\x89PNG Picture data";
|
||||
|
||||
std::u8string expected = u8"METADATA_BLOCK_PICTURE=" + ot::encode_base64(picture_block);
|
||||
opaque_is(ot::make_cover("\x89PNG Picture data"_bsv), expected, "build the picture tag");
|
||||
opaque_is(ot::make_cover("\x89PNG Picture data"sv), expected, "build the picture tag");
|
||||
}
|
||||
|
||||
int main()
|
||||
|
@ -36,7 +36,7 @@ void check_partial_files()
|
||||
|
||||
void check_slurp()
|
||||
{
|
||||
static const ot::byte_string_view pixel = ""_bsv
|
||||
static const ot::byte_string_view pixel = ""sv
|
||||
"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d"
|
||||
"\x49\x48\x44\x52\x00\x00\x00\x01\x00\x00\x00\x01"
|
||||
"\x08\x02\x00\x00\x00\x90\x77\x53\xde\x00\x00\x00"
|
||||
|
Reference in New Issue
Block a user