scan.ha (1968B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use bufio; 5 use io; 6 use strings; 7 8 export type scanner = struct { 9 scan: bufio::scanner, 10 lineno: size, 11 section: str, 12 }; 13 14 // Creates an INI file scanner. Use [[next]] to read entries. The caller must 15 // call [[finish]] once they're done with this object. 16 export fn scan(in: io::handle) scanner = { 17 return scanner { 18 scan = bufio::newscanner(in), 19 lineno = 1, 20 ... 21 }; 22 }; 23 24 // Frees resources associated with a [[scanner]]. 25 export fn finish(sc: *scanner) void = { 26 bufio::finish(&sc.scan); 27 free(sc.section); 28 }; 29 30 // An entry in an INI file: (section, key, value). 31 export type entry = (const str, const str, const str); 32 33 // Duplicates an [[entry]]. Use [[entry_finish]] to get rid of it. 34 export fn entry_dup(ent: entry) entry = ( 35 strings::dup(ent.0), 36 strings::dup(ent.1), 37 strings::dup(ent.2), 38 ); 39 40 // Frees an [[entry]] previously duplicated with [[entry_dup]]. 41 export fn entry_finish(ent: entry) void = { 42 free(ent.0); 43 free(ent.1); 44 free(ent.2); 45 }; 46 47 // Returns the next entry from an INI file. The return value is borrowed from 48 // the [[scanner]]. Use [[entry_dup]] to retain a copy. 49 export fn next(sc: *scanner) (entry | io::EOF | error) = { 50 for (const line => bufio::scan_line(&sc.scan)?) { 51 defer sc.lineno += 1; 52 53 const line = strings::trim(line); 54 if (len(line) == 0 || strings::hasprefix(line, "#")) { 55 continue; 56 }; 57 58 if (strings::hasprefix(line, "[")) { 59 const end = match (strings::index(line, ']')) { 60 case let idx: size => 61 yield idx; 62 case void => 63 return sc.lineno: syntaxerr; 64 }; 65 free(sc.section); 66 sc.section = strings::dup(strings::sub(line, 1, end)); 67 continue; 68 }; 69 70 const eq = match (strings::index(line, '=')) { 71 case let idx: size => 72 yield idx; 73 case void => 74 return sc.lineno: syntaxerr; 75 }; 76 return ( 77 sc.section, 78 strings::sub(line, 0, eq), 79 strings::sub(line, eq + 1, strings::end), 80 ); 81 }; 82 83 return io::EOF; 84 };