comparison 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
comparison
equal deleted inserted replaced
21:ea65ab890f66 22:07406c4af4a5
7 import kotlin.reflect.jvm.javaMethod 7 import kotlin.reflect.jvm.javaMethod
8 import kotlin.reflect.jvm.kotlinFunction 8 import kotlin.reflect.jvm.kotlinFunction
9 import kotlin.system.exitProcess 9 import kotlin.system.exitProcess
10 10
11 fun main(args: Array<String>) { 11 fun main(args: Array<String>) {
12 val NOPASSWORD = setOf<String>("help")
12 if (args.isEmpty()) { 13 if (args.isEmpty()) {
13 error("expecting subcommand") 14 openDatabase()
15 runInteractive()
16 }
17 val subcommand = args[0]
18 val scArgs = args.sliceArray(1 until args.size)
19 if (subcommand !in NOPASSWORD) {
20 openDatabase()
21 }
22 runNonInteractive(subcommand, scArgs)
23 }
24
25 fun openDatabase() {
26 try {
27 Database.default = Database.open()
28 } catch (e: DatabaseException) {
29 handleMessagedException(e)
14 exitProcess(2) 30 exitProcess(2)
15 } 31 }
16 val subcommand = args[0]; 32 }
17 val scArgs = args.sliceArray(1 until args.size) 33
18 Database.default = Database.open() 34 fun runNonInteractive(subcommand: String, scArgs: Array<String>): Unit {
19 runSubcommand(subcommand, scArgs) 35 try {
36 runSubcommand(subcommand, scArgs)
37 } catch (e: SubcommandException) {
38 handleSubcommandException(e)
39 exitProcess(e.status)
40 } catch (e: MessagedException) {
41 handleMessagedException(e)
42 exitProcess(1)
43 }
44 exitProcess(0)
45 }
46
47 fun runInteractive() {
48 val DISALLOWED = setOf<String>("password")
49 val QUIT = setOf<String>("exit", "quit")
50 var lastStatus = 0
51 while (true) {
52 val rawLine = System.console()?.readLine("passman> ") ?: break
53 val s = Shplitter()
54 s.feed(rawLine)
55 if (!s.complete) {
56 error("unterminated quoting")
57 lastStatus = 1
58 continue
59 }
60 val line = s.split().toList()
61 if (line.isEmpty()) {
62 continue
63 }
64 val subcommand = line.first()
65 val scArgs = line.drop(1).toTypedArray()
66 if (subcommand in QUIT) {
67 break
68 }
69 if (subcommand in DISALLOWED) {
70 error("$subcommand subcommand not allowed in interactive mode")
71 lastStatus = 2
72 continue
73 }
74 try {
75 runSubcommand(subcommand, scArgs)
76 lastStatus = 0
77 } catch(e: SubcommandException) {
78 handleSubcommandException(e)
79 lastStatus = e.status
80 } catch(e: MessagedException) {
81 handleMessagedException(e)
82 lastStatus = 1
83 }
84 }
85 println() // ensure shell prompt comes out on a line of its own
86 exitProcess(lastStatus)
20 } 87 }
21 88
22 fun runSubcommand(name: String, args: Array<String>): Unit { 89 fun runSubcommand(name: String, args: Array<String>): Unit {
23 val instance = getInstanceForClass(getClassForSubcommand(name)) 90 val instance = getInstanceForClass(getClassForSubcommand(name))
24 if (instance == null) { 91 if (instance == null) {
25 die("${see(name)} - unknown subcommand", 2) 92 throw SubcommandException(message = "${see(name)} - unknown subcommand", status = 2)
26 } else { 93 } else {
27 instance.run(args) 94 instance.run(args)
28 } 95 }
29 } 96 }
30 97
40 fun getInstanceForClass(klass: Class<Subcommand>?) = try { 107 fun getInstanceForClass(klass: Class<Subcommand>?) = try {
41 klass?.getDeclaredConstructor()?.newInstance() 108 klass?.getDeclaredConstructor()?.newInstance()
42 } catch (e: ReflectiveOperationException) { 109 } catch (e: ReflectiveOperationException) {
43 null 110 null
44 } 111 }
112
113 fun handleMessagedException(e: MessagedException) = error(e.message)
114
115 fun handleSubcommandException(e: SubcommandException): Unit {
116 if (e.message != null) {
117 error(e.message!!)
118 }
119 }