bes Updated for version 3.20.13
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
41static 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 "BESRegex.h"
59
60#include <libdap/Array.h>
61#include <libdap/Structure.h>
62#include <libdap/Sequence.h>
63#include <libdap/Grid.h>
64#include <libdap/Ancillary.h>
65
66#include <libdap/DAS.h>
67//#include <libdap/mime_util.h>
68#include <libdap/util.h>
69
70#include <libdap/debug.h>
71
72using namespace libdap ;
73
74#include "../usage/usage.h"
75
76using namespace std;
77using 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
92namespace 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
103static BESRegex *dim_ptr = 0 ;
104#if HAVE_PTHREAD_H
105static pthread_once_t dim_once_control = PTHREAD_ONCE_INIT;
106#endif
107
108static void
109init_dim_regex()
110{
111 // MT-Safe if called via pthread_once or similar
112 static BESRegex dim(".*_dim_[0-9]*", 1); // HDF `dim' attributes.
113 dim_ptr = &dim;
114}
115
116static bool
117name_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
132static BESRegex *global_ptr = 0 ;
133#if HAVE_PTHREAD_H
134static pthread_once_t global_once_control = PTHREAD_ONCE_INIT;
135#endif
136
137static void
138init_global_regex()
139{
140 // MT-Safe if called via pthread_once or similar
141 static BESRegex global("(.*global.*)|(.*dods.*)", 1);
142 global_ptr = &global;
143}
144
145static bool
146name_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
164static void
165write_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
193static void
194write_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
231static string
232build_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
264static string
265fancy_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
309static void
310write_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
388static string
389build_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
406void
407html_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
435void
436write_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
493string
494get_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
Regular expression matching.
Definition: BESRegex.h:53
int match(const char *s, int len, int pos=0) const
Does the pattern match.
Definition: BESRegex.cc:127