1// Copyright 2015 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "commandlineflags.h"
16
17#include <cctype>
18#include <cstdlib>
19#include <cstring>
20#include <iostream>
21#include <limits>
22
23namespace benchmark {
24// Parses 'str' for a 32-bit signed integer. If successful, writes
25// the result to *value and returns true; otherwise leaves *value
26// unchanged and returns false.
27bool ParseInt32(const std::string& src_text, const char* str, int32_t* value) {
28 // Parses the environment variable as a decimal integer.
29 char* end = nullptr;
30 const long long_value = strtol(str, &end, 10); // NOLINT
31
32 // Has strtol() consumed all characters in the string?
33 if (*end != '\0') {
34 // No - an invalid character was encountered.
35 std::cerr << src_text << " is expected to be a 32-bit integer, "
36 << "but actually has value \"" << str << "\".\n";
37 return false;
38 }
39
40 // Is the parsed value in the range of an Int32?
41 const int32_t result = static_cast<int32_t>(long_value);
42 if (long_value == std::numeric_limits<long>::max() ||
43 long_value == std::numeric_limits<long>::min() ||
44 // The parsed value overflows as a long. (strtol() returns
45 // LONG_MAX or LONG_MIN when the input overflows.)
46 result != long_value
47 // The parsed value overflows as an Int32.
48 ) {
49 std::cerr << src_text << " is expected to be a 32-bit integer, "
50 << "but actually has value \"" << str << "\", "
51 << "which overflows.\n";
52 return false;
53 }
54
55 *value = result;
56 return true;
57}
58
59// Parses 'str' for a double. If successful, writes the result to *value and
60// returns true; otherwise leaves *value unchanged and returns false.
61bool ParseDouble(const std::string& src_text, const char* str, double* value) {
62 // Parses the environment variable as a decimal integer.
63 char* end = nullptr;
64 const double double_value = strtod(str, &end); // NOLINT
65
66 // Has strtol() consumed all characters in the string?
67 if (*end != '\0') {
68 // No - an invalid character was encountered.
69 std::cerr << src_text << " is expected to be a double, "
70 << "but actually has value \"" << str << "\".\n";
71 return false;
72 }
73
74 *value = double_value;
75 return true;
76}
77
78// Returns the name of the environment variable corresponding to the
79// given flag. For example, FlagToEnvVar("foo") will return
80// "BENCHMARK_FOO" in the open-source version.
81static std::string FlagToEnvVar(const char* flag) {
82 const std::string flag_str(flag);
83
84 std::string env_var;
85 for (size_t i = 0; i != flag_str.length(); ++i)
86 env_var += static_cast<char>(::toupper(flag_str.c_str()[i]));
87
88 return "BENCHMARK_" + env_var;
89}
90
91// Reads and returns the Boolean environment variable corresponding to
92// the given flag; if it's not set, returns default_value.
93//
94// The value is considered true iff it's not "0".
95bool BoolFromEnv(const char* flag, bool default_value) {
96 const std::string env_var = FlagToEnvVar(flag);
97 const char* const string_value = getenv(env_var.c_str());
98 return string_value == nullptr ? default_value
99 : strcmp(string_value, "0") != 0;
100}
101
102// Reads and returns a 32-bit integer stored in the environment
103// variable corresponding to the given flag; if it isn't set or
104// doesn't represent a valid 32-bit integer, returns default_value.
105int32_t Int32FromEnv(const char* flag, int32_t default_value) {
106 const std::string env_var = FlagToEnvVar(flag);
107 const char* const string_value = getenv(env_var.c_str());
108 if (string_value == nullptr) {
109 // The environment variable is not set.
110 return default_value;
111 }
112
113 int32_t result = default_value;
114 if (!ParseInt32(std::string("Environment variable ") + env_var, string_value,
115 &result)) {
116 std::cout << "The default value " << default_value << " is used.\n";
117 return default_value;
118 }
119
120 return result;
121}
122
123// Reads and returns the string environment variable corresponding to
124// the given flag; if it's not set, returns default_value.
125const char* StringFromEnv(const char* flag, const char* default_value) {
126 const std::string env_var = FlagToEnvVar(flag);
127 const char* const value = getenv(env_var.c_str());
128 return value == nullptr ? default_value : value;
129}
130
131// Parses a string as a command line flag. The string should have
132// the format "--flag=value". When def_optional is true, the "=value"
133// part can be omitted.
134//
135// Returns the value of the flag, or nullptr if the parsing failed.
136const char* ParseFlagValue(const char* str, const char* flag,
137 bool def_optional) {
138 // str and flag must not be nullptr.
139 if (str == nullptr || flag == nullptr) return nullptr;
140
141 // The flag must start with "--".
142 const std::string flag_str = std::string("--") + std::string(flag);
143 const size_t flag_len = flag_str.length();
144 if (strncmp(str, flag_str.c_str(), flag_len) != 0) return nullptr;
145
146 // Skips the flag name.
147 const char* flag_end = str + flag_len;
148
149 // When def_optional is true, it's OK to not have a "=value" part.
150 if (def_optional && (flag_end[0] == '\0')) return flag_end;
151
152 // If def_optional is true and there are more characters after the
153 // flag name, or if def_optional is false, there must be a '=' after
154 // the flag name.
155 if (flag_end[0] != '=') return nullptr;
156
157 // Returns the string after "=".
158 return flag_end + 1;
159}
160
161bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
162 // Gets the value of the flag as a string.
163 const char* const value_str = ParseFlagValue(str, flag, true);
164
165 // Aborts if the parsing failed.
166 if (value_str == nullptr) return false;
167
168 // Converts the string value to a bool.
169 *value = IsTruthyFlagValue(value_str);
170 return true;
171}
172
173bool ParseInt32Flag(const char* str, const char* flag, int32_t* value) {
174 // Gets the value of the flag as a string.
175 const char* const value_str = ParseFlagValue(str, flag, false);
176
177 // Aborts if the parsing failed.
178 if (value_str == nullptr) return false;
179
180 // Sets *value to the value of the flag.
181 return ParseInt32(std::string("The value of flag --") + flag, value_str,
182 value);
183}
184
185bool ParseDoubleFlag(const char* str, const char* flag, double* value) {
186 // Gets the value of the flag as a string.
187 const char* const value_str = ParseFlagValue(str, flag, false);
188
189 // Aborts if the parsing failed.
190 if (value_str == nullptr) return false;
191
192 // Sets *value to the value of the flag.
193 return ParseDouble(std::string("The value of flag --") + flag, value_str,
194 value);
195}
196
197bool ParseStringFlag(const char* str, const char* flag, std::string* value) {
198 // Gets the value of the flag as a string.
199 const char* const value_str = ParseFlagValue(str, flag, false);
200
201 // Aborts if the parsing failed.
202 if (value_str == nullptr) return false;
203
204 *value = value_str;
205 return true;
206}
207
208bool IsFlag(const char* str, const char* flag) {
209 return (ParseFlagValue(str, flag, true) != nullptr);
210}
211
212bool IsTruthyFlagValue(const std::string& str) {
213 if (str.empty()) return true;
214 char ch = str[0];
215 return isalnum(ch) &&
216 !(ch == '0' || ch == 'f' || ch == 'F' || ch == 'n' || ch == 'N');
217}
218} // end namespace benchmark
219