1 /*
2 Copyright (c) 2003, 2021, Oracle and/or its affiliates.
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 #ifndef CPCD_PARSER_HPP
26 #define CPCD_PARSER_HPP
27
28 #include "Vector.hpp"
29 #include "Properties.hpp"
30 #include "InputStream.hpp"
31
32 class ParserImpl;
33 template<class T> struct ParserRow;
34
35 //#define PARSER_DEBUG
36 #ifdef PARSER_DEBUG
37 #include "NdbOut.hpp"
38 #define DEBUG(x) \
39 ndbout_c("%s:%d:%s", __FILE__, __LINE__, x);
40 #else
41 #define DEBUG(x)
42 #endif
43
44 /**
45 * A generic parser
46 */
47 template<class T>
48 class Parser {
49 public:
50 /**
51 * Status for parser
52 */
53 enum ParserStatus {
54 Ok = 0,
55 Eof = 1,
56 NoLine = 2,
57 EmptyLine = 3,
58 UnknownCommand = 4,
59 UnknownArgument = 5,
60 TypeMismatch = 6,
61 InvalidArgumentFormat = 7,
62 UnknownArgumentType = 8,
63 CommandWithoutFunction = 9,
64 ArgumentGivenTwice = 10,
65 ExternalStop = 11,
66 MissingMandatoryArgument = 12
67 };
68
69 /**
70 * Context for parse
71 */
72 class Context {
73 public:
Context()74 Context() { m_mutex= NULL; }
75 ParserStatus m_status;
76 const ParserRow<T> * m_currentCmd;
77 const ParserRow<T> * m_currentArg;
78 char * m_currentToken;
79 STATIC_CONST(MaxParseBytes = 512);
80 char m_tokenBuffer[ MaxParseBytes ];
81 NdbMutex *m_mutex;
82
83 Vector<const ParserRow<T> *> m_aliasUsed;
84 };
85
86 /**
87 * Initialize parser
88 */
89 Parser(const ParserRow<T> rows[], class InputStream & in);
90 ~Parser();
91
92 /**
93 * Run parser
94 */
95 bool run(Context &, T &, volatile bool * stop = 0) const;
96
97 /**
98 * Parse only one entry and return Properties object representing
99 * the message
100 */
101 const Properties *parse(Context &, T &);
102
103 private:
104 ParserImpl * impl;
105 };
106
107 template<class T>
108 struct ParserRow {
109 public:
110 enum Type { Cmd, Arg, CmdAlias, ArgAlias, End }; // Put new types before end
111 enum ArgType { String, Int, Properties };
112 enum ArgRequired { Mandatory, Optional };
113 enum ArgMinMax { CheckMinMax, IgnoreMinMax };
114
115 const char * name;
116 const char * realName;
117 Type type;
118 ArgType argType;
119 ArgRequired argRequired;
120 ArgMinMax argMinMax;
121 int minVal;
122 int maxVal;
123 void (T::* function)(typename Parser<T>::Context & ctx,
124 const class Properties& args);
125 const char * description;
126 void *user_value;
127 };
128
129 /**
130 * The void* equivalent implementation
131 */
132 class ParserImpl {
133 public:
134 class Dummy {};
135 typedef ParserRow<Dummy> DummyRow;
136 typedef Parser<Dummy>::Context Context;
137
138
139 ParserImpl(const DummyRow rows[], class InputStream & in);
140 ~ParserImpl();
141
142 bool run(Context *ctx, const class Properties **, volatile bool *) const ;
143
144 static const DummyRow* matchCommand(Context*, const char*, const DummyRow*);
145 static const DummyRow* matchArg(Context*, const char *, const DummyRow *);
146 static bool parseArg(Context*, char*, const DummyRow*, Properties*);
147 static bool checkMandatory(Context*, const Properties*);
148 private:
149 const DummyRow * const m_rows;
150 class InputStream & input;
151
152 void check_parser_rows(const DummyRow* rows) const;
153 };
154
155 template<class T>
156 inline
Parser(const ParserRow<T> rows[],class InputStream & in)157 Parser<T>::Parser(const ParserRow<T> rows[], class InputStream & in) {
158 impl = new ParserImpl((ParserImpl::DummyRow *)rows, in);
159 }
160
161 template<class T>
162 inline
~Parser()163 Parser<T>::~Parser(){
164 delete impl;
165 }
166
167 template<class T>
168 inline
169 bool
run(Context & ctx,T & t,volatile bool * stop) const170 Parser<T>::run(Context & ctx, T & t, volatile bool * stop) const {
171 const Properties * p;
172 DEBUG("Executing Parser<T>::run");
173 if(impl->run((ParserImpl::Context*)&ctx, &p, stop)){
174 const ParserRow<T> * cmd = ctx.m_currentCmd; // Cast to correct type
175 if(cmd == 0){
176 /**
177 * Should happen if run returns true
178 */
179 abort();
180 }
181
182 for(unsigned i = 0; i<ctx.m_aliasUsed.size(); i++){
183 const ParserRow<T> * alias = ctx.m_aliasUsed[i];
184 if(alias->function != 0){
185 /**
186 * Report alias usage with callback (if specified by user)
187 */
188 DEBUG("Alias usage with callback");
189 (t.* alias->function)(ctx, * p);
190 }
191 }
192
193 if(cmd->function == 0){
194 ctx.m_status = CommandWithoutFunction;
195 DEBUG("CommandWithoutFunction");
196 delete p;
197 return false;
198 }
199 (t.* cmd->function)(ctx, * p); // Call the function
200 delete p;
201 return true;
202 }
203 DEBUG("");
204 return false;
205 }
206
207 template<class T>
208 inline
209 const Properties *
parse(Context & ctx,T & t)210 Parser<T>::parse(Context &ctx, T &t) {
211 const Properties * p;
212 volatile bool stop = false;
213 DEBUG("Executing Parser<T>::parse");
214
215 if(impl->run((ParserImpl::Context*)&ctx, &p, &stop)){
216 const ParserRow<T> * cmd = ctx.m_currentCmd; // Cast to correct type
217 if(cmd == 0){
218 /**
219 * Should happen if run returns true
220 */
221 abort();
222 }
223
224 for(unsigned i = 0; i<ctx.m_aliasUsed.size(); i++){
225 const ParserRow<T> * alias = ctx.m_aliasUsed[i];
226 if(alias->function != 0){
227 /**
228 * Report alias usage with callback (if specified by user)
229 */
230 DEBUG("Alias usage with callback");
231 (t.* alias->function)(ctx, * p);
232 }
233 }
234
235 if(cmd->function == 0){
236 DEBUG("CommandWithoutFunction");
237 ctx.m_status = CommandWithoutFunction;
238 return p;
239 }
240 return p;
241 }
242 DEBUG("");
243 return NULL;
244 }
245
246 #endif
247