Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package trurl for openSUSE:Factory checked 
in at 2024-02-22 20:59:45
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/trurl (Old)
 and      /work/SRC/openSUSE:Factory/.trurl.new.1706 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "trurl"

Thu Feb 22 20:59:45 2024 rev:10 rq:1149120 version:0.10

Changes:
--------
--- /work/SRC/openSUSE:Factory/trurl/trurl.changes      2023-11-01 
22:11:12.510396927 +0100
+++ /work/SRC/openSUSE:Factory/.trurl.new.1706/trurl.changes    2024-02-22 
21:00:54.812422649 +0100
@@ -1,0 +2,10 @@
+Wed Feb 21 21:20:22 UTC 2024 - Martin Hauke <[email protected]>
+
+- Update to version 0.10
+  Changes:
+  * add --replace
+  Bugfixes:
+  * fixed buffer overflows on %00 use
+  * enable more C compiler warnings and fix them
+
+-------------------------------------------------------------------

Old:
----
  trurl-0.9.tar.gz

New:
----
  trurl-0.10.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ trurl.spec ++++++
--- /var/tmp/diff_new_pack.BFz0Rc/_old  2024-02-22 21:00:55.780458039 +0100
+++ /var/tmp/diff_new_pack.BFz0Rc/_new  2024-02-22 21:00:55.784458186 +0100
@@ -1,8 +1,8 @@
 #
 # spec file for package trurl
 #
-# Copyright (c) 2023 SUSE LLC
-# Copyright (c) 2023, Martin Hauke <[email protected]>
+# Copyright (c) 2024 SUSE LLC
+# Copyright (c) 2023-2024, Martin Hauke <[email protected]>
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 
 Name:           trurl
-Version:        0.9
+Version:        0.10
 Release:        0
 Summary:        Command line tool for URL parsing and manipulation
 License:        MIT

++++++ trurl-0.9.tar.gz -> trurl-0.10.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trurl-trurl-0.9/.github/workflows/curl-for-win.yml 
new/trurl-trurl-0.10/.github/workflows/curl-for-win.yml
--- old/trurl-trurl-0.9/.github/workflows/curl-for-win.yml      1970-01-01 
01:00:00.000000000 +0100
+++ new/trurl-trurl-0.10/.github/workflows/curl-for-win.yml     2024-02-19 
23:14:18.000000000 +0100
@@ -0,0 +1,56 @@
+# Copyright (C) Viktor Szakats. See LICENSE.md
+# SPDX-License-Identifier: curl
+---
+name: curl-for-win
+
+on:
+  push:
+    branches: ["master"]
+  pull_request:
+    branches: ["master"]
+
+permissions: {}
+
+env:
+  CW_GET: 'curl'
+  CW_MAP: '0'
+  CW_JOBS: '3'
+  CW_PKG_NODELETE: '1'
+  CW_PKG_FLATTEN: '1'
+  DOCKER_CONTENT_TRUST: '1'
+
+jobs:
+  win-llvm:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          path: 'trurl'
+          fetch-depth: 8
+      - name: 'build'
+        env:
+          CW_LLVM_MINGW_DL: '1'
+          CW_LLVM_MINGW_ONLY: '0'
+          CW_TURL_TEST: '1'
+        run: |
+          git clone --depth 1 https://github.com/curl/curl-for-win
+          mv curl-for-win/* .
+          export 
CW_CONFIG='-dev-zero-imap-osnotls-osnoidn-nohttp-nocurltool-win'
+          export CW_REVISION='${{ github.sha }}'
+          . ./_versions.sh
+          docker trust inspect --pretty "${DOCKER_IMAGE}"
+          time docker pull "${DOCKER_IMAGE}"
+          docker images --digests
+          time docker run --volume "$(pwd):$(pwd)" --workdir "$(pwd)" \
+            --env-file <(env | grep -a -E \
+              '^(CW_|GITHUB_)') \
+            "${DOCKER_IMAGE}" \
+            sh -c ./_ci-linux-debian.sh
+
+      - name: 'list dependencies'
+        run: cat urls.txt
+      - uses: actions/upload-artifact@v3
+        with:
+          name: 'trurl-windows'
+          retention-days: 5
+          path: curl-*-*-*/trurl*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trurl-trurl-0.9/.reuse/dep5 
new/trurl-trurl-0.10/.reuse/dep5
--- old/trurl-trurl-0.9/.reuse/dep5     2023-10-31 14:08:47.000000000 +0100
+++ new/trurl-trurl-0.10/.reuse/dep5    2024-02-19 23:14:18.000000000 +0100
@@ -9,7 +9,7 @@
 License: curl
 
 # Docs
-Files: CONTRIBUTING.md README.md RELEASE-NOTES THANKS URL-QUIRKS.md
+Files: CONTRIBUTING.md README.md RELEASE-NOTES THANKS URL-QUIRKS.md 
RELEASE-PROCEDURE.md
 Copyright: Daniel Stenberg, <[email protected]>, et al.
 License: curl
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trurl-trurl-0.9/Makefile 
new/trurl-trurl-0.10/Makefile
--- old/trurl-trurl-0.9/Makefile        2023-10-31 14:08:47.000000000 +0100
+++ new/trurl-trurl-0.10/Makefile       2024-02-19 23:14:18.000000000 +0100
@@ -24,8 +24,15 @@
 
 TARGET = trurl
 OBJS = trurl.o
-LDLIBS = $$(curl-config --libs)
-CFLAGS += $$(curl-config --cflags) -W -Wall -Wshadow -Werror -pedantic -g 
-std=gnu99
+ifndef TRURL_IGNORE_CURL_CONFIG
+LDLIBS += $$(curl-config --libs)
+CFLAGS += $$(curl-config --cflags)
+endif
+CFLAGS += -W -Wall -Wshadow -Werror -pedantic
+CFLAGS += -Wconversion -Wmissing-prototypes -Wwrite-strings -Wsign-compare 
-Wno-sign-conversion
+ifndef NDEBUG
+CFLAGS += -g
+endif
 MANUAL = trurl.1
 
 PREFIX ?= /usr/local
