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(Format: "Scudo WARNING: found %d unrecognized flag(s):\n" , |
35 | NumberOfUnknownFlags); |
36 | for (u32 I = 0; I < NumberOfUnknownFlags; ++I) |
37 | Printf(Format: " %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(Format: "Available flags for Scudo:\n" ); |
47 | for (u32 I = 0; I < NumberOfFlags; ++I) |
48 | Printf(Format: "\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(C: Buffer[Pos])) |
60 | ++Pos; |
61 | } |
62 | |
63 | void FlagParser::parseFlag() { |
64 | const uptr NameStart = Pos; |
65 | while (Buffer[Pos] != '=' && !isSeparatorOrNull(C: Buffer[Pos])) |
66 | ++Pos; |
67 | if (Buffer[Pos] != '=') |
68 | reportError(Message: "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(Message: "unterminated string" ); |
78 | Value = Buffer + ValueStart + 1; |
79 | ++Pos; // consume the closing quote |
80 | } else { |
81 | while (!isSeparatorOrNull(C: Buffer[Pos])) |
82 | ++Pos; |
83 | Value = Buffer + ValueStart; |
84 | } |
85 | if (!runHandler(Name, Value, Sep: '=')) |
86 | reportError(Message: "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(s1: Value, s2: "0" , n: 1) == 0 || strncmp(s1: Value, s2: "no" , n: 2) == 0 || |
115 | strncmp(s1: Value, s2: "false" , n: 5) == 0) { |
116 | *b = false; |
117 | return true; |
118 | } |
119 | if (strncmp(s1: Value, s2: "1" , n: 1) == 0 || strncmp(s1: Value, s2: "yes" , n: 3) == 0 || |
120 | strncmp(s1: Value, s2: "true" , n: 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, Sep: '\0')) |
129 | reportError(Message: "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(s: Flags[I].Name); |
136 | if (strncmp(s1: Name, s2: Flags[I].Name, n: 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, b: reinterpret_cast<bool *>(Flags[I].Var)); |
142 | if (!Ok) |
143 | reportInvalidFlag(FlagType: "bool" , Value); |
144 | break; |
145 | case FlagType::FT_int: |
146 | char *ValueEnd; |
147 | errno = 0; |
148 | long V = strtol(nptr: Value, endptr: &ValueEnd, base: 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(C: *ValueEnd))) { |
154 | reportInvalidFlag(FlagType: "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 | |