165 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			165 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
 |