Mercurial > cgi-bin > hgweb.cgi > PassMan
view src/main/kotlin/name/blackcap/passman/Main.kt @ 22:07406c4af4a5
More interactive mode stuff.
author | David Barts <n5jrn@me.com> |
---|---|
date | Tue, 02 Jul 2024 17:34:52 -0700 |
parents | ea65ab890f66 |
children | af86b8e0b88c |
line wrap: on
line source
package name.blackcap.passman import java.io.BufferedReader import java.io.InputStreamReader import java.util.* import java.util.stream.Collectors import kotlin.reflect.jvm.javaMethod import kotlin.reflect.jvm.kotlinFunction import kotlin.system.exitProcess fun main(args: Array<String>) { val NOPASSWORD = setOf<String>("help") if (args.isEmpty()) { openDatabase() runInteractive() } val subcommand = args[0] val scArgs = args.sliceArray(1 until args.size) if (subcommand !in NOPASSWORD) { openDatabase() } runNonInteractive(subcommand, scArgs) } fun openDatabase() { try { Database.default = Database.open() } catch (e: DatabaseException) { handleMessagedException(e) exitProcess(2) } } fun runNonInteractive(subcommand: String, scArgs: Array<String>): Unit { try { runSubcommand(subcommand, scArgs) } catch (e: SubcommandException) { handleSubcommandException(e) exitProcess(e.status) } catch (e: MessagedException) { handleMessagedException(e) exitProcess(1) } exitProcess(0) } fun runInteractive() { val DISALLOWED = setOf<String>("password") val QUIT = setOf<String>("exit", "quit") var lastStatus = 0 while (true) { val rawLine = System.console()?.readLine("passman> ") ?: break val s = Shplitter() s.feed(rawLine) if (!s.complete) { error("unterminated quoting") lastStatus = 1 continue } val line = s.split().toList() if (line.isEmpty()) { continue } val subcommand = line.first() val scArgs = line.drop(1).toTypedArray() if (subcommand in QUIT) { break } if (subcommand in DISALLOWED) { error("$subcommand subcommand not allowed in interactive mode") lastStatus = 2 continue } try { runSubcommand(subcommand, scArgs) lastStatus = 0 } catch(e: SubcommandException) { handleSubcommandException(e) lastStatus = e.status } catch(e: MessagedException) { handleMessagedException(e) lastStatus = 1 } } println() // ensure shell prompt comes out on a line of its own exitProcess(lastStatus) } fun runSubcommand(name: String, args: Array<String>): Unit { val instance = getInstanceForClass(getClassForSubcommand(name)) if (instance == null) { throw SubcommandException(message = "${see(name)} - unknown subcommand", status = 2) } else { instance.run(args) } } fun getClassForSubcommand(name: String): Class<Subcommand>? = try { val shortName = name.replace('-', '_') .lowercase() .replaceFirstChar { it.titlecase(Locale.getDefault()) } Class.forName("$MAIN_PACKAGE.${shortName}Subcommand") as? Class<Subcommand> } catch (e: ClassNotFoundException) { null } fun getInstanceForClass(klass: Class<Subcommand>?) = try { klass?.getDeclaredConstructor()?.newInstance() } catch (e: ReflectiveOperationException) { null } fun handleMessagedException(e: MessagedException) = error(e.message) fun handleSubcommandException(e: SubcommandException): Unit { if (e.message != null) { error(e.message!!) } }