bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
ffdas.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2
3// This file is part of ff_handler a FreeForm API handler for the OPeNDAP
4// DAP2 data server.
5
6// Copyright (c) 2005 OPeNDAP, Inc.
7// Author: James Gallagher <jgallagher@opendap.org>
8//
9// This is free software; you can redistribute it and/or modify it under the
10// terms of the GNU Lesser General Public License as published by the Free
11// Software Foundation; either version 2.1 of the License, or (at your
12// option) any later version.
13//
14// This software is distributed in the hope that it will be useful, but
15// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17// License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22//
23// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24
25// (c) COPYRIGHT URI/MIT 1997-98
26// Please read the full copyright statement in the file COPYRIGHT.
27//
28// Authors: reza (Reza Nekovei)
29
30// This file contains functions which read the variables and their attributes
31// from a netcdf file and build the in-memeory DAS. These functions form the
32// core of the server-side software necessary to extract the DAS from a
33// netcdf data file.
34//
35// ReZa 6/23/97
36
37#include "config_ff.h"
38
39#include <cstdio>
40#include <cstring>
41
42#include <iostream>
43#include <string>
44
45#include <libdap/util.h>
46#include <libdap/DAS.h>
47#include <libdap/Error.h>
48#include <libdap/InternalErr.h>
49
50#include "FreeFormCPP.h"
51#include "FFRequestHandler.h"
52#include "util_ff.h"
53#include "freeform.h"
54
55// Added 7/30/08 as part of the fix for ticket #1163. jhrg
56#define ATTR_STRING_QUOTE_FIX
57
58// Read header information and populate an AttrTable with the information.
59
60static void header_to_attributes(AttrTable *at, DATA_BIN_PTR dbin)
61{
62 PROCESS_INFO_LIST pinfo_list = NULL;
63 PROCESS_INFO_PTR hd_pinfo = NULL;
64
65 char text[256];
66
67 int error = db_ask(dbin, DBASK_PROCESS_INFO, FFF_INPUT | FFF_FILE | FFF_HEADER, &pinfo_list);
68 // A general error indicates that the dataset does not have a header.
69 // Anything else is bad news. 1/29/2001 jhrg
70 if (error) {
71 if (error == ERR_GENERAL) {
72 return;
73 }
74 else {
75 string msg = "Cannot get attribute values. FreeForm error code: ";
76 append_long_to_string((long) error, 10, msg);
77 throw Error(msg);
78 }
79 }
80
81 pinfo_list = dll_first(pinfo_list);
82 hd_pinfo = ((PROCESS_INFO_PTR) (pinfo_list)->data.u.pi);
83 if (hd_pinfo) {
84 VARIABLE_LIST vlist = NULL;
85 VARIABLE_PTR var = NULL;
86
87 vlist = FFV_FIRST_VARIABLE(PINFO_FORMAT(hd_pinfo));
88 var = ((VARIABLE_PTR) (vlist)->data.u.var);
89
90 while (var) {
91 if (IS_EOL(var)) {
92 vlist = (vlist)->next;
93 var = ((VARIABLE_PTR) (vlist)->data.u.var);
94
95 continue;
96 }
97
98 switch (FFV_DATA_TYPE(var)) {
99 case FFV_TEXT:
100 nt_ask(dbin, FFF_FILE | FFF_HEADER, var->name, FFV_TEXT, text);
101#ifndef ATTR_STRING_QUOTE_FIX
102 // Multiple word strings must be quoted.
103 if (strpbrk(text, " \r\t")) { // Any whitespace?
104 string quoted_text;
105 quoted_text = (string)"\"" + text + "\"";
106 at->append_attr(var->name, "STRING",
107 quoted_text.c_str());
108 }
109 else {
110 at->append_attr(var->name, "STRING", text);
111 }
112#else
113 // The new version of libdap provides the quotes when
114 // printing the DAS. The DDX does not need quotes. jhrg
115 // 7/30/08
116 at->append_attr(var->name, "STRING", text);
117#endif
118 break;
119
120 case FFV_INT8:
121 unsigned char int8;
122 nt_ask(dbin, FFF_FILE | FFF_HEADER, var->name, FFV_INT8, &int8);
123 snprintf(text, sizeof(text), "%d", int8);
124 at->append_attr(var->name, "BYTE", text);
125 break;
126
127 case FFV_INT16:
128 short int16;
129 nt_ask(dbin, FFF_FILE | FFF_HEADER, var->name, FFV_INT16, &int16);
130 snprintf(text, sizeof(text), "%d", int16);
131 at->append_attr(var->name, "INT16", text);
132 break;
133
134 case FFV_INT32:
135 int int32;
136 nt_ask(dbin, FFF_FILE | FFF_HEADER, var->name, FFV_INT32, &int32);
137 snprintf(text, sizeof(text), "%d", int32);
138 at->append_attr(var->name, "INT32", text);
139 break;
140
141 case FFV_INT64:
142 long int64;
143 nt_ask(dbin, FFF_FILE | FFF_HEADER, var->name, FFV_INT64, &int64);
144 snprintf(text, sizeof(text), "%ld", int64);
145 at->append_attr(var->name, "INT32", text);
146 break;
147
148 case FFV_UINT8:
149 unsigned char uint8;
150 nt_ask(dbin, FFF_FILE | FFF_HEADER, var->name, FFV_UINT8, &uint8);
151 snprintf(text, sizeof(text), "%d", uint8);
152 at->append_attr(var->name, "BYTE", text);
153 break;
154
155 case FFV_UINT16:
156 unsigned short uint16;
157 nt_ask(dbin, FFF_FILE | FFF_HEADER, var->name, FFV_UINT16, &uint16);
158 snprintf(text, sizeof(text), "%d", uint16);
159 at->append_attr(var->name, "UINT16", text);
160 break;
161
162 case FFV_UINT32:
163 unsigned int uint32;
164 nt_ask(dbin, FFF_FILE | FFF_HEADER, var->name, FFV_UINT32, &uint32);
165 snprintf(text, sizeof(text), "%u", uint32);
166 at->append_attr(var->name, "UINT32", text);
167 break;
168
169 case FFV_UINT64:
170 unsigned long uint64;
171 nt_ask(dbin, FFF_FILE | FFF_HEADER, var->name, FFV_UINT64, &uint64);
172 snprintf(text, sizeof(text), "%lu", uint64);
173 at->append_attr(var->name, "UINT32", text);
174 break;
175
176 case FFV_FLOAT32:
177 float float32;
178 nt_ask(dbin, FFF_FILE | FFF_HEADER, var->name, FFV_FLOAT32, &float32);
179 snprintf(text, sizeof(text), "%f", float32);
180 at->append_attr(var->name, "FLOAT32", text);
181 break;
182
183 case FFV_FLOAT64:
184 double float64;
185 nt_ask(dbin, FFF_FILE | FFF_HEADER, var->name, FFV_FLOAT64, &float64);
186 snprintf(text, sizeof(text), "%f", float64);
187 at->append_attr(var->name, "FLOAT64", text);
188 break;
189
190 case FFV_ENOTE:
191 double enote;
192 nt_ask(dbin, FFF_FILE | FFF_HEADER, var->name, FFV_ENOTE, &enote);
193 snprintf(text, sizeof(text), "%e", enote);
194 at->append_attr(var->name, "FLOAT64", text);
195 break;
196
197 default:
198 throw InternalErr(__FILE__, __LINE__, "Unknown FreeForm type!");
199 }
200 vlist = (vlist)->next;
201 var = ((VARIABLE_PTR) (vlist)->data.u.var);
202 }
203 }
204 if (pinfo_list)
205 ff_destroy_process_info_list(pinfo_list);
206
207}
208
213void read_attributes(string filename, AttrTable *at)
214{
215 int error = 0;
216 FF_BUFSIZE_PTR bufsize = NULL;
217 DATA_BIN_PTR dbin = NULL;
218 FF_STD_ARGS_PTR SetUps = NULL;
219
220 if (!file_exist(filename.c_str()))
221 throw Error((string) "Could not open file " + path_to_filename(filename) + ".");
222
223 // ff_create_std_args uses calloc so the struct's pointers are all initialized
224 // to null.
225 SetUps = ff_create_std_args();
226 if (!SetUps)
227 throw Error("ff_das: Insufficient memory");
228
230 SetUps->user.is_stdin_redirected = 0;
231
232 // Use const_cast because FF only copies the referenced data; the older
233 // version of this handler allocated memory, copied values, leaked the
234 // memory...
235 SetUps->input_file = const_cast<char*>(filename.c_str());
236
237 string iff = "";
238 if (FFRequestHandler::get_RSS_format_support()) {
239 iff = find_ancillary_rss_formats(filename);
240 SetUps->input_format_file = const_cast<char*>(iff.c_str());
241 }
242 // Regex support
243 if (FFRequestHandler::get_Regex_format_support()) {
244 iff = get_Regex_format_file(filename);
245 if (!iff.empty())
246 SetUps->input_format_file = const_cast<char*>(iff.c_str());
247 }
248
249 SetUps->output_file = NULL;
250
251 char Msgt[255];
252 error = SetDodsDB(SetUps, &dbin, Msgt);
253 if (error && error < ERR_WARNING_ONLY) {
254 if (dbin)
255 db_destroy(dbin);
256 ff_destroy_std_args(SetUps);
257 throw Error(Msgt);
258 }
259
260 ff_destroy_std_args(SetUps);
261
262 try {
263 error = db_ask(dbin, DBASK_FORMAT_SUMMARY, FFF_INPUT, &bufsize);
264 if (error) {
265 string msg = "Cannot get Format Summary. FreeForm error code: ";
266 append_long_to_string((long) error, 10, msg);
267 throw Error(msg);
268 }
269
270#ifndef ATTR_STRING_QUOTE_FIX
271 at->append_attr("Server", "STRING",
272 "\"DODS FreeFrom based on FFND release " + FFND_LIB_VER + "\"");
273#else
274 at->append_attr("Server", "STRING", string("DODS FreeFrom based on FFND release ") + FFND_LIB_VER);
275#endif
276
277 header_to_attributes(at, dbin); // throws Error
278 }
279 catch (...) {
280 if (bufsize)
281 ff_destroy_bufsize(bufsize);
282 if (dbin)
283 db_destroy(dbin);
284
285 throw;
286 }
287
288 ff_destroy_bufsize(bufsize);
289 db_destroy(dbin);
290}
291
292static void add_variable_containers(DAS &das, const string &filename) throw (Error)
293{
294 if (!file_exist(filename.c_str()))
295 throw Error(string("ff_dds: Could not open file ") + path_to_filename(filename) + string("."));
296
297 // Setup the DB access.
298 FF_STD_ARGS_PTR SetUps = ff_create_std_args();
299 if (!SetUps)
300 throw Error("Insufficient memory");
301
302 SetUps->user.is_stdin_redirected = 0;
303
304 SetUps->input_file = const_cast<char*>(filename.c_str());
305
306 string iff = "";
307 if (FFRequestHandler::get_RSS_format_support()) {
308 iff = find_ancillary_rss_formats(filename);
309 SetUps->input_format_file = const_cast<char*>(iff.c_str());
310 }
311
312 // Regex support
313 if (FFRequestHandler::get_Regex_format_support()) {
314 iff = get_Regex_format_file(filename);
315 if (!iff.empty())
316 SetUps->input_format_file = const_cast<char*>(iff.c_str());
317 }
318
319 SetUps->output_file = NULL;
320
321 // Set the structure values to create the FreeForm DB
322 char Msgt[255];
323 DATA_BIN_PTR dbin = NULL;
324 int error = SetDodsDB(SetUps, &dbin, Msgt);
325 if (error && error < ERR_WARNING_ONLY) {
326 if (dbin)
327 db_destroy(dbin);
328 ff_destroy_std_args(SetUps);
329 string msg = string(Msgt) + " FreeForm error code: ";
330 append_long_to_string((long) error, 10, msg);
331 throw Error(msg);
332 }
333
334 ff_destroy_std_args(SetUps);
335
336 // These are defined here so that they can be freed in the catch(...)
337 // block below
338 char **var_names_vector = NULL;
339 PROCESS_INFO_LIST pinfo_list = NULL;
340 char **dim_names_vector = NULL;
341
342 try {
343 // Get the names of all the variables.
344 int num_names = 0;
345 error = db_ask(dbin, DBASK_VAR_NAMES, FFF_INPUT | FFF_DATA, &num_names, &var_names_vector);
346 if (error) {
347 string msg = "Could not get varible list from the input file. FreeForm error code: ";
348 append_long_to_string((long) error, 10, msg);
349 throw Error(msg);
350 }
351
352 // I don't understand why this has to happen here, but maybe it's moved
353 // outside the loop because FreeForm is designed so that the pinfo list
354 // only needs to be accessed once and can be used when working with
355 // any/all of the variables. 4/4/2002 jhrg
356 error = db_ask(dbin, DBASK_PROCESS_INFO, FFF_INPUT | FFF_DATA, &pinfo_list);
357 if (error) {
358 string msg = "Could not get process info for the input file. FreeForm error code: ";
359 append_long_to_string((long) error, 10, msg);
360 throw Error(msg);
361 }
362
363 // For each variable, figure out what its name really is (arrays have
364 // funny names).
365 for (int i = 0; i < num_names; i++) {
366 int num_dim_names = 0;
367 error = db_ask(dbin, DBASK_ARRAY_DIM_NAMES, var_names_vector[i], &num_dim_names, &dim_names_vector);
368 if (error) {
369 string msg = "Could not get array dimension names for variable: " + string(var_names_vector[i])
370 + string(", FreeForm error code: ");
371 append_long_to_string((long) error, 10, msg);
372 throw Error(msg);
373 }
374
375 // Note: FreeForm array names are returned appended to their format
376 // name with '::'.
377 char *cp = NULL;
378 if (num_dim_names == 0) // sequence names
379 cp = var_names_vector[i];
380 else {
381 cp = strstr(var_names_vector[i], "::");
382 // If cp is not null, advance past the "::"
383 if (cp)
384 cp += 2;
385 }
386
387 // We need this to figure out if this variable is the/a EOL
388 // variable. We read the pinfo_list just before starting this
389 // for-loop.
390 pinfo_list = dll_first(pinfo_list);
391 PROCESS_INFO_PTR pinfo = ((PROCESS_INFO_PTR) (pinfo_list)->data.u.pi);
392 FORMAT_PTR iformat = PINFO_FORMAT(pinfo);
393 VARIABLE_PTR var = ff_find_variable(cp, iformat);
394
395 // For some formats: Freefrom sends an extra EOL variable at the end of
396 // the list. Add an attribute container for all the other variables.
397 if (!IS_EOL(var))
398 das.add_table(cp, new AttrTable);
399
400 memFree(dim_names_vector, "**dim_names_vector");
401 dim_names_vector = NULL;
402 }
403 }
404 catch (...) {
405 if (var_names_vector)
406 memFree(var_names_vector, "**var_names_vector");
407 if (pinfo_list)
408 ff_destroy_process_info_list(pinfo_list);
409 if (dim_names_vector)
410 memFree(dim_names_vector, "**dim_names_vector");
411 if (dbin)
412 db_destroy(dbin);
413
414 throw;
415 }
416
417 memFree(var_names_vector, "**var_names_vector");
418 var_names_vector = NULL;
419
420 ff_destroy_process_info_list(pinfo_list);
421 db_destroy(dbin);
422}
423
424// Given a reference to an instance of class DAS and a filename that refers
425// to a freefrom file, read the format file and extract the existing
426// attributes and add them to the instance of DAS.
427//
428// Returns: false if an error accessing the file was detected, true
429// otherwise.
430
431void ff_get_attributes(DAS &das, string filename) throw (Error)
432{
433 AttrTable *attr_table_p = new AttrTable;
434
435 das.add_table("FF_GLOBAL", attr_table_p);
436 read_attributes(filename, attr_table_p);
437 // Add a table/container for each variable. See bug 284. The DAP spec
438 // calls for each variable to have an attribute container, even if it is
439 // empty. Previously the server did not have containers for all the
440 // variables. 4/4/2002 jhrg
441 add_variable_containers(das, filename);
442}