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