1//===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8// FuzzerCommand represents a command to run in a subprocess.  It allows callers
9// to manage command line arguments and output and error streams.
10//===----------------------------------------------------------------------===//
11
12#ifndef LLVM_FUZZER_COMMAND_H
13#define LLVM_FUZZER_COMMAND_H
14
15#include "FuzzerDefs.h"
16#include "FuzzerIO.h"
17
18#include <algorithm>
19#include <sstream>
20#include <string>
21#include <vector>
22
23namespace fuzzer {
24
25class Command final {
26public:
27  // This command line flag is used to indicate that the remaining command line
28  // is immutable, meaning this flag effectively marks the end of the mutable
29  // argument list.
30  static inline const char *ignoreRemainingArgs() {
31    return "-ignore_remaining_args=1";
32  }
33
34  Command() : CombinedOutAndErr(false) {}
35
36  explicit Command(const Vector<std::string> &ArgsToAdd)
37      : Args(ArgsToAdd), CombinedOutAndErr(false) {}
38
39  explicit Command(const Command &Other)
40      : Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr),
41        OutputFile(Other.OutputFile) {}
42
43  Command &operator=(const Command &Other) {
44    Args = Other.Args;
45    CombinedOutAndErr = Other.CombinedOutAndErr;
46    OutputFile = Other.OutputFile;
47    return *this;
48  }
49
50  ~Command() {}
51
52  // Returns true if the given Arg is present in Args.  Only checks up to
53  // "-ignore_remaining_args=1".
54  bool hasArgument(const std::string &Arg) const {
55    auto i = endMutableArgs();
56    return std::find(Args.begin(), i, Arg) != i;
57  }
58
59  // Gets all of the current command line arguments, **including** those after
60  // "-ignore-remaining-args=1".
61  const Vector<std::string> &getArguments() const { return Args; }
62
63  // Adds the given argument before "-ignore_remaining_args=1", or at the end
64  // if that flag isn't present.
65  void addArgument(const std::string &Arg) {
66    Args.insert(endMutableArgs(), Arg);
67  }
68
69  // Adds all given arguments before "-ignore_remaining_args=1", or at the end
70  // if that flag isn't present.
71  void addArguments(const Vector<std::string> &ArgsToAdd) {
72    Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end());
73  }
74
75  // Removes the given argument from the command argument list.  Ignores any
76  // occurrences after "-ignore_remaining_args=1", if present.
77  void removeArgument(const std::string &Arg) {
78    auto i = endMutableArgs();
79    Args.erase(std::remove(Args.begin(), i, Arg), i);
80  }
81
82  // Like hasArgument, but checks for "-[Flag]=...".
83  bool hasFlag(const std::string &Flag) const {
84    std::string Arg("-" + Flag + "=");
85    auto IsMatch = [&](const std::string &Other) {
86      return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
87    };
88    return std::any_of(Args.begin(), endMutableArgs(), IsMatch);
89  }
90
91  // Returns the value of the first instance of a given flag, or an empty string
92  // if the flag isn't present.  Ignores any occurrences after
93  // "-ignore_remaining_args=1", if present.
94  std::string getFlagValue(const std::string &Flag) const {
95    std::string Arg("-" + Flag + "=");
96    auto IsMatch = [&](const std::string &Other) {
97      return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
98    };
99    auto i = endMutableArgs();
100    auto j = std::find_if(Args.begin(), i, IsMatch);
101    std::string result;
102    if (j != i) {
103      result = j->substr(Arg.length());
104    }
105    return result;
106  }
107
108  // Like AddArgument, but adds "-[Flag]=[Value]".
109  void addFlag(const std::string &Flag, const std::string &Value) {
110    addArgument("-" + Flag + "=" + Value);
111  }
112
113  // Like RemoveArgument, but removes "-[Flag]=...".
114  void removeFlag(const std::string &Flag) {
115    std::string Arg("-" + Flag + "=");
116    auto IsMatch = [&](const std::string &Other) {
117      return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
118    };
119    auto i = endMutableArgs();
120    Args.erase(std::remove_if(Args.begin(), i, IsMatch), i);
121  }
122
123  // Returns whether the command's stdout is being written to an output file.
124  bool hasOutputFile() const { return !OutputFile.empty(); }
125
126  // Returns the currently set output file.
127  const std::string &getOutputFile() const { return OutputFile; }
128
129  // Configures the command to redirect its output to the name file.
130  void setOutputFile(const std::string &FileName) { OutputFile = FileName; }
131
132  // Returns whether the command's stderr is redirected to stdout.
133  bool isOutAndErrCombined() const { return CombinedOutAndErr; }
134
135  // Sets whether to redirect the command's stderr to its stdout.
136  void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; }
137
138  // Returns a string representation of the command.  On many systems this will
139  // be the equivalent command line.
140  std::string toString() const {
141    std::stringstream SS;
142    for (auto arg : getArguments())
143      SS << arg << " ";
144    if (hasOutputFile())
145      SS << ">" << getOutputFile() << " ";
146    if (isOutAndErrCombined())
147      SS << "2>&1 ";
148    std::string result = SS.str();
149    if (!result.empty())
150      result = result.substr(0, result.length() - 1);
151    return result;
152  }
153
154private:
155  Command(Command &&Other) = delete;
156  Command &operator=(Command &&Other) = delete;
157
158  Vector<std::string>::iterator endMutableArgs() {
159    return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
160  }
161
162  Vector<std::string>::const_iterator endMutableArgs() const {
163    return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
164  }
165
166  // The command arguments.  Args[0] is the command name.
167  Vector<std::string> Args;
168
169  // True indicates stderr is redirected to stdout.
170  bool CombinedOutAndErr;
171
172  // If not empty, stdout is redirected to the named file.
173  std::string OutputFile;
174};
175
176} // namespace fuzzer
177
178#endif // LLVM_FUZZER_COMMAND_H
179