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