bes  Updated for version 3.20.13
util_ff.cc
1 
2 // -*- mode: c++; c-basic-offset:4 -*-
3 
4 // This file is part of ff_handler a FreeForm API handler for the OPeNDAP
5 // DAP2 data server.
6 
7 // Copyright (c) 2005 OPeNDAP, Inc.
8 // Author: James Gallagher <jgallagher@opendap.org>
9 //
10 // This is free software; you can redistribute it and/or modify it under the
11 // terms of the GNU Lesser General Public License as published by the Free
12 // Software Foundation; either version 2.1 of the License, or (at your
13 // option) any later version.
14 //
15 // This software is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
18 // 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 1997-99
27 // Please read the full copyright statement in the file COPYRIGHT.
28 //
29 // Authors: reza (Reza Nekovei)
30 
31 // Utility functions for the FreeForm data server.
32 //
33 // jhrg 3/29/96
34 
35 #include "config_ff.h"
36 
37 static char rcsid[] not_used =
38  { "$Id$" };
39 
40 #ifndef WIN32
41 #include <unistd.h> // for access
42 #else
43 #define F_OK 0
44 #endif
45 
46 #include <iostream>
47 #include <sstream>
48 #include <fstream>
49 #include <string>
50 #include <vector>
51 #include <cstdlib>
52 
53 #include <BESRegex.h>
54 #include <BESDebug.h>
55 
56 #include <libdap/BaseType.h>
57 #include <libdap/Byte.h>
58 #include <libdap/Int16.h>
59 #include <libdap/Int32.h>
60 #include <libdap/UInt16.h>
61 #include <libdap/UInt32.h>
62 #include <libdap/Float32.h>
63 #include <libdap/Float64.h>
64 #include <libdap/InternalErr.h>
65 #include <libdap/dods-limits.h>
66 #include <libdap/util.h>
67 #include <libdap/debug.h>
68 
69 #include "BESRegex.h"
70 #include "BESUtil.h"
71 #include "BESDebug.h"
72 
73 #include "FFRequestHandler.h"
74 #include "util_ff.h"
75 
76 using namespace std;
77 
91 static string &remove_paths(string &src)
92 {
93  size_t p1 = src.find_first_of('/');
94  if (p1 == string::npos)
95  return src;
96  size_t p2 = src.find_last_of('/');
97  // The string has one '/', not a big deal
98  if (p2 == p1)
99  return src;
100 
101  src.erase(p1, p2-p1+1);
102  return src;
103 }
104 
105 // These two functions are defined in FFND/error.c. They were originally
106 // static functions. I used them to read error strings since FreeForm
107 // did not have a good way to get the error text. jhrg 9/11/12
108 extern "C" FF_ERROR_PTR pull_error(void);
109 extern "C" BOOLEAN is_a_warning(FF_ERROR_PTR error);
110 
124 static string freeform_error_message()
125 {
126  FF_ERROR_PTR error = pull_error();
127  if (!error)
128  throw BESInternalError("Called the FreeForm error message code, but there was no error.", __FILE__, __LINE__);
129 
130  ostringstream oss;
131  do {
132  if (is_a_warning(error))
133  oss << "Warning: ";
134  else
135  oss << "Error: ";
136 
137  // if either of these contain a pathname, remove it
138  string problem = error->problem;
139  string message = error->message;
140  oss << remove_paths(problem) << ": " << remove_paths(message) << endl;
141 
142  ff_destroy_error (error);
143  error = pull_error();
144  } while (error);
145 
146  return oss.str();
147 }
148 
161 long read_ff(const char *dataset, const char *if_file, const char *o_format, char *o_buffer, unsigned long bsize)
162 {
163  FF_BUFSIZE_PTR newform_log = NULL;
164  FF_STD_ARGS_PTR std_args = NULL;
165 
166  try {
167  std_args = ff_create_std_args();
168  if (!std_args)
169  throw BESInternalError("FreeForm could not allocate a 'stdargs' object.", __FILE__, __LINE__);
170 
171  // set the std_arg structure values - cast away const for dataset, if_file,
172  // and o_format.
173  std_args->error_prompt = FALSE;
174  std_args->user.is_stdin_redirected = 0;
175  std_args->input_file = (char*) (dataset);
176  std_args->input_format_file = (char*) (if_file);
177  std_args->output_file = NULL;
178  std_args->output_format_buffer = (char*) (o_format);
179  std_args->log_file = (char *) "/dev/null";
180 #if 0
181  // This just doesn't seem to work within the BES framework. jhrg 9/11/12
182  std_args->log_file = (char *)"/tmp/ffdods.log";
183 #endif
184 
185  // memory freed automatically on exit
186  vector<char> l_bufsz(sizeof(FF_BUFSIZE));
187  //bufsz = (FF_BUFSIZE *)l_bufsz.data();
188  FF_BUFSIZE_PTR bufsz = reinterpret_cast<FF_BUFSIZE_PTR>(l_bufsz.data());
189 
190  bufsz->usage = 1;
191  bufsz->buffer = o_buffer;
192  bufsz->total_bytes = (FF_BSS_t) bsize;
193  bufsz->bytes_used = (FF_BSS_t) 0;
194 
195  std_args->output_bufsize = bufsz;
196 
197  newform_log = ff_create_bufsize(SCRATCH_QUANTA);
198  if (!newform_log)
199  throw BESInternalError("FreeForm could not allocate a 'newform_log' object.", __FILE__, __LINE__);
200 
201  // passing 0 for the FILE* param is a wash since a non-null
202  // value for newform_log selects that as the 'logging' sink.
203  // jhrg 9/11/12
204  int status = newform(std_args, newform_log, 0 /*stderr*/);
205 
206  BESDEBUG("ff", "FreeForm: newform returns " << status << endl);
207 
208  if (err_count()) {
209  string message = freeform_error_message();
210  BESDEBUG("ff", "FreeForm: error message " << message << endl);
211  throw BESError(message, BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
212  }
213 
214  ff_destroy_bufsize(newform_log);
215  ff_destroy_std_args(std_args);
216 
217  return bufsz->bytes_used;
218  }
219  catch (...) {
220  if (newform_log)
221  ff_destroy_bufsize(newform_log);
222  if (std_args)
223  ff_destroy_std_args(std_args);
224 
225  throw;
226  }
227 
228  return 0;
229 }
230 
237 void free_ff_char_vector(char **v, int len)
238 {
239  for (int i = 0; i < len; ++i)
240  if (v && v[i])
241  free (v[i]);
242  if (v && len > 0)
243  free (v);
244 }
245 
246 // Given the name of a DODS data type, return the name of the corresponding
247 // FreeForm data type.
248 //
249 // Returns: a const char * if the DODS type maps into a FreeForm type,
250 // otherwise NULL.
251 
252 const string ff_types(Type dods_type)
253 {
254  switch (dods_type) {
255  case dods_byte_c:
256  return "uint8";
257  case dods_int16_c:
258  return "int16";
259  case dods_uint16_c:
260  return "uint16";
261  case dods_int32_c:
262  return "int32";
263  case dods_uint32_c:
264  return "uint32";
265  case dods_float32_c:
266  return "float32";
267  case dods_float64_c:
268  return "float64";
269  case dods_str_c:
270  return "text";
271  case dods_url_c:
272  return "text";
273  default:
274  throw Error("ff_types: DODS type " + D2type_name(dods_type) + " does not map to a FreeForm type.");
275  }
276 }
277 
278 // Given the name of a DODS data type, return the precision of the
279 // corresponding FreeForm data type.
280 //
281 // Returns: a positive integer if the DODS type maps into a FreeForm type,
282 // otherwise -1.
283 
284 int ff_prec(Type dods_type)
285 {
286  switch (dods_type) {
287  case dods_byte_c:
288  case dods_int16_c:
289  case dods_uint16_c:
290  case dods_int32_c:
291  case dods_uint32_c:
292  return 0;
293  case dods_float32_c:
294  return DODS_FLT_DIG;
295  case dods_float64_c:
296  return DODS_DBL_DIG;
297  case dods_str_c:
298  case dods_url_c:
299  return 0;
300  default:
301  throw Error("ff_prec: DODS type " + D2type_name(dods_type) + " does not map to a FreeForm type.");
302  }
303 }
304 
310 const string
311 make_output_format(const string & name, Type type, const int width)
312 {
313  ostringstream str;
314 
315  str << "binary_output_data \"DODS binary output data\"" << endl;
316  str << name << " 1 " << width << " " << ff_types(type)
317  << " " << ff_prec(type) << endl;
318 
319  return str.str();
320 }
321 
322 // format for multi-dimension array
323 const string
324 makeND_output_format(const string & name, Type type, const int width,
325  int ndim, const long *start, const long *edge, const
326  long *stride, string * dname)
327 {
328  ostringstream str;
329  str << "binary_output_data \"DODS binary output data\"" << endl;
330  str << name << " 1 " << width << " ARRAY";
331 
332  for (int i = 0; i < ndim; i++)
333  str << "[" << "\"" << dname[i] << "\" " << start[i] + 1 << " to "
334  << start[i] + (edge[i] - 1) * stride[i] +
335  1 << " by " << stride[i] << " ]";
336 
337  str << " of " << ff_types(type) << " " << ff_prec(type) << endl;
338 
339  DBG(cerr << "ND output format: " << str.str() << endl);
340 
341  return str.str();
342 }
343 
349 const string & format_delimiter(const string & new_delimiter)
350 {
351  static string delimiter = ".";
352 
353  if (new_delimiter != "")
354  delimiter = new_delimiter;
355 
356  return delimiter;
357 }
358 
365 const string & format_extension(const string & new_extension)
366 {
367  static string extension = ".fmt";
368 
369  if (new_extension != "")
370  extension = new_extension;
371 
372  return extension;
373 }
374 
377 static bool
378 cmp_array_conduit(FF_ARRAY_CONDUIT_PTR src_conduit,
379  FF_ARRAY_CONDUIT_PTR trg_conduit)
380 {
381  if (src_conduit->input && trg_conduit->input)
382  return (bool) ff_format_comp(src_conduit->input->fd->format,
383  trg_conduit->input->fd->format);
384  else if (src_conduit->output && trg_conduit->output)
385  return (bool) ff_format_comp(src_conduit->output->fd->format,
386  trg_conduit->output->fd->format);
387  else
388  return false;
389 }
390 
391 static int merge_redundant_conduits(FF_ARRAY_CONDUIT_LIST conduit_list)
392 {
393  int error = 0;
394  error = list_replace_items((pgenobj_cmp_t) cmp_array_conduit,
395  conduit_list);
396  return (error);
397 }
398 
407 int SetDodsDB(FF_STD_ARGS_PTR std_args, DATA_BIN_HANDLE dbin_h, char *Msgt)
408 {
409  FORMAT_DATA_LIST format_data_list = NULL;
410  int error = 0;
411 
412  assert(dbin_h);
413 
414  if (!dbin_h) {
415  snprintf(Msgt, Msgt_size, "Error: NULL DATA_BIN_HANDLE in %s", ROUTINE_NAME);
416  return (ERR_API);
417  }
418 
419  if (!*dbin_h) {
420  *dbin_h = db_make(std_args->input_file);
421 
422  if (!*dbin_h) {
423  snprintf(Msgt, Msgt_size, "Error in Standard Data Bin");
424  return (ERR_MEM_LACK);
425  }
426  }
427 
428  /* Now set the formats and the auxiliary files */
429 
430  if (db_set(*dbin_h, DBSET_READ_EQV, std_args->input_file)) {
431  snprintf(Msgt, Msgt_size, "Error making name table for %s",
432  std_args->input_file);
433  return (DBSET_READ_EQV);
434  }
435 
436  if (db_set(*dbin_h,
437  DBSET_INPUT_FORMATS,
438  std_args->input_file,
439  std_args->output_file,
440  std_args->input_format_file,
441  std_args->input_format_buffer,
442  std_args->input_format_title, &format_data_list)) {
443  if (format_data_list)
444  dll_free_holdings(format_data_list);
445 
446  snprintf(Msgt, Msgt_size, "Error setting an input format for %s",
447  std_args->input_file);
448  return (DBSET_INPUT_FORMATS);
449  }
450 
451  error =
452  db_set(*dbin_h, DBSET_CREATE_CONDUITS, std_args, format_data_list);
453  dll_free_holdings(format_data_list);
454  if (error) {
455  snprintf(Msgt, Msgt_size, "Error creating array information for %s",
456  std_args->input_file);
457  return (DBSET_CREATE_CONDUITS);
458  }
459 
460  if (db_set(*dbin_h, DBSET_HEADER_FILE_NAMES, FFF_INPUT,
461  std_args->input_file)) {
462  snprintf(Msgt, Msgt_size, "Error determining input header file names for %s",
463  std_args->input_file);
464  return (DBSET_HEADER_FILE_NAMES);
465  }
466 
467  if (db_set(*dbin_h, DBSET_HEADERS)) {
468  snprintf(Msgt, Msgt_size, "getting header file for %s", std_args->input_file);
469  return (DBSET_HEADERS);
470  }
471 
472 
473  if (db_set(*dbin_h, DBSET_INIT_CONDUITS, FFF_DATA,
474  std_args->records_to_read)) {
475  snprintf(Msgt, Msgt_size, "Error creating array information for %s",
476  std_args->input_file);
477  return (DBSET_INIT_CONDUITS);
478  }
479 
480  error = merge_redundant_conduits((*dbin_h)->array_conduit_list);
481  if (error)
482  snprintf(Msgt, Msgt_size, "Error merging redundent conduits");
483 
484  return (error);
485 }
486 
492 long Records(const string &filename)
493 {
494  int error = 0;
495  DATA_BIN_PTR dbin = NULL;
496  FF_STD_ARGS_PTR SetUps = NULL;
497  PROCESS_INFO_LIST pinfo_list = NULL;
498  PROCESS_INFO_PTR pinfo = NULL;
499  static char Msgt[255];
500 
501  SetUps = ff_create_std_args();
502  if (!SetUps) {
503  return -1;
504  }
505 
507  SetUps->user.is_stdin_redirected = 0;
508  SetUps->input_file = const_cast<char*>(filename.c_str());
509 
510  SetUps->output_file = NULL;
511 
512  error = SetDodsDB(SetUps, &dbin, Msgt);
513  if (error && error < ERR_WARNING_ONLY) {
514  ff_destroy_std_args(SetUps);
515  db_destroy(dbin);
516  return -1;
517  }
518 
519  ff_destroy_std_args(SetUps);
520 
521  error = db_ask(dbin, DBASK_PROCESS_INFO, FFF_INPUT | FFF_DATA, &pinfo_list);
522  if (error)
523  return (-1);
524 
525  pinfo_list = dll_first(pinfo_list);
526 
527  pinfo = ((PROCESS_INFO_PTR) (pinfo_list)->data.u.pi);
528 
529  long num_records = PINFO_SUPER_ARRAY_ELS(pinfo);
530 
531  ff_destroy_process_info_list(pinfo_list);
532  db_destroy(dbin);
533 
534  return num_records;
535 }
536 
537 
538 bool file_exist(const char *filename)
539 {
540  return access(filename, F_OK) == 0;
541 }
542 
555 const string
556 find_ancillary_rss_formats(const string & dataset, const string & /* delimiter */,
557  const string & /* extension */)
558 {
559  string FormatFile;
560  string FormatPath = FFRequestHandler::get_RSS_format_files();
561  string BaseName;
562  string FileName;
563 
564  // Separate the filename from the pathname, for both plain files
565  // and cached decompressed files (ones with '#' in their names).
566  size_t delim = dataset.rfind("#");
567  if (delim != string::npos)
568  FileName = dataset.substr(delim + 1, dataset.length() - delim + 1);
569  else {
570  delim = dataset.rfind("/");
571  if (delim != string::npos)
572  FileName = dataset.substr(delim + 1, dataset.length() - delim + 1);
573  else
574  FileName = dataset;
575  }
576 
577  // The file/dataset name has to have an underscore...
578  delim = FileName.find("_");
579  if ( delim != string::npos ) {
580  BaseName = FileName.substr(0,delim+1);
581  }
582  else {
583  throw Error("Could not find input format for: " + dataset);
584  }
585 
586  // Now determine if this is files holds averaged or daily data.
587  string DatePart = FileName.substr(delim+1, FileName.length()-delim+1);
588 
590 
591  if ( (DatePart.find("_") != string::npos) || (DatePart.length() < 10) )
592  FormatFile = FormatPath + BaseName + "averaged.fmt";
593  else
594  FormatFile = FormatPath + BaseName + "daily.fmt";
595 
596  return string(FormatFile);
597 }
598 
611 const string
612 find_ancillary_rss_das(const string & dataset, const string & /* delimiter */,
613  const string & /* extension */)
614 {
615  string FormatFile;
616  string FormatPath = FFRequestHandler::get_RSS_format_files();
617  string BaseName;
618  string FileName;
619 
620  size_t delim = dataset.rfind("#");
621  if (delim != string::npos)
622  FileName = dataset.substr(delim + 1, dataset.length() - delim + 1);
623  else {
624  delim = dataset.rfind("/");
625  if (delim != string::npos)
626  FileName = dataset.substr(delim + 1, dataset.length() - delim + 1);
627  else
628  FileName = dataset;
629  }
630 
631  delim = FileName.find("_");
632  if ( delim != string::npos ) {
633  BaseName = FileName.substr(0,delim+1);
634  }
635  else {
636  string msg = "Could not find input format for: ";
637  msg += dataset;
638  throw InternalErr(msg);
639  }
640 
641  string DatePart = FileName.substr(delim+1, FileName.length()-delim+1);
643 
644  if ( (DatePart.find("_") != string::npos) || (DatePart.length() < 10) )
645  FormatFile = FormatPath + BaseName + "averaged.das";
646  else
647  FormatFile = FormatPath + BaseName + "daily.das";
648 
649  return string(FormatFile);
650 }
651 
652 // These functions are used by the Date/Time Factory classes but they might
653 // be generally useful in writing server-side functions. 1/21/2002 jhrg
654 
655 bool is_integer_type(BaseType * btp)
656 {
657  switch (btp->type()) {
658  case dods_null_c:
659  return false;
660 
661  case dods_byte_c:
662  case dods_int16_c:
663  case dods_uint16_c:
664  case dods_int32_c:
665  case dods_uint32_c:
666  return true;
667 
668  case dods_float32_c:
669  case dods_float64_c:
670  case dods_str_c:
671  case dods_url_c:
672  case dods_array_c:
673  case dods_structure_c:
674  case dods_sequence_c:
675  case dods_grid_c:
676  default:
677  return false;
678  }
679 }
680 
681 bool is_float_type(BaseType * btp)
682 {
683  switch (btp->type()) {
684  case dods_null_c:
685  case dods_byte_c:
686  case dods_int16_c:
687  case dods_uint16_c:
688  case dods_int32_c:
689  case dods_uint32_c:
690  return false;
691 
692  case dods_float32_c:
693  case dods_float64_c:
694  return true;
695 
696  case dods_str_c:
697  case dods_url_c:
698  case dods_array_c:
699  case dods_structure_c:
700  case dods_sequence_c:
701  case dods_grid_c:
702  default:
703  return false;
704  }
705 }
706 
711 dods_uint32 get_integer_value(BaseType * var) throw(InternalErr)
712 {
713  if (!var)
714  return 0;
715 
716  switch (var->type()) {
717  case dods_byte_c:
718  return static_cast<Byte*>(var)->value();
719 
720  case dods_int16_c:
721  return static_cast<Int16*>(var)->value();
722 
723  case dods_int32_c:
724  return static_cast<Int32*>(var)->value();
725 
726  case dods_uint16_c:
727  return static_cast<UInt16*>(var)->value();
728 
729  case dods_uint32_c:
730  return static_cast<UInt32*>(var)->value();
731 
732  default:
733  throw InternalErr(__FILE__, __LINE__,
734  "Tried to get an integer value for a non-integer datatype!");
735  }
736 }
737 
738 dods_float64 get_float_value(BaseType * var) throw(InternalErr)
739 {
740  if (!var)
741  return 0.0;
742 
743  switch (var->type()) {
744  case dods_int16_c:
745  case dods_uint16_c:
746  case dods_int32_c:
747  case dods_uint32_c:
748  return get_integer_value(var);
749 
750  case dods_float32_c:
751  return static_cast<Float32*>(var)->value();
752 
753  case dods_float64_c:
754  return static_cast<Float64*>(var)->value();
755 
756  default:
757  throw InternalErr(__FILE__, __LINE__,
758  "Tried to get an float value for a non-numeric datatype!");
759  }
760 }
761 
762 string get_Regex_format_file(const string & filename)
763 {
764  string::size_type found = filename.find_last_of("/\\");
765  string base_name = filename.substr(found+1);
766  string retVal = "";
767  std::map<string,string> mapFF = FFRequestHandler::get_fmt_regex_map();
768  for (auto rgx = mapFF.begin(); rgx != mapFF.end(); ++ rgx) {
769  BESDEBUG("ff", "get_Regex_format_file() - filename: '" << filename << "'" <<
770  " regex: '" << (*rgx).first << "'" <<
771  " format: '" << (*rgx).second << "'" << endl);
772  BESRegex regex(((*rgx).first).c_str());
773  if ( (unsigned long) regex.match(base_name.c_str(), base_name.length()) == base_name.length() ){
774  retVal = string((*rgx).second);
775  break;
776  }
777  }
778  BESDEBUG("ff", "get_Regex_format_file() - returning format filename: '"<< retVal << "'" << endl);
779  return retVal;
780 }
Base exception class for the BES with basic string message.
Definition: BESError.h:59
exception thrown if internal error encountered
Regular expression matching.
Definition: BESRegex.h:53
static void trim_if_trailing_slash(std::string &value)
If the string ends in a slash, remove it This function works for empty strings (doing nothing)....
Definition: BESUtil.cc:110
Type
Type of JSON value.
Definition: rapidjson.h:664