cmCommandArgumentParserHelper.cxx 7.72 KB
Newer Older
1
2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
4
#include "cmCommandArgumentParserHelper.h"

5
#include "cmCommandArgumentLexer.h"
6
#include "cmMakefile.h"
7
8
#include "cmState.h"
#include "cmSystemTools.h"
9
#include "cmake.h"
10

11
12
13
#include <iostream>
#include <sstream>
#include <string.h>
14

15
int cmCommandArgument_yyparse(yyscan_t yyscanner);
16
//
17
18
cmCommandArgumentParserHelper::cmCommandArgumentParserHelper()
{
19
  this->WarnUninitialized = false;
20
  this->CheckSystemVars = false;
Ken Martin's avatar
Ken Martin committed
21
  this->FileLine = -1;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
22
  this->FileName = nullptr;
23
  this->RemoveEmpty = true;
24

Ken Martin's avatar
Ken Martin committed
25
  this->NoEscapeMode = false;
26
  this->ReplaceAtSyntax = false;
27
28
29
30
31
32
33
34
35
}

cmCommandArgumentParserHelper::~cmCommandArgumentParserHelper()
{
  this->CleanupParser();
}

void cmCommandArgumentParserHelper::SetLineFile(long line, const char* file)
{
Ken Martin's avatar
Ken Martin committed
36
37
  this->FileLine = line;
  this->FileName = file;
38
39
}

40
const char* cmCommandArgumentParserHelper::AddString(const std::string& str)
41
{
42
  if (str.empty()) {
43
    return "";
44
45
  }
  char* stVal = new char[str.size() + 1];
46
  strcpy(stVal, str.c_str());
Ken Martin's avatar
Ken Martin committed
47
  this->Variables.push_back(stVal);
Andy Cedilnik's avatar
Andy Cedilnik committed
48
  return stVal;
49
50
}

51
52
const char* cmCommandArgumentParserHelper::ExpandSpecialVariable(
  const char* key, const char* var)
53
{
54
  if (!key) {
55
    return this->ExpandVariable(var);
56
57
  }
  if (!var) {
58
    return "";
59
60
  }
  if (strcmp(key, "ENV") == 0) {
61
62
    std::string str;
    if (cmSystemTools::GetEnv(var, str)) {
63
      if (this->EscapeQuotes) {
64
        return this->AddString(cmSystemTools::EscapeQuotes(str));
65
      }
66
      return this->AddString(str);
67
    }
68
    return "";
69
70
71
72
73
  }
  if (strcmp(key, "CACHE") == 0) {
    if (const char* c =
          this->Makefile->GetState()->GetInitializedCacheValue(var)) {
      if (this->EscapeQuotes) {
Stephen Kelly's avatar
Stephen Kelly committed
74
        return this->AddString(cmSystemTools::EscapeQuotes(c));
Brad King's avatar
Brad King committed
75
      }
76
      return this->AddString(c);
Brad King's avatar
Brad King committed
77
    }
78
    return "";
79
  }
80
  std::ostringstream e;
81
  e << "Syntax $" << key << "{} is not supported.  "
Brad King's avatar
Brad King committed
82
    << "Only ${}, $ENV{}, and $CACHE{} are allowed.";
83
  this->SetError(e.str());
Daniel Pfeifer's avatar
Daniel Pfeifer committed
84
  return nullptr;
85
86
}

