text: //===-- flags_parser.cpp ----------------------------------------*- C++ -*-===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  
  #include "flags_parser.h"
  #include "common.h"
  #include "report.h"
  
  #include <stdlib.h>
  #include <string.h>
  
  namespace scudo {
  
  class UnknownFlagsRegistry {
    static const u32 MaxUnknownFlags = 16;
    const char *UnknownFlagsNames[MaxUnknownFlags];
    u32 NumberOfUnknownFlags;
  
  public:
    void add(const char *Name) {
      CHECK_LT(NumberOfUnknownFlags, MaxUnknownFlags);
      UnknownFlagsNames[NumberOfUnknownFlags++] = Name;
    }
  
    void report() {
      if (!NumberOfUnknownFlags)
        return;
      Printf("Scudo WARNING: found %d unrecognized flag(s):\n",
             NumberOfUnknownFlags);
      for (u32 I = 0; I < NumberOfUnknownFlags; ++I)
        Printf("    %s\n", UnknownFlagsNames[I]);
      NumberOfUnknownFlags = 0;
    }
  };
  static UnknownFlagsRegistry UnknownFlags;
  
  void reportUnrecognizedFlags() { UnknownFlags.report(); }
  
  void FlagParser::printFlagDescriptions() {
    Printf("Available flags for Scudo:\n");
    for (u32 I = 0; I < NumberOfFlags; ++I)
      Printf("\t%s\n\t\t- %s\n", Flags[I].Name, Flags[I].Desc);
  }
  
  static bool isSeparator(char C) {
    return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' ||
           C == '\r';
  }
  
  static bool isSeparatorOrNull(char C) { return !C || isSeparator(C); }
  
  void FlagParser::skipWhitespace() {
    while (isSeparator(Buffer[Pos]))
      ++Pos;
  }
  
  void FlagParser::parseFlag() {
    const uptr NameStart = Pos;
    while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos]))
      ++Pos;
    if (Buffer[Pos] != '=')
      reportError("expected '='");
    const char *Name = Buffer + NameStart;
    const uptr ValueStart = ++Pos;
    const char *Value;
    if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') {
      const char Quote = Buffer[Pos++];
      while (Buffer[Pos] != 0 && Buffer[Pos] != Quote)
        ++Pos;
      if (Buffer[Pos] == 0)
        reportError("unterminated string");
      Value = Buffer + ValueStart + 1;
      ++Pos; // consume the closing quote
    } else {
      while (!isSeparatorOrNull(Buffer[Pos]))
        ++Pos;
      Value = Buffer + ValueStart;
    }
    if (!runHandler(Name, Value))
      reportError("flag parsing failed.");
  }
  
  void FlagParser::parseFlags() {
    while (true) {
      skipWhitespace();
      if (Buffer[Pos] == 0)
        break;
      parseFlag();
    }
  }
  
  void FlagParser::parseString(const char *S) {
    if (!S)
      return;
    // Backup current parser state to allow nested parseString() calls.
    const char *OldBuffer = Buffer;
    const uptr OldPos = Pos;
    Buffer = S;
    Pos = 0;
  
    parseFlags();
  
    Buffer = OldBuffer;
    Pos = OldPos;
  }
  
  inline bool parseBool(const char *Value, bool *b) {
    if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 ||
        strncmp(Value, "false", 5) == 0) {
      *b = false;
      return true;
    }
    if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 ||
        strncmp(Value, "true", 4) == 0) {
      *b = true;
      return true;
    }
    return false;
  }
  
  bool FlagParser::runHandler(const char *Name, const char *Value) {
    for (u32 I = 0; I < NumberOfFlags; ++I) {
      const uptr Len = strlen(Flags[I].Name);
      if (strncmp(Name, Flags[I].Name, Len) != 0 || Name[Len] != '=')
        continue;
      bool Ok = false;
      switch (Flags[I].Type) {
      case FlagType::FT_bool:
        Ok = parseBool(Value, reinterpret_cast<bool *>(Flags[I].Var));
        if (!Ok)
          reportInvalidFlag("bool", Value);
        break;
      case FlagType::FT_int:
        char *ValueEnd;
        *reinterpret_cast<int *>(Flags[I].Var) =
            static_cast<int>(strtol(Value, &ValueEnd, 10));
        Ok =
            *ValueEnd == '"' || *ValueEnd == '\'' || isSeparatorOrNull(*ValueEnd);
        if (!Ok)
          reportInvalidFlag("int", Value);
        break;
      }
      return Ok;
    }
    // Unrecognized flag. This is not a fatal error, we may print a warning later.
    UnknownFlags.add(Name);
    return true;
  }
  
  void FlagParser::registerFlag(const char *Name, const char *Desc, FlagType Type,
                                void *Var) {
    CHECK_LT(NumberOfFlags, MaxFlags);
    Flags[NumberOfFlags].Name = Name;
    Flags[NumberOfFlags].Desc = Desc;
    Flags[NumberOfFlags].Type = Type;
    Flags[NumberOfFlags].Var = Var;
    ++NumberOfFlags;
  }
  
  } // namespace scudo
  