Mercurial > cgi-bin > hgweb.cgi > PassMan
view src/main/kotlin/name/blackcap/passman/Main.kt @ 28:287eadf5ab30 default tip
Check for timeouts inside subcommands while in interactive mode as well.
author | David Barts <n5jrn@me.com> |
---|---|
date | Wed, 31 Jul 2024 11:21:18 -0700 |
parents | 3a3067ba673b |
children |
line wrap: on
line source
package name.blackcap.passman import java.util.* import kotlin.system.exitProcess fun main(args: Array<String>) { val NOPASSWORD = setOf<String>("help", "quit") 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") val MAX_TIME_MILLIS = 10L * 60L * 1000L var lastStatus = 0 println("This is PassMan interactive mode. Type help for help.") while (true) { val beforeRead = System.currentTimeMillis() val rawLine = System.console()?.readLine("passman> ") if (rawLine == null) { println() // ensure shell prompt comes out on a line of its own break } if (System.currentTimeMillis() - beforeRead > MAX_TIME_MILLIS) { error("time limit exceeded, goodbye!") lastStatus = 1 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 } val beforeRun = System.currentTimeMillis() try { runSubcommand(subcommand, scArgs) lastStatus = 0 } catch(e: SubcommandException) { handleSubcommandException(e) lastStatus = e.status } catch(e: MessagedException) { handleMessagedException(e) lastStatus = 1 } if (System.currentTimeMillis() - beforeRun > MAX_TIME_MILLIS) { error("time limit exceeded, goodbye!") lastStatus = 1 break } } 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!!) } }