168d75effSDimitry Andric //===-- flags_parser.cpp ----------------------------------------*- C++ -*-===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric 968d75effSDimitry Andric #include "flags_parser.h" 1068d75effSDimitry Andric #include "common.h" 1168d75effSDimitry Andric #include "report.h" 1268d75effSDimitry Andric 1368d75effSDimitry Andric #include <stdlib.h> 1468d75effSDimitry Andric #include <string.h> 1568d75effSDimitry Andric 1668d75effSDimitry Andric namespace scudo { 1768d75effSDimitry Andric 1868d75effSDimitry Andric class UnknownFlagsRegistry { 1968d75effSDimitry Andric static const u32 MaxUnknownFlags = 16; 2068d75effSDimitry Andric const char *UnknownFlagsNames[MaxUnknownFlags]; 2168d75effSDimitry Andric u32 NumberOfUnknownFlags; 2268d75effSDimitry Andric 2368d75effSDimitry Andric public: 2468d75effSDimitry Andric void add(const char *Name) { 2568d75effSDimitry Andric CHECK_LT(NumberOfUnknownFlags, MaxUnknownFlags); 2668d75effSDimitry Andric UnknownFlagsNames[NumberOfUnknownFlags++] = Name; 2768d75effSDimitry Andric } 2868d75effSDimitry Andric 2968d75effSDimitry Andric void report() { 3068d75effSDimitry Andric if (!NumberOfUnknownFlags) 3168d75effSDimitry Andric return; 3268d75effSDimitry Andric Printf("Scudo WARNING: found %d unrecognized flag(s):\n", 3368d75effSDimitry Andric NumberOfUnknownFlags); 3468d75effSDimitry Andric for (u32 I = 0; I < NumberOfUnknownFlags; ++I) 3568d75effSDimitry Andric Printf(" %s\n", UnknownFlagsNames[I]); 3668d75effSDimitry Andric NumberOfUnknownFlags = 0; 3768d75effSDimitry Andric } 3868d75effSDimitry Andric }; 3968d75effSDimitry Andric static UnknownFlagsRegistry UnknownFlags; 4068d75effSDimitry Andric 4168d75effSDimitry Andric void reportUnrecognizedFlags() { UnknownFlags.report(); } 4268d75effSDimitry Andric 4368d75effSDimitry Andric void FlagParser::printFlagDescriptions() { 4468d75effSDimitry Andric Printf("Available flags for Scudo:\n"); 4568d75effSDimitry Andric for (u32 I = 0; I < NumberOfFlags; ++I) 4668d75effSDimitry Andric Printf("\t%s\n\t\t- %s\n", Flags[I].Name, Flags[I].Desc); 4768d75effSDimitry Andric } 4868d75effSDimitry Andric 4968d75effSDimitry Andric static bool isSeparator(char C) { 5068d75effSDimitry Andric return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' || 5168d75effSDimitry Andric C == '\r'; 5268d75effSDimitry Andric } 5368d75effSDimitry Andric 5468d75effSDimitry Andric static bool isSeparatorOrNull(char C) { return !C || isSeparator(C); } 5568d75effSDimitry Andric 5668d75effSDimitry Andric void FlagParser::skipWhitespace() { 5768d75effSDimitry Andric while (isSeparator(Buffer[Pos])) 5868d75effSDimitry Andric ++Pos; 5968d75effSDimitry Andric } 6068d75effSDimitry Andric 6168d75effSDimitry Andric void FlagParser::parseFlag() { 6268d75effSDimitry Andric const uptr NameStart = Pos; 6368d75effSDimitry Andric while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos])) 6468d75effSDimitry Andric ++Pos; 6568d75effSDimitry Andric if (Buffer[Pos] != '=') 6668d75effSDimitry Andric reportError("expected '='"); 6768d75effSDimitry Andric const char *Name = Buffer + NameStart; 6868d75effSDimitry Andric const uptr ValueStart = ++Pos; 6968d75effSDimitry Andric const char *Value; 7068d75effSDimitry Andric if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') { 7168d75effSDimitry Andric const char Quote = Buffer[Pos++]; 7268d75effSDimitry Andric while (Buffer[Pos] != 0 && Buffer[Pos] != Quote) 7368d75effSDimitry Andric ++Pos; 7468d75effSDimitry Andric if (Buffer[Pos] == 0) 7568d75effSDimitry Andric reportError("unterminated string"); 7668d75effSDimitry Andric Value = Buffer + ValueStart + 1; 7768d75effSDimitry Andric ++Pos; // consume the closing quote 7868d75effSDimitry Andric } else { 7968d75effSDimitry Andric while (!isSeparatorOrNull(Buffer[Pos])) 8068d75effSDimitry Andric ++Pos; 8168d75effSDimitry Andric Value = Buffer + ValueStart; 8268d75effSDimitry Andric } 8368d75effSDimitry Andric if (!runHandler(Name, Value)) 8468d75effSDimitry Andric reportError("flag parsing failed."); 8568d75effSDimitry Andric } 8668d75effSDimitry Andric 8768d75effSDimitry Andric void FlagParser::parseFlags() { 8868d75effSDimitry Andric while (true) { 8968d75effSDimitry Andric skipWhitespace(); 9068d75effSDimitry Andric if (Buffer[Pos] == 0) 9168d75effSDimitry Andric break; 9268d75effSDimitry Andric parseFlag(); 9368d75effSDimitry Andric } 9468d75effSDimitry Andric } 9568d75effSDimitry Andric 9668d75effSDimitry Andric void FlagParser::parseString(const char *S) { 9768d75effSDimitry Andric if (!S) 9868d75effSDimitry Andric return; 9968d75effSDimitry Andric // Backup current parser state to allow nested parseString() calls. 10068d75effSDimitry Andric const char *OldBuffer = Buffer; 10168d75effSDimitry Andric const uptr OldPos = Pos; 10268d75effSDimitry Andric Buffer = S; 10368d75effSDimitry Andric Pos = 0; 10468d75effSDimitry Andric 10568d75effSDimitry Andric parseFlags(); 10668d75effSDimitry Andric 10768d75effSDimitry Andric Buffer = OldBuffer; 10868d75effSDimitry Andric Pos = OldPos; 10968d75effSDimitry Andric } 11068d75effSDimitry Andric 111*480093f4SDimitry Andric inline bool parseBool(const char *Value, bool *b) { 11268d75effSDimitry Andric if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 || 11368d75effSDimitry Andric strncmp(Value, "false", 5) == 0) { 11468d75effSDimitry Andric *b = false; 11568d75effSDimitry Andric return true; 11668d75effSDimitry Andric } 11768d75effSDimitry Andric if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 || 11868d75effSDimitry Andric strncmp(Value, "true", 4) == 0) { 11968d75effSDimitry Andric *b = true; 12068d75effSDimitry Andric return true; 12168d75effSDimitry Andric } 12268d75effSDimitry Andric return false; 12368d75effSDimitry Andric } 12468d75effSDimitry Andric 12568d75effSDimitry Andric bool FlagParser::runHandler(const char *Name, const char *Value) { 12668d75effSDimitry Andric for (u32 I = 0; I < NumberOfFlags; ++I) { 12768d75effSDimitry Andric const uptr Len = strlen(Flags[I].Name); 12868d75effSDimitry Andric if (strncmp(Name, Flags[I].Name, Len) != 0 || Name[Len] != '=') 12968d75effSDimitry Andric continue; 13068d75effSDimitry Andric bool Ok = false; 13168d75effSDimitry Andric switch (Flags[I].Type) { 13268d75effSDimitry Andric case FlagType::FT_bool: 13368d75effSDimitry Andric Ok = parseBool(Value, reinterpret_cast<bool *>(Flags[I].Var)); 13468d75effSDimitry Andric if (!Ok) 13568d75effSDimitry Andric reportInvalidFlag("bool", Value); 13668d75effSDimitry Andric break; 13768d75effSDimitry Andric case FlagType::FT_int: 13868d75effSDimitry Andric char *ValueEnd; 13968d75effSDimitry Andric *reinterpret_cast<int *>(Flags[I].Var) = 14068d75effSDimitry Andric static_cast<int>(strtol(Value, &ValueEnd, 10)); 14168d75effSDimitry Andric Ok = 14268d75effSDimitry Andric *ValueEnd == '"' || *ValueEnd == '\'' || isSeparatorOrNull(*ValueEnd); 14368d75effSDimitry Andric if (!Ok) 14468d75effSDimitry Andric reportInvalidFlag("int", Value); 14568d75effSDimitry Andric break; 14668d75effSDimitry Andric } 14768d75effSDimitry Andric return Ok; 14868d75effSDimitry Andric } 14968d75effSDimitry Andric // Unrecognized flag. This is not a fatal error, we may print a warning later. 15068d75effSDimitry Andric UnknownFlags.add(Name); 15168d75effSDimitry Andric return true; 15268d75effSDimitry Andric } 15368d75effSDimitry Andric 15468d75effSDimitry Andric void FlagParser::registerFlag(const char *Name, const char *Desc, FlagType Type, 15568d75effSDimitry Andric void *Var) { 15668d75effSDimitry Andric CHECK_LT(NumberOfFlags, MaxFlags); 15768d75effSDimitry Andric Flags[NumberOfFlags].Name = Name; 15868d75effSDimitry Andric Flags[NumberOfFlags].Desc = Desc; 15968d75effSDimitry Andric Flags[NumberOfFlags].Type = Type; 16068d75effSDimitry Andric Flags[NumberOfFlags].Var = Var; 16168d75effSDimitry Andric ++NumberOfFlags; 16268d75effSDimitry Andric } 16368d75effSDimitry Andric 16468d75effSDimitry Andric } // namespace scudo 165