1 //===-- flags_parser.cpp ----------------------------------------*- 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
9 #include "flags_parser.h"
10 #include "common.h"
11 #include "report.h"
12
13 #include <errno.h>
14 #include <limits.h>
15 #include <stdlib.h>
16 #include <string.h>
17
18 namespace scudo {
19
20 class UnknownFlagsRegistry {
21 static const u32 MaxUnknownFlags = 16;
22 const char *UnknownFlagsNames[MaxUnknownFlags];
23 u32 NumberOfUnknownFlags;
24
25 public:
add(const char * Name)26 void add(const char *Name) {
27 CHECK_LT(NumberOfUnknownFlags, MaxUnknownFlags);
28 UnknownFlagsNames[NumberOfUnknownFlags++] = Name;
29 }
30
report()31 void report() {
32 if (!NumberOfUnknownFlags)
33 return;
34 Printf("Scudo WARNING: found %d unrecognized flag(s):\n",
35 NumberOfUnknownFlags);
36 for (u32 I = 0; I < NumberOfUnknownFlags; ++I)
37 Printf(" %s\n", UnknownFlagsNames[I]);
38 NumberOfUnknownFlags = 0;
39 }
40 };
41 static UnknownFlagsRegistry UnknownFlags;
42
reportUnrecognizedFlags()43 void reportUnrecognizedFlags() { UnknownFlags.report(); }
44
printFlagDescriptions()45 void FlagParser::printFlagDescriptions() {
46 Printf("Available flags for Scudo:\n");
47 for (u32 I = 0; I < NumberOfFlags; ++I)
48 Printf("\t%s\n\t\t- %s\n", Flags[I].Name, Flags[I].Desc);
49 }
50
isSeparator(char C)51 static bool isSeparator(char C) {
52 return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' ||
53 C == '\r';
54 }
55
isSeparatorOrNull(char C)56 static bool isSeparatorOrNull(char C) { return !C || isSeparator(C); }
57
skipWhitespace()58 void FlagParser::skipWhitespace() {
59 while (isSeparator(Buffer[Pos]))
60 ++Pos;
61 }
62
parseFlag()63 void FlagParser::parseFlag() {
64 const uptr NameStart = Pos;
65 while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos]))
66 ++Pos;
67 if (Buffer[Pos] != '=')
68 reportError("expected '='");
69 const char *Name = Buffer + NameStart;
70 const uptr ValueStart = ++Pos;
71 const char *Value;
72 if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') {
73 const char Quote = Buffer[Pos++];
74 while (Buffer[Pos] != 0 && Buffer[Pos] != Quote)
75 ++Pos;
76 if (Buffer[Pos] == 0)
77 reportError("unterminated string");
78 Value = Buffer + ValueStart + 1;
79 ++Pos; // consume the closing quote
80 } else {
81 while (!isSeparatorOrNull(Buffer[Pos]))
82 ++Pos;
83 Value = Buffer + ValueStart;
84 }
85 if (!runHandler(Name, Value, '='))
86 reportError("flag parsing failed.");
87 }
88
parseFlags()89 void FlagParser::parseFlags() {
90 while (true) {
91 skipWhitespace();
92 if (Buffer[Pos] == 0)
93 break;
94 parseFlag();
95 }
96 }
97
parseString(const char * S)98 void FlagParser::parseString(const char *S) {
99 if (!S)
100 return;
101 // Backup current parser state to allow nested parseString() calls.
102 const char *OldBuffer = Buffer;
103 const uptr OldPos = Pos;
104 Buffer = S;
105 Pos = 0;
106
107 parseFlags();
108
109 Buffer = OldBuffer;
110 Pos = OldPos;
111 }
112
parseBool(const char * Value,bool * b)113 inline bool parseBool(const char *Value, bool *b) {
114 if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 ||
115 strncmp(Value, "false", 5) == 0) {
116 *b = false;
117 return true;
118 }
119 if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 ||
120 strncmp(Value, "true", 4) == 0) {
121 *b = true;
122 return true;
123 }
124 return false;
125 }
126
parseStringPair(const char * Name,const char * Value)127 void FlagParser::parseStringPair(const char *Name, const char *Value) {
128 if (!runHandler(Name, Value, '\0'))
129 reportError("flag parsing failed.");
130 }
131
runHandler(const char * Name,const char * Value,const char Sep)132 bool FlagParser::runHandler(const char *Name, const char *Value,
133 const char Sep) {
134 for (u32 I = 0; I < NumberOfFlags; ++I) {
135 const uptr Len = strlen(Flags[I].Name);
136 if (strncmp(Name, Flags[I].Name, Len) != 0 || Name[Len] != Sep)
137 continue;
138 bool Ok = false;
139 switch (Flags[I].Type) {
140 case FlagType::FT_bool:
141 Ok = parseBool(Value, reinterpret_cast<bool *>(Flags[I].Var));
142 if (!Ok)
143 reportInvalidFlag("bool", Value);
144 break;
145 case FlagType::FT_int:
146 char *ValueEnd;
147 errno = 0;
148 long V = strtol(Value, &ValueEnd, 10);
149 if (errno != 0 || // strtol failed (over or underflow)
150 V > INT_MAX || V < INT_MIN || // overflows integer
151 // contains unexpected characters
152 (*ValueEnd != '"' && *ValueEnd != '\'' &&
153 !isSeparatorOrNull(*ValueEnd))) {
154 reportInvalidFlag("int", Value);
155 break;
156 }
157 *reinterpret_cast<int *>(Flags[I].Var) = static_cast<int>(V);
158 Ok = true;
159 break;
160 }
161 return Ok;
162 }
163 // Unrecognized flag. This is not a fatal error, we may print a warning later.
164 UnknownFlags.add(Name);
165 return true;
166 }
167
registerFlag(const char * Name,const char * Desc,FlagType Type,void * Var)168 void FlagParser::registerFlag(const char *Name, const char *Desc, FlagType Type,
169 void *Var) {
170 CHECK_LT(NumberOfFlags, MaxFlags);
171 Flags[NumberOfFlags].Name = Name;
172 Flags[NumberOfFlags].Desc = Desc;
173 Flags[NumberOfFlags].Type = Type;
174 Flags[NumberOfFlags].Var = Var;
175 ++NumberOfFlags;
176 }
177
178 } // namespace scudo
179