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