MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Parser.cpp
1 /*
2  Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 
19 #include <ndb_global.h>
20 
21 #include "Parser.hpp"
22 #include <Properties.hpp>
23 
24 
25 class ParseInputStream : public InputStream {
26 public:
27  ParseInputStream(InputStream & in, bool trim = true, char eofComment = '#');
28 
29  char* gets(char * buf, int bufLen);
30  void push_back(const char *);
31  void set_mutex(NdbMutex *m) { in.set_mutex(m); };
32 private:
33  InputStream & in;
34  char * buffer;
35 };
36 
37 ParseInputStream::ParseInputStream(InputStream & _in,
38  bool /* unused */,
39  char /* unused */)
40  : in(_in){
41  buffer = 0;
42 }
43 
44 char*
45 ParseInputStream::gets(char * buf, int bufLen){
46  if(buffer != 0){
47  strncpy(buf, buffer, bufLen);
48  free(buffer);
49  buffer = 0;
50  return buf;
51  }
52  char *t = in.gets(buf, bufLen);
53  return t;
54 }
55 
56 void
57 ParseInputStream::push_back(const char * str){
58  if(buffer != 0)
59  abort();
60  buffer = strdup(str);
61 }
62 
63 ParserImpl::ParserImpl(const DummyRow * rows, InputStream & in,
64  bool b_cmd, bool b_empty, bool b_iarg)
65  : m_rows(rows), input(* new ParseInputStream(in))
66 {
67  m_breakOnCmd = b_cmd;
68  m_breakOnEmpty = b_empty;
69  m_breakOnInvalidArg = b_iarg;
70 }
71 
72 ParserImpl::~ParserImpl(){
73  delete & input;
74 }
75 
76 static
77 bool
78 Empty(const char * str){
79  if(str == 0)
80  return true;
81  const int len = strlen(str);
82  if(len == 0)
83  return false;
84  for(int i = 0; i<len; i++)
85  if(str[i] != ' ' && str[i] != '\t' && str[i] != '\n')
86  return false;
87  return true;
88 }
89 
90 static
91 bool
92 Eof(const char * str) { return str == 0;}
93 
94 static
95 void
96 trim(char * str){
97  if(str == NULL)
98  return;
99  int len = strlen(str);
100  for(len--; str[len] == '\n' || str[len] == ' ' || str[len] == '\t'; len--)
101  str[len] = 0;
102 
103  int pos = 0;
104  while(str[pos] == ' ' || str[pos] == '\t')
105  pos++;
106 
107  if(str[pos] == '\"' && str[len] == '\"') {
108  pos++;
109  str[len] = 0;
110  len--;
111  }
112 
113  memmove(str, &str[pos], len - pos + 2);
114 }
115 
116 static
117 bool
118 split(char * buf, char ** name, char ** value){
119 
120  for (*value=buf; **value; (*value)++) {
121  if (**value == ':' || **value == '=') {
122  break;
123  }
124  }
125 
126  if(* value == 0){
127  return false;
128  }
129  (* value)[0] = 0;
130  * value = (* value + 1);
131  * name = buf;
132 
133  trim(* name);
134  trim(* value);
135 
136  return true;
137 }
138 
139 bool
140 ParserImpl::run(Context * ctx, const class Properties ** pDst,
141  volatile bool * stop) const
142 {
143  input.set_mutex(ctx->m_mutex);
144 
145  * pDst = 0;
146  bool ownStop = false;
147  if(stop == 0)
148  stop = &ownStop;
149 
150  ctx->m_aliasUsed.clear();
151 
152  const unsigned sz = sizeof(ctx->m_tokenBuffer);
153  ctx->m_currentToken = input.gets(ctx->m_tokenBuffer, sz);
154  if(Eof(ctx->m_currentToken)){
155  ctx->m_status = Parser<Dummy>::Eof;
156  return false;
157  }
158 
159  int last= strlen(ctx->m_currentToken);
160  if(last>0)
161  last--;
162 
163  if(ctx->m_currentToken[last] !='\n'){
164  ctx->m_status = Parser<Dummy>::NoLine;
165  ctx->m_tokenBuffer[0]= '\0';
166  return false;
167  }
168 
169  if(Empty(ctx->m_currentToken)){
170  ctx->m_status = Parser<Dummy>::EmptyLine;
171  return false;
172  }
173 
174  trim(ctx->m_currentToken);
175  ctx->m_currentCmd = matchCommand(ctx, ctx->m_currentToken, m_rows);
176  if(ctx->m_currentCmd == 0){
177  ctx->m_status = Parser<Dummy>::UnknownCommand;
178  return false;
179  }
180 
181  Properties * p = new Properties();
182 
183  bool invalidArgument = false;
184  ctx->m_currentToken = input.gets(ctx->m_tokenBuffer, sz);
185 
186  while((! * stop) &&
187  !Eof(ctx->m_currentToken) &&
188  !Empty(ctx->m_currentToken)){
189  if(ctx->m_currentToken[0] != 0){
190  trim(ctx->m_currentToken);
191  if(!parseArg(ctx, ctx->m_currentToken, ctx->m_currentCmd + 1, p)){
192  delete p;
193  invalidArgument = true;
194  break;
195  }
196  }
197  ctx->m_currentToken = input.gets(ctx->m_tokenBuffer, sz);
198  }
199 
200  if(invalidArgument){
201  char buf[sz];
202  char * tmp;
203  if(!m_breakOnInvalidArg){
204  do {
205  tmp = input.gets(buf, sz);
206  } while((! * stop) && !Eof(tmp) && !Empty(tmp));
207  }
208  return false;
209  }
210 
211  if(* stop){
212  delete p;
213  ctx->m_status = Parser<Dummy>::ExternalStop;
214  return false;
215  }
216 
217  if(!checkMandatory(ctx, p)){
219  delete p;
220  return false;
221  }
222 
226  for(unsigned i = 0; i<ctx->m_aliasUsed.size(); i++){
227  const ParserRow<Dummy> * alias = ctx->m_aliasUsed[i];
228  Properties tmp;
229  tmp.put("name", alias->name);
230  tmp.put("realName", alias->realName);
231  p->put("$ALIAS", i, &tmp);
232  }
233  p->put("$ALIAS", ctx->m_aliasUsed.size());
234 
235  ctx->m_status = Parser<Dummy>::Ok;
236  * pDst = p;
237  return true;
238 }
239 
240 const ParserImpl::DummyRow*
241 ParserImpl::matchCommand(Context* ctx, const char* buf, const DummyRow rows[]){
242  const char * name = buf;
243  const DummyRow * tmp = &rows[0];
244  while(tmp->name != 0 && name != 0){
245  if(strcmp(tmp->name, name) == 0){
246  if(tmp->type == DummyRow::Cmd)
247  return tmp;
248  if(tmp->type == DummyRow::CmdAlias){
249  if(ctx != 0)
250  ctx->m_aliasUsed.push_back(tmp);
251  name = tmp->realName;
252  tmp = &rows[0];
253  continue;
254  }
255  }
256  tmp++;
257  }
258  return 0;
259 }
260 
261 const ParserImpl::DummyRow*
262 ParserImpl::matchArg(Context* ctx, const char * buf, const DummyRow rows[]){
263  const char * name = buf;
264  const DummyRow * tmp = &rows[0];
265  while(tmp->name != 0){
266  const DummyRow::Type t = tmp->type;
267  if(t != DummyRow::Arg && t != DummyRow::ArgAlias && t !=DummyRow::CmdAlias)
268  break;
269  if(t !=DummyRow::CmdAlias && strcmp(tmp->name, name) == 0){
270  if(tmp->type == DummyRow::Arg){
271  return tmp;
272  }
273  if(tmp->type == DummyRow::ArgAlias){
274  if(ctx != 0)
275  ctx->m_aliasUsed.push_back(tmp);
276  name = tmp->realName;
277  tmp = &rows[0];
278  continue;
279  }
280  }
281  tmp++;
282  }
283  return 0;
284 }
285 
286 bool
287 ParserImpl::parseArg(Context * ctx,
288  char * buf,
289  const DummyRow * rows,
290  Properties * p){
291  char * name;
292  char * value;
293  if(!split(buf, &name, &value)){
294  ctx->m_status = Parser<Dummy>::InvalidArgumentFormat;
295  return false;
296  }
297  const DummyRow * arg = matchArg(ctx, name, rows);
298  if(arg == 0){
299  ctx->m_status = Parser<Dummy>::UnknownArgument;
300  return false;
301  }
302 
303  switch(arg->argType){
304  case DummyRow::String:
305  if(p->put(arg->name, value))
306  return true;
307  break;
308  case DummyRow::Int:{
309  Uint32 i;
310  int c = sscanf(value, "%u", &i);
311  if(c != 1){
312  ctx->m_status = Parser<Dummy>::TypeMismatch;
313  return false;
314  }
315  if(p->put(arg->name, i))
316  return true;
317  break;
318  }
319 
320  case DummyRow::Properties: {
321  abort();
322  break;
323  }
324  default:
325  ctx->m_status = Parser<Dummy>::UnknownArgumentType;
326  return false;
327  }
328  if(p->getPropertiesErrno() == E_PROPERTIES_ELEMENT_ALREADY_EXISTS){
329  ctx->m_status = Parser<Dummy>::ArgumentGivenTwice;
330  return false;
331  }
332 
333  abort();
334  return false;
335 }
336 
337 bool
338 ParserImpl::checkMandatory(Context* ctx, const Properties* props){
339  const DummyRow * tmp = &ctx->m_currentCmd[1];
340  while(tmp->name != 0 && tmp->type == DummyRow::Arg){
341  if(tmp->argRequired == ParserRow<Dummy>::Mandatory &&
342  !props->contains(tmp->name)){
344  ctx->m_currentArg = tmp;
345  return false;
346  }
347  tmp++;
348  }
349  return true;
350 }
351 
353 
354 #ifdef TEST_PARSER
355 #include <NdbTap.hpp>
356 
357 TAPTEST(Parser)
358 {
359  char *str, *name, *value;
360 
361  //split modifies arg so dup
362  str = strdup("x=c:\\windows");
363  OK(split(str, &name, &value));
364  OK(!strcmp(name, "x"));
365  OK(!strcmp(value, "c:\\windows"));
366 
367  return 1;
368 }
369 #endif