@@ -36,9 +43,9 @@
 PYTHON3 ?= python3
 
 $(TARGET): $(OBJS)
-       $(CC) $(OBJS) -o $(TARGET) $(LDLIBS) $(LDFLAGS)
+       $(CC) $(OBJS) -o $(TARGET) $(LDFLAGS) $(LDLIBS)
 
-trurl.o:trurl.c version.h
+trurl.o: trurl.c version.h
 
 .PHONY: install
 install:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trurl-trurl-0.9/RELEASE-NOTES 
new/trurl-trurl-0.10/RELEASE-NOTES
--- old/trurl-trurl-0.9/RELEASE-NOTES   2023-10-31 14:08:47.000000000 +0100
+++ new/trurl-trurl-0.10/RELEASE-NOTES  2024-02-19 23:14:18.000000000 +0100
@@ -1,18 +1,16 @@
-trurl 0.9
+trurl 0.10
 
 Changes since previous release
 
- o add --as-idn and punycode to IDN conversion
- o add --curl to only count as valid URLs supported by libcurl
- o add vs2022 project files
+ o add --replace
 
 Bugfixes since previous release
 
- o accept \* as a trim name to trim a literal asterisk name
- o format null as \u0000 for --json
- o run  --trim query  before  --append query
+ o fixed buffer overflows on %00 use
+ o support compiling with old versions of Visual Studio
+ o enable more C compiler warnings and fix them
+ o ci: add Windows builds
 
 Contributors to this release:
 
-  Daniel Stenberg, Ehsan, Emanuele Torre, Jacob Mealey, Krishean Draconis,
-  Michael Ablassmeier, 積丹尼 Dan Jacobson
+  Daniel Stenberg, Jacob Mealey, Jay Satiro, Michael Lass, Viktor Szakats
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trurl-trurl-0.9/RELEASE-PROCEDURE.md 
new/trurl-trurl-0.10/RELEASE-PROCEDURE.md
--- old/trurl-trurl-0.9/RELEASE-PROCEDURE.md    1970-01-01 01:00:00.000000000 
+0100
+++ new/trurl-trurl-0.10/RELEASE-PROCEDURE.md   2024-02-19 23:14:18.000000000 
+0100
@@ -0,0 +1,24 @@
+trurl release procedure - how to do a release
+==============================================
+
+in the source code repo
+-----------------------
+
+- edit `RELEASE-NOTES` to be accurate
+
+- edit `version.h` to contain the correct version
+
+- make sure all relevant changes are committed on the master branch
+
+- tag the git repo in this style: `git tag -a trurl-[version]` -a annotates
+  the tag
+
+- push the git commits and the new tag
+
+- Go to https://github.com/curl/trurl/tags and edit the tag as a release
+  Consider allowing it to make a discussion post about it.
+
+celebrate
+---------
+
+- suitable beverage intake is encouraged for the festivities
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trurl-trurl-0.9/test.py new/trurl-trurl-0.10/test.py
--- old/trurl-trurl-0.9/test.py 2023-10-31 14:08:47.000000000 +0100
+++ new/trurl-trurl-0.10/test.py        2024-02-19 23:14:18.000000000 +0100
@@ -59,8 +59,9 @@
 
 
 class TestCase:
-    def __init__(self, testIndex, baseCmd, **testCase):
+    def __init__(self, testIndex, runnerCmd, baseCmd, **testCase):
         self.testIndex = testIndex
+        self.runnerCmd = runnerCmd
         self.baseCmd = baseCmd
         self.arguments = testCase["input"]["arguments"]
         self.expected = testCase["expected"]
@@ -74,7 +75,10 @@
 
         cmd = [self.baseCmd]
         args = self.arguments
-        if runWithValgrind:
+        if self.runnerCmd != "":
+            cmd = [self.runnerCmd]
+            args = [self.baseCmd] + self.arguments
+        elif runWithValgrind:
             cmd = [VALGRINDTEST]
             args = VALGRINDARGS + [self.baseCmd] + self.arguments
 
@@ -96,6 +100,11 @@
         # assume stderr is always going to be string
         stderr = output.stderr
 
+        # runners (e.g. wine) spill their own output into stderr,
+        # ignore stderr tests when using a runner.
+        if self.runnerCmd != "" and "stderr" in self.expected:
+            stderr = self.expected["stderr"]
+
         self.commandOutput = CommandOutput(stdout, output.returncode, stderr)
         return True
 
@@ -161,41 +170,53 @@
     # the .exe on the end is necessary when using absolute paths
     if sys.platform == "win32" or sys.platform == "cygwin":
         baseCmd += ".exe"
+
+    with open(path.join(baseDir, TESTFILE), "r") as file:
+        allTests = json.load(file)
+        testIndexesToRun = []
+
+    # if argv[1] exists and starts with int
+    cmdfilter = ""
+    testIndexesToRun = list(range(len(allTests)))
+    runWithValgrind = False
+    verboseDetail = False
+    runnerCmd = ""
+
+    if argc > 1:
+        for arg in argv[1:]:
+            if arg[0].isnumeric():
+                # run only test cases separated by ","
+                testIndexesToRun = []
+
+                for caseIndex in arg.split(","):
+                    testIndexesToRun.append(int(caseIndex))
+            elif arg == "--with-valgrind":
+                runWithValgrind = True
+            elif arg == "--verbose":
+                verboseDetail = True
+            elif arg.startswith("--trurl="):
+                baseCmd = arg[len("--trurl="):]
+            elif arg.startswith("--runner="):
+                runnerCmd = arg[len("--runner="):]
+            else:
+                cmdfilter = argv[1]
+
     # check if the trurl executable exists
     if path.isfile(baseCmd):
         # get the version info for the feature list
