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