|  | //===-- 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 <errno.h> | 
|  | #include <limits.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; | 
|  | } | 
|  |  | 
|  | void FlagParser::parseStringPair(const char *Name, const char *Value) { | 
|  | if (!runHandler(Name, Value, '\0')) | 
|  | reportError("flag parsing failed."); | 
|  | } | 
|  |  | 
|  | bool FlagParser::runHandler(const char *Name, const char *Value, | 
|  | const char Sep) { | 
|  | for (u32 I = 0; I < NumberOfFlags; ++I) { | 
|  | const uptr Len = strlen(Flags[I].Name); | 
|  | if (strncmp(Name, Flags[I].Name, Len) != 0 || Name[Len] != Sep) | 
|  | 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; | 
|  | errno = 0; | 
|  | long V = strtol(Value, &ValueEnd, 10); | 
|  | if (errno != 0 ||                 // strtol failed (over or underflow) | 
|  | V > INT_MAX || V < INT_MIN || // overflows integer | 
|  | // contains unexpected characters | 
|  | (*ValueEnd != '"' && *ValueEnd != '\'' && | 
|  | !isSeparatorOrNull(*ValueEnd))) { | 
|  | reportInvalidFlag("int", Value); | 
|  | break; | 
|  | } | 
|  | *reinterpret_cast<int *>(Flags[I].Var) = static_cast<int>(V); | 
|  | Ok = true; | 
|  | 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 |