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