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