diff options
| -rw-r--r-- | imp.ha | 90 |
1 files changed, 89 insertions, 1 deletions
@@ -5,7 +5,11 @@ use os::exec; use io; use strings; use memio; +use bufio; use format::ini; +use encoding::utf8; +use unix::tty; +use errors; let verbose: bool = false; @@ -32,6 +36,8 @@ export fn main() void = { ('v', "Verbose mode"), ('l', "List the accounts"), ('p', "Display the account passwords"), + ('c', "Print ini config from parsed accounts"), + ('a', "Add an account"), ('f', "file", "The accounts file to use (IMP_FILE by default)"), ('g', "groups...", "Filters by a comma-separated list of groups"), ('m', "matches...", "Filters by a comma-separated list of matches"), @@ -53,11 +59,18 @@ export fn main() void = { let accfilter = filter {...}; + let add = false; + let printconf = false; + for (let i = 0z; i < len(cmd.opts); i += 1) { let opt = cmd.opts[i]; switch (opt.0) { case 'v' => verbose = true; + case 'a' => + add = true; + case 'c' => + printconf = true; case 'p' => displaypass = true; case 'l' => @@ -112,9 +125,35 @@ export fn main() void = { fmt::fatal("Insufficient memory"); }; + if (add) { + const acc = account { + ... + }; + const n = getinput("name: ")!; + const u = getinput("user: ")!; + const p = getinput("pass: ", true)!; + const cp = getinput("confirm pass: ", true)!; + if (p != cp) { + fmt::fatal("Passwords don't match"); + }; + if (n == "" || u == "" || p == "") { + free(n); + free(u); + free(p); + } else { + acc.name = n; + acc.user = u; + acc.pass = p; + append(accounts, acc)!; + }; + }; + accounts = accs_filter(accounts, accfilter); defer accounts_free(accounts); - if (list) { + + if (printconf) { + printaccs(os::stdout, accounts); + } else if (list) { for (let i = 0z; i < len(accounts); i += 1) { let acc = accounts[i]; fmt::println(acc.name)!; @@ -271,6 +310,24 @@ fn parse(data: []u8) ([]account | format::ini::error | nomem) = { return accounts; }; +// Returns the accounts as an ini string. The return value must be freed. +fn printaccs(h: io::handle, accs: []account) void = { + for (let i = 0z; i < len(accs); i += 1) { + const acc = accs[i]; + fmt::fprintf(h, "[{}]\nuser={}\npass={}\n", acc.name, acc.user, + acc.pass)!; + if (acc.notes != "") { + fmt::fprintf(h, "notes={}\n", acc.notes)!; + }; + if (acc.url != "") { + fmt::fprintf(h, "match={}\n", acc.url)!; + }; + if (i < len(accs) - 1) { + fmt::fprint(h, "\n")!; + }; + }; +}; + fn setfieldaccount(acc: *account, key: str, val: str) void = { key = strings::trim(key, ' '); defer free(key); @@ -329,3 +386,34 @@ fn decrypt(file: str) ([]u8 | os::exec::error | io::error | nomem) = { return out; }; + +fn readin(termf: io::file) (str | io::error | utf8::invalid | nomem) = { + const scanner = bufio::newscanner(termf); + defer bufio::finish(&scanner); + const line = match (bufio::scan_line(&scanner)?) { + case io::EOF => + yield ""; + case let line: const str => + yield line; + }; + return strings::dup(line)?; +}; + +// Gets user input from the tty (supports pipes) +fn getinput(text: str = "", noecho: bool = false) (str | tty::error | io::error + | utf8::invalid | errors::error | nomem) = { + + let termf = tty::open()?; + defer io::close(termf)!; + + fmt::fprintf(termf, "{}", text)!; + + if (noecho) { + const termios = tty::termios_query(termf)?; + tty::noecho(&termios)?; + defer tty::termios_restore(&termios); + defer fmt::fprintln(termf)!; + return readin(termf)?; + }; + return readin(termf)?; +}; |
