From 16cba5732380ad63991c1c39e512aa11fc23a87e Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Thu, 27 Dec 2018 16:37:28 +0100
Subject: [PATCH] Fixes bug that could cause wrong alias expansion
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

When using an alias with $i..j parameter it would instead only use (i,j]
instead of [i,j]. The same applied to $i.. where (i, ∞] was used instead
of [i, ∞].
---
 .../quassel/syncables/AliasManager.kt         | 19 ++++--
 .../quassel/syncables/AliasManagerTest.kt     | 61 +++++++++++++++++++
 2 files changed, 76 insertions(+), 4 deletions(-)

diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/AliasManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/AliasManager.kt
index 5ac5b64b3..79d0d5649 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/AliasManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/AliasManager.kt
@@ -146,19 +146,30 @@ class AliasManager constructor(
       var command = commands[i]
 
       if (params.isNotEmpty()) {
+        val commandBuffer = StringBuilder()
+        var index = 0
         for (match in paramRange.findAll(command)) {
-          val start = match.groups[1]?.value?.toIntOrNull() ?: -1
+          val start = match.groups[1]?.value?.toIntOrNull() ?: 0
           val replacement: String
           val end = match.groups[2]?.value?.toIntOrNull() ?: params.size
           // $1.. would be "arg1 and all following"
           replacement = if (end < start) {
             ""
           } else {
-            params.subList(start, end).joinToString(" ")
+            params.subList(start - 1, end).joinToString(" ")
           }
-          command = command.substring(0, match.range.start) + replacement +
-            command.substring(match.range.endInclusive + 1)
+
+          // Append text between last match and this match
+          commandBuffer.append(command.substring(index, match.range.start))
+
+          // Append new replacement text
+          commandBuffer.append(replacement)
+          
+          index = match.range.endInclusive + 1
         }
+        // Append remaining text
+        commandBuffer.append(command.substring(index, command.length))
+        command = commandBuffer.toString()
       }
 
       for (j in params.size downTo 1) {
diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/AliasManagerTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/AliasManagerTest.kt
index b20faf3d1..0fe36d65c 100644
--- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/AliasManagerTest.kt
+++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/AliasManagerTest.kt
@@ -19,9 +19,14 @@
 
 package de.kuschku.libquassel.quassel.syncables
 
+import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.protocol.Buffer_Types
 import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer
+import de.kuschku.libquassel.quassel.BufferInfo
+import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager
 import de.kuschku.libquassel.session.SignalProxy
 import de.kuschku.libquassel.util.roundTrip
+import org.junit.Assert.assertEquals
 import org.junit.Test
 
 class AliasManagerTest {
@@ -44,4 +49,60 @@ class AliasManagerTest {
     copy.fromVariantMap(original.toVariantMap())
     assert(original.isEqual(copy))
   }
+
+  @Test
+  fun testExpansion() {
+    fun testExpansion(aliases: List<IAliasManager.Alias>, original: String,
+                      expanded: List<String>) {
+      val manager = AliasManager(SignalProxy.NULL)
+      manager.setAliasList(manager.defaults() + aliases)
+
+      val bufferInfo = BufferInfo(
+        bufferId = -1,
+        networkId = -1,
+        type = Buffer_Types.of(Buffer_Type.StatusBuffer),
+        bufferName = "#quassel-test",
+        groupId = -1
+      )
+
+      val previousCommands = mutableListOf<IAliasManager.Command>()
+      manager.processInput(
+        info = bufferInfo,
+        message = original,
+        previousCommands = previousCommands
+      )
+
+      assertEquals(previousCommands, expanded.map {
+        IAliasManager.Command(bufferInfo, it)
+      })
+    }
+
+    testExpansion(
+      listOf(
+        IAliasManager.Alias(
+          name = "d",
+          expansion = "/say first \"\$1\" second \"\$2\" some \"\$3..4\" more \"\$3..\""
+        )
+      ),
+      "/d a b c d e f",
+      listOf(
+        "/say first \"a\" second \"b\" some \"c d\" more \"c d e f\""
+      )
+    )
+
+    testExpansion(
+      listOf(
+        IAliasManager.Alias(
+          name = "test",
+          expansion = "Test $1; Test $2; Test All $0"
+        )
+      ),
+      "/test 1 2 3",
+      listOf(
+        "Test 1",
+        "Test 2",
+        "Test All 1 2 3"
+      )
+    )
+  }
 }
-- 
GitLab