hare

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

wordexp.ha (1922B)


      1 // SPDX-License-Identifier: MIT
      2 // (c) Hare authors <https://harelang.org>
      3 // (c) 2005-2020 Rich Felker, et al
      4 // Based on the musl libc implementation
      5 
      6 use bufio;
      7 use io;
      8 use os;
      9 use os::exec;
     10 use strings;
     11 
     12 // Flags applicable to a [[wordexp]] operation.
     13 export type flag = enum uint {
     14 	NONE = 0,
     15 	// DOOFFS = (1 << 0),  // not implemented
     16 	// APPEND = (1 << 1),  // not implemented
     17 	// REUSE  = (1 << 3),  // not implemented
     18 	// NOCMD   = (1 << 2), // not implemented
     19 	SHOWERR = (1 << 4),
     20 	UNDEF   = (1 << 5),
     21 };
     22 
     23 // Performs shell expansion and word splitting on the provided string, returning
     24 // a list of expanded words, similar to POSIX wordexp(3). Note that this
     25 // function, by design, will execute arbitrary commands from the input string.
     26 //
     27 // Pass the return value to [[strings::freeall]] to free resources associated
     28 // with the return value.
     29 export fn wordexp(s: str, flags: flag = flag::NONE) ([]str | error) = {
     30 	const (rd, wr) = exec::pipe();
     31 
     32 	// "x" is added to handle the list of expanded words being empty
     33 	const cmd = exec::cmd("/bin/sh",
     34 		if (flags & flag::UNDEF != 0) "-uc" else "-c",
     35 		`eval "printf %s\\\\0 x $1"`, "sh", s)!;
     36 	exec::unsetenv(&cmd, "IFS")!;
     37 	exec::addfile(&cmd, os::stdout_file, wr);
     38 	if (flags & flag::SHOWERR == 0) {
     39 		exec::addfile(&cmd, os::stderr_file, exec::nullfd);
     40 	};
     41 	const child = exec::start(&cmd)!;
     42 	io::close(wr)!;
     43 
     44 	const scan = bufio::newscanner(rd);
     45 	defer bufio::finish(&scan);
     46 
     47 	match (bufio::scan_string(&scan, "\0")?) {
     48 	case io::EOF =>
     49 		return sh_error;
     50 	case => void; // Discard the first "x" argument
     51 	};
     52 
     53 	let words: []str = [];
     54 	for (true) {
     55 		match (bufio::scan_string(&scan, "\0")?) {
     56 		case io::EOF => break;
     57 		case let word: const str =>
     58 			append(words, strings::dup(word));
     59 		};
     60 	};
     61 
     62 	io::close(rd)!;
     63 	const st = exec::wait(&child)!;
     64 	match (exec::check(&st)) {
     65 	case !exec::exit_status =>
     66 		return sh_error;
     67 	case void =>
     68 		return words;
     69 	};
     70 };