diff src/name/blackcap/osdep/Osdep.kt @ 0:895aa9a8d628

Initial commit.
author David Barts <n5jrn@me.com>
date Sat, 08 Feb 2020 08:46:17 -0800
parents
children 5ea54efa8e45
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/name/blackcap/osdep/Osdep.kt	Sat Feb 08 08:46:17 2020 -0800
@@ -0,0 +1,164 @@
+/*
+ * Recursively examines the specified directories for files that end in
+ * .<type>.osdep, where <type> is one of unix, mac, windows, or default.
+ * The file that matches the current OS type (or the type specified with
+ * the -t option)
+ */
+package name.blackcap.osdep
+
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.io.IOException
+import kotlin.collections.HashMap
+import kotlin.collections.HashSet
+import org.apache.commons.cli.CommandLine
+import org.apache.commons.cli.DefaultParser
+import org.apache.commons.cli.HelpFormatter
+import org.apache.commons.cli.Option
+import org.apache.commons.cli.Options
+import org.apache.commons.cli.ParseException
+
+var ostype = run {
+    val rawType = System.getProperty("os.name")?.toLowerCase()
+    if (rawType == null) {
+        "default"
+    } else if (rawType.contains("win")) {
+        "windows"
+    } else if (rawType.contains("mac")) {
+        "mac"
+    } else if (rawType.contains("nix") || rawType.contains("nux") || rawType.contains("aix") || rawType.contains("sunos")) {
+        "unix"
+    } else {
+        "default"
+    }
+}
+
+val MYNAME = "osdep"
+val MYEXT = ".osdep"
+
+val files = HashMap<String, HashSet<String>>()
+
+var errors = 0
+
+fun main(args: Array<String>) {
+    val options = Options().apply {
+        addOption("?", "help", false, "Print this help message.")
+        addOption("c", "clean", false, "Clean instead of copying.")
+        addOption("t", "type", true, "Specify OS type (default ${ostype}).")
+    }
+    var cmdLine: CommandLine? = null
+    try {
+        cmdLine = DefaultParser().parse(options, args)
+    } catch (e: ParseException) {
+        System.err.format("%s: %s%n", MYNAME, e.message)
+        System.exit(2)
+    }
+
+    if (cmdLine!!.hasOption("help")) {
+        val usage = MYNAME + " [--help] [--clean] [--type=type] directory [...]"
+        HelpFormatter().printHelp(usage, options, false);
+        System.exit(0);
+    }
+
+    val clOstype = cmdLine!!.getOptionValue("type")
+    if (clOstype != null) {
+        ostype = clOstype
+    }
+
+    for (arg in cmdLine.args) {
+        try {
+            File(arg).walk().forEach { build(it) }
+        } catch (e: Exception) {
+            handle(e, "cannot walk '${arg}'")
+        }
+    }
+
+    val process = if (cmdLine!!.hasOption("clean")) ::clean else ::copy
+    files.forEach {
+        process(it.key, it.value)
+    }
+    System.exit(if (errors == 0) 0 else 1)
+}
+
+fun build(file: File) {
+    if (!file.isFile()) {
+        return
+    }
+    val path = file.toString()
+    if (!path.endsWith(MYEXT)) {
+        return
+    }
+    val dot2 = path.lastIndexOf('.')
+    if (dot2 <= 0) {
+        return
+    }
+    val dot1 = path.lastIndexOf('.', dot2 - 1)
+    if (dot1 <= 0) {
+        return
+    }
+    val base = path.substring(0 .. (dot1 - 1))
+    val type = path.substring((dot1 + 1) .. (dot2 - 1))
+    println("base=${base}, type=${type}") /* debug */
+    val fset = files[base]
+    if (fset != null) {
+        fset.add(type)
+    } else {
+        files[base] = HashSet<String>().apply { add(type) }
+    }
+}
+
+fun copy(base: String, types: HashSet<String>) {
+    val type = if (ostype in types) ostype else "default"
+    if (type !in types) {
+        System.err.format("%s: type '%s' missing for '%s'%n", MYNAME, type, base)
+        errors += 1
+        return
+    }
+    copf("${base}.${type}${MYEXT}", base)
+}
+
+fun copf(source: String, target: String) {
+    val sf = File(source)
+    val tf = File(target)
+    try {
+        FileInputStream(sf).use { sfp ->
+            FileOutputStream(tf).use { tfp ->
+                sfp.copyTo(tfp)
+            }
+        }
+    } catch (e: Exception) {
+        handle(e, "cannot copy '${source}' to '${target}'")
+        return
+    }
+    try {
+        tf.setLastModified(sf.lastModified())
+    } catch (e: Exception) {
+        handle(e, "cannot set time of '${target}'")
+    }
+}
+
+fun clean(base: String, @Suppress("UNUSED_PARAMETER") ignored: HashSet<String>) {
+    val file = File(base)
+    if (file.exists()) {
+        try {
+            if (!file.delete()) {
+                System.err.format("%s: '%s' not deleted%n", MYNAME, base)
+                errors += 1
+            }
+        } catch (e: Exception) {
+            handle(e, "cannot delete '${base}'")
+        }
+    }
+}
+
+fun handle(exception: Exception, message: String) {
+    val fallback = when (exception) {
+        is SecurityException -> "security violation"
+        is IOException -> "I/O error"
+        else -> throw exception
+    }
+    System.err.format("%s: %s%n", MYNAME, exception.message ?: fallback)
+    System.err.format("%s: %s%n", MYNAME, message)
+    errors += 1
+}