1*68d75effSDimitry Andric //===-- flags_parser.cpp ----------------------------------------*- C++ -*-===// 2*68d75effSDimitry Andric // 3*68d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*68d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*68d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*68d75effSDimitry Andric // 7*68d75effSDimitry Andric //===----------------------------------------------------------------------===// 8*68d75effSDimitry Andric 9*68d75effSDimitry Andric #include "flags_parser.h" 10*68d75effSDimitry Andric #include "common.h" 11*68d75effSDimitry Andric #include "report.h" 12*68d75effSDimitry Andric 13*68d75effSDimitry Andric #include <stdlib.h> 14*68d75effSDimitry Andric #include <string.h> 15*68d75effSDimitry Andric 16*68d75effSDimitry Andric namespace scudo { 17*68d75effSDimitry Andric 18*68d75effSDimitry Andric class UnknownFlagsRegistry { 19*68d75effSDimitry Andric static const u32 MaxUnknownFlags = 16; 20*68d75effSDimitry Andric const char *UnknownFlagsNames[MaxUnknownFlags]; 21*68d75effSDimitry Andric u32 NumberOfUnknownFlags; 22*68d75effSDimitry Andric 23*68d75effSDimitry Andric public: 24*68d75effSDimitry Andric void add(const char *Name) { 25*68d75effSDimitry Andric CHECK_LT(NumberOfUnknownFlags, MaxUnknownFlags); 26*68d75effSDimitry Andric UnknownFlagsNames[NumberOfUnknownFlags++] = Name; 27*68d75effSDimitry Andric } 28*68d75effSDimitry Andric 29*68d75effSDimitry Andric void report() { 30*68d75effSDimitry Andric if (!NumberOfUnknownFlags) 31*68d75effSDimitry Andric return; 32*68d75effSDimitry Andric Printf("Scudo WARNING: found %d unrecognized flag(s):\n", 33*68d75effSDimitry Andric NumberOfUnknownFlags); 34*68d75effSDimitry Andric for (u32 I = 0; I < NumberOfUnknownFlags; ++I) 35*68d75effSDimitry Andric Printf(" %s\n", UnknownFlagsNames[I]); 36*68d75effSDimitry Andric NumberOfUnknownFlags = 0; 37*68d75effSDimitry Andric } 38*68d75effSDimitry Andric }; 39*68d75effSDimitry Andric static UnknownFlagsRegistry UnknownFlags; 40*68d75effSDimitry Andric 41*68d75effSDimitry Andric void reportUnrecognizedFlags() { UnknownFlags.report(); } 42*68d75effSDimitry Andric 43*68d75effSDimitry Andric void FlagParser::printFlagDescriptions() { 44*68d75effSDimitry Andric Printf("Available flags for Scudo:\n"); 45*68d75effSDimitry Andric for (u32 I = 0; I < NumberOfFlags; ++I) 46*68d75effSDimitry Andric Printf("\t%s\n\t\t- %s\n", Flags[I].Name, Flags[I].Desc); 47*68d75effSDimitry Andric } 48*68d75effSDimitry Andric 49*68d75effSDimitry Andric static bool isSeparator(char C) { 50*68d75effSDimitry Andric return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' || 51*68d75effSDimitry Andric C == '\r'; 52*68d75effSDimitry Andric } 53*68d75effSDimitry Andric 54*68d75effSDimitry Andric static bool isSeparatorOrNull(char C) { return !C || isSeparator(C); } 55*68d75effSDimitry Andric 56*68d75effSDimitry Andric void FlagParser::skipWhitespace() { 57*68d75effSDimitry Andric while (isSeparator(Buffer[Pos])) 58*68d75effSDimitry Andric ++Pos; 59*68d75effSDimitry Andric } 60*68d75effSDimitry Andric 61*68d75effSDimitry Andric void FlagParser::parseFlag() { 62*68d75effSDimitry Andric const uptr NameStart = Pos; 63*68d75effSDimitry Andric while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos])) 64*68d75effSDimitry Andric ++Pos; 65*68d75effSDimitry Andric if (Buffer[Pos] != '=') 66*68d75effSDimitry Andric reportError("expected '='"); 67*68d75effSDimitry Andric const char *Name = Buffer + NameStart; 68*68d75effSDimitry Andric const uptr ValueStart = ++Pos; 69*68d75effSDimitry Andric const char *Value; 70*68d75effSDimitry Andric if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') { 71*68d75effSDimitry Andric const char Quote = Buffer[Pos++]; 72*68d75effSDimitry Andric while (Buffer[Pos] != 0 && Buffer[Pos] != Quote) 73*68d75effSDimitry Andric ++Pos; 74*68d75effSDimitry Andric if (Buffer[Pos] == 0) 75*68d75effSDimitry Andric reportError("unterminated string"); 76*68d75effSDimitry Andric Value = Buffer + ValueStart + 1; 77*68d75effSDimitry Andric ++Pos; // consume the closing quote 78*68d75effSDimitry Andric } else { 79*68d75effSDimitry Andric while (!isSeparatorOrNull(Buffer[Pos])) 80*68d75effSDimitry Andric ++Pos; 81*68d75effSDimitry Andric Value = Buffer + ValueStart; 82*68d75effSDimitry Andric } 83*68d75effSDimitry Andric if (!runHandler(Name, Value)) 84*68d75effSDimitry Andric reportError("flag parsing failed."); 85*68d75effSDimitry Andric } 86*68d75effSDimitry Andric 87*68d75effSDimitry Andric void FlagParser::parseFlags() { 88*68d75effSDimitry Andric while (true) { 89*68d75effSDimitry Andric skipWhitespace(); 90*68d75effSDimitry Andric if (Buffer[Pos] == 0) 91*68d75effSDimitry Andric break; 92*68d75effSDimitry Andric parseFlag(); 93*68d75effSDimitry Andric } 94*68d75effSDimitry Andric } 95*68d75effSDimitry Andric 96*68d75effSDimitry Andric void FlagParser::parseString(const char *S) { 97*68d75effSDimitry Andric if (!S) 98*68d75effSDimitry Andric return; 99*68d75effSDimitry Andric // Backup current parser state to allow nested parseString() calls. 100*68d75effSDimitry Andric const char *OldBuffer = Buffer; 101*68d75effSDimitry Andric const uptr OldPos = Pos; 102*68d75effSDimitry Andric Buffer = S; 103*68d75effSDimitry Andric Pos = 0; 104*68d75effSDimitry Andric 105*68d75effSDimitry Andric parseFlags(); 106*68d75effSDimitry Andric 107*68d75effSDimitry Andric Buffer = OldBuffer; 108*68d75effSDimitry Andric Pos = OldPos; 109*68d75effSDimitry Andric } 110*68d75effSDimitry Andric 111*68d75effSDimitry Andric INLINE bool parseBool(const char *Value, bool *b) { 112*68d75effSDimitry Andric if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 || 113*68d75effSDimitry Andric strncmp(Value, "false", 5) == 0) { 114*68d75effSDimitry Andric *b = false; 115*68d75effSDimitry Andric return true; 116*68d75effSDimitry Andric } 117*68d75effSDimitry Andric if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 || 118*68d75effSDimitry Andric strncmp(Value, "true", 4) == 0) { 119*68d75effSDimitry Andric *b = true; 120*68d75effSDimitry Andric return true; 121*68d75effSDimitry Andric } 122*68d75effSDimitry Andric return false; 123*68d75effSDimitry Andric } 124*68d75effSDimitry Andric 125*68d75effSDimitry Andric bool FlagParser::runHandler(const char *Name, const char *Value) { 126*68d75effSDimitry Andric for (u32 I = 0; I < NumberOfFlags; ++I) { 127*68d75effSDimitry Andric const uptr Len = strlen(Flags[I].Name); 128*68d75effSDimitry Andric if (strncmp(Name, Flags[I].Name, Len) != 0 || Name[Len] != '=') 129*68d75effSDimitry Andric continue; 130*68d75effSDimitry Andric bool Ok = false; 131*68d75effSDimitry Andric switch (Flags[I].Type) { 132*68d75effSDimitry Andric case FlagType::FT_bool: 133*68d75effSDimitry Andric Ok = parseBool(Value, reinterpret_cast<bool *>(Flags[I].Var)); 134*68d75effSDimitry Andric if (!Ok) 135*68d75effSDimitry Andric reportInvalidFlag("bool", Value); 136*68d75effSDimitry Andric break; 137*68d75effSDimitry Andric case FlagType::FT_int: 138*68d75effSDimitry Andric char *ValueEnd; 139*68d75effSDimitry Andric *reinterpret_cast<int *>(Flags[I].Var) = 140*68d75effSDimitry Andric static_cast<int>(strtol(Value, &ValueEnd, 10)); 141*68d75effSDimitry Andric Ok = 142*68d75effSDimitry Andric *ValueEnd == '"' || *ValueEnd == '\'' || isSeparatorOrNull(*ValueEnd); 143*68d75effSDimitry Andric if (!Ok) 144*68d75effSDimitry Andric reportInvalidFlag("int", Value); 145*68d75effSDimitry Andric break; 146*68d75effSDimitry Andric } 147*68d75effSDimitry Andric return Ok; 148*68d75effSDimitry Andric } 149*68d75effSDimitry Andric // Unrecognized flag. This is not a fatal error, we may print a warning later. 150*68d75effSDimitry Andric UnknownFlags.add(Name); 151*68d75effSDimitry Andric return true; 152*68d75effSDimitry Andric } 153*68d75effSDimitry Andric 154*68d75effSDimitry Andric void FlagParser::registerFlag(const char *Name, const char *Desc, FlagType Type, 155*68d75effSDimitry Andric void *Var) { 156*68d75effSDimitry Andric CHECK_LT(NumberOfFlags, MaxFlags); 157*68d75effSDimitry Andric Flags[NumberOfFlags].Name = Name; 158*68d75effSDimitry Andric Flags[NumberOfFlags].Desc = Desc; 159*68d75effSDimitry Andric Flags[NumberOfFlags].Type = Type; 160*68d75effSDimitry Andric Flags[NumberOfFlags].Var = Var; 161*68d75effSDimitry Andric ++NumberOfFlags; 162*68d75effSDimitry Andric } 163*68d75effSDimitry Andric 164*68d75effSDimitry Andric } // namespace scudo 165