Compare commits
42 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5680a3a4b7 | ||
![]() |
15ce8eb487 | ||
![]() |
b7744be208 | ||
![]() |
63c5d66016 | ||
![]() |
d09bd9178f | ||
![]() |
7d8b1860c3 | ||
![]() |
b06825829b | ||
![]() |
ba4cd47fd8 | ||
![]() |
bbe403f141 | ||
![]() |
5df2707d98 | ||
![]() |
4859ea468f | ||
![]() |
2a8830db70 | ||
![]() |
fed9b6fd74 | ||
![]() |
b02890eb8a | ||
![]() |
da882a6eb6 | ||
![]() |
aeb89aa9d6 | ||
![]() |
f885807ecc | ||
![]() |
b826fd71f0 | ||
![]() |
ae35df1126 | ||
![]() |
80e55f6bfc | ||
![]() |
e7411c0c4b | ||
![]() |
e9af692973 | ||
![]() |
0cf90ee8b6 | ||
![]() |
dc3c0c8866 | ||
![]() |
1c46bb1ba6 | ||
![]() |
2e8f42c6ad | ||
![]() |
2b301ffd2c | ||
![]() |
ef0765ca10 | ||
![]() |
9766ac6db3 | ||
![]() |
32799ff682 | ||
![]() |
ce093be12c | ||
![]() |
2c276770f0 | ||
![]() |
75a592f629 | ||
![]() |
13ce07d181 | ||
![]() |
d659c7df19 | ||
![]() |
f8403a1d29 | ||
![]() |
ebb952c4ad | ||
![]() |
bea3b954a5 | ||
![]() |
129d8e89b9 | ||
![]() |
65778a3774 | ||
![]() |
d9841668ff | ||
![]() |
85d27cbcb9 |
NEWS
android
doc
meson.buildpython/build
src
LocateUri.cxxPlaylistDatabase.cxxSongPrint.cxxSongSave.cxxSongUpdate.cxxStats.cxxTimePrint.cxx
archive
plugins
command
db
decoder
plugins
input
lib
curl
Global.cxxGlobal.hxxHandler.hxxInit.cxxMulti.hxxRequest.cxxRequest.hxxSlist.hxxVersion.cxxVersion.hxx
gcrypt
nfs
sqlite
xiph
neighbor
net
output
pcm
song
storage
plugins
system
tag
time
util
zeroconf
test
11
NEWS
11
NEWS
@@ -1,3 +1,14 @@
|
||||
ver 0.21.17 (2019/12/16)
|
||||
* protocol
|
||||
- relax the ISO 8601 parser: allow omitting field separators, the
|
||||
time of day and the "Z" suffix
|
||||
* archive
|
||||
- zzip: improve error reporting
|
||||
* outputs
|
||||
- jack: mark ports as terminal
|
||||
- shout: declare metadata as UTF-8
|
||||
* fix build failure with -Ddatabase=false
|
||||
|
||||
ver 0.21.16 (2019/10/16)
|
||||
* queue
|
||||
- fix relative destination offset when moving a range
|
||||
|
@@ -2,8 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="39"
|
||||
android:versionName="0.21.16">
|
||||
android:versionCode="40"
|
||||
android:versionName="0.21.17">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
|
||||
|
||||
|
@@ -38,7 +38,7 @@ author = 'Max Kellermann'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.21.16'
|
||||
version = '0.21.17'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
@@ -42,7 +42,7 @@ Provides access to the database of another :program:`MPD` instance using libmpdc
|
||||
* - **password**
|
||||
- The password used to log in to the "master" :program:`MPD` instance.
|
||||
* - **keepalive yes|no**
|
||||
- Send TCP keepalive packets to the "master" :program:`MPD` instance? This option can help avoid certain firewalls dropping inactive connections, at the expensive of a very small amount of additional network traffic. Disabled by default.
|
||||
- Send TCP keepalive packets to the "master" :program:`MPD` instance? This option can help avoid certain firewalls dropping inactive connections, at the expense of a very small amount of additional network traffic. Disabled by default.
|
||||
|
||||
upnp
|
||||
----
|
||||
@@ -1069,7 +1069,7 @@ Filter plugins
|
||||
normalize
|
||||
---------
|
||||
|
||||
Normalize the volume during playback (at the expensve of quality).
|
||||
Normalize the volume during playback (at the expense of quality).
|
||||
|
||||
|
||||
null
|
||||
|
12
doc/user.rst
12
doc/user.rst
@@ -62,16 +62,16 @@ In any case, you need:
|
||||
Each plugin usually needs a codec library, which you also need to
|
||||
install. Check the :doc:`plugins` for details about required libraries
|
||||
|
||||
For example, the following installs a fairly complete list of build dependencies on Debian Jessie:
|
||||
For example, the following installs a fairly complete list of build dependencies on Debian Buster:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
apt install g++ \
|
||||
apt install meson g++ \
|
||||
libpcre3-dev \
|
||||
libmad0-dev libmpg123-dev libid3tag0-dev \
|
||||
libflac-dev libvorbis-dev libopus-dev \
|
||||
libflac-dev libvorbis-dev libopus-dev libogg-dev \
|
||||
libadplug-dev libaudiofile-dev libsndfile1-dev libfaad-dev \
|
||||
libfluidsynth-dev libgme-dev libmikmod2-dev libmodplug-dev \
|
||||
libfluidsynth-dev libgme-dev libmikmod-dev libmodplug-dev \
|
||||
libmpcdec-dev libwavpack-dev libwildmidi-dev \
|
||||
libsidplay2-dev libsidutils-dev libresid-builder-dev \
|
||||
libavcodec-dev libavformat-dev \
|
||||
@@ -91,7 +91,9 @@ For example, the following installs a fairly complete list of build dependencies
|
||||
libsystemd-dev \
|
||||
libgtest-dev \
|
||||
libboost-dev \
|
||||
libicu-dev
|
||||
libicu-dev \
|
||||
libchromaprint-dev \
|
||||
libgcrypt20-dev
|
||||
|
||||
|
||||
Now configure the source tree:
|
||||
|
@@ -1,7 +1,7 @@
|
||||
project(
|
||||
'mpd',
|
||||
['c', 'cpp'],
|
||||
version: '0.21.16',
|
||||
version: '0.21.17',
|
||||
meson_version: '>= 0.49.0',
|
||||
default_options: [
|
||||
'c_std=c99',
|
||||
@@ -304,6 +304,7 @@ if enable_database
|
||||
endif
|
||||
|
||||
subdir('src/util')
|
||||
subdir('src/time')
|
||||
subdir('src/system')
|
||||
subdir('src/thread')
|
||||
subdir('src/event')
|
||||
@@ -385,8 +386,11 @@ endif
|
||||
if archive_glue_dep.found()
|
||||
sources += [
|
||||
'src/TagArchive.cxx',
|
||||
'src/db/update/Archive.cxx',
|
||||
]
|
||||
|
||||
if enable_database
|
||||
sources += ['src/db/update/Archive.cxx']
|
||||
endif
|
||||
endif
|
||||
|
||||
if is_windows
|
||||
|
@@ -112,8 +112,8 @@ liblame = AutotoolsProject(
|
||||
)
|
||||
|
||||
ffmpeg = FfmpegProject(
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.2.tar.xz',
|
||||
'023f10831a97ad93d798f53a3640e55cd564abfeba807ecbe8524dac4fedecd5',
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.2.1.tar.xz',
|
||||
'cec7c87e9b60d174509e263ac4011b522385fd0775292e1670ecc1180c9bb6d4',
|
||||
'lib/libavcodec.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -341,8 +341,8 @@ ffmpeg = FfmpegProject(
|
||||
)
|
||||
|
||||
curl = AutotoolsProject(
|
||||
'http://curl.haxx.se/download/curl-7.65.3.tar.xz',
|
||||
'f2d98854813948d157f6a91236ae34ca4a1b4cb302617cebad263d79b0235fea',
|
||||
'http://curl.haxx.se/download/curl-7.66.0.tar.xz',
|
||||
'dbb48088193016d079b97c5c3efde8efa56ada2ebf336e8a97d04eb8e2ed98c1',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
|
@@ -29,6 +29,8 @@
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
static LocatedUri
|
||||
LocateFileUri(const char *uri, const Client *client
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
@@ -21,8 +21,8 @@
|
||||
#include "db/PlaylistVector.hxx"
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/StringStrip.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
#include <string.h>
|
||||
|
@@ -27,7 +27,7 @@
|
||||
#include "TagPrint.hxx"
|
||||
#include "client/Response.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
|
||||
#define SONG_FILE "file: "
|
||||
|
@@ -27,7 +27,7 @@
|
||||
#include "tag/ParseName.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "tag/Builder.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/StringAPI.hxx"
|
||||
#include "util/StringBuffer.hxx"
|
||||
#include "util/StringStrip.hxx"
|
||||
|
@@ -98,8 +98,6 @@ Song::UpdateFile(Storage &storage) noexcept
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
|
||||
Song *
|
||||
@@ -145,6 +143,8 @@ Song::UpdateFileInArchive(ArchiveFile &archive) noexcept
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* ENABLE_DATABASE */
|
||||
|
||||
bool
|
||||
DetachedSong::LoadFile(Path path) noexcept
|
||||
{
|
||||
|
@@ -28,7 +28,7 @@
|
||||
#include "db/Stats.hxx"
|
||||
#include "system/Clock.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "TimePrint.hxx"
|
||||
#include "client/Response.hxx"
|
||||
#include "util/TimeISO8601.hxx"
|
||||
#include "time/ISO8601.hxx"
|
||||
|
||||
void
|
||||
time_print(Response &r, const char *name,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* Copyright 2003-2019 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "../ArchiveVisitor.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "system/Error.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
#include <zzip/zzip.h>
|
||||
@@ -120,9 +121,19 @@ ZzipArchiveFile::OpenStream(const char *pathname,
|
||||
Mutex &mutex)
|
||||
{
|
||||
ZZIP_FILE *_file = zzip_file_open(dir->dir, pathname, 0);
|
||||
if (_file == nullptr)
|
||||
throw FormatRuntimeError("not found in the ZIP file: %s",
|
||||
pathname);
|
||||
if (_file == nullptr) {
|
||||
const auto error = (zzip_error_t)zzip_error(dir->dir);
|
||||
switch (error) {
|
||||
case ZZIP_ENOENT:
|
||||
throw FormatFileNotFound("Failed to open '%s' in ZIP file",
|
||||
pathname);
|
||||
|
||||
default:
|
||||
throw FormatRuntimeError("Failed to open '%s' in ZIP file: %s",
|
||||
pathname,
|
||||
zzip_strerror(error));
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<ZzipInputStream>(dir, pathname,
|
||||
mutex,
|
||||
|
@@ -35,7 +35,7 @@
|
||||
#include "decoder/DecoderPrint.hxx"
|
||||
#include "ls.hxx"
|
||||
#include "mixer/Volume.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/StringAPI.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
|
@@ -37,9 +37,9 @@
|
||||
#include "client/Response.hxx"
|
||||
#include "Mapper.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "LocateUri.hxx"
|
||||
|
||||
bool
|
||||
|
@@ -23,8 +23,8 @@
|
||||
#include "StorageCommands.hxx"
|
||||
#include "Request.hxx"
|
||||
#include "CommandError.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "client/Client.hxx"
|
||||
|
@@ -34,7 +34,7 @@
|
||||
#include "PlaylistInfo.hxx"
|
||||
#include "Interface.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/RecursiveMap.hxx"
|
||||
|
||||
#include <functional>
|
||||
|
@@ -25,7 +25,7 @@
|
||||
#include "PlaylistDatabase.hxx"
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/NumberParser.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
@@ -719,6 +719,11 @@ MadDecoder::DecodeFirstFrame(Tag *tag) noexcept
|
||||
{
|
||||
struct xing xing;
|
||||
|
||||
#if GCC_CHECK_VERSION(10,0)
|
||||
/* work around bogus -Wuninitialized in GCC 10 */
|
||||
xing.frames = 0;
|
||||
#endif
|
||||
|
||||
while (true) {
|
||||
MadDecoderAction ret;
|
||||
do {
|
||||
|
@@ -35,6 +35,9 @@ input_glue = static_library(
|
||||
'BufferedInputStream.cxx',
|
||||
'MaybeBufferedInputStream.cxx',
|
||||
include_directories: inc,
|
||||
dependencies: [
|
||||
boost_dep,
|
||||
],
|
||||
)
|
||||
|
||||
input_glue_dep = declare_dependency(
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2008-2016 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2008-2016 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2008-2018 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2008-2018 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2016-2018 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2008-2018 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2008-2018 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2008-2018 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
/**
|
||||
* OO wrapper for "struct curl_slist *".
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2017-2018 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2017-2018 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@@ -35,6 +35,8 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
template<typename T> struct ConstBuffer;
|
||||
|
||||
gcc_pure
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include "util/ASCII.hxx"
|
||||
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
@@ -1,4 +1,9 @@
|
||||
sqlite_dep = dependency('sqlite3', version: '>= 3.7.3', required: get_option('sqlite'))
|
||||
if enable_database
|
||||
sqlite_dep = dependency('sqlite3', version: '>= 3.7.3', required: get_option('sqlite'))
|
||||
else
|
||||
sqlite_dep = dependency('', required: false)
|
||||
endif
|
||||
|
||||
conf.set('ENABLE_SQLITE', sqlite_dep.found())
|
||||
if not sqlite_dep.found()
|
||||
subdir_done()
|
||||
|
@@ -49,6 +49,9 @@ xiph = static_library(
|
||||
'VorbisComments.cxx',
|
||||
'XiphTags.cxx',
|
||||
include_directories: inc,
|
||||
dependencies: [
|
||||
libvorbis_dep,
|
||||
],
|
||||
)
|
||||
|
||||
xiph_dep = declare_dependency(
|
||||
|
@@ -1,4 +1,4 @@
|
||||
if not get_option('neighbor')
|
||||
if not get_option('neighbor') or not enable_database
|
||||
conf.set('ENABLE_NEIGHBOR_PLUGINS', false)
|
||||
neighbor_glue_dep = dependency('', required: false)
|
||||
subdir_done()
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#define STATIC_SOCKET_ADDRESS_HXX
|
||||
|
||||
#include "SocketAddress.hxx"
|
||||
#include "Features.hxx"
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
@@ -403,10 +403,11 @@ JackOutput::Connect()
|
||||
jack_on_shutdown(client, mpd_jack_shutdown, this);
|
||||
|
||||
for (unsigned i = 0; i < num_source_ports; ++i) {
|
||||
unsigned long portflags = JackPortIsOutput | JackPortIsTerminal;
|
||||
ports[i] = jack_port_register(client,
|
||||
source_ports[i].c_str(),
|
||||
JACK_DEFAULT_AUDIO_TYPE,
|
||||
JackPortIsOutput, 0);
|
||||
portflags, 0);
|
||||
if (ports[i] == nullptr) {
|
||||
Disconnect();
|
||||
throw FormatRuntimeError("Cannot register output port \"%s\"",
|
||||
|
@@ -383,6 +383,7 @@ ShoutOutput::SendTag(const Tag &tag)
|
||||
shout_tag_to_metadata(tag, song, sizeof(song));
|
||||
|
||||
shout_metadata_add(meta, "song", song);
|
||||
shout_metadata_add(meta, "charset", "UTF-8");
|
||||
if (SHOUTERR_SUCCESS != shout_set_metadata(shout_conn, meta)) {
|
||||
LogWarning(shout_output_domain,
|
||||
"error setting shout metadata");
|
||||
|
@@ -21,6 +21,8 @@
|
||||
#include "ConfiguredResampler.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void
|
||||
|
@@ -29,8 +29,9 @@
|
||||
#include "AudioParser.hxx"
|
||||
#include "tag/ParseName.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "time/ISO8601.hxx"
|
||||
#include "util/CharUtil.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/StringAPI.hxx"
|
||||
@@ -38,7 +39,6 @@
|
||||
#include "util/StringStrip.hxx"
|
||||
#include "util/StringView.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/TimeISO8601.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "lib/icu/CaseFold.hxx"
|
||||
|
||||
@@ -113,14 +113,19 @@ ParseTimeStamp(const char *s)
|
||||
{
|
||||
assert(s != nullptr);
|
||||
|
||||
char *endptr;
|
||||
unsigned long long value = strtoull(s, &endptr, 10);
|
||||
if (*endptr == 0 && endptr > s)
|
||||
/* it's an integral UNIX time stamp */
|
||||
return std::chrono::system_clock::from_time_t((time_t)value);
|
||||
try {
|
||||
/* try ISO 8601 */
|
||||
return ParseISO8601(s).first;
|
||||
} catch (...) {
|
||||
char *endptr;
|
||||
unsigned long long value = strtoull(s, &endptr, 10);
|
||||
if (*endptr == 0 && endptr > s)
|
||||
/* it's an integral UNIX time stamp */
|
||||
return std::chrono::system_clock::from_time_t((time_t)value);
|
||||
|
||||
/* try ISO 8601 */
|
||||
return ParseISO8601(s);
|
||||
/* rethrow the ParseISO8601() error */
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool
|
||||
|
@@ -19,7 +19,8 @@
|
||||
|
||||
#include "ModifiedSinceSongFilter.hxx"
|
||||
#include "LightSong.hxx"
|
||||
#include "util/TimeISO8601.hxx"
|
||||
#include "time/ISO8601.hxx"
|
||||
#include "util/StringBuffer.hxx"
|
||||
|
||||
std::string
|
||||
ModifiedSinceSongFilter::ToExpression() const noexcept
|
||||
|
@@ -24,6 +24,7 @@ song_dep = declare_dependency(
|
||||
icu_dep,
|
||||
pcre_dep,
|
||||
tag_dep,
|
||||
time_dep,
|
||||
util_dep,
|
||||
],
|
||||
)
|
||||
|
@@ -35,12 +35,13 @@
|
||||
#include "event/DeferEvent.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "thread/Cond.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "time/Parser.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
#include "util/TimeParser.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
|
@@ -57,5 +57,6 @@ storage_plugins_dep = declare_dependency(
|
||||
dependencies: [
|
||||
storage_api_dep,
|
||||
fs_dep,
|
||||
time_dep,
|
||||
],
|
||||
)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2018 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2013-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2015 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2013-2015 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -147,6 +147,26 @@ FormatErrno(const char *fmt, Args&&... args) noexcept
|
||||
return FormatErrno(errno, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
static inline std::system_error
|
||||
FormatFileNotFound(const char *fmt, Args&&... args) noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return FormatLastError(ERROR_FILE_NOT_FOUND, fmt,
|
||||
std::forward<Args>(args)...);
|
||||
#else
|
||||
return FormatErrno(ENOENT, fmt, std::forward<Args>(args)...);
|
||||
#endif
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
inline bool
|
||||
IsErrno(const std::system_error &e, int code) noexcept
|
||||
{
|
||||
return e.code().category() == ErrnoCategory() &&
|
||||
e.code().value() == code;
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
static inline bool
|
||||
IsFileNotFound(const std::system_error &e) noexcept
|
||||
@@ -155,8 +175,7 @@ IsFileNotFound(const std::system_error &e) noexcept
|
||||
return e.code().category() == std::system_category() &&
|
||||
e.code().value() == ERROR_FILE_NOT_FOUND;
|
||||
#else
|
||||
return e.code().category() == ErrnoCategory() &&
|
||||
e.code().value() == ENOENT;
|
||||
return IsErrno(e, ENOENT);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -168,8 +187,7 @@ IsPathNotFound(const std::system_error &e) noexcept
|
||||
return e.code().category() == std::system_category() &&
|
||||
e.code().value() == ERROR_PATH_NOT_FOUND;
|
||||
#else
|
||||
return e.code().category() == ErrnoCategory() &&
|
||||
e.code().value() == ENOTDIR;
|
||||
return IsErrno(e, ENOTDIR);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -181,8 +199,7 @@ IsAccessDenied(const std::system_error &e) noexcept
|
||||
return e.code().category() == std::system_category() &&
|
||||
e.code().value() == ERROR_ACCESS_DENIED;
|
||||
#else
|
||||
return e.code().category() == ErrnoCategory() &&
|
||||
e.code().value() == EACCES;
|
||||
return IsErrno(e, EACCES);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -20,9 +20,9 @@
|
||||
#include "Format.hxx"
|
||||
#include "Tag.hxx"
|
||||
#include "ParseName.hxx"
|
||||
#include "time/Convert.hxx"
|
||||
#include "util/format.h"
|
||||
#include "util/TruncateString.hxx"
|
||||
#include "util/TimeConvert.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@@ -48,6 +48,7 @@ tag = static_library(
|
||||
tag_dep = declare_dependency(
|
||||
link_with: tag,
|
||||
dependencies: [
|
||||
time_dep,
|
||||
util_dep,
|
||||
],
|
||||
)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2007-2017 Content Management AG
|
||||
* Copyright 2007-2019 Content Management AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* author: Max Kellermann <mk@cm4all.com>
|
||||
@@ -30,11 +30,12 @@
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "TimeConvert.hxx"
|
||||
#include "Convert.hxx"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h> /* for struct timeval */
|
||||
|
||||
struct tm
|
||||
GmTime(std::chrono::system_clock::time_point tp)
|
||||
@@ -66,18 +67,52 @@ LocalTime(std::chrono::system_clock::time_point tp)
|
||||
return *tm;
|
||||
}
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#ifndef __GLIBC__
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
TimeGm(struct tm &tm)
|
||||
/**
|
||||
* Determine the time zone offset in a portable way.
|
||||
*/
|
||||
gcc_const
|
||||
static time_t
|
||||
GetTimeZoneOffset() noexcept
|
||||
{
|
||||
return std::chrono::system_clock::from_time_t(timegm(&tm));
|
||||
time_t t = 1234567890;
|
||||
struct tm tm;
|
||||
tm.tm_isdst = 0;
|
||||
#ifdef _WIN32
|
||||
struct tm *p = gmtime(&t);
|
||||
#else
|
||||
struct tm *p = &tm;
|
||||
gmtime_r(&t, p);
|
||||
#endif
|
||||
return t - mktime(&tm);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* !__GLIBC__ */
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
MakeTime(struct tm &tm)
|
||||
TimeGm(struct tm &tm) noexcept
|
||||
{
|
||||
#ifdef __GLIBC__
|
||||
/* timegm() is a GNU extension */
|
||||
const auto t = timegm(&tm);
|
||||
#else
|
||||
tm.tm_isdst = 0;
|
||||
const auto t = mktime(&tm) + GetTimeZoneOffset();
|
||||
#endif /* !__GLIBC__ */
|
||||
|
||||
return std::chrono::system_clock::from_time_t(t);
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
MakeTime(struct tm &tm) noexcept
|
||||
{
|
||||
return std::chrono::system_clock::from_time_t(mktime(&tm));
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::duration
|
||||
ToSteadyClockDuration(const struct timeval &tv) noexcept
|
||||
{
|
||||
return std::chrono::steady_clock::duration(std::chrono::seconds(tv.tv_sec)) +
|
||||
std::chrono::steady_clock::duration(std::chrono::microseconds(tv.tv_usec));
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2007-2017 Content Management AG
|
||||
* Copyright 2007-2019 Content Management AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* author: Max Kellermann <mk@cm4all.com>
|
||||
@@ -33,34 +33,42 @@
|
||||
#ifndef TIME_CONVERT_HXX
|
||||
#define TIME_CONVERT_HXX
|
||||
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
/**
|
||||
* Convert a UTC-based time point to a UTC-based "struct tm".
|
||||
*
|
||||
* Throws on error.
|
||||
*/
|
||||
struct tm
|
||||
GmTime(std::chrono::system_clock::time_point tp);
|
||||
|
||||
/**
|
||||
* Convert a UTC-based time point to a local "struct tm".
|
||||
*
|
||||
* Throws on error.
|
||||
*/
|
||||
struct tm
|
||||
LocalTime(std::chrono::system_clock::time_point tp);
|
||||
|
||||
#ifdef __GLIBC__
|
||||
|
||||
/**
|
||||
* Convert a UTC-based "struct tm" to a UTC-based time point.
|
||||
*/
|
||||
gcc_pure
|
||||
std::chrono::system_clock::time_point
|
||||
TimeGm(struct tm &tm);
|
||||
|
||||
#endif
|
||||
TimeGm(struct tm &tm) noexcept;
|
||||
|
||||
/**
|
||||
* Convert a local "struct tm" to a UTC-based time point.
|
||||
*/
|
||||
gcc_pure
|
||||
std::chrono::system_clock::time_point
|
||||
MakeTime(struct tm &tm);
|
||||
MakeTime(struct tm &tm) noexcept;
|
||||
|
||||
gcc_pure
|
||||
std::chrono::steady_clock::duration
|
||||
ToSteadyClockDuration(const struct timeval &tv) noexcept;
|
||||
|
||||
#endif
|
172
src/time/ISO8601.cxx
Normal file
172
src/time/ISO8601.cxx
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2007-2019 Content Management AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* author: Max Kellermann <mk@cm4all.com>
|
||||
*
|
||||
* 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 "ISO8601.hxx"
|
||||
#include "Convert.hxx"
|
||||
#include "util/StringBuffer.hxx"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
StringBuffer<64>
|
||||
FormatISO8601(const struct tm &tm) noexcept
|
||||
{
|
||||
StringBuffer<64> buffer;
|
||||
strftime(buffer.data(), buffer.capacity(),
|
||||
#ifdef _WIN32
|
||||
"%Y-%m-%dT%H:%M:%SZ",
|
||||
#else
|
||||
"%FT%TZ",
|
||||
#endif
|
||||
&tm);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
StringBuffer<64>
|
||||
FormatISO8601(std::chrono::system_clock::time_point tp)
|
||||
{
|
||||
return FormatISO8601(GmTime(tp));
|
||||
}
|
||||
|
||||
static std::pair<unsigned, unsigned>
|
||||
ParseTimeZoneOffsetRaw(const char *&s)
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long value = strtoul(s, &endptr, 10);
|
||||
if (endptr == s + 4) {
|
||||
s = endptr;
|
||||
return std::make_pair(value / 100, value % 100);
|
||||
} else if (endptr == s + 2) {
|
||||
s = endptr;
|
||||
|
||||
unsigned hours = value, minutes = 0;
|
||||
if (*s == ':') {
|
||||
++s;
|
||||
minutes = strtoul(s, &endptr, 10);
|
||||
if (endptr != s + 2)
|
||||
throw std::runtime_error("Failed to parse time zone offset");
|
||||
|
||||
s = endptr;
|
||||
}
|
||||
|
||||
return std::make_pair(hours, minutes);
|
||||
} else
|
||||
throw std::runtime_error("Failed to parse time zone offset");
|
||||
}
|
||||
|
||||
static std::chrono::system_clock::duration
|
||||
ParseTimeZoneOffset(const char *&s)
|
||||
{
|
||||
assert(*s == '+' || *s == '-');
|
||||
|
||||
bool negative = *s == '-';
|
||||
++s;
|
||||
|
||||
auto raw = ParseTimeZoneOffsetRaw(s);
|
||||
if (raw.first > 13)
|
||||
throw std::runtime_error("Time offset hours out of range");
|
||||
|
||||
if (raw.second >= 60)
|
||||
throw std::runtime_error("Time offset minutes out of range");
|
||||
|
||||
std::chrono::system_clock::duration d = std::chrono::hours(raw.first);
|
||||
d += std::chrono::minutes(raw.second);
|
||||
|
||||
if (negative)
|
||||
d = -d;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
std::pair<std::chrono::system_clock::time_point,
|
||||
std::chrono::system_clock::duration>
|
||||
ParseISO8601(const char *s)
|
||||
{
|
||||
assert(s != nullptr);
|
||||
|
||||
#ifdef _WIN32
|
||||
/* TODO: emulate strptime()? */
|
||||
(void)s;
|
||||
throw std::runtime_error("Time parsing not implemented on Windows");
|
||||
#else
|
||||
struct tm tm{};
|
||||
|
||||
/* parse the date */
|
||||
const char *end = strptime(s, "%F", &tm);
|
||||
if (end == nullptr) {
|
||||
/* try without field separators */
|
||||
end = strptime(s, "%Y%m%d", &tm);
|
||||
if (end == nullptr)
|
||||
throw std::runtime_error("Failed to parse date");
|
||||
}
|
||||
|
||||
s = end;
|
||||
|
||||
std::chrono::system_clock::duration precision = std::chrono::hours(24);
|
||||
|
||||
/* parse the time of day */
|
||||
if (*s == 'T') {
|
||||
++s;
|
||||
|
||||
if ((end = strptime(s, "%T", &tm)) != nullptr)
|
||||
precision = std::chrono::seconds(1);
|
||||
else if ((end = strptime(s, "%H%M%S", &tm)) != nullptr)
|
||||
/* no field separators */
|
||||
precision = std::chrono::seconds(1);
|
||||
else if ((end = strptime(s, "%H%M", &tm)) != nullptr)
|
||||
/* no field separators */
|
||||
precision = std::chrono::minutes(1);
|
||||
else if ((end = strptime(s, "%H:%M", &tm)) != nullptr)
|
||||
precision = std::chrono::minutes(1);
|
||||
else if ((end = strptime(s, "%H", &tm)) != nullptr)
|
||||
precision = std::chrono::hours(1);
|
||||
else
|
||||
throw std::runtime_error("Failed to parse time of day");
|
||||
|
||||
s = end;
|
||||
}
|
||||
|
||||
auto tp = TimeGm(tm);
|
||||
|
||||
/* time zone */
|
||||
if (*s == 'Z')
|
||||
++s;
|
||||
else if (*s == '+' || *s == '-')
|
||||
tp -= ParseTimeZoneOffset(s);
|
||||
|
||||
if (*s != 0)
|
||||
throw std::runtime_error("Garbage at end of time stamp");
|
||||
|
||||
return std::make_pair(tp, precision);
|
||||
#endif /* !_WIN32 */
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2007-2017 Content Management AG
|
||||
* Copyright 2007-2019 Content Management AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* author: Max Kellermann <mk@cm4all.com>
|
||||
@@ -33,13 +33,15 @@
|
||||
#ifndef TIME_ISO8601_HXX
|
||||
#define TIME_ISO8601_HXX
|
||||
|
||||
#include "StringBuffer.hxx"
|
||||
#include "Compiler.h"
|
||||
#include "util/Compiler.h"
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct tm;
|
||||
template<size_t CAPACITY> class StringBuffer;
|
||||
|
||||
gcc_pure
|
||||
StringBuffer<64>
|
||||
@@ -49,7 +51,16 @@ gcc_pure
|
||||
StringBuffer<64>
|
||||
FormatISO8601(std::chrono::system_clock::time_point tp);
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
/**
|
||||
* Parse a time stamp in ISO8601 format.
|
||||
*
|
||||
* Throws on error.
|
||||
*
|
||||
* @return a pair consisting of the time point and the specified
|
||||
* precision; e.g. for a date, the second value is "one day"
|
||||
*/
|
||||
std::pair<std::chrono::system_clock::time_point,
|
||||
std::chrono::system_clock::duration>
|
||||
ParseISO8601(const char *s);
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -27,32 +27,14 @@
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "TimeParser.hxx"
|
||||
#include "Compiler.h"
|
||||
#include "Parser.hxx"
|
||||
#include "Convert.hxx"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
|
||||
#if !defined(__GLIBC__) && !defined(_WIN32)
|
||||
|
||||
/**
|
||||
* Determine the time zone offset in a portable way.
|
||||
*/
|
||||
gcc_const
|
||||
static time_t
|
||||
GetTimeZoneOffset() noexcept
|
||||
{
|
||||
time_t t = 1234567890;
|
||||
struct tm tm;
|
||||
tm.tm_isdst = 0;
|
||||
gmtime_r(&t, &tm);
|
||||
return t - mktime(&tm);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
ParseTimePoint(const char *s, const char *format)
|
||||
{
|
||||
@@ -65,20 +47,11 @@ ParseTimePoint(const char *s, const char *format)
|
||||
(void)format;
|
||||
throw std::runtime_error("Time parsing not implemented on Windows");
|
||||
#else
|
||||
struct tm tm;
|
||||
struct tm tm{};
|
||||
const char *end = strptime(s, format, &tm);
|
||||
if (end == nullptr || *end != 0)
|
||||
throw std::runtime_error("Failed to parse time stamp");
|
||||
|
||||
#ifdef __GLIBC__
|
||||
/* timegm() is a GNU extension */
|
||||
const auto t = timegm(&tm);
|
||||
#else
|
||||
tm.tm_isdst = 0;
|
||||
const auto t = mktime(&tm) + GetTimeZoneOffset();
|
||||
#endif /* !__GLIBC__ */
|
||||
|
||||
return std::chrono::system_clock::from_time_t(t);
|
||||
|
||||
return TimeGm(tm);
|
||||
#endif /* !_WIN32 */
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
11
src/time/meson.build
Normal file
11
src/time/meson.build
Normal file
@@ -0,0 +1,11 @@
|
||||
time = static_library(
|
||||
'time',
|
||||
'Parser.cxx',
|
||||
'Convert.cxx',
|
||||
'ISO8601.cxx',
|
||||
include_directories: inc,
|
||||
)
|
||||
|
||||
time_dep = declare_dependency(
|
||||
link_with: time,
|
||||
)
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2007-2017 Content Management AG
|
||||
* Copyright 2007-2019 Content Management AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* author: Max Kellermann <mk@cm4all.com>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2007-2017 Content Management AG
|
||||
* Copyright 2007-2019 Content Management AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* author: Max Kellermann <mk@cm4all.com>
|
||||
|
@@ -35,14 +35,14 @@
|
||||
/**
|
||||
* A statically allocated string buffer.
|
||||
*/
|
||||
template<typename T, size_t CAPACITY>
|
||||
template<typename T, std::size_t CAPACITY>
|
||||
class BasicStringBuffer {
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef T &reference;
|
||||
typedef T *pointer;
|
||||
typedef const T *const_pointer;
|
||||
typedef size_t size_type;
|
||||
typedef std::size_t size_type;
|
||||
|
||||
static constexpr value_type SENTINEL = '\0';
|
||||
|
||||
@@ -104,7 +104,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t CAPACITY>
|
||||
template<std::size_t CAPACITY>
|
||||
class StringBuffer : public BasicStringBuffer<char, CAPACITY> {};
|
||||
|
||||
#endif
|
||||
|
@@ -36,13 +36,13 @@
|
||||
|
||||
template<typename... Args>
|
||||
static inline void
|
||||
StringFormat(char *buffer, size_t size,
|
||||
StringFormat(char *buffer, std::size_t size,
|
||||
const char *fmt, Args&&... args) noexcept
|
||||
{
|
||||
snprintf(buffer, size, fmt, args...);
|
||||
}
|
||||
|
||||
template<size_t CAPACITY, typename... Args>
|
||||
template<std::size_t CAPACITY, typename... Args>
|
||||
static inline void
|
||||
StringFormat(StringBuffer<CAPACITY> &buffer,
|
||||
const char *fmt, Args&&... args) noexcept
|
||||
@@ -50,7 +50,7 @@ StringFormat(StringBuffer<CAPACITY> &buffer,
|
||||
StringFormat(buffer.data(), buffer.capacity(), fmt, args...);
|
||||
}
|
||||
|
||||
template<size_t CAPACITY, typename... Args>
|
||||
template<std::size_t CAPACITY, typename... Args>
|
||||
static inline StringBuffer<CAPACITY>
|
||||
StringFormat(const char *fmt, Args&&... args) noexcept
|
||||
{
|
||||
|
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007-2017 Content Management AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* author: Max Kellermann <mk@cm4all.com>
|
||||
*
|
||||
* 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 "TimeISO8601.hxx"
|
||||
#include "TimeConvert.hxx"
|
||||
#include "TimeParser.hxx"
|
||||
|
||||
StringBuffer<64>
|
||||
FormatISO8601(const struct tm &tm) noexcept
|
||||
{
|
||||
StringBuffer<64> buffer;
|
||||
strftime(buffer.data(), buffer.capacity(),
|
||||
#ifdef _WIN32
|
||||
"%Y-%m-%dT%H:%M:%SZ",
|
||||
#else
|
||||
"%FT%TZ",
|
||||
#endif
|
||||
&tm);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
StringBuffer<64>
|
||||
FormatISO8601(std::chrono::system_clock::time_point tp)
|
||||
{
|
||||
return FormatISO8601(GmTime(tp));
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
ParseISO8601(const char *s)
|
||||
{
|
||||
return ParseTimePoint(s, "%FT%TZ");
|
||||
}
|
@@ -16,9 +16,6 @@ util = static_library(
|
||||
'SplitString.cxx',
|
||||
'FormatString.cxx',
|
||||
'Tokenizer.cxx',
|
||||
'TimeParser.cxx',
|
||||
'TimeConvert.cxx',
|
||||
'TimeISO8601.cxx',
|
||||
'UriUtil.cxx',
|
||||
'LazyRandomEngine.cxx',
|
||||
'HugeAllocator.cxx',
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include "AvahiPoll.hxx"
|
||||
#include "event/SocketMonitor.hxx"
|
||||
#include "event/TimerEvent.hxx"
|
||||
#include "time/Convert.hxx"
|
||||
|
||||
static unsigned
|
||||
FromAvahiWatchEvent(AvahiWatchEvent e)
|
||||
@@ -78,12 +79,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr std::chrono::steady_clock::duration
|
||||
TimevalToChrono(const timeval &tv)
|
||||
{
|
||||
return std::chrono::seconds(tv.tv_sec) + std::chrono::microseconds(tv.tv_usec);
|
||||
}
|
||||
|
||||
struct AvahiTimeout final {
|
||||
TimerEvent timer;
|
||||
|
||||
@@ -97,12 +92,12 @@ public:
|
||||
:timer(_loop, BIND_THIS_METHOD(OnTimeout)),
|
||||
callback(_callback), userdata(_userdata) {
|
||||
if (tv != nullptr)
|
||||
timer.Schedule(TimevalToChrono(*tv));
|
||||
timer.Schedule(ToSteadyClockDuration(*tv));
|
||||
}
|
||||
|
||||
static void TimeoutUpdate(AvahiTimeout *t, const struct timeval *tv) {
|
||||
if (tv != nullptr)
|
||||
t->timer.Schedule(TimevalToChrono(*tv));
|
||||
t->timer.Schedule(ToSteadyClockDuration(*tv));
|
||||
else
|
||||
t->timer.Cancel();
|
||||
}
|
||||
|
@@ -67,6 +67,7 @@ else
|
||||
dependencies: [
|
||||
libavahi_client_dep,
|
||||
dbus_dep,
|
||||
time_dep,
|
||||
],
|
||||
)
|
||||
|
||||
|
92
test/TestISO8601.cxx
Normal file
92
test/TestISO8601.cxx
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2019 Content Management AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* author: Max Kellermann <mk@cm4all.com>
|
||||
*
|
||||
* 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 "time/ISO8601.hxx"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
static constexpr struct {
|
||||
const char *s;
|
||||
time_t t;
|
||||
std::chrono::system_clock::duration d;
|
||||
} parse_tests[] = {
|
||||
/* full ISO8601 */
|
||||
{ "1970-01-01T00:00:00Z", 0, std::chrono::seconds(1) },
|
||||
{ "1970-01-01T00:00:01Z", 1, std::chrono::seconds(1) },
|
||||
{ "2019-02-04T16:46:41Z", 1549298801, std::chrono::seconds(1) },
|
||||
{ "2018-12-31T23:59:59Z", 1546300799, std::chrono::seconds(1) },
|
||||
{ "2019-01-01T00:00:00Z", 1546300800, std::chrono::seconds(1) },
|
||||
|
||||
/* only date */
|
||||
{ "1970-01-01", 0, std::chrono::hours(24) },
|
||||
{ "2019-02-04", 1549238400, std::chrono::hours(24) },
|
||||
{ "2018-12-31", 1546214400, std::chrono::hours(24) },
|
||||
{ "2019-01-01", 1546300800, std::chrono::hours(24) },
|
||||
|
||||
/* date with time zone */
|
||||
{ "2019-02-04Z", 1549238400, std::chrono::hours(24) },
|
||||
|
||||
/* without time zone */
|
||||
{ "2019-02-04T16:46:41", 1549298801, std::chrono::seconds(1) },
|
||||
|
||||
/* without seconds */
|
||||
{ "2019-02-04T16:46", 1549298760, std::chrono::minutes(1) },
|
||||
{ "2019-02-04T16:46Z", 1549298760, std::chrono::minutes(1) },
|
||||
|
||||
/* without minutes */
|
||||
{ "2019-02-04T16", 1549296000, std::chrono::hours(1) },
|
||||
{ "2019-02-04T16Z", 1549296000, std::chrono::hours(1) },
|
||||
|
||||
/* with time zone */
|
||||
{ "2019-02-04T16:46:41+02", 1549291601, std::chrono::seconds(1) },
|
||||
{ "2019-02-04T16:46:41+0200", 1549291601, std::chrono::seconds(1) },
|
||||
{ "2019-02-04T16:46:41+02:00", 1549291601, std::chrono::seconds(1) },
|
||||
{ "2019-02-04T16:46:41-0200", 1549306001, std::chrono::seconds(1) },
|
||||
|
||||
/* without field separators */
|
||||
{ "19700101T000000Z", 0, std::chrono::seconds(1) },
|
||||
{ "19700101T000001Z", 1, std::chrono::seconds(1) },
|
||||
{ "20190204T164641Z", 1549298801, std::chrono::seconds(1) },
|
||||
{ "19700101", 0, std::chrono::hours(24) },
|
||||
{ "20190204", 1549238400, std::chrono::hours(24) },
|
||||
{ "20190204T1646", 1549298760, std::chrono::minutes(1) },
|
||||
{ "20190204T16", 1549296000, std::chrono::hours(1) },
|
||||
};
|
||||
|
||||
TEST(ISO8601, Parse)
|
||||
{
|
||||
for (const auto &i : parse_tests) {
|
||||
const auto result = ParseISO8601(i.s);
|
||||
EXPECT_EQ(std::chrono::system_clock::to_time_t(result.first), i.t);
|
||||
EXPECT_EQ(result.second, i.d);
|
||||
}
|
||||
}
|
@@ -46,6 +46,19 @@ test('TestUtil', executable(
|
||||
],
|
||||
))
|
||||
|
||||
test(
|
||||
'TestTime',
|
||||
executable(
|
||||
'TestTime',
|
||||
'TestISO8601.cxx',
|
||||
include_directories: inc,
|
||||
dependencies: [
|
||||
time_dep,
|
||||
gtest_dep,
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
test('TestRewindInputStream', executable(
|
||||
'TestRewindInputStream',
|
||||
'TestRewindInputStream.cxx',
|
||||
|
@@ -22,7 +22,7 @@
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#include "storage/FileInfo.hxx"
|
||||
#include "net/Init.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/PrintException.hxx"
|
||||
|
||||
#include <memory>
|
||||
|
@@ -17,7 +17,7 @@
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#include "storage/plugins/LocalStorage.hxx"
|
||||
#include "Mapper.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
Reference in New Issue
Block a user