You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
4.4 KiB
165 lines
4.4 KiB
//===-- flags_parser.cpp ----------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "flags_parser.h"
|
|
#include "common.h"
|
|
#include "report.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
namespace scudo {
|
|
|
|
class UnknownFlagsRegistry {
|
|
static const u32 MaxUnknownFlags = 16;
|
|
const char *UnknownFlagsNames[MaxUnknownFlags];
|
|
u32 NumberOfUnknownFlags;
|
|
|
|
public:
|
|
void add(const char *Name) {
|
|
CHECK_LT(NumberOfUnknownFlags, MaxUnknownFlags);
|
|
UnknownFlagsNames[NumberOfUnknownFlags++] = Name;
|
|
}
|
|
|
|
void report() {
|
|
if (!NumberOfUnknownFlags)
|
|
return;
|
|
Printf("Scudo WARNING: found %d unrecognized flag(s):\n",
|
|
NumberOfUnknownFlags);
|
|
for (u32 I = 0; I < NumberOfUnknownFlags; ++I)
|
|
Printf(" %s\n", UnknownFlagsNames[I]);
|
|
NumberOfUnknownFlags = 0;
|
|
}
|
|
};
|
|
static UnknownFlagsRegistry UnknownFlags;
|
|
|
|
void reportUnrecognizedFlags() { UnknownFlags.report(); }
|
|
|
|
void FlagParser::printFlagDescriptions() {
|
|
Printf("Available flags for Scudo:\n");
|
|
for (u32 I = 0; I < NumberOfFlags; ++I)
|
|
Printf("\t%s\n\t\t- %s\n", Flags[I].Name, Flags[I].Desc);
|
|
}
|
|
|
|
static bool isSeparator(char C) {
|
|
return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' ||
|
|
C == '\r';
|
|
}
|
|
|
|
static bool isSeparatorOrNull(char C) { return !C || isSeparator(C); }
|
|
|
|
void FlagParser::skipWhitespace() {
|
|
while (isSeparator(Buffer[Pos]))
|
|
++Pos;
|
|
}
|
|
|
|
void FlagParser::parseFlag() {
|
|
const uptr NameStart = Pos;
|
|
while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos]))
|
|
++Pos;
|
|
if (Buffer[Pos] != '=')
|
|
reportError("expected '='");
|
|
const char *Name = Buffer + NameStart;
|
|
const uptr ValueStart = ++Pos;
|
|
const char *Value;
|
|
if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') {
|
|
const char Quote = Buffer[Pos++];
|
|
while (Buffer[Pos] != 0 && Buffer[Pos] != Quote)
|
|
++Pos;
|
|
if (Buffer[Pos] == 0)
|
|
reportError("unterminated string");
|
|
Value = Buffer + ValueStart + 1;
|
|
++Pos; // consume the closing quote
|
|
} else {
|
|
while (!isSeparatorOrNull(Buffer[Pos]))
|
|
++Pos;
|
|
Value = Buffer + ValueStart;
|
|
}
|
|
if (!runHandler(Name, Value))
|
|
reportError("flag parsing failed.");
|
|
}
|
|
|
|
void FlagParser::parseFlags() {
|
|
while (true) {
|
|
skipWhitespace();
|
|
if (Buffer[Pos] == 0)
|
|
break;
|
|
parseFlag();
|
|
}
|
|
}
|
|
|
|
void FlagParser::parseString(const char *S) {
|
|
if (!S)
|
|
return;
|
|
// Backup current parser state to allow nested parseString() calls.
|
|
const char *OldBuffer = Buffer;
|
|
const uptr OldPos = Pos;
|
|
Buffer = S;
|
|
Pos = 0;
|
|
|
|
parseFlags();
|
|
|
|
Buffer = OldBuffer;
|
|
Pos = OldPos;
|
|
}
|
|
|
|
inline bool parseBool(const char *Value, bool *b) {
|
|
if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 ||
|
|
strncmp(Value, "false", 5) == 0) {
|
|
*b = false;
|
|
return true;
|
|
}
|
|
if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 ||
|
|
strncmp(Value, "true", 4) == 0) {
|
|
*b = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FlagParser::runHandler(const char *Name, const char *Value) {
|
|
for (u32 I = 0; I < NumberOfFlags; ++I) {
|
|
const uptr Len = strlen(Flags[I].Name);
|
|
if (strncmp(Name, Flags[I].Name, Len) != 0 || Name[Len] != '=')
|
|
continue;
|
|
bool Ok = false;
|
|
switch (Flags[I].Type) {
|
|
case FlagType::FT_bool:
|
|
Ok = parseBool(Value, reinterpret_cast<bool *>(Flags[I].Var));
|
|
if (!Ok)
|
|
reportInvalidFlag("bool", Value);
|
|
break;
|
|
case FlagType::FT_int:
|
|
char *ValueEnd;
|
|
*reinterpret_cast<int *>(Flags[I].Var) =
|
|
static_cast<int>(strtol(Value, &ValueEnd, 10));
|
|
Ok =
|
|
*ValueEnd == '"' || *ValueEnd == '\'' || isSeparatorOrNull(*ValueEnd);
|
|
if (!Ok)
|
|
reportInvalidFlag("int", Value);
|
|
break;
|
|
}
|
|
return Ok;
|
|
}
|
|
// Unrecognized flag. This is not a fatal error, we may print a warning later.
|
|
UnknownFlags.add(Name);
|
|
return true;
|
|
}
|
|
|
|
void FlagParser::registerFlag(const char *Name, const char *Desc, FlagType Type,
|
|
void *Var) {
|
|
CHECK_LT(NumberOfFlags, MaxFlags);
|
|
Flags[NumberOfFlags].Name = Name;
|
|
Flags[NumberOfFlags].Desc = Desc;
|
|
Flags[NumberOfFlags].Type = Type;
|
|
Flags[NumberOfFlags].Var = Var;
|
|
++NumberOfFlags;
|
|
}
|
|
|
|
} // namespace scudo
|