+        args = ["--version"]
+        if runnerCmd != "":
+            cmd = [runnerCmd]
+            args = [baseCmd] + args
+        else:
+            cmd = [baseCmd]
         output = run(
-            [baseCmd, "--version"],
+            cmd + args,
             stdout=PIPE, stderr=PIPE,
             encoding="utf-8"
         )
         features = output.stdout.split('\n')[1].split()[1:]
 
-        with open(path.join(baseDir, TESTFILE), "r") as file:
-            allTests = json.load(file)
-            testIndexesToRun = []
-
-        # if argv[1] exists and starts with int
-        cmdfilter = ""
-        testIndexesToRun = list(range(len(allTests)))
-        runWithValgrind = False
-        verboseDetail = False
-
-        if argc > 1:
-            for arg in argv[1:]:
-                if arg[0].isnumeric():
-                    # run only test cases separated by ","
-                    testIndexesToRun = []
-
-                    for caseIndex in arg.split(","):
-                        testIndexesToRun.append(int(caseIndex))
-                elif arg == "--with-valgrind":
-                    runWithValgrind = True
-                elif arg == "--verbose":
-                    verboseDetail = True
-                else:
-                    cmdfilter = argv[1]
-
         numTestsFailed = 0
         numTestsPassed = 0
         numTestsSkipped = 0
@@ -208,7 +229,7 @@
                     numTestsSkipped += 1
                     continue
 
-            test = TestCase(testIndex + 1, baseCmd, **allTests[testIndex])
+            test = TestCase(testIndex + 1, runnerCmd, baseCmd, 
**allTests[testIndex])
 
             if test.runCommand(cmdfilter, runWithValgrind):
                 if test.test():  # passed
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trurl-trurl-0.9/tests.json 
new/trurl-trurl-0.10/tests.json
--- old/trurl-trurl-0.9/tests.json      2023-10-31 14:08:47.000000000 +0100
+++ new/trurl-trurl-0.10/tests.json     2024-02-19 23:14:18.000000000 +0100
@@ -260,13 +260,29 @@
     {
         "input": {
             "arguments": [
+                "imap://curl.se:22/",
+                "-s",
+                "port=143"
+            ]
+        },
+        "required": ["imap-options"],
+        "expected": {
+            "stdout": "imap://curl.se/\n",
+            "stderr": "",
+            "returncode": 0
+        }
+    },
+    {
+        "input": {
+            "arguments": [
+                "--keep-port",
                 "https://curl.se:22/";,
                 "-s",
                 "port=443"
             ]
         },
         "expected": {
-            "stdout": "https://curl.se/\n";,
+            "stdout": "https://curl.se:443/\n";,
             "stderr": "",
             "returncode": 0
         }
@@ -274,6 +290,7 @@
     {
         "input": {
             "arguments": [
+                "--keep-port",
                 "https://curl.se:22/";,
                 "-s",
                 "port=443",
@@ -282,7 +299,7 @@
             ]
         },
         "expected": {
-            "stdout": "https://curl.se/\n";,
+            "stdout": "https://curl.se:443/\n";,
             "stderr": "",
             "returncode": 0
         }
@@ -307,13 +324,14 @@
             "arguments": [
                 "--default-port",
                 "--url",
-                "https://curl.se/we/are.html";,
+                "imap://curl.se/we/are.html",
                 "--get",
                 "{port}"
             ]
         },
+        "required": ["imap-options"],
         "expected": {
-            "stdout": "443\n",
+            "stdout": "143\n",
             "stderr": "",
             "returncode": 0
         }
@@ -372,6 +390,7 @@
                 "{options}"
             ]
         },
+        "required": ["imap-options"],
         "expected": {
             "stdout": "crazy\n",
             "stderr": "",
@@ -489,13 +508,14 @@
         "input": {
             "arguments": [
                 "--url",
-                "https://curl.se/we/are.html";,
+                "imap://curl.se/we/are.html",
                 "-g",
                 "{default:port}"
             ]
         },
+        "required": ["imap-options"],
         "expected": {
-            "stdout": "443\n",
+            "stdout": "143\n",
             "stderr": "",
             "returncode": 0
         }
@@ -846,11 +866,12 @@
     {
         "input": {
             "arguments": [
+                "--keep-port",
                 "https://hello:443/foo";
             ]
         },
         "expected": {
-            "stdout": "https://hello/foo\n";,
+            "stdout": "https://hello:443/foo\n";,
             "stderr": "",
             "returncode": 0
         }
@@ -858,11 +879,12 @@
     {
         "input": {
             "arguments": [
+                "--keep-port",
                 "ftp://hello:21/foo";
             ]
         },
         "expected": {
-            "stdout": "ftp://hello/foo\n";,
+            "stdout": "ftp://hello:21/foo\n";,
             "stderr": "",
             "returncode": 0
         }
@@ -884,13 +906,14 @@
     {
         "input": {
             "arguments": [
+                "--keep-port",
                 "ftp://hello:443/foo";,
                 "-s",
                 "scheme=https"
             ]
         },
         "expected": {
-            "stdout": "https://hello/foo\n";,
+            "stdout": "https://hello:443/foo\n";,
             "stderr": "",
             "returncode": 0
         }
@@ -1191,6 +1214,7 @@
     {
         "input": {
             "arguments": [
+                "--keep-port",
                 "https://curl.se";,
                 "--iterate",
                 "port=80 81 443"
@@ -1199,7 +1223,7 @@
         "expected": {
             "stderr": "",
             "returncode": 0,
-            "stdout": 
"https://curl.se:80/\nhttps://curl.se:81/\nhttps://curl.se/\n";
+            "stdout": 
"https://curl.se:80/\nhttps://curl.se:81/\nhttps://curl.se:443/\n";
         }
     },
     {
@@ -1656,17 +1680,18 @@
     {
         "input": {
             "arguments": [
-                
"imaps://user:password;crazy@[ff00::1234%hello]:1234/path?a=b&c=d#fragment",
+                
"imap://user:password;crazy@[ff00::1234%hello]:1234/path?a=b&c=d#fragment",
                 "--json"
             ]
         },
+        "required": ["imap-options"],
         "expected": {
             "returncode": 0,
             "stdout": [
                 {
-                    "url": 
"imaps://user:password;crazy@[ff00::1234%25hello]:1234/path?a=b&c=d#fragment",
+                    "url": 
"imap://user:password;crazy@[ff00::1234%25hello]:1234/path?a=b&c=d#fragment",
                     "parts": {
-                        "scheme": "imaps",
+                        "scheme": "imap",
                         "user": "user",
                         "password": "password",
                         "options": "crazy",
@@ -1694,14 +1719,15 @@
     {
         "input": {
             "arguments": [
-                "http://example.com/";,
+                "imap://example.com/",
                 "--get",
                 "port: {port}, default:port: {default:port}"
             ]
         },
+        "required": ["imap-options"],
         "expected": {
             "returncode": 0,
-            "stdout": "port: , default:port: 80\n"
+            "stdout": "port: , default:port: 143\n"
         }
     },
     {
@@ -2014,7 +2040,7 @@
             "returncode": 0,
             "stderr": ""
         }
-    }, 
+    },
     {
         "input" : {
             "arguments": [
@@ -2040,7 +2066,7 @@
                         }
                     ]
                 }
-            ]        
+            ]
         }
     },
     {
@@ -2068,7 +2094,7 @@
                         }
                     ]
                 }
-            ]        
+            ]
         }
     },
     {
@@ -2096,7 +2122,7 @@
                         }
                     ]
                 }
