xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/scudo/standalone/flags_parser.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
168d75effSDimitry Andric //===-- flags_parser.cpp ----------------------------------------*- C++ -*-===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric 
968d75effSDimitry Andric #include "flags_parser.h"
1068d75effSDimitry Andric #include "common.h"
1168d75effSDimitry Andric #include "report.h"
1268d75effSDimitry Andric 
13*5f757f3fSDimitry Andric #include <errno.h>
14*5f757f3fSDimitry Andric #include <limits.h>
1568d75effSDimitry Andric #include <stdlib.h>
1668d75effSDimitry Andric #include <string.h>
1768d75effSDimitry Andric 
1868d75effSDimitry Andric namespace scudo {
1968d75effSDimitry Andric 
2068d75effSDimitry Andric class UnknownFlagsRegistry {
2168d75effSDimitry Andric   static const u32 MaxUnknownFlags = 16;
2268d75effSDimitry Andric   const char *UnknownFlagsNames[MaxUnknownFlags];
2368d75effSDimitry Andric   u32 NumberOfUnknownFlags;
2468d75effSDimitry Andric 
2568d75effSDimitry Andric public:
add(const char * Name)2668d75effSDimitry Andric   void add(const char *Name) {
2768d75effSDimitry Andric     CHECK_LT(NumberOfUnknownFlags, MaxUnknownFlags);
2868d75effSDimitry Andric     UnknownFlagsNames[NumberOfUnknownFlags++] = Name;
2968d75effSDimitry Andric   }
3068d75effSDimitry Andric 
report()3168d75effSDimitry Andric   void report() {
3268d75effSDimitry Andric     if (!NumberOfUnknownFlags)
3368d75effSDimitry Andric       return;
3468d75effSDimitry Andric     Printf("Scudo WARNING: found %d unrecognized flag(s):\n",
3568d75effSDimitry Andric            NumberOfUnknownFlags);
3668d75effSDimitry Andric     for (u32 I = 0; I < NumberOfUnknownFlags; ++I)
3768d75effSDimitry Andric       Printf("    %s\n", UnknownFlagsNames[I]);
3868d75effSDimitry Andric     NumberOfUnknownFlags = 0;
3968d75effSDimitry Andric   }
4068d75effSDimitry Andric };
4168d75effSDimitry Andric static UnknownFlagsRegistry UnknownFlags;
4268d75effSDimitry Andric 
reportUnrecognizedFlags()4368d75effSDimitry Andric void reportUnrecognizedFlags() { UnknownFlags.report(); }
4468d75effSDimitry Andric 
printFlagDescriptions()4568d75effSDimitry Andric void FlagParser::printFlagDescriptions() {
4668d75effSDimitry Andric   Printf("Available flags for Scudo:\n");
4768d75effSDimitry Andric   for (u32 I = 0; I < NumberOfFlags; ++I)
4868d75effSDimitry Andric     Printf("\t%s\n\t\t- %s\n", Flags[I].Name, Flags[I].Desc);
4968d75effSDimitry Andric }
5068d75effSDimitry Andric 
isSeparator(char C)5168d75effSDimitry Andric static bool isSeparator(char C) {
5268d75effSDimitry Andric   return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' ||
5368d75effSDimitry Andric          C == '\r';
5468d75effSDimitry Andric }
5568d75effSDimitry Andric 
isSeparatorOrNull(char C)5668d75effSDimitry Andric static bool isSeparatorOrNull(char C) { return !C || isSeparator(C); }
5768d75effSDimitry Andric 
skipWhitespace()5868d75effSDimitry Andric void FlagParser::skipWhitespace() {
5968d75effSDimitry Andric   while (isSeparator(Buffer[Pos]))
6068d75effSDimitry Andric     ++Pos;
6168d75effSDimitry Andric }
6268d75effSDimitry Andric 
parseFlag()6368d75effSDimitry Andric void FlagParser::parseFlag() {
6468d75effSDimitry Andric   const uptr NameStart = Pos;
6568d75effSDimitry Andric   while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos]))
6668d75effSDimitry Andric     ++Pos;
6768d75effSDimitry Andric   if (Buffer[Pos] != '=')
6868d75effSDimitry Andric     reportError("expected '='");
6968d75effSDimitry Andric   const char *Name = Buffer + NameStart;
7068d75effSDimitry Andric   const uptr ValueStart = ++Pos;
7168d75effSDimitry Andric   const char *Value;
7268d75effSDimitry Andric   if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') {
7368d75effSDimitry Andric     const char Quote = Buffer[Pos++];
7468d75effSDimitry Andric     while (Buffer[Pos] != 0 && Buffer[Pos] != Quote)
7568d75effSDimitry Andric       ++Pos;
7668d75effSDimitry Andric     if (Buffer[Pos] == 0)
7768d75effSDimitry Andric       reportError("unterminated string");
7868d75effSDimitry Andric     Value = Buffer + ValueStart + 1;
7968d75effSDimitry Andric     ++Pos; // consume the closing quote
8068d75effSDimitry Andric   } else {
8168d75effSDimitry Andric     while (!isSeparatorOrNull(Buffer[Pos]))
8268d75effSDimitry Andric       ++Pos;
8368d75effSDimitry Andric     Value = Buffer + ValueStart;
8468d75effSDimitry Andric   }
85*5f757f3fSDimitry Andric   if (!runHandler(Name, Value, '='))
8668d75effSDimitry Andric     reportError("flag parsing failed.");
8768d75effSDimitry Andric }
8868d75effSDimitry Andric 
parseFlags()8968d75effSDimitry Andric void FlagParser::parseFlags() {
9068d75effSDimitry Andric   while (true) {
9168d75effSDimitry Andric     skipWhitespace();
9268d75effSDimitry Andric     if (Buffer[Pos] == 0)
9368d75effSDimitry Andric       break;
9468d75effSDimitry Andric     parseFlag();
9568d75effSDimitry Andric   }
9668d75effSDimitry Andric }
9768d75effSDimitry Andric 
parseString(const char * S)9868d75effSDimitry Andric void FlagParser::parseString(const char *S) {
9968d75effSDimitry Andric   if (!S)
10068d75effSDimitry Andric     return;
10168d75effSDimitry Andric   // Backup current parser state to allow nested parseString() calls.
10268d75effSDimitry Andric   const char *OldBuffer = Buffer;
10368d75effSDimitry Andric   const uptr OldPos = Pos;
10468d75effSDimitry Andric   Buffer = S;
10568d75effSDimitry Andric   Pos = 0;
10668d75effSDimitry Andric 
10768d75effSDimitry Andric   parseFlags();
10868d75effSDimitry Andric 
10968d75effSDimitry Andric   Buffer = OldBuffer;
11068d75effSDimitry Andric   Pos = OldPos;
11168d75effSDimitry Andric }
11268d75effSDimitry Andric 
parseBool(const char * Value,bool * b)113480093f4SDimitry Andric inline bool parseBool(const char *Value, bool *b) {
11468d75effSDimitry Andric   if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 ||
11568d75effSDimitry Andric       strncmp(Value, "false", 5) == 0) {
11668d75effSDimitry Andric     *b = false;
11768d75effSDimitry Andric     return true;
11868d75effSDimitry Andric   }
11968d75effSDimitry Andric   if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 ||
12068d75effSDimitry Andric       strncmp(Value, "true", 4) == 0) {
12168d75effSDimitry Andric     *b = true;
12268d75effSDimitry Andric     return true;
12368d75effSDimitry Andric   }
12468d75effSDimitry Andric   return false;
12568d75effSDimitry Andric }
12668d75effSDimitry Andric 
parseStringPair(const char * Name,const char * Value)127*5f757f3fSDimitry Andric void FlagParser::parseStringPair(const char *Name, const char *Value) {
128*5f757f3fSDimitry Andric   if (!runHandler(Name, Value, '\0'))
129*5f757f3fSDimitry Andric     reportError("flag parsing failed.");
130*5f757f3fSDimitry Andric }
131*5f757f3fSDimitry Andric 
runHandler(const char * Name,const char * Value,const char Sep)132*5f757f3fSDimitry Andric bool FlagParser::runHandler(const char *Name, const char *Value,
133*5f757f3fSDimitry Andric                             const char Sep) {
13468d75effSDimitry Andric   for (u32 I = 0; I < NumberOfFlags; ++I) {
13568d75effSDimitry Andric     const uptr Len = strlen(Flags[I].Name);
136*5f757f3fSDimitry Andric     if (strncmp(Name, Flags[I].Name, Len) != 0 || Name[Len] != Sep)
13768d75effSDimitry Andric       continue;
13868d75effSDimitry Andric     bool Ok = false;
13968d75effSDimitry Andric     switch (Flags[I].Type) {
14068d75effSDimitry Andric     case FlagType::FT_bool:
14168d75effSDimitry Andric       Ok = parseBool(Value, reinterpret_cast<bool *>(Flags[I].Var));
14268d75effSDimitry Andric       if (!Ok)
14368d75effSDimitry Andric         reportInvalidFlag("bool", Value);
14468d75effSDimitry Andric       break;
14568d75effSDimitry Andric     case FlagType::FT_int:
14668d75effSDimitry Andric       char *ValueEnd;
147*5f757f3fSDimitry Andric       errno = 0;
148*5f757f3fSDimitry Andric       long V = strtol(Value, &ValueEnd, 10);
149*5f757f3fSDimitry Andric       if (errno != 0 ||                 // strtol failed (over or underflow)
150*5f757f3fSDimitry Andric           V > INT_MAX || V < INT_MIN || // overflows integer
151*5f757f3fSDimitry Andric           // contains unexpected characters
152*5f757f3fSDimitry Andric           (*ValueEnd != '"' && *ValueEnd != '\'' &&
153*5f757f3fSDimitry Andric            !isSeparatorOrNull(*ValueEnd))) {
15468d75effSDimitry Andric         reportInvalidFlag("int", Value);
15568d75effSDimitry Andric         break;
15668d75effSDimitry Andric       }
157*5f757f3fSDimitry Andric       *reinterpret_cast<int *>(Flags[I].Var) = static_cast<int>(V);
158*5f757f3fSDimitry Andric       Ok = true;
159*5f757f3fSDimitry Andric       break;
160*5f757f3fSDimitry Andric     }
16168d75effSDimitry Andric     return Ok;
16268d75effSDimitry Andric   }
16368d75effSDimitry Andric   // Unrecognized flag. This is not a fatal error, we may print a warning later.
16468d75effSDimitry Andric   UnknownFlags.add(Name);
16568d75effSDimitry Andric   return true;
16668d75effSDimitry Andric }
16768d75effSDimitry Andric 
registerFlag(const char * Name,const char * Desc,FlagType Type,void * Var)16868d75effSDimitry Andric void FlagParser::registerFlag(const char *Name, const char *Desc, FlagType Type,
16968d75effSDimitry Andric                               void *Var) {
17068d75effSDimitry Andric   CHECK_LT(NumberOfFlags, MaxFlags);
17168d75effSDimitry Andric   Flags[NumberOfFlags].Name = Name;
17268d75effSDimitry Andric   Flags[NumberOfFlags].Desc = Desc;
17368d75effSDimitry Andric   Flags[NumberOfFlags].Type = Type;
17468d75effSDimitry Andric   Flags[NumberOfFlags].Var = Var;
17568d75effSDimitry Andric   ++NumberOfFlags;
17668d75effSDimitry Andric }
17768d75effSDimitry Andric 
17868d75effSDimitry Andric } // namespace scudo
179