flags_parser.cpp 4.9 KB

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