-            ]        
+            ]
         }
     },
     {
@@ -2204,5 +2230,198 @@
             "stderr": true,
             "returncode": 9
         }
-    }
+    },
+    {
+        "input": {
+            "arguments": [
+                "http://test.org/?key=val";,
+                "--replace",
+                "key=foo"
+            ]
+        },
+        "expected": {
+            "stdout": "http://test.org/?key=foo\n";,
+            "stderr": "",
+            "returncode": 0
+        }
+    },
+    {
+        "input": {
+            "arguments": [
+                "http://test.org/?that=thing&key=val";,
+                "--replace",
+                "key=foo"
+            ]
+        },
+        "expected": {
+            "stdout": "http://test.org/?that=thing&key=foo\n";,
+            "stderr": "",
+            "returncode": 0
+        }
+    },
+    {
+        "input": {
+            "arguments": [
+                "http://test.org/?that=thing&key";,
+                "--replace",
+                "key=foo"
+            ]
+        },
+        "expected": {
+            "stdout": "http://test.org/?that=thing&key=foo\n";,
+            "stderr": "",
+            "returncode": 0
+        }
+    },
+    {
+        "input": {
+            "arguments": [
+                "http://test.org/?that=thing&key=foo";,
+                "--replace",
+                "key"
+            ]
+        },
+        "expected": {
+            "stdout": "http://test.org/?that=thing&key\n";,
+            "stderr": "",
+            "returncode": 0
+        }
+    },
+    {
+        "input": {
+            "arguments": [
+                "https://example.com?a=123&b=321&b=987";,
+                "--replace",
+                "b=foo"
+            ]
+        },
+        "expected": {
+            "stdout": "https://example.com/?a=123&b=foo\n";,
+            "stderr": "",
+            "returncode": 0
+        }
+    },
+    {
+        "input": {
+            "arguments": [
+                "example.org/?quest=best",
+                "--replace",
+                "quest=%00",
+                "--json"
+            ]
+        },
+        "expected": {
+                       "stdout": [{
+                               "url": "http://example.org/?quest=%00";,
+                               "parts": {
+                                       "scheme": "http",
+                                       "host": "example.org",
+                                       "path": "/"
+                               },
+                               "params": [
+                                       {
+                                               "key": "quest",
+                                               "value": "\u0000"
+                                       }
+                               ]
+                       }],
+                       "stderr": "",
+            "returncode": 0
+        }
+    },
+    {
+        "input": {
+            "arguments": [
+                "example.com",
+                "--replace"
+            ]
+        }, 
+        "expected": {
+            "stderr": "trurl error: No data passed to replace component\ntrurl 
error: Try trurl -h for help\n",
+            "stdout":"",
+            "returncode": 12
+        }
+    },
+    {
+    "input": {
+            "arguments": [
+                "http://test.org/?that=thing";,
+                "--force-replace",
+                "key=foo"
+            ]
+        },
+        "expected": {
+            "stdout": "http://test.org/?that=thing&key=foo\n";,
+            "stderr": "trurl note: key 'key' not in url, appending to query\n",
+            "returncode": 0
+        }
+  },
+  {
+      "input": {
+          "arguments": [
+              "0?00%000000000000000000000=0000000000"
+          ]
+      },
+      "expected": {
+          "stdout": "http://0.0.0.0/?00%000000000000000000000=0000000000\n";,
+          "stderr": "",
+          "returncode": 0
+      }
+  },
+  {
+      "input": {
+          "arguments": [
+              "--json",
+              "0?0%000000000000000000000000000000000"
+          ]
+      },
+      "expected": {
+          "returncode": 0,
+          "stderr": "",
+          "stdout": [
+              {
+                  "url": "http://0.0.0.0/?0%000000000000000000000000000000000";,
+                  "parts": {
+                      "scheme": "http",
+                      "host": "0.0.0.0",
+                      "path": "/"
+                  },
+                  "params": [
+                      {
+                          "key": "0\u00000000000000000000000000000000000",
+                          "value": ""
+                      }
+                  ]
+              }
+          ]
+      }
+  },
+  {
+      "input": {
+          "arguments": [
+              "--json",
+              "0?0%000000000000000000000000000000000=000%0000000000"
+          ]
+      },
+      "expected": {
+          "returncode": 0,
+          "stderr": "",
+          "stdout": [
+              {
+                  "url": 
"http://0.0.0.0/?0%000000000000000000000000000000000=000%0000000000";,
+                  "parts": {
+                      "scheme": "http",
+                      "host": "0.0.0.0",
+                      "path": "/"
+                  },
+                  "params": [
+                      {
+                          "key": "0\u00000000000000000000000000000000000",
+                          "value": "000\u000000000000"
+                      }
+                  ]
+              }
+          ]
+      }
+  }
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trurl-trurl-0.9/trurl.1 new/trurl-trurl-0.10/trurl.1
--- old/trurl-trurl-0.9/trurl.1 2023-10-31 14:08:47.000000000 +0100
+++ new/trurl-trurl-0.10/trurl.1        2024-02-19 23:14:18.000000000 +0100
@@ -196,6 +196,15 @@
 Redirect the URL to this new location.
 The redirection is performed on the base URL, so, if no base URL is specified,
 no redirection will be performed.
