aboutsummaryrefslogtreecommitdiffstats
path: root/src/command/QueueCommands.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/command/QueueCommands.cxx')
-rw-r--r--src/command/QueueCommands.cxx216
1 files changed, 132 insertions, 84 deletions
diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx
index a987e1bc9..d0b789eb1 100644
--- a/src/command/QueueCommands.cxx
+++ b/src/command/QueueCommands.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,19 +20,21 @@
#include "config.h"
#include "QueueCommands.hxx"
#include "CommandError.hxx"
-#include "DatabaseQueue.hxx"
+#include "db/DatabaseQueue.hxx"
+#include "db/Selection.hxx"
#include "SongFilter.hxx"
-#include "DatabaseSelection.hxx"
-#include "Playlist.hxx"
+#include "SongLoader.hxx"
+#include "queue/Playlist.hxx"
#include "PlaylistPrint.hxx"
-#include "ClientFile.hxx"
-#include "Client.hxx"
+#include "client/Client.hxx"
#include "Partition.hxx"
#include "BulkEdit.hxx"
#include "protocol/ArgParser.hxx"
#include "protocol/Result.hxx"
#include "ls.hxx"
+#include "util/ConstBuffer.hxx"
#include "util/UriUtil.hxx"
+#include "util/NumberParser.hxx"
#include "util/Error.hxx"
#include "fs/AllocatedPath.hxx"
@@ -40,40 +42,49 @@
#include <string.h>
-CommandResult
-handle_add(Client &client, gcc_unused int argc, char *argv[])
+static const char *
+translate_uri(Client &client, const char *uri)
{
- char *uri = argv[1];
+ if (memcmp(uri, "file:///", 8) == 0)
+ /* drop the "file://", leave only an absolute path
+ (starting with a slash) */
+ return uri + 7;
+
+ if (PathTraitsUTF8::IsAbsolute(uri)) {
+ command_error(client, ACK_ERROR_NO_EXIST, "Malformed URI");
+ return nullptr;
+ }
- if (memcmp(uri, "file:///", 8) == 0) {
- const char *path_utf8 = uri + 7;
- const auto path_fs = AllocatedPath::FromUTF8(path_utf8);
+ return uri;
+}
- if (path_fs.IsNull()) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "unsupported file name");
- return CommandResult::ERROR;
- }
+CommandResult
+handle_add(Client &client, gcc_unused unsigned argc, char *argv[])
+{
+ const char *uri = argv[1];
+ if (memcmp(uri, "/", 2) == 0)
+ /* this URI is malformed, but some clients are buggy
+ and use "add /" to add the whole database, which
+ was never intended to work, but once did; in order
+ to retain backwards compatibility, work around this
+ here */
+ uri = "";
+
+ uri = translate_uri(client, uri);
+ if (uri == nullptr)
+ return CommandResult::ERROR;
+ if (uri_has_scheme(uri) || PathTraitsUTF8::IsAbsolute(uri)) {
+ const SongLoader loader(client);
Error error;
- if (!client_allow_file(client, path_fs, error))
+ unsigned id = client.partition.AppendURI(loader, uri, error);
+ if (id == 0)
return print_error(client, error);
- auto result = client.partition.AppendFile(path_utf8);
- return print_playlist_result(client, result);
- }
-
- if (uri_has_scheme(uri)) {
- if (!uri_supported_scheme(uri)) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "unsupported URI scheme");
- return CommandResult::ERROR;
- }
-
- auto result = client.partition.AppendURI(uri);
- return print_playlist_result(client, result);
+ return CommandResult::OK;
}
+#ifdef ENABLE_DATABASE
const ScopeBulkEdit bulk_edit(client.partition);
const DatabaseSelection selection(uri, true);
@@ -81,48 +92,30 @@ handle_add(Client &client, gcc_unused int argc, char *argv[])
return AddFromDatabase(client.partition, selection, error)
? CommandResult::OK
: print_error(client, error);
+#else
+ command_error(client, ACK_ERROR_NO_EXIST, "No database");
+ return CommandResult::ERROR;
+#endif
}
CommandResult
-handle_addid(Client &client, int argc, char *argv[])
+handle_addid(Client &client, unsigned argc, char *argv[])
{
- char *uri = argv[1];
- unsigned added_id;
- PlaylistResult result;
-
- if (memcmp(uri, "file:///", 8) == 0) {
- const char *path_utf8 = uri + 7;
- const auto path_fs = AllocatedPath::FromUTF8(path_utf8);
-
- if (path_fs.IsNull()) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "unsupported file name");
- return CommandResult::ERROR;
- }
-
- Error error;
- if (!client_allow_file(client, path_fs, error))
- return print_error(client, error);
-
- result = client.partition.AppendFile(path_utf8, &added_id);
- } else {
- if (uri_has_scheme(uri) && !uri_supported_scheme(uri)) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "unsupported URI scheme");
- return CommandResult::ERROR;
- }
-
- result = client.partition.AppendURI(uri, &added_id);
- }
+ const char *const uri = translate_uri(client, argv[1]);
+ if (uri == nullptr)
+ return CommandResult::ERROR;
- if (result != PlaylistResult::SUCCESS)
- return print_playlist_result(client, result);
+ const SongLoader loader(client);
+ Error error;
+ unsigned added_id = client.partition.AppendURI(loader, uri, error);
+ if (added_id == 0)
+ return print_error(client, error);
if (argc == 3) {
unsigned to;
if (!check_unsigned(client, &to, argv[2]))
return CommandResult::ERROR;
- result = client.partition.MoveId(added_id, to);
+ PlaylistResult result = client.partition.MoveId(added_id, to);
if (result != PlaylistResult::SUCCESS) {
CommandResult ret =
print_playlist_result(client, result);
@@ -135,8 +128,61 @@ handle_addid(Client &client, int argc, char *argv[])
return CommandResult::OK;
}
+/**
+ * Parse a string in the form "START:END", both being (optional)
+ * fractional non-negative time offsets in seconds. Returns both in
+ * integer milliseconds. Omitted values are zero.
+ */
+static bool
+parse_time_range(const char *p, SongTime &start_r, SongTime &end_r)
+{
+ char *endptr;
+
+ const float start = ParseFloat(p, &endptr);
+ if (*endptr != ':' || start < 0)
+ return false;
+
+ start_r = endptr > p
+ ? SongTime::FromS(start)
+ : SongTime::zero();
+
+ p = endptr + 1;
+
+ const float end = ParseFloat(p, &endptr);
+ if (*endptr != 0 || end < 0)
+ return false;
+
+ end_r = endptr > p
+ ? SongTime::FromS(end)
+ : SongTime::zero();
+
+ return end_r.IsZero() || end_r > start_r;
+}
+
+CommandResult
+handle_rangeid(Client &client, gcc_unused unsigned argc, char *argv[])
+{
+ unsigned id;
+ if (!check_unsigned(client, &id, argv[1]))
+ return CommandResult::ERROR;
+
+ SongTime start, end;
+ if (!parse_time_range(argv[2], start, end)) {
+ command_error(client, ACK_ERROR_ARG, "Bad range");
+ return CommandResult::ERROR;
+ }
+
+ Error error;
+ if (!client.partition.playlist.SetSongIdRange(client.partition.pc,
+ id, start, end,
+ error))
+ return print_error(client, error);
+
+ return CommandResult::OK;
+}
+
CommandResult
-handle_delete(Client &client, gcc_unused int argc, char *argv[])
+handle_delete(Client &client, gcc_unused unsigned argc, char *argv[])
{
unsigned start, end;
@@ -148,7 +194,7 @@ handle_delete(Client &client, gcc_unused int argc, char *argv[])
}
CommandResult
-handle_deleteid(Client &client, gcc_unused int argc, char *argv[])
+handle_deleteid(Client &client, gcc_unused unsigned argc, char *argv[])
{
unsigned id;
@@ -161,7 +207,7 @@ handle_deleteid(Client &client, gcc_unused int argc, char *argv[])
CommandResult
handle_playlist(Client &client,
- gcc_unused int argc, gcc_unused char *argv[])
+ gcc_unused unsigned argc, gcc_unused char *argv[])
{
playlist_print_uris(client, client.playlist);
return CommandResult::OK;
@@ -169,7 +215,7 @@ handle_playlist(Client &client,
CommandResult
handle_shuffle(gcc_unused Client &client,
- gcc_unused int argc, gcc_unused char *argv[])
+ gcc_unused unsigned argc, gcc_unused char *argv[])
{
unsigned start = 0, end = client.playlist.queue.GetLength();
if (argc == 2 && !check_range(client, &start, &end, argv[1]))
@@ -181,14 +227,14 @@ handle_shuffle(gcc_unused Client &client,
CommandResult
handle_clear(gcc_unused Client &client,
- gcc_unused int argc, gcc_unused char *argv[])
+ gcc_unused unsigned argc, gcc_unused char *argv[])
{
client.partition.ClearQueue();
return CommandResult::OK;
}
CommandResult
-handle_plchanges(Client &client, gcc_unused int argc, char *argv[])
+handle_plchanges(Client &client, gcc_unused unsigned argc, char *argv[])
{
uint32_t version;
@@ -200,7 +246,7 @@ handle_plchanges(Client &client, gcc_unused int argc, char *argv[])
}
CommandResult
-handle_plchangesposid(Client &client, gcc_unused int argc, char *argv[])
+handle_plchangesposid(Client &client, gcc_unused unsigned argc, char *argv[])
{
uint32_t version;
@@ -212,7 +258,7 @@ handle_plchangesposid(Client &client, gcc_unused int argc, char *argv[])
}
CommandResult
-handle_playlistinfo(Client &client, int argc, char *argv[])
+handle_playlistinfo(Client &client, unsigned argc, char *argv[])
{
unsigned start = 0, end = std::numeric_limits<unsigned>::max();
bool ret;
@@ -229,7 +275,7 @@ handle_playlistinfo(Client &client, int argc, char *argv[])
}
CommandResult
-handle_playlistid(Client &client, int argc, char *argv[])
+handle_playlistid(Client &client, unsigned argc, char *argv[])
{
if (argc >= 2) {
unsigned id;
@@ -249,11 +295,13 @@ handle_playlistid(Client &client, int argc, char *argv[])
}
static CommandResult
-handle_playlist_match(Client &client, int argc, char *argv[],
+handle_playlist_match(Client &client, unsigned argc, char *argv[],
bool fold_case)
{
+ ConstBuffer<const char *> args(argv + 1, argc - 1);
+
SongFilter filter;
- if (!filter.Parse(argc - 1, argv + 1, fold_case)) {
+ if (!filter.Parse(args, fold_case)) {
command_error(client, ACK_ERROR_ARG, "incorrect arguments");
return CommandResult::ERROR;
}
@@ -263,19 +311,19 @@ handle_playlist_match(Client &client, int argc, char *argv[],
}
CommandResult
-handle_playlistfind(Client &client, int argc, char *argv[])
+handle_playlistfind(Client &client, unsigned argc, char *argv[])
{
return handle_playlist_match(client, argc, argv, false);
}
CommandResult
-handle_playlistsearch(Client &client, int argc, char *argv[])
+handle_playlistsearch(Client &client, unsigned argc, char *argv[])
{
return handle_playlist_match(client, argc, argv, true);
}
CommandResult
-handle_prio(Client &client, int argc, char *argv[])
+handle_prio(Client &client, unsigned argc, char *argv[])
{
unsigned priority;
@@ -288,7 +336,7 @@ handle_prio(Client &client, int argc, char *argv[])
return CommandResult::ERROR;
}
- for (int i = 2; i < argc; ++i) {
+ for (unsigned i = 2; i < argc; ++i) {
unsigned start_position, end_position;
if (!check_range(client, &start_position, &end_position,
argv[i]))
@@ -306,7 +354,7 @@ handle_prio(Client &client, int argc, char *argv[])
}
CommandResult
-handle_prioid(Client &client, int argc, char *argv[])
+handle_prioid(Client &client, unsigned argc, char *argv[])
{
unsigned priority;
@@ -319,7 +367,7 @@ handle_prioid(Client &client, int argc, char *argv[])
return CommandResult::ERROR;
}
- for (int i = 2; i < argc; ++i) {
+ for (unsigned i = 2; i < argc; ++i) {
unsigned song_id;
if (!check_unsigned(client, &song_id, argv[i]))
return CommandResult::ERROR;
@@ -334,7 +382,7 @@ handle_prioid(Client &client, int argc, char *argv[])
}
CommandResult
-handle_move(Client &client, gcc_unused int argc, char *argv[])
+handle_move(Client &client, gcc_unused unsigned argc, char *argv[])
{
unsigned start, end;
int to;
@@ -350,7 +398,7 @@ handle_move(Client &client, gcc_unused int argc, char *argv[])
}
CommandResult
-handle_moveid(Client &client, gcc_unused int argc, char *argv[])
+handle_moveid(Client &client, gcc_unused unsigned argc, char *argv[])
{
unsigned id;
int to;
@@ -364,7 +412,7 @@ handle_moveid(Client &client, gcc_unused int argc, char *argv[])
}
CommandResult
-handle_swap(Client &client, gcc_unused int argc, char *argv[])
+handle_swap(Client &client, gcc_unused unsigned argc, char *argv[])
{
unsigned song1, song2;
@@ -379,7 +427,7 @@ handle_swap(Client &client, gcc_unused int argc, char *argv[])
}
CommandResult
-handle_swapid(Client &client, gcc_unused int argc, char *argv[])
+handle_swapid(Client &client, gcc_unused unsigned argc, char *argv[])
{
unsigned id1, id2;