87
const char* cmCommandArgumentParserHelper::ExpandVariable(const char* var)
88
{
89
  if (!var) {
Daniel Pfeifer's avatar
Daniel Pfeifer committed
90
    return nullptr;
91
92
  }
  if (this->FileLine >= 0 && strcmp(var, "CMAKE_CURRENT_LIST_LINE") == 0) {
93
    std::ostringstream ostr;
Ken Martin's avatar
Ken Martin committed
94
    ostr << this->FileLine;
Stephen Kelly's avatar
Stephen Kelly committed
95
    return this->AddString(ostr.str());
96
  }
Ken Martin's avatar
Ken Martin committed
97
  const char* value = this->Makefile->GetDefinition(var);
98
  if (!value && !this->RemoveEmpty) {
99
100
101
    // check to see if we need to print a warning
    // if strict mode is on and the variable has
    // not been "cleared"/initialized with a set(foo ) call
102
    if (this->WarnUninitialized && !this->Makefile->VariableInitialized(var)) {
103
      if (this->CheckSystemVars ||
104
105
106
107
108
          (this->FileName &&
           (cmSystemTools::IsSubDirectory(
              this->FileName, this->Makefile->GetHomeDirectory()) ||
            cmSystemTools::IsSubDirectory(
              this->FileName, this->Makefile->GetHomeOutputDirectory())))) {
109
        std::ostringstream msg;
110
        msg << "uninitialized variable \'" << var << "\'";
111
        this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, msg.str());
112
      }
113
    }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
114
    return nullptr;
115
116
  }
  if (this->EscapeQuotes && value) {
Stephen Kelly's avatar
Stephen Kelly committed
117
    return this->AddString(cmSystemTools::EscapeQuotes(value));
118
  }
119
  return this->AddString(value ? value : "");
120
121
}

122
const char* cmCommandArgumentParserHelper::ExpandVariableForAt(const char* var)
123
{
124
  if (this->ReplaceAtSyntax) {
125
    // try to expand the variable
126
    const char* ret = this->ExpandVariable(var);
127
    // if the return was 0 and we want to replace empty strings
128
    // then return an empty string
129
    if (!ret && this->RemoveEmpty) {
130
      return this->AddString("");
131
    }
132
    // if the ret was not 0, then return it
133
    if (ret) {
134
      return ret;
135
    }
136
  }
137
  // at this point we want to put it back because of one of these cases:
138
  // - this->ReplaceAtSyntax is false
139
140
141
142
143
  // - this->ReplaceAtSyntax is true, but this->RemoveEmpty is false,
  //   and the variable was not defined
  std::string ref = "@";
  ref += var;
  ref += "@";
Stephen Kelly's avatar
Stephen Kelly committed
144
  return this->AddString(ref);
145
146
}

147
148
const char* cmCommandArgumentParserHelper::CombineUnions(const char* in1,
                                                         const char* in2)
149
{
150
  if (!in1) {
Andy Cedilnik's avatar
Andy Cedilnik committed
151
    return in2;
152
153
  }
  if (!in2) {
Andy Cedilnik's avatar
Andy Cedilnik committed
154
    return in1;
155
  }
156
  size_t len = strlen(in1) + strlen(in2) + 1;
157
  char* out = new char[len];
Andy Cedilnik's avatar
Andy Cedilnik committed
158
159
  strcpy(out, in1);
  strcat(out, in2);
Ken Martin's avatar
Ken Martin committed
160
  this->Variables.push_back(out);
Andy Cedilnik's avatar
Andy Cedilnik committed
161
  return out;
162
163
}

164
165
void cmCommandArgumentParserHelper::AllocateParserType(
  cmCommandArgumentParserHelper::ParserType* pt, const char* str, int len)
166
{
Daniel Pfeifer's avatar
Daniel Pfeifer committed
167
  pt->str = nullptr;
168
  if (len == 0) {
169
    len = static_cast<int>(strlen(str));
170
171
  }
  if (len == 0) {
172
    return;
173
  }
174
  char* out = new char[len + 1];
175
  memcpy(out, str, len);
176
177
178
  out[len] = 0;
  pt->str = out;
  this->Variables.push_back(out);
179
180
}

181
182
bool cmCommandArgumentParserHelper::HandleEscapeSymbol(
  cmCommandArgumentParserHelper::ParserType* pt, char symbol)
