NCBI C++ ToolKit
netservice_protocol_parser.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: netservice_protocol_parser.cpp 80143 2017-11-09 18:11:13Z gouriano $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author: Pavel Ivanov, Victor Joukov
27  *
28  * File Description:
29  * Implementation of protocol parser used for NetCache and NetSchedule.
30  *
31  */
32 
33 #include <ncbi_pch.hpp>
34 
37 
38 
40 
41 
42 const char*
44 {
45  switch (GetErrCode())
46  {
47  case eNoCommand: return "eNoCommand";
48  case eWrongCommand: return "eWrongCommand";
49  case eBadToken: return "eBadToken";
50  case eArgumentsMissing: return "eArgumentsMissing";
51  case eWrongMap: return "eWrongMap";
52  default: return CException::GetErrCodeString();
53  }
54 }
55 
56 
57 template <class T>
58 inline T*
60 {
61  return reinterpret_cast<T*>(reinterpret_cast<const char*>(ptr) + m_CmdDefSize);
62 }
63 
64 
67  const char* str_end,
68  CTempString* token)
69 {
71  const char* s = *str;
72 
73  if (s >= str_end) {
74  return ttype;
75  }
76 
77  const char* tok_start = s;
78  char quote_char;
79  if (*s == '"' || *s == '\'') {
80  quote_char = *s;
81  ++s;
82  tok_start = s;
83  ttype = eNSTT_Str;
84  }
85  else {
86  quote_char = '\0';
87  ttype = eNSTT_Int;
88  if (*s == '-') {
89  ++s;
90  }
91  }
92 
93  for (; s < str_end; ++s) {
94  if (ttype == eNSTT_Str) {
95  if (*s == quote_char) {
96  break;
97  }
98  else if (*s == '\\' && (s + 1 < str_end)) {
99  ++s;
100  }
101  }
102  else if (*s == '=' || isspace(*s))
103  break;
104  else if (ttype == eNSTT_Int && !isdigit(*s)) {
105  ttype = eNSTT_Id;
106  }
107  }
108 
109  if (*s == '=') {
110  ttype = eNSTT_Key;
111  }
112  else if (ttype == eNSTT_Str && *s != quote_char) {
114  "String is not ended correctly: \""
115  << string(tok_start, s - tok_start));
116  }
117  else if (ttype == eNSTT_Int && s > *str && *(s - 1) == '-') {
118  ttype = eNSTT_Id;
119  }
120 
121  token->assign(tok_start, s - tok_start);
122 
123  if (ttype == eNSTT_Id) {
124  if (token->size() > 4 && tok_start[0] == 'I' && tok_start[1] == 'C'
125  && tok_start[2] == '(' && *(s - 1) == ')')
126  {
127  ttype = eNSTT_ICPrefix;
128  }
129  else if (CNetCacheKey::IsValidKey(tok_start, token->size())) {
130  ttype = eNSTT_NCID;
131  }
132  }
133  if (ttype == eNSTT_Key) {
134  ++s;
135  }
136  else {
137  if (ttype == eNSTT_Str) {
138  ++s;
139  }
140  while ((s < str_end) && isspace(*s)) {
141  ++s;
142  }
143  }
144  *str = s;
145 
146  return ttype;
147 }
148 
149 void
151  const void** match_cmd,
152  TNSProtoParams* params)
153 {
154  const char* str = command.data();
155  const char* str_end = str + command.size();
156  CTempString token, cache_name;
157  ENSProtoTokenType ttype;
158 
159  ttype = x_GetToken(&str, str_end, &token);
160  if (ttype == eNSTT_ICPrefix) {
161  cache_name.assign(token.data() + 3, token.size() - 4);
162  ttype = x_GetToken(&str, str_end, &token);
163  }
164  if (ttype != eNSTT_Id) {
166  "Command name is absent: '" << command << "'");
167  }
168 
169  const char* const* cmd_name = m_CmdMapName;
170  const SNSProtoArgument* argsDescr = m_CmdMapArgs;
171  while (*cmd_name) {
172  if (strlen(*cmd_name) == token.size()
173  && strncmp(*cmd_name, token.data(), token.size()) == 0
174  && ((!cache_name.empty() && (argsDescr->flags & fNSPA_ICPrefix))
175  || (cache_name.empty() && !(argsDescr->flags & fNSPA_ICPrefix))))
176  {
177  break;
178  }
179  cmd_name = x_GetNextInCmdMap(cmd_name );
180  argsDescr = x_GetNextInCmdMap(argsDescr);
181  }
182  if (!*cmd_name) {
184  "Unknown command name '" << token << "' in command '"
185  << command << "'");
186  }
187  *match_cmd = cmd_name;
188 
189  if (!cache_name.empty()) {
190  _ASSERT(argsDescr->flags & fNSPA_ICPrefix);
191 
192  (*params)[argsDescr->key] = cache_name;
193  ++argsDescr;
194  }
195 
196  try {
197  ParseArguments(CTempString(str, str_end - str), argsDescr, params);
198  }
199  catch (CNSProtoParserException& ex) {
201  FORMAT("Command being parsed is '" << command << "'"));
202  throw;
203  }
204 }
205 
206 static inline void
208  const SNSProtoArgument* arg_descr)
209 {
210  const char* from = arg_descr->dflt;
211  if (from) {
212  (*params)[arg_descr->key] = from;
213  }
214 }
215 
216 void
218  const SNSProtoArgument* arg_descr,
219  TNSProtoParams* params)
220 {
221  const char* str = args.data();
222  const char* str_end = str + args.size();
224  ENSProtoTokenType val_type = eNSTT_None; // if arglist is empty, it should be successful
225 
226  while (arg_descr->flags != eNSPA_None) // extra arguments are just ignored
227  {
228  val_type = x_GetToken(&str, str_end, &key);
229  if (val_type == eNSTT_Key) {
230  val_type = x_GetToken(&str, str_end, &val);
231  if (val_type == eNSTT_Key) {
233  "Second equal sign met in the parameter value: '"
234  << string(val.data(), val.size() + 1) << "'");
235  }
236  }
237  else {
238  val = key;
239  key.clear();
240  }
241  if (val_type == eNSTT_None && key.empty()) {
242  break;
243  }
244 
245  // token processing here
246  bool matched = false;
247  while (arg_descr->flags != eNSPA_None) {
248  if (arg_descr->flags & fNSPA_Ellipsis) {
249  if (key.empty()) {
251  "Only key=value pairs allowed at the end of command");
252  }
253  matched = true;
254  }
255  else if (arg_descr->flags & fNSPA_Match) {
256  matched = strlen(arg_descr->key) == val.size()
257  && strncmp(arg_descr->key, val.data(), val.size()) == 0;
258  }
259  else {
260  matched = x_IsArgumentMatch(key, val_type, arg_descr);
261  }
262  if (matched) {
263  break;
264  }
265 
266  // Can skip optional arguments
267  if (!(arg_descr->flags & fNSPA_Optional)) {
268  break;
269  }
270  bool is_chain = (arg_descr->flags & fNSPA_Chain) != 0;
271  do {
272  s_SetDefaultValue(params, arg_descr);
273  ++arg_descr;
274  }
275  while (arg_descr->flags != eNSPA_None && is_chain);
276  }
277 
278  if (arg_descr->flags & fNSPA_Obsolete) {
279  NCBI_THROW_FMT(CNSProtoParserException, eWrongArgument,
280  "Argument " << arg_descr->key << " is obsolete."
281  " It cannot be used in command now.");
282  }
283  else if (arg_descr->flags == eNSPA_None || !matched) {
284  break;
285  }
286 
287  // accept argument
288  if (arg_descr->flags & fNSPA_Ellipsis) {
289  (*params)[key] = val;
290  } else {
291  (*params)[arg_descr->key]
292  = (arg_descr->flags & fNSPA_Match)? "match" : val;
293  }
294  // Process OR by skipping argument descriptions until OR flags is set
295  while (arg_descr->flags != eNSPA_None
296  && arg_descr->flags & fNSPA_Or)
297  {
298  ++arg_descr;
299  s_SetDefaultValue(params, arg_descr);
300  }
301  if (!(arg_descr->flags & fNSPA_Ellipsis)) {
302  // Ellipsis consumes all the rest, so we increment this only
303  // for regular, non-ellipsis arguments.
304  ++arg_descr;
305  }
306  }
307  // Check that remaining arg descriptors are optional
308  while (arg_descr->flags != eNSPA_None
309  && arg_descr->flags & fNSPA_Optional)
310  {
311  s_SetDefaultValue(params, arg_descr);
312  ++arg_descr;
313  }
314 
315  if (arg_descr->flags != eNSPA_None) {
316  NCBI_THROW_FMT(CNSProtoParserException, eArgumentsMissing,
317  "Not all required parameters given. "
318  "Next parameter needed is '" << arg_descr->key << "'");
319  }
320 }
321 
322 // Compare actual token type with argument type.
323 // Int type IS-A Id type.
324 bool
326  ENSProtoTokenType val_type,
327  const SNSProtoArgument* arg_descr)
328 {
329  if (arg_descr->flags & fNSPA_Clear) {
330  return false;
331  }
332  if (arg_descr->flags & fNSPA_ICPrefix) {
334  "IC Prefix can be only the first in the arguments list "
335  "of a command map.");
336  }
337  if (!key.empty() && !(strlen(arg_descr->key) == key.size()
338  && strncmp(arg_descr->key, key.data(), key.size()) == 0))
339  {
340  return false;
341  }
342  if (val_type == eNSTT_ICPrefix || val_type == eNSTT_None) {
343  val_type = eNSTT_Id;
344  }
345  if (arg_descr->atype == eNSPT_Str
346  || (arg_descr->atype == eNSPT_Id && (val_type == eNSTT_Int
347  || val_type == eNSTT_NCID))
348  || static_cast<int>(arg_descr->atype) == static_cast<int>(val_type))
349  {
350  return true;
351  }
352 
353  return false;
354 }
355 
@ eNoCommand
Empty line in protocol or invalid command.
virtual const char * GetErrCodeString(void) const override
Get error code interpreted as text.
void ParseCommand(CTempString command, const void **match_cmd, TNSProtoParams *params)
const SNSProtoArgument * m_CmdMapArgs
bool x_IsArgumentMatch(const CTempString key, ENSProtoTokenType val_type, const SNSProtoArgument *arg_descr)
ENSProtoTokenType x_GetToken(const char **str, const char *str_end, CTempString *token)
void ParseArguments(CTempString str, const SNSProtoArgument *arg_descr, TNSProtoParams *params)
CTempString implements a light-weight string on top of a storage buffer whose lifetime management is ...
Definition: tempstr.hpp:65
#define T(s)
Definition: common.h:230
static const char * str(char *buf, int n)
Definition: stats.c:84
#define DIAG_COMPILE_INFO
Make compile time diagnostic information object to use in CNcbiDiag and CException.
Definition: ncbidiag.hpp:170
TErrCode GetErrCode(void) const
Get error code.
Definition: ncbiexpt.cpp:453
#define NCBI_THROW(exception_class, err_code, message)
Generic macro to throw an exception, given the exception class, error code and message string.
Definition: ncbiexpt.hpp:704
#define NCBI_THROW_FMT(exception_class, err_code, message)
The same as NCBI_THROW but with message processed as output to ostream.
Definition: ncbiexpt.hpp:719
virtual const char * GetErrCodeString(void) const
Get error code interpreted as text.
Definition: ncbiexpt.cpp:444
#define FORMAT(message)
Format message using iostreams library.
Definition: ncbiexpt.hpp:672
void AddBacklog(const CDiagCompileInfo &info, const string &message, EDiagSev severity=eDiag_Error)
Add a message to backlog (to re-throw the same exception then).
Definition: ncbiexpt.cpp:274
static bool IsValidKey(const char *key_str, size_t key_len, CCompoundIDPool::TInstance id_pool=NULL)
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
CTempString & assign(const char *src_str, size_type len)
Assign new values to the content of the a string.
Definition: tempstr.hpp:733
const char * data(void) const
Return a pointer to the array represented.
Definition: tempstr.hpp:313
bool empty(void) const
Return true if the represented string is empty (i.e., the length is zero)
Definition: tempstr.hpp:334
size_type size(void) const
Return the length of the represented array.
Definition: tempstr.hpp:327
const struct ncbi::grid::netcache::search::fields::KEY key
int strncmp(const char *str1, const char *str2, size_t count)
Definition: odbc_utils.hpp:133
int isspace(Uchar c)
Definition: ncbictype.hpp:69
int isdigit(Uchar c)
Definition: ncbictype.hpp:64
NetCache client specs.
const char * command
static void s_SetDefaultValue(TNSProtoParams *params, const SNSProtoArgument *arg_descr)
@ fNSPA_Match
This argument is exact match with "key" field and uses "dflt" field as value as opposed to default us...
@ fNSPA_Or
This argument is ORed to next a|b as opposed to default sequence a b.
@ fNSPA_Ellipsis
Just parse the rest as key=value, do not check anything, put these pairs in map.
@ fNSPA_Clear
Do not match this arg, just set to default.
@ fNSPA_Optional
The argument may be omitted.
@ fNSPA_Obsolete
Argument is now obsolete and using it causes parser error.
@ fNSPA_Chain
If the argument is absent, whole chain is ignored.
@ fNSPA_ICPrefix
Special value for IC prefix that precedes command itself.
@ eNSPA_None
end of arg list, intentionally set to 0
#define _ASSERT
Modified on Tue Sep 03 10:58:36 2024 by modify_doxy.py rev. 669887