hare

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

reader.ha (3804B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use bufio;
      5 use io;
      6 use net::ip;
      7 use strconv;
      8 use strings;
      9 
     10 export type reader = struct {
     11 	scan: bufio::scanner,
     12 
     13 	// Only one of these is valid at a time (return values from [[next]] are
     14 	// borrowed from this).
     15 	union {
     16 		addr_list: []ip::addr,
     17 		subnet_list: []ip::subnet,
     18 		str_list: []str,
     19 	},
     20 
     21 	options: options,
     22 };
     23 
     24 // Reads an /etc/resolv.conf-formatted file from the provided I/O handle. Use
     25 // [[next]] to enumerate directives from the file and pass the return value to
     26 // [[finish]] to free resources associated with the reader.
     27 export fn read(in: io::handle) reader = {
     28 	return reader {
     29 		scan = bufio::newscanner(in),
     30 		...
     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.addr_list);
     38 };
     39 
     40 // Reads the next [[parameter]] from a resolv.conf [[reader]]. The return value
     41 // is borrowed from the [[reader]].
     42 export fn next(rd: *reader) (parameter | io::EOF | error) = {
     43 	for (const line => bufio::scan_line(&rd.scan)?) {
     44 		if (strings::hasprefix(line, '#') || strings::hasprefix(line, ';')) {
     45 			continue;
     46 		};
     47 		if (len(line) == 0) {
     48 			continue;
     49 		};
     50 
     51 		const tok = strings::tokenize(line, " \t");
     52 
     53 		const name = match (strings::next_token(&tok)) {
     54 		case let name: str =>
     55 			yield name;
     56 		case done =>
     57 			continue;
     58 		};
     59 
     60 		const val = switch (name) {
     61 		case "nameserver" =>
     62 			yield parse_addr(rd, &tok)?;
     63 		case "search" =>
     64 			yield parse_str_list(rd, &tok)?;
     65 		case "sortlist" =>
     66 			yield parse_subnet_list(rd, &tok)?;
     67 		case "options" =>
     68 			yield parse_options(rd, &tok)?;
     69 		case =>
     70 			continue;
     71 		};
     72 
     73 		return parameter {
     74 			name = name,
     75 			value = val,
     76 		};
     77 	};
     78 
     79 	return io::EOF;
     80 };
     81 
     82 fn parse_addr(rd: *reader, tok: *strings::tokenizer) (value | error) = {
     83 	const addr = match (strings::next_token(tok)) {
     84 	case let addr: str =>
     85 		yield addr;
     86 	case done =>
     87 		return invalid;
     88 	};
     89 
     90 	return ip::parse(addr)?;
     91 };
     92 
     93 fn parse_subnet_list(rd: *reader, tok: *strings::tokenizer) (value | error) = {
     94 	rd.subnet_list = rd.subnet_list[..0];
     95 
     96 	for (const tok => strings::next_token(tok)) {
     97 		if (len(tok) == 0) {
     98 			continue;
     99 		};
    100 
    101 		const subnet = ip::parsecidr(tok)?;
    102 		append(rd.subnet_list, subnet)!;
    103 	};
    104 
    105 	return rd.subnet_list;
    106 };
    107 
    108 fn parse_str_list(rd: *reader, tok: *strings::tokenizer) (value | error) = {
    109 	rd.str_list = rd.str_list[..0];
    110 
    111 	for (const tok => strings::next_token(tok)) {
    112 		if (len(tok) == 0) {
    113 			continue;
    114 		};
    115 		append(rd.str_list, tok)!;
    116 	};
    117 
    118 	return rd.str_list;
    119 };
    120 
    121 fn parse_options(rd: *reader, tok: *strings::tokenizer) (value | error) = {
    122 	rd.options = DEFAULT_OPTIONS;
    123 	let opts = &rd.options;
    124 
    125 	for (const tok => strings::next_token(tok)) {
    126 		if (len(tok) == 0) {
    127 			continue;
    128 		};
    129 
    130 		const (name, val) = strings::cut(tok, ":");
    131 		switch (name) {
    132 		case "debug" =>
    133 			opts.debug = true;
    134 		case "ndots" =>
    135 			match (strconv::stou(val)) {
    136 			case let u: uint =>
    137 				opts.ndots = u;
    138 			case =>
    139 				return invalid;
    140 			};
    141 		case "timeout" =>
    142 			match (strconv::stou(val)) {
    143 			case let u: uint =>
    144 				opts.timeout = u;
    145 			case =>
    146 				return invalid;
    147 			};
    148 		case "attempts" =>
    149 			match (strconv::stou(val)) {
    150 			case let u: uint =>
    151 				opts.attempts = u;
    152 			case =>
    153 				return invalid;
    154 			};
    155 		case "rotate" =>
    156 			opts.rotate = true;
    157 		case "no-aaaa" =>
    158 			opts.no_aaaa = true;
    159 		case "no-check-names" =>
    160 			opts.no_check_names = true;
    161 		case "inet6" =>
    162 			opts.inet6 = true;
    163 		case "edns0" =>
    164 			opts.edns0 = true;
    165 		case "single-request" =>
    166 			opts.single_request = true;
    167 		case "no-tld-query" =>
    168 			opts.no_tld_query = true;
    169 		case "use-vc" =>
    170 			opts.use_vc = true;
    171 		case "no-reload" =>
    172 			opts.no_reload = true;
    173 		case "trust-ad" =>
    174 			opts.trust_ad = true;
    175 		case => void;
    176 		};
    177 	};
    178 
    179 	return opts;
    180 };