libdap  Updated for version 3.18.3
parser-util.cc
1 
2 // -*- mode: c++; c-basic-offset:4 -*-
3 
4 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
5 // Access Protocol.
6 
7 // Copyright (c) 2002,2003 OPeNDAP, Inc.
8 // Author: James Gallagher <jgallagher@opendap.org>
9 //
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
14 //
15 // This library 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 GNU
18 // Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25 
26 // (c) COPYRIGHT URI/MIT 1995-1999
27 // Please read the full copyright statement in the file COPYRIGHT_URI.
28 //
29 // Authors:
30 // jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
31 
32 // These functions are utility functions used by the various DAP parsers (the
33 // DAS, DDS and constraint expression parsers).
34 // jhrg 9/7/95
35 
36 #include "config.h"
37 
38 #include <cerrno>
39 #include <cassert>
40 #include <cstring>
41 #include <cmath>
42 #include <cstdlib>
43 
44 #include <iostream>
45 #include <sstream>
46 
47 // We wrap VC++ 6.x strtod() to account for a short comming
48 // in that function in regards to "NaN".
49 #ifdef WIN32
50 #include <limits>
51 double w32strtod(const char *, char **);
52 #endif
53 
54 #include "Error.h"
55 #include "debug.h"
56 #include "parser.h" // defines constants such as ID_MAX
57 #include "dods-limits.h"
58 #include "util.h" // Jose Garcia: for append_long_to_string.
59 
60 using std::cerr;
61 using std::endl;
62 
63 #ifdef WIN32
64 // VC++ 6.x strtod() doesn't recognize "NaN". Account for it
65 // by wrapping it around a check for the Nan string. Use of
66 // the product is obsolete as of 1/2007, but it is unknown if
67 // the issue is still there in later releases of that product.
68 // ROM - 01/2007
69 double w32strtod(const char *val, char **ptr)
70 {
71  // Convert the two char arrays to compare to strings.
72  string *sval = new string(val);
73  string *snan = new string("NaN");
74 
75  // If val doesn't contain "NaN|Nan|nan|etc", use strtod as
76  // provided.
77  if (stricmp(sval->c_str(), snan->c_str()) != 0)
78  return (strtod(val, ptr));
79 
80  // But if it does, return the bit pattern for Nan and point
81  // the parsing ptr arg at the trailing '\0'.
82  *ptr = (char *) val + strlen(val);
83  return (std::numeric_limits < double >::quiet_NaN());
84 }
85 #endif
86 
87 namespace libdap {
88 
89 // Deprecated, but still used by the HDF4 EOS server code.
90 void
91 parse_error(parser_arg * arg, const char *msg, const int line_num,
92  const char *context)
93 {
94  // Jose Garcia
95  // This assert(s) is (are) only for developing purposes
96  // For production servers remove it by compiling with NDEBUG
97  assert(arg);
98  assert(msg);
99 
100  arg->set_status(FALSE);
101 
102  string oss = "";
103 
104  if (line_num != 0) {
105  oss += "Error parsing the text on line ";
106  append_long_to_string(line_num, 10, oss);
107  }
108  else {
109  oss += "Parse error.";
110  }
111 
112  if (context)
113  oss += (string) " at or near: " + context + (string) "\n" + msg
114  + (string) "\n";
115  else
116  oss += (string) "\n" + msg + (string) "\n";
117 
118  arg->set_error(new Error(unknown_error, oss));
119 }
120 
121 void
122 parse_error(const char *msg, const int line_num, const char *context)
123 {
124  // Jose Garcia
125  // This assert(s) is (are) only for developing purposes
126  // For production servers remove it by compiling with NDEBUG
127  assert(msg);
128 
129  string oss = "";
130 
131  if (line_num != 0) {
132  oss += "Error parsing the text on line ";
133  append_long_to_string(line_num, 10, oss);
134  }
135  else {
136  oss += "Parse error.";
137  }
138 
139  if (context)
140  oss += (string) " at or near: " + context + (string) "\n" + msg
141  + (string) "\n";
142  else
143  oss += (string) "\n" + msg + (string) "\n";
144 
145  throw Error(malformed_expr, oss);
146 }
147 
148 // context comes from the parser and will always be a char * unless the
149 // parsers change dramatically.
150 void
151 parse_error(const string & msg, const int line_num, const char *context)
152 {
153  parse_error(msg.c_str(), line_num, context);
154 }
155 
156 void save_str(char *dst, const char *src, const int line_num)
157 {
158  if (strlen(src) >= ID_MAX)
159  parse_error(string("The word `") + string(src)
160  + string("' is too long (it should be no longer than ")
161  + long_to_string(ID_MAX) + string(")."), line_num);
162 
163  strncpy(dst, src, ID_MAX);
164  dst[ID_MAX - 1] = '\0'; /* in case ... */
165 }
166 
167 void save_str(string & dst, const char *src, const int)
168 {
169  dst = src;
170 }
171 
172 bool is_keyword(string id, const string & keyword)
173 {
174  downcase(id);
175  id = prune_spaces(id);
176  DBG(cerr << "is_keyword: " << keyword << " = " << id << endl);
177  return id == keyword;
178 }
179 
190 int check_byte(const char *val)
191 {
192  char *ptr;
193  long v = strtol(val, &ptr, 0);
194 
195  if ((v == 0 && val == ptr) || *ptr != '\0') {
196  return FALSE;
197  }
198 
199  DBG(cerr << "v: " << v << endl);
200 
201  // We're very liberal here with values. Anything that can fit into 8 bits
202  // is allowed through. Clients will have to deal with the fact that the
203  // ASCII representation for the value might need to be tweaked. This is
204  // especially the case for Java clients where Byte datatypes are
205  // signed. 3/20/2000 jhrg
206  if ((v < 0 && v < DODS_SCHAR_MIN)
207  || (v > 0 && static_cast < unsigned long >(v) > DODS_UCHAR_MAX))
208  return FALSE;
209 
210  return TRUE;
211 }
212 
213 // This version of check_int will pass base 8, 10 and 16 numbers when they
214 // use the ANSI standard for string representation of those number bases.
215 
216 int check_int16(const char *val)
217 {
218  char *ptr;
219  long v = strtol(val, &ptr, 0); // `0' --> use val to determine base
220 
221  if ((v == 0 && val == ptr) || *ptr != '\0') {
222  return FALSE;
223  }
224  // Don't use the constant from limits.h, use the ones in dods-limits.h
225  if (v > DODS_SHRT_MAX || v < DODS_SHRT_MIN) {
226  return FALSE;
227  }
228 
229  return TRUE;
230 }
231 
232 int check_uint16(const char *val)
233 {
234  char *ptr;
235  unsigned long v = strtol(val, &ptr, 0);
236 
237  if ((v == 0 && val == ptr) || *ptr != '\0') {
238  return FALSE;
239  }
240 
241  if (v > DODS_USHRT_MAX) {
242  return FALSE;
243  }
244 
245  return TRUE;
246 }
247 
248 int check_int32(const char *val)
249 {
250  char *ptr;
251  errno = 0;
252  long v = strtol(val, &ptr, 0); // `0' --> use val to determine base
253 
254  if ((v == 0 && val == ptr) || *ptr != '\0') {
255  return FALSE;
256  }
257 
258  // We need to check errno since strtol return clamps on overflow so the
259  // check against the DODS values below will always pass, even for out of
260  // bounds values in the string. mjohnson 7/20/09
261  if (errno == ERANGE) {
262  return FALSE;
263  }
264  // This could be combined with the above, or course, but I'm making it
265  // separate to highlight the test. On 64-bit linux boxes 'long' may be
266  // 64-bits and so 'v' can hold more than a DODS_INT32. jhrg 3/23/10
267  else if (v > DODS_INT_MAX || v < DODS_INT_MIN) {
268  return FALSE;
269  }
270  else {
271  return TRUE;
272  }
273 }
274 
275 int check_uint32(const char *val)
276 {
277  // Eat whitespace and check for an initial '-' sign...
278  // strtoul allows an initial minus. mjohnson
279  const char* c = val;
280  while (c && isspace(*c)) {
281  c++;
282  }
283  if (c && (*c == '-')) {
284  return FALSE;
285  }
286 
287  char *ptr;
288  errno = 0;
289  unsigned long v = strtoul(val, &ptr, 0);
290 
291  if ((v == 0 && val == ptr) || *ptr != '\0') {
292  return FALSE;
293  }
294 
295  // check overflow first, or the below check is invalid due to
296  // clamping to the maximum value by strtoul
297  // maybe consider using long long for these checks? mjohnson
298  if (errno == ERANGE) {
299  return FALSE;
300  }
301  // See above.
302  else if (v > DODS_UINT_MAX) {
303  return FALSE;
304  }
305  else {
306  return TRUE;
307  }
308 }
309 
310 int check_int64(const char *val)
311 {
312  char *ptr;
313  errno = 0;
314  long long v = strtoll(val, &ptr, 0); // `0' --> use val to determine base
315 
316  if ((v == 0 && val == ptr) || *ptr != '\0') {
317  return FALSE;
318  }
319 
320  // We need to check errno since strtol return clamps on overflow so the
321  // check against the DODS values below will always pass, even for out of
322  // bounds values in the string. mjohnson 7/20/09
323  if (errno == ERANGE) {
324  return FALSE;
325  }
326 #if 0
327  // This could be combined with the above, or course, but I'm making it
328  // separate to highlight the test. On 64-bit linux boxes 'long' may be
329  // 64-bits and so 'v' can hold more than a DODS_INT32. jhrg 3/23/10
330  //
331  // Removed - Coverity says it can never be false. Makes sense. jhrg 5/10/16
332  else if (v <= DODS_LLONG_MAX && v >= DODS_LLONG_MIN) {
333  return FALSE;
334  }
335 #endif
336  else {
337  return TRUE;
338  }
339 }
340 
341 int check_uint64(const char *val)
342 {
343  // Eat whitespace and check for an initial '-' sign...
344  // strtoul allows an initial minus. mjohnson
345  const char* c = val;
346  while (c && isspace(*c)) {
347  c++;
348  }
349  if (c && (*c == '-')) {
350  return FALSE;
351  }
352 
353  char *ptr;
354  errno = 0;
355  unsigned long long v = strtoull(val, &ptr, 0);
356 
357  if ((v == 0 && val == ptr) || *ptr != '\0') {
358  return FALSE;
359  }
360 
361  if (errno == ERANGE) {
362  return FALSE;
363  }
364  else if (v > DODS_ULLONG_MAX) { // 2^61
365  return FALSE;
366  }
367  else {
368  return v;
369  }
370 }
371 
372 // Check first for system errors (like numbers so small they convert
373 // (erroneously) to zero. Then make sure that the value is within
374 // limits.
375 
376 int check_float32(const char *val)
377 {
378  char *ptr;
379  errno = 0; // Clear previous value. Fix for the 64bit
380  // IRIX from Rob Morris. 5/21/2001 jhrg
381 
382 #ifdef WIN32
383  double v = w32strtod(val, &ptr);
384 #else
385  double v = strtod(val, &ptr);
386 #endif
387 
388  DBG(cerr << "v: " << v << ", ptr: " << ptr
389  << ", errno: " << errno << ", val==ptr: " << (val == ptr) << endl);
390 
391  if (errno == ERANGE || (v == 0.0 && val == ptr) || *ptr != '\0')
392  return FALSE;
393 
394 #if 0
395  if ((v == 0.0 && (val == ptr || errno == HUGE_VAL || errno == ERANGE))
396  || *ptr != '\0') {
397  return FALSE;
398  }
399 #endif
400 
401  DBG(cerr << "fabs(" << val << ") = " << fabs(v) << endl);
402  double abs_val = fabs(v);
403  if (abs_val > DODS_FLT_MAX
404  || (abs_val != 0.0 && abs_val < DODS_FLT_MIN))
405  return FALSE;
406 
407  return TRUE;
408 }
409 
410 int check_float64(const char *val)
411 {
412  DBG(cerr << "val: " << val << endl);
413  char *ptr;
414  errno = 0; // Clear previous value. 5/21/2001 jhrg
415 
416 #ifdef WIN32
417  double v = w32strtod(val, &ptr);
418 #else
419  double v = strtod(val, &ptr);
420 #endif
421 
422  DBG(cerr << "v: " << v << ", ptr: " << ptr
423  << ", errno: " << errno << ", val==ptr: " << (val == ptr) << endl);
424 
425 
426  if (errno == ERANGE || (v == 0.0 && val == ptr) || *ptr != '\0')
427  return FALSE;
428 #if 0
429  if ((v == 0.0 && (val == ptr || errno == HUGE_VAL || errno == ERANGE))
430  || *ptr != '\0') {
431  return FALSE;
432  }
433 #endif
434  DBG(cerr << "fabs(" << val << ") = " << fabs(v) << endl);
435  double abs_val = fabs(v);
436  if (abs_val > DODS_DBL_MAX
437  || (abs_val != 0.0 && abs_val < DODS_DBL_MIN))
438  return FALSE;
439 
440  return TRUE;
441 }
442 
443 long long get_int64(const char *val)
444 {
445  char *ptr;
446  errno = 0;
447  long long v = strtoll(val, &ptr, 0); // `0' --> use val to determine base
448 
449  if ((v == 0 && val == ptr) || *ptr != '\0') {
450  throw Error("The value '" + string(val) + "' contains extra characters.");
451  }
452 
453  // We need to check errno since strtol return clamps on overflow so the
454  // check against the DODS values below will always pass, even for out of
455  // bounds values in the string. mjohnson 7/20/09
456  if (errno == ERANGE) {
457  throw Error("The value '" + string(val) + "' is out of range.");
458  }
459 
460 #if 0
461  // This could be combined with the above, or course, but I'm making it
462  // separate to highlight the test. On 64-bit linux boxes 'long' may be
463  // 64-bits and so 'v' can hold more than a DODS_INT32. jhrg 3/23/10
464  //
465  // Removed because coverity flags it as useless, which it is until we
466  // have 128-bit ints... jhrg 5/9/16
467  else if (v > DODS_LLONG_MAX || v < DODS_LLONG_MIN) {
468  throw Error("The value '" + string(val) + "' is out of range.");
469  }
470 #endif
471 
472  else {
473  return v;
474  }
475 }
476 
477 unsigned long long get_uint64(const char *val)
478 {
479  // Eat whitespace and check for an initial '-' sign...
480  // strtoul allows an initial minus. mjohnson
481  const char* c = val;
482  while (c && isspace(*c)) {
483  c++;
484  }
485  if (c && (*c == '-')) {
486  throw Error("The value '" + string(val) + "' is not a valid array index.");
487  }
488 
489  char *ptr;
490  errno = 0;
491  unsigned long long v = strtoull(val, &ptr, 0);
492 
493  if ((v == 0 && val == ptr) || *ptr != '\0') {
494  throw Error("The value '" + string(val) + "' contains extra characters.");
495  }
496 
497  if (errno == ERANGE) {
498  throw Error("The value '" + string(val) + "' is out of range.");
499  }
500 #if 0
501  // Coverity; see above. jhrg 5/9/16
502  else if (v > DODS_MAX_ARRAY_INDEX) { // 2^61
503  throw Error("The value '" + string(val) + "' is out of range.");
504  }
505 #endif
506  else {
507  return v;
508  }
509 }
510 
511 double get_float64(const char *val)
512 {
513  DBG(cerr << "val: " << val << endl);
514  char *ptr;
515  errno = 0; // Clear previous value. 5/21/2001 jhrg
516 
517 #ifdef WIN32
518  double v = w32strtod(val, &ptr);
519 #else
520  double v = strtod(val, &ptr);
521 #endif
522 
523  if (errno == ERANGE || (v == 0.0 && val == ptr) || *ptr != '\0')
524  throw Error("The value '" + string(val) + "' is out of range.");;
525 
526  DBG(cerr << "fabs(" << val << ") = " << fabs(v) << endl);
527  double abs_val = fabs(v);
528  if (abs_val > DODS_DBL_MAX || (abs_val != 0.0 && abs_val < DODS_DBL_MIN))
529  throw Error("The value '" + string(val) + "' is out of range.");;
530 
531  return v;
532 }
533 
534 /*
535  Maybe someday we will really check the Urls to see if they are valid...
536 */
537 
538 int check_url(const char *)
539 {
540  return TRUE;
541 }
542 
543 } // namespace libdap
void downcase(string &s)
Definition: util.cc:562
string prune_spaces(const string &name)
Definition: util.cc:458
int check_url(const char *)
Is the value a valid URL?
Definition: parser-util.cc:538
A class for error processing.
Definition: Error.h:90
int check_byte(const char *val)
Is the value a valid byte?
Definition: parser-util.cc:190
void save_str(char *dst, const char *src, const int line_num)
Save a string to a temporary variable during the parse.
Definition: parser-util.cc:156