libdap  Updated for version 3.20.9
libdap4 is an implementation of OPeNDAP's DAP protocol.
getdap4.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 1997-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 // This is the source to `getdap'; a simple tool to exercise the Connect
33 // class. It can be used to get naked URLs as well as the DAP2 DAS and DDS
34 // objects. jhrg.
35 
36 #include "config.h"
37 
38 #ifdef WIN32
39 #include <io.h>
40 #include <fcntl.h>
41 #endif
42 
43 #include <cstring>
44 #include <string>
45 #include <sstream>
46 #include <unistd.h> // getopt
47 
48 #include "DMR.h"
49 #include "XMLWriter.h"
50 #include "D4BaseTypeFactory.h"
51 #include "D4Group.h"
52 #include "D4Sequence.h"
53 #include "D4Connect.h"
54 #include "StdinResponse.h"
55 #include "HTTPConnect.h"
56 #include "RCReader.h"
57 
58 using namespace std;
59 using namespace libdap;
60 
61 const char *version = CVER " (" DVR " DAP/" DAP_PROTOCOL_VERSION ")";
62 
63 static void usage(const string &)
64 {
65  const char *message = R"(
66  Usage: getdap4 [dD vVmzsM][-c <expr>][-m <num>] <url> [<url> ...]
67  getdap4 [dD vVmzsM][-c <expr>][-m <num>] <file> [<file> ...]
68 
69  In the first form of the command, dereference the URL and perform
70  the requested operations. This includes routing the returned
71  information through the DAP processing library (parsing the
72  returned objects, et c.). If none of d, or D are used with a URL,
73  then the DAP library routines are NOT used and the URLs contents
74  are dumped to standard output.
75 
76  Note: If the URL contains a query string the query string will be
77  preserved in the request. However, if the query string contains
78  DAP4 keys they may interfere with the operation of getdap4. A
79  warning will be written to stderr when getdap4 identifies the
80  presence of a DAP4 query key in the submitted URL's query string.
81 
82  In the second form of the command, assume the files are DAP4 data
83  responses (stored in files or read from pipes)
84 
85  Options:
86  d: For each URL, get the (DAP4) DMR object. Does not get data.
87  D: For each URL, get the DAP4 Data response.
88 
89  v: Verbose output.
90  V: Version of this client
91  i: For each URL, get the server version.
92  m: Request the same URL <num> times.
93  z: Ask the server to compress data.
94  s: Print Sequences using numbered rows.
95  M: Assume data read from a file has no MIME headers; use only
96  with files
97 
98  c: <expr> is a constraint expression. Used with -d/D
99  NB: You can use a `?' for the CE also.
100  S: Used in conjunction with -d and will report the total size
101  of the data referenced in the DMR.)";
102 
103  cerr << message << endl;
104 }
105 
106 // Used for raw http access/transfer
107 bool read_data(FILE *fp)
108 {
109  if (!fp) {
110  fprintf(stderr, "getdap4: Whoa!!! Null stream pointer.\n");
111  return false;
112  }
113  // Changed from a loop that used getc() to one that uses fread(). getc()
114  // worked fine for transfers of text information, but *not* for binary
115  // transfers. fread() will handle both.
116  char c = 0;
117  while (fp && !feof(fp) && fread(&c, 1, 1, fp))
118  printf("%c", c); // stick with stdio
119 
120  return true;
121 }
122 
123 static void
124 read_response_from_file(D4Connect *url, DMR &dmr, Response &r, bool mime_headers, bool get_dap4_data, bool get_dmr)
125 {
126  if (mime_headers) {
127  if (get_dap4_data)
128  url->read_data(dmr, r);
129  else if (get_dmr)
130  url->read_dmr(dmr, r);
131  else
132  throw Error("Only supports Data or DMR responses");
133  }
134  else {
135  if (get_dap4_data)
136  url->read_data_no_mime(dmr, r);
137  else if (get_dmr)
138  url->read_dmr_no_mime(dmr, r);
139  else
140  throw Error("Only supports Data or DMR responses");
141  }
142 }
143 
144 static void print_group_data(D4Group *g, bool print_rows = false)
145 {
146  for (Constructor::Vars_iter i = g->var_begin(), e = g->var_end(); i != e; i++) {
147  if (print_rows && (*i)->type() == dods_sequence_c)
148  dynamic_cast<D4Sequence &>(**i).print_val_by_rows(cout);
149  else
150  (*i)->print_val(cout);
151  }
152 
153  for (D4Group::groupsIter gi = g->grp_begin(), ge = g->grp_end(); gi != ge; ++gi) {
154  print_group_data(*gi, print_rows);
155  }
156 }
157 
158 static void print_data(DMR &dmr, bool print_rows = false)
159 {
160  cout << "The data:" << endl;
161 
162  D4Group *g = dmr.root();
163 
164  print_group_data(g, print_rows);
165 
166  cout << endl << flush;
167 }
168 
179 unsigned long long get_size(D4Group *grp, bool constrained = false)
180 {
181  unsigned long long w = 0;
182 
183  for (auto var_itr = grp->var_begin(); var_itr != grp->var_end(); var_itr++) {
184  if (constrained) {
185  if ((*var_itr)->send_p())
186  w += (*var_itr)->width(constrained);
187  }
188  else {
189  w += (*var_itr)->width(constrained);
190  }
191  }
192  for (auto grp_itr = grp->grp_begin(); grp_itr != grp->grp_end(); grp_itr++) {
193  w += get_size(*grp_itr, constrained);
194  }
195 
196  return w;
197 }
198 
199 unsigned long long get_size(DMR &dmr, bool constrained = false)
200 {
201  return get_size(dmr.root(), constrained);
202 }
203 
204 
205 int main(int argc, char *argv[])
206 {
207  int option_char;
208 
209  bool get_dmr = false;
210  bool get_dap4_data = false;
211  bool cexpr = false;
212  bool verbose = false;
213  bool multi = false;
214  bool accept_deflate = false;
215  bool print_rows = false;
216  bool mime_headers = true;
217  bool report_errors = false;
218  int times = 1;
219  int dap_client_major = 4;
220  int dap_client_minor = 0;
221  string expr = "";
222  bool compute_size = false;
223 
224 #ifdef WIN32
225  _setmode(_fileno(stdout), _O_BINARY);
226 #endif
227 
228  while ((option_char = getopt(argc, argv, "dDvVrm:Mzsc:S")) != -1) {
229  switch (option_char) {
230  case 'd':
231  get_dmr = true;
232  break;
233  case 'D':
234  get_dap4_data = true;
235  break;
236  case 'v':
237  verbose = true;
238  break;
239  case 'V':
240  cerr << "getdap4 version: " << version << endl;
241  exit(0);
242  case 'S':
243  compute_size = true;
244  break;
245  case 'r':
246  report_errors = true;
247  break;
248  case 'm':
249  multi = true;
250  times = atoi(optarg);
251  break;
252  case 'z':
253  accept_deflate = true;
254  break;
255  case 's':
256  print_rows = true;
257  break;
258  case 'M':
259  mime_headers = false;
260  break;
261  case 'c':
262  cexpr = true;
263  expr = optarg;
264  break;
265  case 'h':
266  case '?':
267  default:
268  usage(argv[0]);
269  exit(1);
270  }
271  }
272 
273  D4Connect *url = nullptr;
274  try {
275  // If after processing all the command line options there is nothing
276  // left (no URL or file) assume that we should read from stdin.
277  for (int i = optind; i < argc; ++i) {
278  if (verbose)
279  cerr << "Fetching: " << argv[i] << endl;
280 
281  string name = argv[i];
282  url = new D4Connect(name);
283 
284  // This overrides the value set in the .dodsrc file.
285  if (accept_deflate)
286  url->set_accept_deflate(accept_deflate);
287 
288  if (dap_client_major > 2)
289  url->set_xdap_protocol(dap_client_major, dap_client_minor);
290 
291  if (url->is_local()) {
292  if (verbose)
293  cerr << "Assuming " << argv[i] << " is a file that contains a response object; decoding." << endl;
294 
295  try {
296  D4BaseTypeFactory factory;
297  DMR dmr(&factory);
298 
299  if (strcmp(argv[i], "-") == 0) {
300  StdinResponse r(cin);
301 
302  if (!r.get_cpp_stream())
303  throw Error("Could not open standard input.");
304 
305  read_response_from_file(url, dmr, r, mime_headers, get_dap4_data, get_dmr);
306  }
307  else {
308  fstream f(argv[i], std::ios_base::in);
309  if (!f.is_open() || f.bad() || f.eof())
310  throw Error((string) "Could not open: " + argv[i]);
311 
312  Response r(&f, 0);
313 
314  read_response_from_file(url, dmr, r, mime_headers, get_dap4_data, get_dmr);
315  }
316 
317  if (verbose)
318  cerr << "DAP version: " << url->get_protocol().c_str() << " Server version: "
319  << url->get_version().c_str() << endl;
320 
321  // Always write the DMR
322  XMLWriter xml;
323  dmr.print_dap4(xml);
324  cout << xml.get_doc() << endl;
325 
326  if (get_dap4_data)
327  print_data(dmr, print_rows);
328  }
329  catch (Error &e) {
330  cerr << "Error: " << e.get_error_message() << endl;
331  delete url;
332  url = nullptr;
333  if (report_errors)
334  return EXIT_FAILURE;
335  }
336  }
337  else if (get_dmr) {
338  for (int j = 0; j < times; ++j) {
339  D4BaseTypeFactory factory;
340  DMR dmr(&factory);
341  try {
342  url->request_dmr(dmr, expr);
343 
344  if (verbose) {
345  cout << "DAP version: " << url->get_protocol() << ", Server version: " << url->get_version()
346  << endl;
347  cout << "DMR:" << endl;
348  }
349 
350  XMLWriter xml;
351  dmr.print_dap4(xml);
352  cout << xml.get_doc() << endl;
353  if (compute_size) {
354  cout << "DMR References " << get_size(dmr) << " bytes of data," << endl;
355  }
356  }
357  catch (Error &e) {
358  cerr << e.get_error_message() << endl;
359  if (report_errors)
360  return EXIT_FAILURE;
361  continue; // Goto the next URL or exit the loop.
362  }
363  }
364  }
365  else if (get_dap4_data) {
366  for (int j = 0; j < times; ++j) {
367  D4BaseTypeFactory factory;
368  DMR dmr(&factory);
369  try {
370  url->request_dap4_data(dmr, expr);
371 
372  if (verbose) {
373  cout << "DAP version: " << url->get_protocol() << ", Server version: " << url->get_version()
374  << endl;
375  cout << "DMR:" << endl;
376  }
377 
378  XMLWriter xml;
379  dmr.print_dap4(xml);
380  cout << xml.get_doc() << endl;
381 
382  print_data(dmr, print_rows);
383  }
384  catch (Error &e) {
385  cerr << e.get_error_message() << endl;
386  if (report_errors)
387  return EXIT_FAILURE;
388  continue; // Goto the next URL or exit the loop.
389  }
390  }
391  }
392  else {
393  HTTPConnect http(RCReader::instance());
394 
395  // This overrides the value set in the .dodsrc file.
396  if (accept_deflate)
397  http.set_accept_deflate(accept_deflate);
398 
399  if (dap_client_major > 2)
400  url->set_xdap_protocol(dap_client_major, dap_client_minor);
401 
402  string url_string = argv[i];
403  for (int j = 0; j < times; ++j) {
404  try {
405  HTTPResponse *r = http.fetch_url(url_string);
406  if (verbose) {
407  vector <string> *headers = r->get_headers();
408  copy(headers->begin(), headers->end(), ostream_iterator<string>(cout, "\n"));
409  }
410  if (!read_data(r->get_stream())) {
411  continue;
412  }
413  delete r;
414  r = 0;
415  }
416  catch (Error &e) {
417  cerr << e.get_error_message() << endl;
418  if (report_errors)
419  return EXIT_FAILURE;
420  continue;
421  }
422  }
423  }
424 
425  delete url;
426  url = nullptr;
427  }
428  }
429  catch (Error &e) {
430  delete url;
431  if (e.get_error_code() == malformed_expr) {
432  cerr << e.get_error_message() << endl;
433  usage(argv[0]);
434  }
435  else {
436  cerr << e.get_error_message() << endl;
437 
438  }
439 
440  cerr << "Exiting." << endl;
441  return EXIT_FAILURE;
442  }
443  catch (exception &e) {
444  delete url;
445  cerr << "C++ library exception: " << e.what() << endl;
446  cerr << "Exiting." << endl;
447  return EXIT_FAILURE;
448  }
449 
450  return EXIT_SUCCESS;
451 }
Vars_iter var_end()
Definition: Constructor.cc:364
Vars_iter var_begin()
Definition: Constructor.cc:356
std::string get_protocol()
Definition: D4Connect.h:99
void set_accept_deflate(bool deflate)
Definition: D4Connect.cc:483
void set_xdap_protocol(int major, int minor)
Definition: D4Connect.cc:493
std::string get_version()
Definition: D4Connect.h:94
groupsIter grp_begin()
Get an iterator to the start of the values.
Definition: D4Group.h:112
groupsIter grp_end()
Get an iterator to the end of the values.
Definition: D4Group.h:115
Holds a sequence.
Definition: D4Sequence.h:134
D4Group * root()
Definition: DMR.cc:410
void print_dap4(XMLWriter &xml, bool constrained=false)
Definition: DMR.cc:500
A class for error processing.
Definition: Error.h:94
ErrorCode get_error_code() const
Definition: Error.cc:214
std::string get_error_message() const
Definition: Error.cc:243
Encapsulate a response read from stdin.
Definition: StdinResponse.h:45
top level DAP object to house generic methods
Definition: AlarmHandler.h:36