183
{
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
  switch (symbol) {
    case '\\':
    case '"':
    case ' ':
    case '#':
    case '(':
    case ')':
    case '$':
    case '@':
    case '^':
      this->AllocateParserType(pt, &symbol, 1);
      break;
    case ';':
      this->AllocateParserType(pt, "\\;", 2);
      break;
    case 't':
      this->AllocateParserType(pt, "\t", 1);
      break;
    case 'n':
      this->AllocateParserType(pt, "\n", 1);
      break;
    case 'r':
      this->AllocateParserType(pt, "\r", 1);
      break;
    case '0':
      this->AllocateParserType(pt, "\0", 1);
      break;
    default: {
      std::ostringstream e;
      e << "Invalid escape sequence \\" << symbol;
      this->SetError(e.str());
215
    }
216
217
      return false;
  }
218
219
220
  return true;
}

221
222
void cmCommandArgument_SetupEscapes(yyscan_t yyscanner, bool noEscapes);

223
224
int cmCommandArgumentParserHelper::ParseString(const char* str, int verb)
{
225
  if (!str) {
226
    return 0;
227
  }
228
229
230
231
  this->Verbose = verb;
  this->InputBuffer = str;
  this->InputBufferPos = 0;
  this->CurrentLine = 0;
232

233
  this->Result.clear();
234
235
236
237

  yyscan_t yyscanner;
  cmCommandArgument_yylex_init(&yyscanner);
  cmCommandArgument_yyset_extra(this, yyscanner);
238
  cmCommandArgument_SetupEscapes(yyscanner, this->NoEscapeMode);
239
240
  int res = cmCommandArgument_yyparse(yyscanner);
  cmCommandArgument_yylex_destroy(yyscanner);
241
  if (res != 0) {
242
    return 0;
243
  }
244
245
246

  this->CleanupParser();

247
248
249
250
  if (Verbose) {
    std::cerr << "Expanding [" << str << "] produced: [" << this->Result << "]"
              << std::endl;
  }
251
252
253
254
255
  return 1;
}

void cmCommandArgumentParserHelper::CleanupParser()
{
256
257
  for (char* var : this->Variables) {
    delete[] var;
258
  }
Ken Martin's avatar
Ken Martin committed
259
  this->Variables.erase(this->Variables.begin(), this->Variables.end());
260
261
262
263
}

int cmCommandArgumentParserHelper::LexInput(char* buf, int maxlen)
{
264
  if (maxlen < 1) {
265
    return 0;
266
267
268
269
270
  }
  if (this->InputBufferPos < this->InputBuffer.size()) {
    buf[0] = this->InputBuffer[this->InputBufferPos++];
    if (buf[0] == '\n') {
      this->CurrentLine++;
271
    }
272
273
    return (1);
  }
274
275
  buf[0] = '\n';
  return (0);
276
}
Andy Cedilnik's avatar
Andy Cedilnik committed
277

278
279
280
void cmCommandArgumentParserHelper::Error(const char* str)
{
  unsigned long pos = static_cast<unsigned long>(this->InputBufferPos);
281
  std::ostringstream ostr;
282
  ostr << str << " (" << pos << ")";
283
  this->SetError(ostr.str());
284
285
286
287
}

void cmCommandArgumentParserHelper::SetMakefile(const cmMakefile* mf)
{
Ken Martin's avatar
Ken Martin committed
288
  this->Makefile = mf;
289
  this->WarnUninitialized = mf->GetCMakeInstance()->GetWarnUninitialized();
290
  this->CheckSystemVars = mf->GetCMakeInstance()->GetCheckSystemVars();
291
292
293
294
}

void cmCommandArgumentParserHelper::SetResult(const char* value)
{
295
  if (!value) {
296
    this->Result.clear();
297
    return;
298
  }
Ken Martin's avatar
Ken Martin committed
299
  this->Result = value;
300
301
}

302
303
304
void cmCommandArgumentParserHelper::SetError(std::string const& msg)
{
  // Keep only the first error.
305
  if (this->ErrorString.empty()) {
306
    this->ErrorString = msg;
307
  }
308
}