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
18namespace scudo {
19
20class UnknownFlagsRegistry {
21 static const u32 MaxUnknownFlags = 16;
22 const char *UnknownFlagsNames[MaxUnknownFlags];
23 u32 NumberOfUnknownFlags;
24
25public:
26 void add(const char *Name) {
27 CHECK_LT(NumberOfUnknownFlags, MaxUnknownFlags);
28 UnknownFlagsNames[NumberOfUnknownFlags++] = Name;
29 }
30
31 void report() {
32 if (!NumberOfUnknownFlags)
33 return;
34 Printf(Format: "Scudo WARNING: found %d unrecognized flag(s):\n",
35 NumberOfUnknownFlags);
36 for (u32 I = 0; I < NumberOfUnknownFlags; ++I)
37 Printf(Format: " %s\n", UnknownFlagsNames[I]);
38 NumberOfUnknownFlags = 0;
39 }
40};
41static UnknownFlagsRegistry UnknownFlags;
42
43void reportUnrecognizedFlags() { UnknownFlags.report(); }
44
45void FlagParser::printFlagDescriptions() {
46 Printf(Format: "Available flags for Scudo:\n");
47 for (u32 I = 0; I < NumberOfFlags; ++I)
48 Printf(Format: "\t%s\n\t\t- %s\n", Flags[I].Name, Flags[I].Desc);
49}
50
51static bool isSeparator(char C) {
52 return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' ||
53 C == '\r';
54}
55
56static bool isSeparatorOrNull(char C) { return !C || isSeparator(C); }
57
58void FlagParser::skipWhitespace() {
59 while (isSeparator(C: Buffer[Pos]))
60 ++Pos;
61}
62
63void FlagParser::parseFlag() {
64 const uptr NameStart = Pos;
65 while (Buffer[Pos] != '=' && !isSeparatorOrNull(C: Buffer[Pos]))
66 ++Pos;
67 if (Buffer[Pos] != '=')
68 reportError(Message: "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(Message: "unterminated string");
78 Value = Buffer + ValueStart + 1;
79 ++Pos; // consume the closing quote
80 } else {
81 while (!isSeparatorOrNull(C: Buffer[Pos]))
82 ++Pos;
83 Value = Buffer + ValueStart;
84 }
85 if (!runHandler(Name, Value, Sep: '='))
86 reportError(Message: "flag parsing failed.");
87}
88
89void FlagParser::parseFlags() {
90 while (true) {
91 skipWhitespace();
92 if (Buffer[Pos] == 0)
93 break;
94 parseFlag();
95 }
96}
97
98void 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
113inline bool parseBool(const char *Value, bool *b) {
114 if (strncmp(s1: Value, s2: "0", n: 1) == 0 || strncmp(s1: Value, s2: "no", n: 2) == 0 ||
115 strncmp(s1: Value, s2: "false", n: 5) == 0) {
116 *b = false;
117 return true;
118 }
119 if (strncmp(s1: Value, s2: "1", n: 1) == 0 || strncmp(s1: Value, s2: "yes", n: 3) == 0 ||
120 strncmp(s1: Value, s2: "true", n: 4) == 0) {
121 *b = true;
122 return true;
123 }
124 return false;
125}
126
127void FlagParser::parseStringPair(const char *Name, const char *Value) {
128 if (!runHandler(Name, Value, Sep: '\0'))
129 reportError(Message: "flag parsing failed.");
130}
131
132bool 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(s: Flags[I].Name);
136 if (strncmp(s1: Name, s2: Flags[I].Name, n: 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, b: reinterpret_cast<bool *>(Flags[I].Var));
142 if (!Ok)
143 reportInvalidFlag(FlagType: "bool", Value);
144 break;
145 case FlagType::FT_int:
146 char *ValueEnd;
147 errno = 0;
148 long V = strtol(nptr: Value, endptr: &ValueEnd, base: 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(C: *ValueEnd))) {
154 reportInvalidFlag(FlagType: "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
168void 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