xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/scudo/standalone/flags_parser.cpp (revision 480093f4440d54b30b3025afeac24b48f2ba7a2e)
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 
1368d75effSDimitry Andric #include <stdlib.h>
1468d75effSDimitry Andric #include <string.h>
1568d75effSDimitry Andric 
1668d75effSDimitry Andric namespace scudo {
1768d75effSDimitry Andric 
1868d75effSDimitry Andric class UnknownFlagsRegistry {
1968d75effSDimitry Andric   static const u32 MaxUnknownFlags = 16;
2068d75effSDimitry Andric   const char *UnknownFlagsNames[MaxUnknownFlags];
2168d75effSDimitry Andric   u32 NumberOfUnknownFlags;
2268d75effSDimitry Andric 
2368d75effSDimitry Andric public:
2468d75effSDimitry Andric   void add(const char *Name) {
2568d75effSDimitry Andric     CHECK_LT(NumberOfUnknownFlags, MaxUnknownFlags);
2668d75effSDimitry Andric     UnknownFlagsNames[NumberOfUnknownFlags++] = Name;
2768d75effSDimitry Andric   }
2868d75effSDimitry Andric 
2968d75effSDimitry Andric   void report() {
3068d75effSDimitry Andric     if (!NumberOfUnknownFlags)
3168d75effSDimitry Andric       return;
3268d75effSDimitry Andric     Printf("Scudo WARNING: found %d unrecognized flag(s):\n",
3368d75effSDimitry Andric            NumberOfUnknownFlags);
3468d75effSDimitry Andric     for (u32 I = 0; I < NumberOfUnknownFlags; ++I)
3568d75effSDimitry Andric       Printf("    %s\n", UnknownFlagsNames[I]);
3668d75effSDimitry Andric     NumberOfUnknownFlags = 0;
3768d75effSDimitry Andric   }
3868d75effSDimitry Andric };
3968d75effSDimitry Andric static UnknownFlagsRegistry UnknownFlags;
4068d75effSDimitry Andric 
4168d75effSDimitry Andric void reportUnrecognizedFlags() { UnknownFlags.report(); }
4268d75effSDimitry Andric 
4368d75effSDimitry Andric void FlagParser::printFlagDescriptions() {
4468d75effSDimitry Andric   Printf("Available flags for Scudo:\n");
4568d75effSDimitry Andric   for (u32 I = 0; I < NumberOfFlags; ++I)
4668d75effSDimitry Andric     Printf("\t%s\n\t\t- %s\n", Flags[I].Name, Flags[I].Desc);
4768d75effSDimitry Andric }
4868d75effSDimitry Andric 
4968d75effSDimitry Andric static bool isSeparator(char C) {
5068d75effSDimitry Andric   return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' ||
5168d75effSDimitry Andric          C == '\r';
5268d75effSDimitry Andric }
5368d75effSDimitry Andric 
5468d75effSDimitry Andric static bool isSeparatorOrNull(char C) { return !C || isSeparator(C); }
5568d75effSDimitry Andric 
5668d75effSDimitry Andric void FlagParser::skipWhitespace() {
5768d75effSDimitry Andric   while (isSeparator(Buffer[Pos]))
5868d75effSDimitry Andric     ++Pos;
5968d75effSDimitry Andric }
6068d75effSDimitry Andric 
6168d75effSDimitry Andric void FlagParser::parseFlag() {
6268d75effSDimitry Andric   const uptr NameStart = Pos;
6368d75effSDimitry Andric   while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos]))
6468d75effSDimitry Andric     ++Pos;
6568d75effSDimitry Andric   if (Buffer[Pos] != '=')
6668d75effSDimitry Andric     reportError("expected '='");
6768d75effSDimitry Andric   const char *Name = Buffer + NameStart;
6868d75effSDimitry Andric   const uptr ValueStart = ++Pos;
6968d75effSDimitry Andric   const char *Value;
7068d75effSDimitry Andric   if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') {
7168d75effSDimitry Andric     const char Quote = Buffer[Pos++];
7268d75effSDimitry Andric     while (Buffer[Pos] != 0 && Buffer[Pos] != Quote)
7368d75effSDimitry Andric       ++Pos;
7468d75effSDimitry Andric     if (Buffer[Pos] == 0)
7568d75effSDimitry Andric       reportError("unterminated string");
7668d75effSDimitry Andric     Value = Buffer + ValueStart + 1;
7768d75effSDimitry Andric     ++Pos; // consume the closing quote
7868d75effSDimitry Andric   } else {
7968d75effSDimitry Andric     while (!isSeparatorOrNull(Buffer[Pos]))
8068d75effSDimitry Andric       ++Pos;
8168d75effSDimitry Andric     Value = Buffer + ValueStart;
8268d75effSDimitry Andric   }
8368d75effSDimitry Andric   if (!runHandler(Name, Value))
8468d75effSDimitry Andric     reportError("flag parsing failed.");
8568d75effSDimitry Andric }
8668d75effSDimitry Andric 
8768d75effSDimitry Andric void FlagParser::parseFlags() {
8868d75effSDimitry Andric   while (true) {
8968d75effSDimitry Andric     skipWhitespace();
9068d75effSDimitry Andric     if (Buffer[Pos] == 0)
9168d75effSDimitry Andric       break;
9268d75effSDimitry Andric     parseFlag();
9368d75effSDimitry Andric   }
9468d75effSDimitry Andric }
9568d75effSDimitry Andric 
9668d75effSDimitry Andric void FlagParser::parseString(const char *S) {
9768d75effSDimitry Andric   if (!S)
9868d75effSDimitry Andric     return;
9968d75effSDimitry Andric   // Backup current parser state to allow nested parseString() calls.
10068d75effSDimitry Andric   const char *OldBuffer = Buffer;
10168d75effSDimitry Andric   const uptr OldPos = Pos;
10268d75effSDimitry Andric   Buffer = S;
10368d75effSDimitry Andric   Pos = 0;
10468d75effSDimitry Andric 
10568d75effSDimitry Andric   parseFlags();
10668d75effSDimitry Andric 
10768d75effSDimitry Andric   Buffer = OldBuffer;
10868d75effSDimitry Andric   Pos = OldPos;
10968d75effSDimitry Andric }
11068d75effSDimitry Andric 
111*480093f4SDimitry Andric inline bool parseBool(const char *Value, bool *b) {
11268d75effSDimitry Andric   if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 ||
11368d75effSDimitry Andric       strncmp(Value, "false", 5) == 0) {
11468d75effSDimitry Andric     *b = false;
11568d75effSDimitry Andric     return true;
11668d75effSDimitry Andric   }
11768d75effSDimitry Andric   if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 ||
11868d75effSDimitry Andric       strncmp(Value, "true", 4) == 0) {
11968d75effSDimitry Andric     *b = true;
12068d75effSDimitry Andric     return true;
12168d75effSDimitry Andric   }
12268d75effSDimitry Andric   return false;
12368d75effSDimitry Andric }
12468d75effSDimitry Andric 
12568d75effSDimitry Andric bool FlagParser::runHandler(const char *Name, const char *Value) {
12668d75effSDimitry Andric   for (u32 I = 0; I < NumberOfFlags; ++I) {
12768d75effSDimitry Andric     const uptr Len = strlen(Flags[I].Name);
12868d75effSDimitry Andric     if (strncmp(Name, Flags[I].Name, Len) != 0 || Name[Len] != '=')
12968d75effSDimitry Andric       continue;
13068d75effSDimitry Andric     bool Ok = false;
13168d75effSDimitry Andric     switch (Flags[I].Type) {
13268d75effSDimitry Andric     case FlagType::FT_bool:
13368d75effSDimitry Andric       Ok = parseBool(Value, reinterpret_cast<bool *>(Flags[I].Var));
13468d75effSDimitry Andric       if (!Ok)
13568d75effSDimitry Andric         reportInvalidFlag("bool", Value);
13668d75effSDimitry Andric       break;
13768d75effSDimitry Andric     case FlagType::FT_int:
13868d75effSDimitry Andric       char *ValueEnd;
13968d75effSDimitry Andric       *reinterpret_cast<int *>(Flags[I].Var) =
14068d75effSDimitry Andric           static_cast<int>(strtol(Value, &ValueEnd, 10));
14168d75effSDimitry Andric       Ok =
14268d75effSDimitry Andric           *ValueEnd == '"' || *ValueEnd == '\'' || isSeparatorOrNull(*ValueEnd);
14368d75effSDimitry Andric       if (!Ok)
14468d75effSDimitry Andric         reportInvalidFlag("int", Value);
14568d75effSDimitry Andric       break;
14668d75effSDimitry Andric     }
14768d75effSDimitry Andric     return Ok;
14868d75effSDimitry Andric   }
14968d75effSDimitry Andric   // Unrecognized flag. This is not a fatal error, we may print a warning later.
15068d75effSDimitry Andric   UnknownFlags.add(Name);
15168d75effSDimitry Andric   return true;
15268d75effSDimitry Andric }
15368d75effSDimitry Andric 
15468d75effSDimitry Andric void FlagParser::registerFlag(const char *Name, const char *Desc, FlagType Type,
15568d75effSDimitry Andric                               void *Var) {
15668d75effSDimitry Andric   CHECK_LT(NumberOfFlags, MaxFlags);
15768d75effSDimitry Andric   Flags[NumberOfFlags].Name = Name;
15868d75effSDimitry Andric   Flags[NumberOfFlags].Desc = Desc;
15968d75effSDimitry Andric   Flags[NumberOfFlags].Type = Type;
16068d75effSDimitry Andric   Flags[NumberOfFlags].Var = Var;
16168d75effSDimitry Andric   ++NumberOfFlags;
16268d75effSDimitry Andric }
16368d75effSDimitry Andric 
16468d75effSDimitry Andric } // namespace scudo
165