bes  Updated for version 3.20.6
usage.cc
1 
2 // -*- mode: c++; c-basic-offset:4 -*-
3 
4 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
5 // Access Protocol.
6 
7 // Copyright (c) 2002,2003 OPeNDAP, Inc.
8 // Author: James Gallagher <jgallagher@opendap.org>
9 //
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
14 //
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public 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 1996, 1999
27 // Please read the full copyright statement in the file COPYRIGHT_URI.
28 //
29 // Authors:
30 // jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
31 
32 // The Usage server. Arguments: three arguments; filter options, the dataset
33 // name and the pathname and `api prefix' of the data server. Returns a HTML
34 // document that describes what information this dataset contains, special
35 // characteristics of the server users might want to know and any special
36 // information that the dataset providers want to make available. jhrg
37 // 12/9/96
38 
39 #include "config.h"
40 
41 static char rcsid[] not_used = {"$Id$"};
42 
43 #include <stdio.h>
44 
45 // I've added the pthread code here because this might someday move inside a
46 // library as a function/object and it should be MT-safe. In the current
47 // build HAVE_PTHREAD_H is set by configure; not having it makes no practical
48 // difference. jhrg 6/10/05
49 #ifdef HAVE_PTHREAD_H
50 #include <pthread.h>
51 #endif
52 
53 #include <iostream>
54 #include <fstream>
55 #include <string>
56 #include <sstream>
57 
58 #include <GNURegex.h>
59 
60 #include <Array.h>
61 #include <Structure.h>
62 #include <Sequence.h>
63 #include <Grid.h>
64 #include <Ancillary.h>
65 
66 #include <DAS.h>
67 //#include <mime_util.h>
68 #include <util.h>
69 
70 #include <debug.h>
71 
72 using namespace libdap ;
73 
74 #include "../usage/usage.h"
75 
76 using namespace std;
77 using namespace dap_usage;
78 
79 #ifdef WIN32
80 #define popen _popen
81 #define pclose _pclose
82 #include <io.h>
83 #include <fcntl.h>
84 #endif
85 
86 #ifdef WIN32
87 #define RETURN_TYPE void
88 #else
89 #define RETURN_TYPE int
90 #endif
91 
92 namespace dap_usage {
93 
94 // This code could use a real `kill-file' some day - about the same time that
95 // the rest of the server gets a `rc' file... For the present just see if a
96 // small collection of regexs match the name.
97 
98 // The pthread code here is used to ensure that the static objects dim and
99 // global (in name_in_kill_file() and name_is_global()) are initialized only
100 // once. If the pthread package is not present when libdap++ is built, this
101 // code is *not* MT-Safe.
102 
103 static Regex *dim_ptr = 0 ;
104 #if HAVE_PTHREAD_H
105 static pthread_once_t dim_once_control = PTHREAD_ONCE_INIT;
106 #endif
107 
108 static void
109 init_dim_regex()
110 {
111  // MT-Safe if called via pthread_once or similar
112  static Regex dim(".*_dim_[0-9]*", 1); // HDF `dim' attributes.
113  dim_ptr = &dim;
114 }
115 
116 static bool
117 name_in_kill_file(const string &name)
118 {
119 #if HAVE_PTHREAD_H
120  pthread_once(&dim_once_control, init_dim_regex);
121 #else
122  if (!dim_ptr)
123  {
124  init_dim_regex();
125  }
126 #endif
127 
128  bool ret = dim_ptr->match(name.c_str(), name.length()) != -1;
129  return ret ;
130 }
131 
132 static Regex *global_ptr = 0 ;
133 #if HAVE_PTHREAD_H
134 static pthread_once_t global_once_control = PTHREAD_ONCE_INIT;
135 #endif
136 
137 static void
138 init_global_regex()
139 {
140  // MT-Safe if called via pthread_once or similar
141  static Regex global("(.*global.*)|(.*dods.*)", 1);
142  global_ptr = &global;
143 }
144 
145 static bool
146 name_is_global(string &name)
147 {
148 #if HAVE_PTHREAD_H
149  pthread_once(&global_once_control, init_global_regex);
150 #else
151  if (!global_ptr)
152  init_global_regex();
153 #endif
154 
155  downcase(name);
156  return global_ptr->match(name.c_str(), name.length()) != -1;
157 }
158 
159 // write_global_attributes and write_attributes are almost the same except
160 // that the global attributes use fancier formatting. The formatting could be
161 // passed in as params, but that would make the code much harder to
162 // understand. So, I'm keeping this as two separate functions even though
163 // there's some duplication... 3/27/2002 jhrg
164 static void
165 write_global_attributes(ostringstream &oss, AttrTable *attr,
166  const string prefix = "")
167 {
168  if (attr) {
169  AttrTable::Attr_iter a;
170  for (a = attr->attr_begin(); a != attr->attr_end(); a++) {
171  if (attr->is_container(a))
172  write_global_attributes(oss, attr->get_attr_table(a),
173  (prefix == "") ? attr->get_name(a)
174  : prefix + string(".") + attr->get_name(a));
175  else {
176  oss << "\n<tr><td align=right valign=top><b>";
177  if (prefix != "")
178  oss << prefix << "." << attr->get_name(a);
179  else
180  oss << attr->get_name(a);
181  oss << "</b>:</td>\n";
182 
183  int num_attr = attr->get_attr_num(a) - 1;
184  oss << "<td align=left>";
185  for (int i = 0; i < num_attr; ++i)
186  oss << attr->get_attr(a, i) << ", ";
187  oss << attr->get_attr(a, num_attr) << "<br></td></tr>\n";
188  }
189  }
190  }
191 }
192 
193 static void
194 write_attributes(ostringstream &oss, AttrTable *attr, const string prefix = "")
195 {
196  if (attr) {
197  AttrTable::Attr_iter a;
198  for (a = attr->attr_begin(); a != attr->attr_end(); a++) {
199  if (attr->is_container(a))
200  write_attributes(oss, attr->get_attr_table(a),
201  (prefix == "") ? attr->get_name(a)
202  : prefix + string(".") + attr->get_name(a));
203  else {
204  if (prefix != "")
205  oss << prefix << "." << attr->get_name(a);
206  else
207  oss << attr->get_name(a);
208  oss << ": ";
209 
210  int num_attr = attr->get_attr_num(a) - 1 ;
211  for (int i = 0; i < num_attr; ++i)
212  oss << attr->get_attr(a, i) << ", ";
213  oss << attr->get_attr(a, num_attr) << "<br>\n";
214  }
215  }
216  }
217 }
218 
231 static string
232 build_global_attributes(DAS &das, DDS &)
233 {
234  bool found = false;
235  ostringstream ga;
236 
237  ga << "<h3>Dataset Information</h3>\n<center>\n<table>\n";
238 
239  for (AttrTable::Attr_iter p = das.var_begin(); p != das.var_end(); p++) {
240  string name = das.get_name(p);
241 
242  // I used `name_in_dds' originally, but changed to `name_is_global'
243  // because aliases between groups of attributes can result in
244  // attribute group names which are not in the DDS and are *not*
245  // global attributes. jhrg. 5/22/97
246  if (!name_in_kill_file(name) )
247  {
248  if( name_is_global(name)) {
249  AttrTable *attr = das.get_table(p);
250  found = true;
251  write_global_attributes(ga, attr, "");
252  }
253  }
254  }
255 
256  ga << "</table>\n</center><p>\n";
257 
258  if (found)
259  return ga.str();
260 
261  return "";
262 }
263 
264 static string
265 fancy_typename(BaseType *v)
266 {
267  string fancy;
268  switch (v->type()) {
269  case dods_byte_c:
270  return "Byte";
271  case dods_int16_c:
272  return "16 bit Integer";
273  case dods_uint16_c:
274  return "16 bit Unsigned integer";
275  case dods_int32_c:
276  return "32 bit Integer";
277  case dods_uint32_c:
278  return "32 bit Unsigned integer";
279  case dods_float32_c:
280  return "32 bit Real";
281  case dods_float64_c:
282  return "64 bit Real";
283  case dods_str_c:
284  return "String";
285  case dods_url_c:
286  return "URL";
287  case dods_array_c: {
288  ostringstream type;
289  Array *a = (Array *)v;
290  type << "Array of " << fancy_typename(a->var()) <<"s ";
291  for (Array::Dim_iter p = a->dim_begin(); p != a->dim_end(); p++) {
292  type << "[" << a->dimension_name(p) << " = 0.."
293  << a->dimension_size(p, false)-1 << "]";
294  }
295  return type.str();
296  }
297 
298  case dods_structure_c:
299  return "Structure";
300  case dods_sequence_c:
301  return "Sequence";
302  case dods_grid_c:
303  return "Grid";
304  default:
305  return "Unknown";
306  }
307 }
308 
309 static void
310 write_variable(BaseType *btp, DAS &das, ostringstream &vs)
311 {
312  vs << "<td align=right valign=top><b>" << btp->name()
313  << "</b>:</td>\n"
314  << "<td align=left valign=top>" << fancy_typename(btp)
315  << "<br>";
316 
317  AttrTable *attr = das.get_table(btp->name());
318 
319  write_attributes(vs, attr, "");
320 
321  switch (btp->type()) {
322  case dods_byte_c:
323  case dods_int16_c:
324  case dods_uint16_c:
325  case dods_int32_c:
326  case dods_uint32_c:
327  case dods_float32_c:
328  case dods_float64_c:
329  case dods_str_c:
330  case dods_url_c:
331  case dods_array_c:
332  vs << "</td>\n";
333  break;
334 
335  case dods_structure_c: {
336  vs << "<table>\n";
337  Structure *sp = dynamic_cast<Structure *>(btp);
338  for (Constructor::Vars_iter p = sp->var_begin(); p != sp->var_end(); p++)
339  {
340  vs << "<tr>";
341  write_variable((*p), das, vs);
342  vs << "</tr>";
343  }
344  vs << "</table>\n";
345  break;
346  }
347 
348  case dods_sequence_c: {
349  vs << "<table>\n";
350  Sequence *sp = dynamic_cast<Sequence *>(btp);
351  for (Constructor::Vars_iter p = sp->var_begin(); p != sp->var_end(); p++)
352  {
353  vs << "<tr>";
354  write_variable((*p), das, vs);
355  vs << "</tr>";
356  }
357  vs << "</table>\n";
358  break;
359  }
360 
361  case dods_grid_c: {
362  vs << "<table>\n";
363  Grid *gp = dynamic_cast<Grid *>(btp);
364  write_variable(gp->array_var(), das, vs);
365  Grid::Map_iter p;
366  for (p = gp->map_begin(); p != gp->map_end(); p++) {
367  vs << "<tr>";
368  write_variable((*p), das, vs);
369  vs << "</tr>";
370  }
371  vs << "</table>\n";
372  break;
373  }
374 
375  default:
376  throw InternalErr(__FILE__, __LINE__, "Unknown type");
377  }
378 }
379 
388 static string
389 build_variable_summaries(DAS &das, DDS &dds)
390 {
391  ostringstream vs;
392  vs << "<h3>Variables in this Dataset</h3>\n<center>\n<table>\n";
393  // vs << "<tr><th>Variable</th><th>Information</th></tr>\n";
394 
395  for (DDS::Vars_iter p = dds.var_begin(); p != dds.var_end(); p++) {
396  vs << "<tr>";
397  write_variable((*p), das, vs);
398  vs << "</tr>";
399  }
400 
401  vs << "</table>\n</center><p>\n";
402 
403  return vs.str();
404 }
405 
406 void
407 html_header( ostream &strm )
408 {
409  strm << "HTTP/1.0 200 OK\r\n" ;
410  strm << "XDODS-Server: " << PACKAGE_VERSION << "\r\n" ;
411  strm << "XDAP: " << DAP_PROTOCOL_VERSION << "\r\n" ;
412  strm << "Content-type: text/html\r\n" ;
413  strm << "Content-Description: dods_description\r\n" ;
414  strm << "\r\n" ; // MIME header ends with a blank line
415 }
416 
435 void
436 write_usage_response(ostream &strm, DDS &dds, DAS &das,
437  const string &dataset_name,
438  const string &server_name,
439  bool httpheader) throw(Error)
440 {
441  // This will require some hacking in libdap; maybe that code should
442  // move here? jhrg
443  string user_html = get_user_supplied_docs(dataset_name, server_name);
444 
445  string global_attrs = build_global_attributes(das, dds);
446 
447  string variable_sum = build_variable_summaries(das, dds);
448 
449  // Write out the HTML document.
450 
451  if( httpheader )
452  html_header( strm );
453 
454  strm << "<html><head><title>Dataset Information</title></head>"
455  << "\n" << "<body>" << "\n" ;
456 
457  if (global_attrs.length())
458  {
459  strm << global_attrs.c_str() << "\n" << "<hr>" << "\n" ;
460  }
461 
462  strm << variable_sum.c_str() << "\n" ;
463 
464  strm << "<hr>\n" ;
465 
466  strm << user_html.c_str() << "\n" ;
467 
468  strm << "</body>\n</html>\n" ;
469 }
470 
493 string
494 get_user_supplied_docs(string name, string cgi)
495 {
496  char tmp[256];
497  ostringstream oss;
498  ifstream ifs((cgi + ".html").c_str());
499 
500  if (ifs) {
501  while (!ifs.eof()) {
502  ifs.getline(tmp, 255);
503  oss << tmp << "\n";
504  }
505  ifs.close();
506 
507  oss << "<hr>";
508  }
509 
510  // Problem: This code is run with the CWD as the CGI-BIN directory but
511  // the data are in DocumentRoot (and we don't have the pathname of the
512  // data relative to DocumentRoot). So the only time this will work is
513  // when the server is in the same directory as the data. See bug 815.
514  // 10/08/04 jhrg
515  ifs.open((name + ".html").c_str());
516 
517  // If name.html cannot be opened, look for basename.html
518  if (!ifs) {
519  string new_name = Ancillary::find_group_ancillary_file(name, ".html");
520  if (new_name != "")
521  ifs.open(new_name.c_str());
522  }
523 
524  if (ifs) {
525  while (!ifs.eof()) {
526  ifs.getline(tmp, 255);
527  oss << tmp << "\n";
528  }
529  ifs.close();
530  }
531 
532  return oss.str();
533 }
534 
535 } // namespace dap_usage
libdap
Definition: BESDapFunctionResponseCache.h:35
Error