File: hosts.ha

package info (click to toggle)
hare 0.24.2-4
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,756 kB
  • sloc: asm: 1,180; sh: 119; makefile: 116; lisp: 99
file content (113 lines) | stat: -rw-r--r-- 2,418 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>

use bufio;
use errors;
use encoding::utf8;
use fs;
use io;
use net::ip;
use os;
use strings;

// Represents a host line in /etc/hosts, guaranteed to have at least a single
// name. The first name is the canonical one.
export type host = struct {
	addr: ip::addr,
	names: []str,
};

export type reader = struct {
	scan: bufio::scanner,
	names: []str,
};

// Read from an /etc/hosts-formatted file. Call [[next]] to enumerate entries
// and [[finish]] to free state associated with the [[reader]].
export fn read(in: io::handle) reader = {
	return reader {
		scan = bufio::newscanner(in),
		names = [],
	};
};

// Frees resources associated with a [[reader]].
export fn finish(rd: *reader) void = {
	bufio::finish(&rd.scan);
	free(rd.names);
};

// Returns the next host line as a [[host]] type. The host value is borrowed
// from the [[reader]]; see [[host_dup]] to extend its lifetime.
export fn next(rd: *reader) (host | done | error) = {
	for (const line => bufio::scan_line(&rd.scan)?) {
		if (len(line) == 0 || strings::hasprefix(line, "#")) {
			continue;
		};

		const tok = strings::tokenize(line, " \t");
		const addr = strings::next_token(&tok) as str;
		const addr = ip::parse(addr)?;

		rd.names = rd.names[..0];

		for (const tok => strings::next_token(&tok)) {
			if (len(tok) == 0) {
				continue;
			};
			append(rd.names, tok);
		};

		if (len(rd.names) == 0) {
			return invalid;
		};

		return host {
			addr = addr,
			names = rd.names,
		};
	};

	return done;
};

// Looks up a slice of addresses from /etc/hosts. The caller must free the
// return value.
export fn lookup(name: const str) ([]ip::addr | error) = {
	const file = os::open(PATH)?;
	defer io::close(file)!;

	const rd = read(file);
	defer finish(&rd);
	return _lookup(&rd, name);
};

fn _lookup(rd: *reader, name: const str) ([]ip::addr | error) = {
	let addrs: []ip::addr = [];
	for (const host => next(rd)?) {
		for (const cand .. host.names) {
			if (cand == name) {
				append(addrs, host.addr);
			};
		};
	};

	if (len(addrs) != 0) {
		return addrs;
	};

	return [];
};

// Duplicates a [[host]] value.
export fn host_dup(src: *host) host = {
	return host {
		addr = src.addr,
		names = strings::dupall(src.names),
	};
};

// Frees resources associated with a [[host]].
export fn host_finish(host: *host) void = {
	strings::freeall(host.names);
};