hosts.ha (2420B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use bufio; 5 use errors; 6 use encoding::utf8; 7 use fs; 8 use io; 9 use net::ip; 10 use os; 11 use strings; 12 13 // Represents a host line in /etc/hosts, guaranteed to have at least a single 14 // name. The first name is the canonical one. 15 export type host = struct { 16 addr: ip::addr, 17 names: []str, 18 }; 19 20 export type reader = struct { 21 scan: bufio::scanner, 22 names: []str, 23 }; 24 25 // Read from an /etc/hosts-formatted file. Call [[next]] to enumerate entries 26 // and [[finish]] to free state associated with the [[reader]]. 27 export fn read(in: io::handle) reader = { 28 return reader { 29 scan = bufio::newscanner(in), 30 names = [], 31 }; 32 }; 33 34 // Frees resources associated with a [[reader]]. 35 export fn finish(rd: *reader) void = { 36 bufio::finish(&rd.scan); 37 free(rd.names); 38 }; 39 40 // Returns the next host line as a [[host]] type. The host value is borrowed 41 // from the [[reader]]; see [[host_dup]] to extend its lifetime. 42 export fn next(rd: *reader) (host | done | error) = { 43 for (const line => bufio::scan_line(&rd.scan)?) { 44 if (len(line) == 0 || strings::hasprefix(line, "#")) { 45 continue; 46 }; 47 48 const tok = strings::tokenize(line, " \t"); 49 const addr = strings::next_token(&tok) as str; 50 const addr = ip::parse(addr)?; 51 52 rd.names = rd.names[..0]; 53 54 for (const tok => strings::next_token(&tok)) { 55 if (len(tok) == 0) { 56 continue; 57 }; 58 append(rd.names, tok)!; 59 }; 60 61 if (len(rd.names) == 0) { 62 return invalid; 63 }; 64 65 return host { 66 addr = addr, 67 names = rd.names, 68 }; 69 }; 70 71 return done; 72 }; 73 74 // Looks up a slice of addresses from /etc/hosts. The caller must free the 75 // return value. 76 export fn lookup(name: const str) ([]ip::addr | error) = { 77 const file = os::open(PATH)?; 78 defer io::close(file)!; 79 80 const rd = read(file); 81 defer finish(&rd); 82 return _lookup(&rd, name); 83 }; 84 85 fn _lookup(rd: *reader, name: const str) ([]ip::addr | error) = { 86 let addrs: []ip::addr = []; 87 for (const host => next(rd)?) { 88 for (const cand .. host.names) { 89 if (cand == name) { 90 append(addrs, host.addr)!; 91 }; 92 }; 93 }; 94 95 if (len(addrs) != 0) { 96 return addrs; 97 }; 98 99 return []; 100 }; 101 102 // Duplicates a [[host]] value. 103 export fn host_dup(src: *host) host = { 104 return host { 105 addr = src.addr, 106 names = strings::dupall(src.names), 107 }; 108 }; 109 110 // Frees resources associated with a [[host]]. 111 export fn host_finish(host: *host) void = { 112 strings::freeall(host.names); 113 };