+.IP "--replace [data]"
+Replaces a URL query.
+
+data can either take the form of a single value, or as a key/value pair in the
+shape \fIfoo=bar\fP. If replace is called on an item that isn't in the list of
+queries trurl will ignore that item.
+.IP "--force-replace [data]"
+Works the same as \fI--replace\fP, but trurl will append a missing query 
string if 
+it is not in the query list already.
 .IP "-s, --set [component][:]=[data]"
 Set this URL component. Setting blank string ("") will clear the component
 from the URL.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trurl-trurl-0.9/trurl.c new/trurl-trurl-0.10/trurl.c
--- old/trurl-trurl-0.9/trurl.c 2023-10-31 14:08:47.000000000 +0100
+++ new/trurl-trurl-0.10/trurl.c        2024-02-19 23:14:18.000000000 +0100
@@ -26,11 +26,21 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <stdbool.h>
 #include <curl/curl.h>
 #include <curl/mprintf.h>
 #include <stdint.h>
 
+#if defined(_MSC_VER) && (_MSC_VER < 1800)
+typedef enum {
+  bool_false = 0,
+  bool_true  = 1
+} bool;
+#define false bool_false
+#define true  bool_true
+#else
+#include <stdbool.h>
+#endif
+
 #include <locale.h> /* for setlocale() */
 
 #include "version.h"
@@ -61,6 +71,9 @@
 #if CURL_AT_LEAST_VERSION(8,3,0)
 #define SUPPORTS_PUNY2IDN
 #endif
+#if CURL_AT_LEAST_VERSION(7,30,0)
+#define SUPPORTS_IMAP_OPTIONS
+#endif
 
 #define OUTPUT_URL      0  /* default */
 #define OUTPUT_SCHEME   1
@@ -126,6 +139,7 @@
 #define ERROR_BADURL 9 /* if --verify is set and the URL cannot parse */
 #define ERROR_GET   10 /* bad --get syntax */
 #define ERROR_ITER  11 /* bad --iterate syntax */
+#define ERROR_REPL  12 /* a --replace problem */
 
 #ifndef SUPPORTS_URL_STRERROR
 /* provide a fake local mockup */
@@ -137,28 +151,27 @@
 }
 #endif
 
-static void warnf(char *fmt, ...)
+static void message_low(const char *prefix, const char *suffix,
+                        const char *fmt, va_list ap)
+{
+  fputs(prefix, stderr);
+  vfprintf(stderr, fmt, ap);
+  fputs(suffix, stderr);
+}
+
+static void warnf_low(const char *fmt, va_list ap)
+{
+  message_low(WARN_PREFIX, "\n", fmt, ap);
+}
+
+static void warnf(const char *fmt, ...)
 {
   va_list ap;
   va_start(ap, fmt);
-  fputs(WARN_PREFIX, stderr);
-  vfprintf(stderr, fmt, ap);
-  fputs("\n", stderr);
+  warnf_low(fmt, ap);
   va_end(ap);
 }
 
