bes  Updated for version 3.20.5
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 <util.h>
46 #include <DAS.h>
47 #include <Error.h>
48 #include <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 
60 static 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 }
205 
210 void read_attributes(string filename, AttrTable *at)
211 {
212  int error = 0;
213  FF_BUFSIZE_PTR bufsize = NULL;
214  DATA_BIN_PTR dbin = NULL;
215  FF_STD_ARGS_PTR SetUps = NULL;
216 
217  if (!file_exist(filename.c_str()))
218  throw Error((string) "Could not open file " + path_to_filename(filename) + ".");
219 
220  // ff_create_std_args uses calloc so the struct's pointers are all initialized
221  // to null.
222  SetUps = ff_create_std_args();
223  if (!SetUps)
224  throw Error("ff_das: Insufficient memory");
225 
227  SetUps->user.is_stdin_redirected = 0;
228 
229  // Use const_cast because FF only copies the referenced data; the older
230  // version of this handler allocated memory, copied values, leaked the
231  // memory...
232  SetUps->input_file = const_cast<char*>(filename.c_str());
233 
234  string iff = "";
235  if (FFRequestHandler::get_RSS_format_support()) {
236  iff = find_ancillary_rss_formats(filename);
237  SetUps->input_format_file = const_cast<char*>(iff.c_str());
238  }
239  // Regex support
240  if (FFRequestHandler::get_Regex_format_support()) {
241  iff = get_Regex_format_file(filename);
242  if (!iff.empty())
243  SetUps->input_format_file = const_cast<char*>(iff.c_str());
244  }
245 
246  SetUps->output_file = NULL;
247 
248  char Msgt[255];
249  error = SetDodsDB(SetUps, &dbin, Msgt);
250  if (error && error < ERR_WARNING_ONLY) {
251  if (dbin)
252  db_destroy(dbin);
253  ff_destroy_std_args(SetUps);
254  throw Error(Msgt);
255  }
256 
257  ff_destroy_std_args(SetUps);
258 
259  try {
260  error = db_ask(dbin, DBASK_FORMAT_SUMMARY, FFF_INPUT, &bufsize);
261  if (error) {
262  string msg = "Cannot get Format Summary. FreeForm error code: ";
263  append_long_to_string((long) error, 10, msg);
264  throw Error(msg);
265  }
266 
267 #ifndef ATTR_STRING_QUOTE_FIX
268  at->append_attr("Server", "STRING",
269  "\"DODS FreeFrom based on FFND release " + FFND_LIB_VER + "\"");
270 #else
271  at->append_attr("Server", "STRING", string("DODS FreeFrom based on FFND release ") + FFND_LIB_VER);
272 #endif
273 
274  header_to_attributes(at, dbin); // throws Error
275  }
276  catch (...) {
277  if (bufsize)
278  ff_destroy_bufsize(bufsize);
279  if (dbin)
280  db_destroy(dbin);
281 
282  throw;
283  }
284 
285  ff_destroy_bufsize(bufsize);
286  db_destroy(dbin);
287 }
288 
289 static void add_variable_containers(DAS &das, const string &filename) throw (Error)
290 {
291  if (!file_exist(filename.c_str()))
292  throw Error(string("ff_dds: Could not open file ") + path_to_filename(filename) + string("."));
293 
294  // Setup the DB access.
295  FF_STD_ARGS_PTR SetUps = ff_create_std_args();
296  if (!SetUps)
297  throw Error("Insufficient memory");
298 
299  SetUps->user.is_stdin_redirected = 0;
300 
301  SetUps->input_file = const_cast<char*>(filename.c_str());
302 
303  string iff = "";
304  if (FFRequestHandler::get_RSS_format_support()) {
305  iff = find_ancillary_rss_formats(filename);
306  SetUps->input_format_file = const_cast<char*>(iff.c_str());
307  }
308 
309  // Regex support
310  if (FFRequestHandler::get_Regex_format_support()) {
311  iff = get_Regex_format_file(filename);
312  if (!iff.empty())
313  SetUps->input_format_file = const_cast<char*>(iff.c_str());
314  }
315 
316  SetUps->output_file = NULL;
317 
318  // Set the structure values to create the FreeForm DB
319  char Msgt[255];
320  DATA_BIN_PTR dbin = NULL;
321  int error = SetDodsDB(SetUps, &dbin, Msgt);
322  if (error && error < ERR_WARNING_ONLY) {
323  if (dbin)
324  db_destroy(dbin);
325  ff_destroy_std_args(SetUps);
326  string msg = string(Msgt) + " FreeForm error code: ";
327  append_long_to_string((long) error, 10, msg);
328  throw Error(msg);
329  }
330 
331  ff_destroy_std_args(SetUps);
332 
333  // These are defined here so that they can be freed in the catch(...)
334  // block below
335  char **var_names_vector = NULL;
336  PROCESS_INFO_LIST pinfo_list = NULL;
337  char **dim_names_vector = NULL;
338 
339  try {
340  // Get the names of all the variables.
341  int num_names = 0;
342  error = db_ask(dbin, DBASK_VAR_NAMES, FFF_INPUT | FFF_DATA, &num_names, &var_names_vector);
343  if (error) {
344  string msg = "Could not get varible list from the input file. FreeForm error code: ";
345  append_long_to_string((long) error, 10, msg);
346  throw Error(msg);
347  }
348 
349  // I don't understand why this has to happen here, but maybe it's moved
350  // outside the loop because FreeForm is designed so that the pinfo list
351  // only needs to be accessed once and can be used when working with
352  // any/all of the variables. 4/4/2002 jhrg
353  error = db_ask(dbin, DBASK_PROCESS_INFO, FFF_INPUT | FFF_DATA, &pinfo_list);
354  if (error) {
355  string msg = "Could not get process info for the input file. FreeForm error code: ";
356  append_long_to_string((long) error, 10, msg);
357  throw Error(msg);
358  }
359 
360  // For each variable, figure out what its name really is (arrays have
361  // funny names).
362  for (int i = 0; i < num_names; i++) {
363  int num_dim_names = 0;
364  error = db_ask(dbin, DBASK_ARRAY_DIM_NAMES, var_names_vector[i], &num_dim_names, &dim_names_vector);
365  if (error) {
366  string msg = "Could not get array dimension names for variable: " + string(var_names_vector[i])
367  + string(", FreeForm error code: ");
368  append_long_to_string((long) error, 10, msg);
369  throw Error(msg);
370  }
371 
372  // Note: FreeForm array names are returned appended to their format
373  // name with '::'.
374  char *cp = NULL;
375  if (num_dim_names == 0) // sequence names
376  cp = var_names_vector[i];
377  else {
378  cp = strstr(var_names_vector[i], "::");
379  // If cp is not null, advance past the "::"
380  if (cp)
381  cp += 2;
382  }
383 
384  // We need this to figure out if this variable is the/a EOL
385  // variable. We read the pinfo_list just before starting this
386  // for-loop.
387  pinfo_list = dll_first(pinfo_list);
388  PROCESS_INFO_PTR pinfo = ((PROCESS_INFO_PTR) (pinfo_list)->data.u.pi);
389  FORMAT_PTR iformat = PINFO_FORMAT(pinfo);
390  VARIABLE_PTR var = ff_find_variable(cp, iformat);
391 
392  // For some formats: Freefrom sends an extra EOL variable at the end of
393  // the list. Add an attribute container for all the other variables.
394  if (!IS_EOL(var))
395  das.add_table(cp, new AttrTable);
396 
397  memFree(dim_names_vector, "**dim_names_vector");
398  dim_names_vector = NULL;
399  }
400  }
401  catch (...) {
402  if (var_names_vector)
403  memFree(var_names_vector, "**var_names_vector");
404  if (pinfo_list)
405  ff_destroy_process_info_list(pinfo_list);
406  if (dim_names_vector)
407  memFree(dim_names_vector, "**dim_names_vector");
408  if (dbin)
409  db_destroy(dbin);
410 
411  throw;
412  }
413 
414  memFree(var_names_vector, "**var_names_vector");
415  var_names_vector = NULL;
416 
417  ff_destroy_process_info_list(pinfo_list);
418  db_destroy(dbin);
419 }
420 
421 // Given a reference to an instance of class DAS and a filename that refers
422 // to a freefrom file, read the format file and extract the existing
423 // attributes and add them to the instance of DAS.
424 //
425 // Returns: false if an error accessing the file was detected, true
426 // otherwise.
427 
428 void ff_get_attributes(DAS &das, string filename) throw (Error)
429 {
430  AttrTable *attr_table_p = new AttrTable;
431 
432  das.add_table("FF_GLOBAL", attr_table_p);
433  read_attributes(filename, attr_table_p);
434  // Add a table/container for each variable. See bug 284. The DAP spec
435  // calls for each variable to have an attribute container, even if it is
436  // empty. Previously the server did not have containers for all the
437  // variables. 4/4/2002 jhrg
438  add_variable_containers(das, filename);
439 }
struct_ff_std_args
Definition: freeform.h:867
struct_ff_dll_node
Definition: freeform.h:1278
struct_ff_process_info
Definition: freeform.h:1193
struct_ff_variable
Definition: freeform.h:1034
struct_ff_bufsize
Definition: freeform.h:845
struct_databin
Definition: freeform.h:1294
Error
struct_ff_format
Definition: freeform.h:973