bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
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
37static 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
76using namespace std;
77
91static 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
108extern "C" FF_ERROR_PTR pull_error(void);
109extern "C" BOOLEAN is_a_warning(FF_ERROR_PTR error);
110
124static 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
161long 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
237void 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
252const 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
284int 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
310const string
311make_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
323const string
324makeND_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
349const 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
364
365const 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
376
377static bool
378cmp_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
391static 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
407int 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
492long 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
538bool file_exist(const char *filename)
539{
540 return access(filename, F_OK) == 0;
541}
542
555const string
556find_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.size() - delim + 1);
569 else {
570 delim = dataset.rfind("/");
571 if (delim != string::npos)
572 FileName = dataset.substr(delim + 1, dataset.size() - 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.size()-delim+1);
588
590
591 if ( (DatePart.find("_") != string::npos) || (DatePart.size() < 10) )
592 FormatFile = FormatPath + BaseName + "averaged.fmt";
593 else
594 FormatFile = FormatPath + BaseName + "daily.fmt";
595
596 return string(FormatFile);
597}
598
611const string
612find_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.size() - delim + 1);
623 else {
624 delim = dataset.rfind("/");
625 if (delim != string::npos)
626 FileName = dataset.substr(delim + 1, dataset.size() - 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.size()-delim+1);
643
644 if ( (DatePart.find("_") != string::npos) || (DatePart.size() < 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
655bool 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
681bool 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
711dods_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
738dods_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
762string 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.size()) == base_name.size() ){
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:66
exception thrown if internal error encountered
Regular expression matching.
Definition BESRegex.h:89
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:113
Type
Type of JSON value.
Definition rapidjson.h:664