view src/name/blackcap/exifwasher/exiv2/Initialize.kt @ 2:efd9fe2d70d7

Rationalize exceptions, code whitelist, add command-line tool.
author David Barts <n5jrn@me.com>
date Wed, 01 Apr 2020 14:23:54 -0700
parents 42277ce58ace
children 9ac6136c710c
line wrap: on
line source

/*
 * Logic common to initializing all external (JNI) classes goes here.
 */
package name.blackcap.exifwasher.exiv2

import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.jar.JarEntry
import java.util.jar.JarFile
import name.blackcap.exifwasher.LF_DIR
import name.blackcap.exifwasher.OS

object Initialize {
    private var initialized = false

    public fun libraries() {
        /* no-op if already initialized */
        if (initialized) {
            return
        }

        /* use the appropriate binary for the system we're on */
        val subdir = when(OS.type) {
            OS.UNIX -> "linux"
            OS.MAC -> "mac"
            OS.WINDOWS -> "windows"
            OS.OTHER -> throw RuntimeException("unsupported OS!")
        }
        val ext = when(OS.type) {
            OS.UNIX -> "so"
            OS.MAC -> "dylib"
            OS.WINDOWS -> "dll"
            OS.OTHER -> throw RuntimeException("unsupported OS!")
        }

        /* extract to scratch files, if needed, then load */
        val klass = Initialize::class.java
        var myJar = JarFile(File(klass.getProtectionDomain().getCodeSource().getLocation().toURI()))
        for (base in arrayOf("libexiv2", "libjni")) {
            val eBase = "${base}.${ext}"
            val sPath = "name/blackcap/exifwasher/binaries/${subdir}/${eBase}"
            val source = myJar.getJarEntry(sPath)
            if (source == null) {
                die("${sPath} not found in jar")
            }
            val target = File(LF_DIR, eBase)
            val tPath = target.absolutePath
            try {
                if (!target.exists() || target.lastModified() < source.getTime()) {
                    myJar.getInputStream(source).use { sfp ->
                        FileOutputStream(target).use { tfp ->
                            sfp.copyTo(tfp)
                        }
                    }
                    target.setExecutable(true)
                    target.setLastModified(source.getTime())
                }
            } catch (e: IOException) {
                val message = e.message ?: "I/O error"
                die("unable to create ${tPath}: ${message}")
            }
            try {
                System.load(tPath)
            } catch (e: UnsatisfiedLinkError) {
                val message = e.message ?: "unsatisfied link"
                die("unable to link ${tPath}: ${message}")
            }
        }

        initialized = true
    }

    private fun die(message: String) {
        /* should open a dialog */
        System.err.println(message)
        System.exit(1)
    }
}