From 3993176b76487bf82ccd267a181926747a471993 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Thu, 7 Oct 2021 21:27:35 +0200
Subject: [PATCH] command/QueueCommands: support relative offsets in "addid"

A similar feature was present long ago in MPD, but was deprecated in
version 0.16 because the implementation was broken.  This commit
re-adds the feature in a way that's well-defined and not broken.

Close https://github.com/MusicPlayerDaemon/MPD/issues/1221
---
 NEWS                          |  1 +
 doc/protocol.rst              |  6 ++++++
 src/command/QueueCommands.cxx | 33 ++++++++++++++++++++++++++++++++-
 3 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 979f928e7..839e79835 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ ver 0.23 (not yet released)
   - new command "getvol"
   - show the audio format in "playlistinfo"
   - support "listfiles" with arbitrary storage plugins
+  - support relative positions in "addid"
 * database
   - proxy: require MPD 0.20 or later
   - proxy: require libmpdclient 2.11 or later
diff --git a/doc/protocol.rst b/doc/protocol.rst
index dd50ad8b0..bd0f7dcd7 100644
--- a/doc/protocol.rst
+++ b/doc/protocol.rst
@@ -709,6 +709,12 @@ Whenever possible, ids should be used.
      Id: 999
      OK
 
+    If the second parameter is given, then the song is inserted at the
+    specified position.  If the parameter starts with ``+`` or ``-``,
+    then it is relative to the current song; e.g. ``+1`` inserts right
+    after the current song and ``-1`` inserts right before the current
+    song.  (``±0`` is not a legal value.)
+
 .. _command_clear:
 
 :command:`clear`
diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx
index 5e24c0919..1bc30bbc7 100644
--- a/src/command/QueueCommands.cxx
+++ b/src/command/QueueCommands.cxx
@@ -115,9 +115,40 @@ handle_addid(Client &client, Request args, Response &r)
 	auto &partition = client.GetPartition();
 
 	int to = -1;
+
 	if (args.size > 1) {
 		const auto queue_length = partition.playlist.queue.GetLength();
-		to = args.ParseUnsigned(1, queue_length);
+
+		const char *const s = args[1];
+		if (*s == '+' || *s == '-') {
+			/* relative to the current song */
+
+			const int current =
+				partition.playlist.GetCurrentPosition();
+			if (current < 0)
+				throw ProtocolError(ACK_ERROR_PLAYER_SYNC,
+						    "No current song");
+
+			to = args.ParseInt(1, -current - 1,
+					   queue_length - current);
+			if (to == 0)
+				throw ProtocolError(ACK_ERROR_ARG,
+						    "Zero is not a legal relative position");
+
+			/* special case for negative offsets: the
+			   offset "-1" shall insert the new song right
+			   before the current song (just like "+1"
+			   inserts right after the current song);
+			   computationally, that would be a zero
+			   offset, but that's not intuitive, so we
+			   need to add one here */
+			if (to < 0)
+				++to;
+
+			to += current;
+		} else
+			/* absolute position */
+			to = args.ParseUnsigned(1, queue_length);
 	}
 
 	const SongLoader loader(client);