-#define VERIFY(o, exit_code, ...) \
-  do { \
-    if(!o->verify) \
-      warnf(__VA_ARGS__); \
-    else { \
-      /* make sure to terminate the JSON array */ \
-      if(o->jsonout) \
-        printf("%s]\n", o->urls ? "\n" : ""); \
-      errorf(o, exit_code, __VA_ARGS__); \
-    } \
-  } while(0)
-
 static void help(void)
 {
   int i;
@@ -179,6 +192,8 @@
     "      --as-idn                     - encode hostnames in idn\n"
     "      --query-separator [letter]   - if something else than '&'\n"
     "      --redirect [URL]             - redirect to this\n"
+    "      --replace [data]             - replaces a query [data]\n"
+    "      --force-replace [data]       - appends a new query if not found\n"
     "  -s, --set [component]=[data]     - set component content\n"
     "      --sort-query                 - alpha-sort the query pairs\n"
     "      --trim [component]=[what]    - trim component\n"
@@ -199,34 +214,46 @@
 static void show_version(void)
 {
   curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
-  fprintf(stdout, "%s version %s libcurl/%s [built-with %s]\n",
-          PROGNAME, TRURL_VERSION_TXT, data->version, LIBCURL_VERSION);
   /* puny code isn't guaranteed based on the version, so it must be polled
    * from libcurl */
-  bool supports_puny = false;
-#ifdef SUPPORTS_PUNYCODE
-  const char *const *feature_name = data->feature_names;
-  while(*feature_name && !supports_puny) {
-    supports_puny = !strncmp(*feature_name, "IDN", 3);
-    feature_name++;
+#if defined(SUPPORTS_PUNYCODE) || defined(SUPPORTS_PUNY2IDN)
+  bool supports_puny = (data->features & CURL_VERSION_IDN) != 0;
+#endif
+#if defined(SUPPORTS_IMAP_OPTIONS)
+  bool supports_imap = false;
+  const char *const *protocol_name = data->protocols;
+  while(*protocol_name && !supports_imap) {
+    supports_imap = !strncmp(*protocol_name, "imap", 3);
+    protocol_name++;
   }
 #endif
 
-  fprintf(stdout, "features: %s", supports_puny?"punycode ":"");
+  fprintf(stdout, "%s version %s libcurl/%s [built-with %s]\n",
+          PROGNAME, TRURL_VERSION_TXT, data->version, LIBCURL_VERSION);
+  fprintf(stdout, "features:");
+#ifdef SUPPORTS_PUNYCODE
+  if(supports_puny)
+    fprintf(stdout, " punycode");
+#endif
 #ifdef SUPPORTS_ALLOW_SPACE
-  fprintf(stdout, "white-space ");
+  fprintf(stdout, " white-space");
 #endif
 #ifdef SUPPORTS_ZONEID
-  fprintf(stdout, "zone-id ");
+  fprintf(stdout, " zone-id");
 #endif
 #ifdef SUPPORTS_URL_STRERROR
-  fprintf(stdout, "url-strerror ");
+  fprintf(stdout, " url-strerror");
 #endif
 #ifdef SUPPORTS_NORM_IPV4
-  fprintf(stdout, "normalize-ipv4 ");
+  fprintf(stdout, " normalize-ipv4");
+#endif
+#ifdef SUPPORTS_IMAP_OPTIONS
+  if(supports_imap)
+    fprintf(stdout, " imap-options");
 #endif
 #ifdef SUPPORTS_PUNY2IDN
-  fprintf(stdout, "punycode2idn");
+  if(supports_puny)
+    fprintf(stdout, " punycode2idn");
 #endif
 
   fprintf(stdout, "\n");
@@ -248,6 +275,7 @@
   struct curl_slist *set_list;
   struct curl_slist *trim_list;
   struct curl_slist *iter_list;
+  struct curl_slist *replace_list;
   const char *redirect;
   const char *qsep;
   const char *format;
@@ -266,12 +294,13 @@
   bool urlencode;
   bool end_of_options;
   bool quiet_warnings;
+  bool force_replace;
 
   /* -- stats -- */
   unsigned int urls;
 };
 
-void trurl_warnf(struct option *o, char *fmt, ...)
+static void trurl_warnf(struct option *o, const char *fmt, ...)
 {
   if(!o->quiet_warnings) {
     va_list ap;
@@ -297,22 +326,47 @@
   curl_slist_free_all(o->iter_list);
   curl_slist_free_all(o->append_query);
   curl_slist_free_all(o->trim_list);
+  curl_slist_free_all(o->replace_list);
   curl_slist_free_all(o->append_path);
 }
 
-static void errorf(struct option *o, int exit_code, char *fmt, ...)
+static void errorf_low(const char *fmt, va_list ap)
+{
+  message_low(ERROR_PREFIX, "\n"
+              ERROR_PREFIX "Try " PROGNAME " -h for help\n", fmt, ap);
+}
+
+static void errorf(struct option *o, int exit_code, const char *fmt, ...)
 {
   va_list ap;
   va_start(ap, fmt);
-  fputs(ERROR_PREFIX, stderr);
-  vfprintf(stderr, fmt, ap);
-  fputs("\n" ERROR_PREFIX "Try " PROGNAME " -h for help\n", stderr);
+  errorf_low(fmt, ap);
   va_end(ap);
   trurl_cleanup_options(o);
   curl_global_cleanup();
   exit(exit_code);
 }
 
+static void verify(struct option *o, int exit_code, const char *fmt, ...)
+{
+  va_list ap;
+  va_start(ap, fmt);
+  if(!o->verify) {
+    warnf_low(fmt, ap);
+    va_end(ap);
+  }
+  else {
+    /* make sure to terminate the JSON array */
+    if(o->jsonout)
+      printf("%s]\n", o->urls ? "\n" : "");
+    errorf_low(fmt, ap);
+    va_end(ap);
+    trurl_cleanup_options(o);
+    curl_global_cleanup();
+    exit(exit_code);
+  }
+}
+
 static char *strurldecode(const char *url, int inlength, int *outlength)
 {
   return curl_easy_unescape(NULL, inlength ? url : "", inlength,
@@ -365,7 +419,7 @@
   char *urle;
   if(p) {
     /* URL encode the left and the right side of the '=' separately */
-    char *f1 = curl_easy_escape(NULL, query, p - query);
+    char *f1 = curl_easy_escape(NULL, query, (int)(p - query));
     char *f2 = curl_easy_escape(NULL, p + 1, 0);
     urle = curl_maprintf("%s=%s", f1, f2);
     curl_free(f1);
@@ -420,6 +474,18 @@
     o->trim_list = n;
 }
 
+static void replaceadd(struct option *o,
+                       const char *replace_list) /* [component]=[data] */
+{
+  struct curl_slist *n = NULL;
+  if(replace_list)
+    n = curl_slist_append(o->replace_list, replace_list);
+  else
+    errorf(o, ERROR_REPL, "No data passed to replace component");
+
+  if(n)
+    o->replace_list = n;
+}
 static bool checkoptarg(struct option *o, const char *str,
                         const char *given,
                         const char *arg)
@@ -536,6 +602,15 @@
     o->urlencode = true;
   else if(!strcmp("--quiet", flag))
     o->quiet_warnings = true;
+  else if(!strcmp("--replace", flag)) {
+    replaceadd(o, arg);
+    *usedarg = true;
+  }
+  else if(!strcmp("--force-replace", flag)) {
+    replaceadd(o, arg);
+    o->force_replace = true;
+    *usedarg = true;
+  }
   else
     return 1;  /* unrecognized option */
   return 0;
@@ -549,11 +624,11 @@
   struct string *qp = urldecode ? qpairsdec : qpairs;
 
   for(i = 0; i< nqpairs; i++) {
-    if(!strncmp(key, qp[i].str, klen) &&
-       (qp[i].str[klen] == '=')) {
+    if(!strncmp(key, qp[i].str, klen) && (qp[i].str[klen] == '=')) {
       if(shown)
         fputc(' ', stream);
-      fputs(&qp[i].str[klen + 1], stream);
+      fprintf(stream, "%.*s", (int) (qp[i].len - klen - 1),
+              &qp[i].str[klen + 1]);
       if(!showall)
         break;
       shown = true;
@@ -617,7 +692,7 @@
   CURLUcode rc = geturlpart(o, modifiers, uh, CURLUPART_URL, &url);
   if(rc) {
     trurl_cleanup_options(o);
-    VERIFY(o, ERROR_BADURL, "invalid url [%s]", curl_url_strerror(rc));
+    verify(o, ERROR_BADURL, "invalid url [%s]", curl_url_strerror(rc));
     return;
   }
   fputs(url, stream);
@@ -889,7 +964,7 @@
   CURLUcode rc = geturlpart(o, 0, uh, CURLUPART_URL, &url);
   if(rc) {
     trurl_cleanup_options(o);
-    VERIFY(o, ERROR_BADURL, "invalid url [%s]", curl_url_strerror(rc));
+    verify(o, ERROR_BADURL, "invalid url [%s]", curl_url_strerror(rc));
     return;
   }
   printf("%s\n  {\n    \"url\": ", o->urls ? "," : "");
@@ -914,11 +989,11 @@
     int j;
     fputs(",\n    \"params\": [\n", stdout);
     for(j = 0 ; j < nqpairs; j++) {
-      const char *sep = strchr(qpairsdec[j].str, '=');
+      const char *sep = memchr(qpairsdec[j].str, '=', qpairsdec[j].len);
       const char *value = sep ? sep + 1 : "";
-
+      int value_len = (int) qpairsdec[j].len - (int)(value - qpairsdec[j].str);
       /* don't print out empty/trimmed values */
-      if(!qpairsdec[j].str[0])
+      if(!qpairsdec[j].len || !qpairsdec[j].str[0])
         continue;
       if(!first)
         fputs(",\n", stdout);
@@ -929,7 +1004,7 @@
                        qpairsdec[j].len,
                  false);
       fputs(",\n        \"value\": ", stdout);
-      jsonString(stdout, sep?value:"", sep?qpairsdec[j].len:0, false);
+      jsonString(stdout, sep?value:"", sep?value_len:0, false);
       fputs("\n      }", stdout);
     }
     fputs("\n    ]", stdout);
@@ -942,11 +1017,12 @@
 {
   struct curl_slist *node;
   for(node = o->trim_list; node; node = node->next) {
+    char *ptr;
     char *instr = node->data;
     if(strncmp(instr, "query", 5))
       /* for now we can only trim query components */
       errorf(o, ERROR_TRIM, "Unsupported trim component: %s", instr);
-    char *ptr = strchr(instr, '=');
+    ptr = strchr(instr, '=');
     if(ptr && (ptr > instr)) {
       /* 'ptr' should be a fixed string or a pattern ending with an
          asterisk */
@@ -992,7 +1068,7 @@
             !strncasecmp(q, ptr, inslen))) {
           /* this qpair should be stripped out */
           free(qpairs[i].str);
-          curl_free(qpairsdec[i].str);
+          free(qpairsdec[i].str);
           qpairs[i].str = strdup(""); /* marked as deleted */
           qpairs[i].len = 0;
           qpairsdec[i].str = strdup(""); /* marked as deleted */
@@ -1005,7 +1081,7 @@
 }
 
 /* memdup the amount and add a trailing zero */
-struct string *memdupzero(char *source, size_t len)
+static struct string *memdupzero(char *source, size_t len)
 {
   struct string *ret = malloc(sizeof(struct string));
   if(!ret)
@@ -1022,7 +1098,7 @@
 }
 
 /* URL decode the pair and return it in an allocated chunk */
-struct string *memdupdec(char *source, size_t len, bool json)
+static struct string *memdupdec(char *source, size_t len, bool json)
 {
   char *sep = memchr(source, '=', len);
   char *left = NULL;
@@ -1030,14 +1106,14 @@
   int right_len = 0;
   int left_len = 0;
   char *str;
-
-  left = strurldecode(source, sep ? (size_t)(sep - source) : len,
+  struct string *ret;
+  left = strurldecode(source, (int)(sep ? (size_t)(sep - source) : len),
                       &left_len);
   if(sep) {
     char *p;
     int plen;
-    right = strurldecode(sep + 1, len - (sep - source) - 1,
-            (int *)&right_len);
+    right = strurldecode(sep + 1, (int)(len - (sep - source) - 1),
+            &right_len);
 
     /* convert null bytes to periods */
     for(plen = right_len, p = right; plen; plen--, p++) {
@@ -1046,26 +1122,22 @@
       }
      }
   }
-
-  str = curl_maprintf("%.*s%s%.*s", left_len, left,
-                      right ? "=":"",
-                      right_len, right?right:"");
-  /* handle strings with null characters */
-  if(sep && right) {
-    memcpy(str + left_len + 1, right, right_len);
+  str = malloc(sizeof(char) * (left_len + (sep?(right_len + 1):0)));
+  if(!str)
+    return NULL;
+  memcpy(str, left, left_len);
+  if(sep) {
+    str[left_len] = '=';
+    memcpy(str + 1 + left_len, right, right_len);
   }
   curl_free(right);
   curl_free(left);
-  struct string *ret = malloc(sizeof(struct string));
+  ret = malloc(sizeof(struct string));
   if(!ret) {
     return NULL;
   }
   ret->str = str;
-  if(right)
-    ret->len = right_len;
-  else {
-    ret->len = left_len;
-  }
+  ret->len = left_len + (sep?(right_len + 1):0);
   return ret;
 }
 
@@ -1077,7 +1149,7 @@
     if(qpairs[i].len) {
       free(qpairs[i].str);
       qpairs[i].str = NULL;
-      curl_free(qpairsdec[i].str);
+      free(qpairsdec[i].str);
       qpairsdec[i].str = NULL;
     }
   }
@@ -1162,10 +1234,11 @@
 /* sort case insensitively */
 static int cmpfunc(const void *p1, const void *p2)
 {
-  int len = (((struct string *)p1)->len) < (((struct string *)p2)->len)?
-             (((struct string *)p1)->len):(((struct string *)p2)->len);
+  int i;
+  int len = (int)((((struct string *)p1)->len) < (((struct string *)p2)->len)?
+                  (((struct string *)p1)->len) : (((struct string *)p2)->len));
 
-  for(int i = 0; i < len; i++) {
+  for(i = 0; i < len; i++) {
     char c1 = ((struct string *)p1)->str[i] | ('a' - 'A');
     char c2 = ((struct string *)p2)->str[i] | ('a' - 'A');
     if(c1 != c2)
@@ -1184,6 +1257,60 @@
   }
 }
 
+static void replace(struct option *o)
+{
+  struct curl_slist *node;
+  for(node = o->replace_list; node; node = node->next) {
+    struct string key;
+    struct string value;
+    bool replaced = false;
+    int i;
+    key.str = node->data;
+    value.str = strchr(key.str, '=');
+    if(value.str) {
+      key.len = value.str++ - key.str;
+      value.len = strlen(value.str);
+    }
+    else {
+      key.len = strlen(key.str);
+      value.str = NULL;
+      value.len = 0;
+    }
+    for(i = 0; i < nqpairs; i++) {
+      char *q = qpairs[i].str;
+      /* not the correct query, move on */
+      if(strncmp(q, key.str, key.len))
+        continue;
+      free(qpairs[i].str);
+      curl_free(qpairsdec[i].str);
+      /* this is a duplicate remove it. */
+      if(replaced) {
+        qpairs[i].len = 0;
+        qpairs[i].str = strdup("");
+        qpairsdec[i].len = 0;
+        qpairsdec[i].str = strdup("");
+        continue;
+      }
+      struct string *pdec =
+        memdupdec(key.str, key.len + value.len + 1, o->jsonout);
+      struct string *p = memdupzero(key.str, key.len + value.len + 1);
+      qpairs[i].len = p->len;
+      qpairs[i].str = p->str;
+      qpairsdec[i].len = pdec->len;
+      qpairsdec[i].str = pdec->str;
+      free(pdec);
+      free(p);
+      replaced = true;
+    }
+
+    if(!replaced && o->force_replace) {
+      trurl_warnf(o, "key '%.*s' not in url, appending to query",
+                  (int) (key.len),
+                  key.str);
+      addqpair(key.str, strlen(key.str), o->jsonout);
+    }
+  }
+}
 static CURLUcode seturl(struct option *o, CURLU *uh, const char *url)
 {
   return curl_url_set(uh, CURLUPART_URL, url,
@@ -1209,14 +1336,14 @@
       CURLUcode rc = seturl(o, uh, url);
       if(rc) {
         curl_url_cleanup(uh);
-        VERIFY(o, ERROR_BADURL, "%s [%s]", curl_url_strerror(rc), url);
+        verify(o, ERROR_BADURL, "%s [%s]", curl_url_strerror(rc), url);
         return;
       }
       if(o->redirect) {
         rc = seturl(o, uh, o->redirect);
         if(rc) {
           curl_url_cleanup(uh);
-          VERIFY(o, ERROR_BADURL, "invalid redirection: %s [%s]",
+          verify(o, ERROR_BADURL, "invalid redirection: %s [%s]",
                  curl_url_strerror(rc), o->redirect);
           return;
         }
@@ -1333,6 +1460,9 @@
     /* trim parts */
     trim(o);
 
+    /* replace parts */
+    replace(o);
+
     /* append query segments */
     for(p = o->append_query; p; p = p->next) {
       addqpair(p->data, strlen(p->data), o->jsonout);
@@ -1350,7 +1480,7 @@
       if(rc) {
         if(o->verify) /* only clean up if we're exiting */
           curl_url_cleanup(uh);
-        VERIFY(o, ERROR_URL, "not enough input for a URL");
+        verify(o, ERROR_URL, "not enough input for a URL");
         url_is_invalid = true;
       }
       else {
@@ -1358,7 +1488,7 @@
         if(rc) {
           if(o->verify) /* only clean up if we're exiting */
             curl_url_cleanup(uh);
-          VERIFY(o, ERROR_BADURL, "%s [%s]", curl_url_strerror(rc),
+          verify(o, ERROR_BADURL, "%s [%s]", curl_url_strerror(rc),
                  ourl);
           url_is_invalid = true;
         }
@@ -1370,7 +1500,7 @@
           else {
             if(o->verify) /* only clean up if we're exiting */
               curl_url_cleanup(uh);
-            VERIFY(o, ERROR_BADURL, "url became invalid");
+            verify(o, ERROR_BADURL, "url became invalid");
             url_is_invalid = true;
           }
         }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/trurl-trurl-0.9/version.h 
new/trurl-trurl-0.10/version.h
--- old/trurl-trurl-0.9/version.h       2023-10-31 14:08:47.000000000 +0100
+++ new/trurl-trurl-0.10/version.h      2024-02-19 23:14:18.000000000 +0100
@@ -24,6 +24,6 @@
  *
  ***************************************************************************/
 
-#define TRURL_VERSION_TXT "0.8"
+#define TRURL_VERSION_TXT "0.10"
 
 #endif

Reply via email to