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
114
115
116
117
118
119
120
121
122
|
// file : libbutl/command.mxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
#ifndef __cpp_modules_ts
#pragma once
#endif
#ifndef __cpp_lib_modules_ts
#include <map>
#include <string>
#include <cstddef> // size_t
#include <functional>
#endif
// Other includes.
#ifdef __cpp_modules_ts
export module butl.command;
#ifdef __cpp_lib_modules_ts
import std.core;
#endif
import butl.process;
import butl.optional;
#else
#include <libbutl/process.mxx>
#include <libbutl/optional.mxx>
#endif
#include <libbutl/export.hxx>
LIBBUTL_MODEXPORT namespace butl
{
// Run a process or a builtin, interpreting the command line as
// whitespace-separated, potentially quoted program path/builtin name,
// arguments, and redirects. Throw std::invalid_argument on the parsing
// error, ios::failure on the underlying OS error, process_error on the
// process running error and std::system_error on the builtin running error.
//
// To run a system utility rather than a builtin prefix its name with `^`,
// for example:
//
// ^cat --squeeze-blank file
//
// The process environment path is unused and must point to the empty
// process path.
//
// Currently only the following stdout redirects are supported:
//
// >file # Overwrite file.
// >>file # Append to file.
//
// In particular, the file descriptor cannot be specified. The file path can
// optionally be separated from '>' by whitespaces. Note that redirects are
// distinguished from arguments by the presence of leading '>' and prior to
// possible substitutions (so the redirect character cannot be the result of
// a substitution; see below).
//
// The relative redirect file paths are completed using the command
// current working directory. Note that if it is altered via the process
// environment, then the new value is used.
//
// The command line elements (program, arguments, etc) may optionally
// contain substitutions - variable names enclosed with the substitution
// symbol ('@' by default) - which are replaced with the corresponding
// variable values to produce the actual command. Variable names must not
// contain whitespaces and an attempt to substitute an unknown or a
// malformed variable is an error. Double substitution character ('@@' by
// default) is an escape sequence.
//
// If the variable map is absent, then '@' has no special meaning and is
// treated as a regular character.
//
// The callback function, if specified, is called prior to running the
// command process with the substituted command elements and including
// redirects which will be in the "canonical" form (single argument without
// space after '>'). The callback can be used, for example, for tracing the
// resulting command line, etc.
//
using command_substitution_map = std::map<std::string, std::string>;
using command_callback = void (const char* const args[], std::size_t n);
LIBBUTL_SYMEXPORT process_exit
command_run (const std::string& command,
const optional<process_env>& = nullopt,
const optional<command_substitution_map>& = nullopt,
char subst = '@',
const std::function<command_callback>& = {});
// Reusable substitution utility functions.
//
// Unlike command_run(), these support different opening and closing
// substitution characters (e.g., <name>). Note that unmatched closing
// characters are treated literally and there is no support for their
// escaping (which would only be necessary if we needed to support variable
// names containing the closing character).
// Perform substitutions in a string. The second argument should be the
// position of the openning substitution character in the passed string.
// Throw invalid_argument for a malformed substitution or an unknown
// variable name.
//
LIBBUTL_SYMEXPORT std::string
command_substitute (const std::string&, std::size_t,
const command_substitution_map&,
char open, char close);
// As above but using a callback instead of a map.
//
// Specifically, on success, the callback should substitute the specified
// variable in out by appending its value and returning true. On failure,
// the callback can either throw invalid_argument or return false, in which
// case the standard "unknown substitution variable ..." exception will be
// thrown.
//
using command_substitution_callback =
bool (const std::string& var, std::string& out);
LIBBUTL_SYMEXPORT std::string
command_substitute (const std::string&, std::size_t,
const std::function<command_substitution_callback>&,
char open, char close);
}
|