hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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 };