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 13*5f757f3fSDimitry Andric #include <errno.h> 14*5f757f3fSDimitry Andric #include <limits.h> 1568d75effSDimitry Andric #include <stdlib.h> 1668d75effSDimitry Andric #include <string.h> 1768d75effSDimitry Andric 1868d75effSDimitry Andric namespace scudo { 1968d75effSDimitry Andric 2068d75effSDimitry Andric class UnknownFlagsRegistry { 2168d75effSDimitry Andric static const u32 MaxUnknownFlags = 16; 2268d75effSDimitry Andric const char *UnknownFlagsNames[MaxUnknownFlags]; 2368d75effSDimitry Andric u32 NumberOfUnknownFlags; 2468d75effSDimitry Andric 2568d75effSDimitry Andric public: 2668d75effSDimitry Andric void add(const char *Name) { 2768d75effSDimitry Andric CHECK_LT(NumberOfUnknownFlags, MaxUnknownFlags); 2868d75effSDimitry Andric UnknownFlagsNames[NumberOfUnknownFlags++] = Name; 2968d75effSDimitry Andric } 3068d75effSDimitry Andric 3168d75effSDimitry Andric void report() { 3268d75effSDimitry Andric if (!NumberOfUnknownFlags) 3368d75effSDimitry Andric return; 3468d75effSDimitry Andric Printf("Scudo WARNING: found %d unrecognized flag(s):\n", 3568d75effSDimitry Andric NumberOfUnknownFlags); 3668d75effSDimitry Andric for (u32 I = 0; I < NumberOfUnknownFlags; ++I) 3768d75effSDimitry Andric Printf(" %s\n", UnknownFlagsNames[I]); 3868d75effSDimitry Andric NumberOfUnknownFlags = 0; 3968d75effSDimitry Andric } 4068d75effSDimitry Andric }; 4168d75effSDimitry Andric static UnknownFlagsRegistry UnknownFlags; 4268d75effSDimitry Andric 4368d75effSDimitry Andric void reportUnrecognizedFlags() { UnknownFlags.report(); } 4468d75effSDimitry Andric 4568d75effSDimitry Andric void FlagParser::printFlagDescriptions() { 4668d75effSDimitry Andric Printf("Available flags for Scudo:\n"); 4768d75effSDimitry Andric for (u32 I = 0; I < NumberOfFlags; ++I) 4868d75effSDimitry Andric Printf("\t%s\n\t\t- %s\n", Flags[I].Name, Flags[I].Desc); 4968d75effSDimitry Andric } 5068d75effSDimitry Andric 5168d75effSDimitry Andric static bool isSeparator(char C) { 5268d75effSDimitry Andric return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' || 5368d75effSDimitry Andric C == '\r'; 5468d75effSDimitry Andric } 5568d75effSDimitry Andric 5668d75effSDimitry Andric static bool isSeparatorOrNull(char C) { return !C || isSeparator(C); } 5768d75effSDimitry Andric 5868d75effSDimitry Andric void FlagParser::skipWhitespace() { 5968d75effSDimitry Andric while (isSeparator(Buffer[Pos])) 6068d75effSDimitry Andric ++Pos; 6168d75effSDimitry Andric } 6268d75effSDimitry Andric 6368d75effSDimitry Andric void FlagParser::parseFlag() { 6468d75effSDimitry Andric const uptr NameStart = Pos; 6568d75effSDimitry Andric while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos])) 6668d75effSDimitry Andric ++Pos; 6768d75effSDimitry Andric if (Buffer[Pos] != '=') 6868d75effSDimitry Andric reportError("expected '='"); 6968d75effSDimitry Andric const char *Name = Buffer + NameStart; 7068d75effSDimitry Andric const uptr ValueStart = ++Pos; 7168d75effSDimitry Andric const char *Value; 7268d75effSDimitry Andric if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') { 7368d75effSDimitry Andric const char Quote = Buffer[Pos++]; 7468d75effSDimitry Andric while (Buffer[Pos] != 0 && Buffer[Pos] != Quote) 7568d75effSDimitry Andric ++Pos; 7668d75effSDimitry Andric if (Buffer[Pos] == 0) 7768d75effSDimitry Andric reportError("unterminated string"); 7868d75effSDimitry Andric Value = Buffer + ValueStart + 1; 7968d75effSDimitry Andric ++Pos; // consume the closing quote 8068d75effSDimitry Andric } else { 8168d75effSDimitry Andric while (!isSeparatorOrNull(Buffer[Pos])) 8268d75effSDimitry Andric ++Pos; 8368d75effSDimitry Andric Value = Buffer + ValueStart; 8468d75effSDimitry Andric } 85*5f757f3fSDimitry Andric if (!runHandler(Name, Value, '=')) 8668d75effSDimitry Andric reportError("flag parsing failed."); 8768d75effSDimitry Andric } 8868d75effSDimitry Andric 8968d75effSDimitry Andric void FlagParser::parseFlags() { 9068d75effSDimitry Andric while (true) { 9168d75effSDimitry Andric skipWhitespace(); 9268d75effSDimitry Andric if (Buffer[Pos] == 0) 9368d75effSDimitry Andric break; 9468d75effSDimitry Andric parseFlag(); 9568d75effSDimitry Andric } 9668d75effSDimitry Andric } 9768d75effSDimitry Andric 9868d75effSDimitry Andric void FlagParser::parseString(const char *S) { 9968d75effSDimitry Andric if (!S) 10068d75effSDimitry Andric return; 10168d75effSDimitry Andric // Backup current parser state to allow nested parseString() calls. 10268d75effSDimitry Andric const char *OldBuffer = Buffer; 10368d75effSDimitry Andric const uptr OldPos = Pos; 10468d75effSDimitry Andric Buffer = S; 10568d75effSDimitry Andric Pos = 0; 10668d75effSDimitry Andric 10768d75effSDimitry Andric parseFlags(); 10868d75effSDimitry Andric 10968d75effSDimitry Andric Buffer = OldBuffer; 11068d75effSDimitry Andric Pos = OldPos; 11168d75effSDimitry Andric } 11268d75effSDimitry Andric 113480093f4SDimitry Andric inline bool parseBool(const char *Value, bool *b) { 11468d75effSDimitry Andric if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 || 11568d75effSDimitry Andric strncmp(Value, "false", 5) == 0) { 11668d75effSDimitry Andric *b = false; 11768d75effSDimitry Andric return true; 11868d75effSDimitry Andric } 11968d75effSDimitry Andric if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 || 12068d75effSDimitry Andric strncmp(Value, "true", 4) == 0) { 12168d75effSDimitry Andric *b = true; 12268d75effSDimitry Andric return true; 12368d75effSDimitry Andric } 12468d75effSDimitry Andric return false; 12568d75effSDimitry Andric } 12668d75effSDimitry Andric 127*5f757f3fSDimitry Andric void FlagParser::parseStringPair(const char *Name, const char *Value) { 128*5f757f3fSDimitry Andric if (!runHandler(Name, Value, '\0')) 129*5f757f3fSDimitry Andric reportError("flag parsing failed."); 130*5f757f3fSDimitry Andric } 131*5f757f3fSDimitry Andric 132*5f757f3fSDimitry Andric bool FlagParser::runHandler(const char *Name, const char *Value, 133*5f757f3fSDimitry Andric const char Sep) { 13468d75effSDimitry Andric for (u32 I = 0; I < NumberOfFlags; ++I) { 13568d75effSDimitry Andric const uptr Len = strlen(Flags[I].Name); 136*5f757f3fSDimitry Andric if (strncmp(Name, Flags[I].Name, Len) != 0 || Name[Len] != Sep) 13768d75effSDimitry Andric continue; 13868d75effSDimitry Andric bool Ok = false; 13968d75effSDimitry Andric switch (Flags[I].Type) { 14068d75effSDimitry Andric case FlagType::FT_bool: 14168d75effSDimitry Andric Ok = parseBool(Value, reinterpret_cast<bool *>(Flags[I].Var)); 14268d75effSDimitry Andric if (!Ok) 14368d75effSDimitry Andric reportInvalidFlag("bool", Value); 14468d75effSDimitry Andric break; 14568d75effSDimitry Andric case FlagType::FT_int: 14668d75effSDimitry Andric char *ValueEnd; 147*5f757f3fSDimitry Andric errno = 0; 148*5f757f3fSDimitry Andric long V = strtol(Value, &ValueEnd, 10); 149*5f757f3fSDimitry Andric if (errno != 0 || // strtol failed (over or underflow) 150*5f757f3fSDimitry Andric V > INT_MAX || V < INT_MIN || // overflows integer 151*5f757f3fSDimitry Andric // contains unexpected characters 152*5f757f3fSDimitry Andric (*ValueEnd != '"' && *ValueEnd != '\'' && 153*5f757f3fSDimitry Andric !isSeparatorOrNull(*ValueEnd))) { 15468d75effSDimitry Andric reportInvalidFlag("int", Value); 15568d75effSDimitry Andric break; 15668d75effSDimitry Andric } 157*5f757f3fSDimitry Andric *reinterpret_cast<int *>(Flags[I].Var) = static_cast<int>(V); 158*5f757f3fSDimitry Andric Ok = true; 159*5f757f3fSDimitry Andric break; 160*5f757f3fSDimitry Andric } 16168d75effSDimitry Andric return Ok; 16268d75effSDimitry Andric } 16368d75effSDimitry Andric // Unrecognized flag. This is not a fatal error, we may print a warning later. 16468d75effSDimitry Andric UnknownFlags.add(Name); 16568d75effSDimitry Andric return true; 16668d75effSDimitry Andric } 16768d75effSDimitry Andric 16868d75effSDimitry Andric void FlagParser::registerFlag(const char *Name, const char *Desc, FlagType Type, 16968d75effSDimitry Andric void *Var) { 17068d75effSDimitry Andric CHECK_LT(NumberOfFlags, MaxFlags); 17168d75effSDimitry Andric Flags[NumberOfFlags].Name = Name; 17268d75effSDimitry Andric Flags[NumberOfFlags].Desc = Desc; 17368d75effSDimitry Andric Flags[NumberOfFlags].Type = Type; 17468d75effSDimitry Andric Flags[NumberOfFlags].Var = Var; 17568d75effSDimitry Andric ++NumberOfFlags; 17668d75effSDimitry Andric } 17768d75effSDimitry Andric 17868d75effSDimitry Andric } // namespace scudo 179