bes  Updated for version 3.20.6
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  if (pinfo_list)
205  ff_destroy_process_info_list(pinfo_list);
206 
207 }
208 
213 void 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 
292 static 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 
431 void 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 }
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