blob: 3d8c6f3789b4ccb4707233e1cedbbc284026d956 [file] [log] [blame]
//===-- 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 <errno.h>
#include <limits.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;
}
void FlagParser::parseStringPair(const char *Name, const char *Value) {
if (!runHandler(Name, Value, '\0'))
reportError("flag parsing failed.");
}
bool FlagParser::runHandler(const char *Name, const char *Value,
const char Sep) {
for (u32 I = 0; I < NumberOfFlags; ++I) {
const uptr Len = strlen(Flags[I].Name);
if (strncmp(Name, Flags[I].Name, Len) != 0 || Name[Len] != Sep)
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;
errno = 0;
long V = strtol(Value, &ValueEnd, 10);
if (errno != 0 || // strtol failed (over or underflow)
V > INT_MAX || V < INT_MIN || // overflows integer
// contains unexpected characters
(*ValueEnd != '"' && *ValueEnd != '\'' &&
!isSeparatorOrNull(*ValueEnd))) {
reportInvalidFlag("int", Value);
break;
}
*reinterpret_cast<int *>(Flags[I].Var) = static_cast<int>(V);
Ok = true;
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