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:
add(const char * Name)2668d75effSDimitry Andric void add(const char *Name) {
2768d75effSDimitry Andric CHECK_LT(NumberOfUnknownFlags, MaxUnknownFlags);
2868d75effSDimitry Andric UnknownFlagsNames[NumberOfUnknownFlags++] = Name;
2968d75effSDimitry Andric }
3068d75effSDimitry Andric
report()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
reportUnrecognizedFlags()4368d75effSDimitry Andric void reportUnrecognizedFlags() { UnknownFlags.report(); }
4468d75effSDimitry Andric
printFlagDescriptions()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
isSeparator(char C)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
isSeparatorOrNull(char C)5668d75effSDimitry Andric static bool isSeparatorOrNull(char C) { return !C || isSeparator(C); }
5768d75effSDimitry Andric
skipWhitespace()5868d75effSDimitry Andric void FlagParser::skipWhitespace() {
5968d75effSDimitry Andric while (isSeparator(Buffer[Pos]))
6068d75effSDimitry Andric ++Pos;
6168d75effSDimitry Andric }
6268d75effSDimitry Andric
parseFlag()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
parseFlags()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
parseString(const char * S)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
parseBool(const char * Value,bool * b)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
parseStringPair(const char * Name,const char * Value)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
runHandler(const char * Name,const char * Value,const char Sep)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
registerFlag(const char * Name,const char * Desc,FlagType Type,void * Var)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