164 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
//===-- 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
 |