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