hare

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

hosts.ha (3031B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use bufio;
      5 use encoding::utf8;
      6 use fs;
      7 use io;
      8 use memio;
      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 // Iterator through the host lines in a host file.
     21 export type iterator = struct {
     22 	handle: io::handle,
     23 };
     24 
     25 // Returned when an invalid host line was found.
     26 export type invalid = !void;
     27 
     28 // All possible errors returned from this module.
     29 export type error = !(io::error | invalid | utf8::invalid | ip::invalid
     30 	| fs::error);
     31 
     32 // Converts an [[error]] to a human-friendly representation.
     33 export fn strerror(err: error) const str = match (err) {
     34 case invalid =>
     35 	return "Host file format is invalid";
     36 case utf8::invalid =>
     37 	return "File is invalid UTF-8";
     38 case ip::invalid =>
     39 	return "IP address is invalid";
     40 case let err: io::error =>
     41 	return io::strerror(err);
     42 case let err: fs::error =>
     43 	return fs::strerror(err);
     44 };
     45 
     46 // Creates an [[iterator]] for a provided [[io::handle]] pointing to
     47 // the /etc/hosts file. The user should call [[next]] to iterate through
     48 // host lines.
     49 export fn iter(in: io::handle) iterator = iterator {
     50 	handle = in,
     51 };
     52 
     53 // Returns the next host line as a [[host]] type.
     54 export fn next(it: *iterator) (host | void | error) = for (true) {
     55 	const line = match (bufio::read_line(it.handle)) {
     56 	case io::EOF =>
     57 		return void;
     58 	case let line: []u8 =>
     59 		yield line;
     60 	};
     61 	defer free(line);
     62 	if (len(line) == 0 || line[0] == '#') {
     63 		continue;
     64 	};
     65 
     66 	const scanner = memio::fixed(line);
     67 	const tok = match (bufio::read_tok(&scanner, ' ', '\t')?) {
     68 	case io::EOF =>
     69 		return void;
     70 	case let tok: []u8 =>
     71 		yield tok;
     72 	};
     73 	defer free(tok);
     74 	const addr = ip::parse(strings::fromutf8(tok)?)?;
     75 
     76 	let names: []str = [];
     77 	for (true) {
     78 		const tok = match (bufio::read_tok(&scanner, ' ', '\t')?) {
     79 		case io::EOF =>
     80 			break;
     81 		case let tok: []u8 =>
     82 			yield tok;
     83 		};
     84 		if (len(tok) == 0) continue;
     85 
     86 		append(names, strings::fromutf8(tok)?);
     87 	};
     88 	if (len(names) == 0) {
     89 		return invalid;
     90 	};
     91 
     92 	return host {
     93 		addr = addr,
     94 		names = names,
     95 	};
     96 };
     97 
     98 // Looks up a slice of addresses from /etc/hosts.
     99 export fn lookup(name: const str) ([]ip::addr | error) = {
    100 	const file = os::open(PATH)?;
    101 	defer io::close(file)!;
    102 	let it = iter(file);
    103 	return iter_lookup(&it, name);
    104 };
    105 
    106 // Looks up a slice of addresses given an [[iterator]] to /etc/hosts.
    107 export fn iter_lookup(it: *iterator, name: const str) ([]ip::addr | error) = {
    108 	let addrs: []ip::addr = [];
    109 	for (true) match(next(it)?) {
    110 	case void => break;
    111 	case let h: host =>
    112 		defer finish(h);
    113 		for (let i = 0z; i < len(h.names); i += 1) {
    114 			if (h.names[i] == name) {
    115 				append(addrs, h.addr);
    116 			};
    117 		};
    118 	};
    119 	return addrs;
    120 };
    121 
    122 // Frees resources associated with a [[host]].
    123 export fn finish(host: host) void = {
    124 	for (let i = 0z; i < len(host.names); i += 1) {
    125 		free(host.names[i]);
    126 	};
    127 	free(host.names);
    128 };