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