diff --git a/.idea/artifacts/util_js_0_3_0_beta_4.xml b/.idea/artifacts/util_js_0_3_0_beta_4.xml
new file mode 100644
index 0000000..b612884
--- /dev/null
+++ b/.idea/artifacts/util_js_0_3_0_beta_4.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/util_jvm_0_3_0_beta_4.xml b/.idea/artifacts/util_jvm_0_3_0_beta_4.xml
new file mode 100644
index 0000000..e64ce9a
--- /dev/null
+++ b/.idea/artifacts/util_jvm_0_3_0_beta_4.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/util_wasm_js_0_3_0_beta_4.xml b/.idea/artifacts/util_wasm_js_0_3_0_beta_4.xml
new file mode 100644
index 0000000..971f2c1
--- /dev/null
+++ b/.idea/artifacts/util_wasm_js_0_3_0_beta_4.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/util_wasm_wasi_0_3_0_beta_4.xml b/.idea/artifacts/util_wasm_wasi_0_3_0_beta_4.xml
new file mode 100644
index 0000000..ff662b4
--- /dev/null
+++ b/.idea/artifacts/util_wasm_wasi_0_3_0_beta_4.xml
@@ -0,0 +1,8 @@
+
+
+ $PROJECT_DIR$/build/libs
+
+
+
+
+
\ No newline at end of file
diff --git a/buildnumber.properties b/buildnumber.properties
index ba32cd2..4e78d91 100644
--- a/buildnumber.properties
+++ b/buildnumber.properties
@@ -1,3 +1,3 @@
#auto generated
-#Sun Jan 18 17:59:47 CET 2026
-build.number=111
+#Sun Jan 18 23:38:13 CET 2026
+build.number=114
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 840bafb..066964c 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -19,7 +19,7 @@ dependencyResolutionManagement {
versionCatalogs {
create("masterLibs") {
// from(files("gradle/masterlibs.versions.toml"))
- from("net.keinekekse:version-catalog:110")
+ from("net.keinekekse:version-catalog:113")
}
}
}
diff --git a/src/commonMain/kotlin/net/keinekekse/args/impl.kt b/src/commonMain/kotlin/net/keinekekse/args/impl.kt
index a8705ec..cedcab5 100644
--- a/src/commonMain/kotlin/net/keinekekse/args/impl.kt
+++ b/src/commonMain/kotlin/net/keinekekse/args/impl.kt
@@ -2,8 +2,28 @@ package net.keinekekse.args
import net.keinekekse.version.Version
+/**
+ * Terminates the currently running application with the specified exit code.
+ * This function does not return as it stops the application process.
+ *
+ * Works only for JVM and macOS-/Linux-native targets.
+ *
+ * @param rc The exit code to return when the application terminates. Defaults to 0,
+ * indicating normal termination.
+ * @return This function does not return as the application is terminated.
+ */
expect fun exitApplication(rc: Int = 0): Nothing
+/**
+ * Checks if standard input is ready for reading.
+ * This function is used to determine if there is data available to be read from standard input.
+ *
+ * Works only for JVM and macOS-/Linux-native targets.
+ *
+ * @return True if standard input is ready for reading, false otherwise.
+ */
+expect fun isStdInputReady(): Boolean
+
class VerifyParameterException(message: String) : RuntimeException(message)
fun showParsingError(msg: String): Nothing {
@@ -196,7 +216,12 @@ class CommandLine(
val resultSubCommand = parseSubCommand(remaining)
validateMandatoryParameters(resultSubCommand)
- if (resultSubCommand.additionalParameters.isNotEmpty() && remainingDescription == null) {
+ if (remainingDescription != null) {
+ val additionalFromPipe = getRemainingParametersFromPipe()
+ if (additionalFromPipe.isNotEmpty()) {
+ resultSubCommand.additionalParameters += additionalFromPipe
+ }
+ } else if (resultSubCommand.additionalParameters.isNotEmpty()) {
throw ArgumentParserException.noMoreOptions()
}
@@ -261,6 +286,19 @@ class CommandLine(
}
}
+ private fun getRemainingParametersFromPipe(): List {
+ if (isStdInputReady()) {
+ var input: String?
+ val pipe = mutableListOf()
+ do {
+ input = readlnOrNull()
+ input?.let { pipe.add(it) }
+ } while (!input.isNullOrEmpty())
+ return pipe
+ }
+ return emptyList()
+ }
+
private fun checkExecutors() {
globalSubCommand.obj.checkExecutors()
subCommands.values.forEach { it.checkExecutors() }
@@ -282,7 +320,7 @@ class CommandLine(
val globalParam = globalSubCommand.obj.params.isNotEmpty()
val remain = remainingDescription != null
- println("${programVersion.displayName} ${programVersion.version}, ${programVersion.copyright}\n")
+ println("${programVersion.displayName} ${programVersion.version} ${programVersion.copyright}\n")
println(programVersion.description)
println()
println(
@@ -346,7 +384,7 @@ class CommandLine(
}
private fun showVersion(): Nothing {
- println("${programVersion.displayName} ${programVersion.fullVersion}, ${programVersion.copyright}\n")
+ println("${programVersion.displayName} ${programVersion.fullVersion} ${programVersion.copyright}\n")
exitApplication(0)
}
diff --git a/src/commonMain/kotlin/net/keinekekse/util/Version.kt b/src/commonMain/kotlin/net/keinekekse/util/Version.kt
index 460fd0c..bb52d5d 100644
--- a/src/commonMain/kotlin/net/keinekekse/util/Version.kt
+++ b/src/commonMain/kotlin/net/keinekekse/util/Version.kt
@@ -3,14 +3,14 @@ package net.keinekekse.util
import net.keinekekse.version.Version
object VERSION: Version {
- override val version = "0.3.0-beta.2+build.111"
- override val fullVersion = "0.3.0-beta.2+build.111"
+ override val version = "0.3.0-beta.5+build.114"
+ override val fullVersion = "0.3.0-beta.5+build.114"
override val major = 0
override val minor = 3
override val patch = 0
- override val preRelease = "beta.2"
- override val buildMetadata = "build.111"
- override val buildDate = "2026-01-18T16:59:47.995114Z"
+ override val preRelease = "beta.5"
+ override val buildMetadata = "build.114"
+ override val buildDate = "2026-01-18T22:38:13.309402Z"
override val displayName = "Utility Library"
override val copyright = "Copyright (c) 2025-2026 Hartmut Bauer"
override val name = "util"
diff --git a/src/jvmMain/kotlin/net/keinekekse/args/impl.jvm.kt b/src/jvmMain/kotlin/net/keinekekse/args/impl.jvm.kt
new file mode 100644
index 0000000..b9dccb4
--- /dev/null
+++ b/src/jvmMain/kotlin/net/keinekekse/args/impl.jvm.kt
@@ -0,0 +1,9 @@
+package net.keinekekse.args
+
+import kotlin.system.exitProcess
+
+actual fun isStdInputReady(): Boolean =
+ (System.`in`.available() > 0)
+
+actual fun exitApplication(rc: Int): Nothing =
+ exitProcess(rc)
diff --git a/src/jvmMain/kotlin/net/keinekekse/args/priv.jvm.kt b/src/jvmMain/kotlin/net/keinekekse/args/priv.jvm.kt
deleted file mode 100644
index e2687e7..0000000
--- a/src/jvmMain/kotlin/net/keinekekse/args/priv.jvm.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package net.keinekekse.args
-
-import kotlin.system.exitProcess
-
-actual fun exitApplication(rc: Int): Nothing {
- exitProcess(rc)
-}
diff --git a/src/linuxX64Main/kotlin/net/keinekekse/args/priv.linuxX64.kt b/src/linuxX64Main/kotlin/net/keinekekse/args/priv.linuxX64.kt
deleted file mode 100644
index 4a5b50e..0000000
--- a/src/linuxX64Main/kotlin/net/keinekekse/args/priv.linuxX64.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package net.keinekekse.args
-
-import platform.posix.exit
-
-actual fun exitApplication(rc: Int): Nothing {
- exit(rc)
- return Unit as Nothing
-}
diff --git a/src/macosArm64Main/kotlin/net/keinekekse/args/priv.macosArm64.kt b/src/macosArm64Main/kotlin/net/keinekekse/args/priv.macosArm64.kt
deleted file mode 100644
index 4a5b50e..0000000
--- a/src/macosArm64Main/kotlin/net/keinekekse/args/priv.macosArm64.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package net.keinekekse.args
-
-import platform.posix.exit
-
-actual fun exitApplication(rc: Int): Nothing {
- exit(rc)
- return Unit as Nothing
-}
diff --git a/src/nativeMain/kotlin/net/keinekekse/args/impl.native.kt b/src/nativeMain/kotlin/net/keinekekse/args/impl.native.kt
new file mode 100644
index 0000000..aba074d
--- /dev/null
+++ b/src/nativeMain/kotlin/net/keinekekse/args/impl.native.kt
@@ -0,0 +1,22 @@
+package net.keinekekse.args
+
+import kotlinx.cinterop.*
+import platform.posix.*
+
+// macOS + Linux
+
+@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class)
+actual fun isStdInputReady(): Boolean {
+ return memScoped {
+ val pfd = alloc()
+ pfd.fd = STDIN_FILENO
+ pfd.events = POLLIN.toShort()
+ // timeout 0 means return immediately
+ poll(pfd.ptr.reinterpret(), 1u, 10) > 0
+ }
+}
+
+actual fun exitApplication(rc: Int): Nothing {
+ exit(rc)
+ return Unit as Nothing
+}
diff --git a/src/wasmWasiMain/kotlin/net/keinekekse/args/priv.wasmWasi.kt b/src/wasmWasiMain/kotlin/net/keinekekse/args/impl.wasmWasi.kt
similarity index 58%
rename from src/wasmWasiMain/kotlin/net/keinekekse/args/priv.wasmWasi.kt
rename to src/wasmWasiMain/kotlin/net/keinekekse/args/impl.wasmWasi.kt
index 66a21bd..71e7ed7 100644
--- a/src/wasmWasiMain/kotlin/net/keinekekse/args/priv.wasmWasi.kt
+++ b/src/wasmWasiMain/kotlin/net/keinekekse/args/impl.wasmWasi.kt
@@ -1,5 +1,9 @@
package net.keinekekse.args
+actual fun isStdInputReady(): Boolean {
+ TODO("Not implemented on this platform")
+}
+
actual fun exitApplication(rc: Int): Nothing {
TODO("Not implemented on this platform")
}
diff --git a/src/webMain/kotlin/net/keinekekse/args/impl.web.kt b/src/webMain/kotlin/net/keinekekse/args/impl.web.kt
new file mode 100644
index 0000000..024b93a
--- /dev/null
+++ b/src/webMain/kotlin/net/keinekekse/args/impl.web.kt
@@ -0,0 +1,11 @@
+package net.keinekekse.args
+
+// js + wasmJs
+
+actual fun isStdInputReady(): Boolean {
+ TODO("Not implemented for this platform")
+}
+
+actual fun exitApplication(rc: Int): Nothing {
+ TODO("Not implemented for this platform")
+}
diff --git a/src/webMain/kotlin/net/keinekekse/args/priv.web.kt b/src/webMain/kotlin/net/keinekekse/args/priv.web.kt
deleted file mode 100644
index af5033f..0000000
--- a/src/webMain/kotlin/net/keinekekse/args/priv.web.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package net.keinekekse.args
-
-actual fun exitApplication(rc: Int): Nothing {
- TODO("Not yet implemented") // js + wasmJs
-}