libdap++  Updated for version 3.8.2
util.cc
Go to the documentation of this file.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 //
24 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25 
26 // (c) COPYRIGHT URI/MIT 1994-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 // Utility functions used by the api.
33 //
34 // jhrg 9/21/94
35 
36 #include "config.h"
37 
38 static char rcsid[] not_used =
39  {"$Id: util.cc 21699 2009-11-05 00:06:01Z jimg $"
40  };
41 
42 #include <cassert>
43 #include <cstring>
44 
45 #include <ctype.h>
46 #ifndef TM_IN_SYS_TIME
47 #include <time.h>
48 #else
49 #include <sys/time.h>
50 #endif
51 
52 #ifndef WIN32
53 #include <unistd.h> // for stat
54 #else
55 #include <io.h>
56 #include <fcntl.h>
57 #include <process.h>
58 #endif
59 
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 
63 #include <string>
64 #include <sstream>
65 #include <vector>
66 #include <algorithm>
67 #include <stdexcept>
68 
69 #include "BaseType.h"
70 #include "Str.h"
71 #include "Url.h"
72 #include "Sequence.h"
73 #include "Error.h"
74 #include "parser.h"
75 #include "util.h"
76 #include "GNURegex.h"
77 #include "debug.h"
78 
79 
80 using namespace std;
81 
82 namespace libdap {
83 
84 // Remove spaces from the start of a URL and from the start of any constraint
85 // expression it contains. 4/7/98 jhrg
86 
95 string
96 prune_spaces(const string &name)
97 {
98  // If the URL does not even have white space return.
99  if (name.find_first_of(' ') == name.npos)
100  return name;
101  else {
102  // Strip leading spaces from http://...
103  unsigned int i = name.find_first_not_of(' ');
104  string tmp_name = name.substr(i);
105 
106  // Strip leading spaces from constraint part (following `?').
107  unsigned int j = tmp_name.find('?') + 1;
108  i = tmp_name.find_first_not_of(' ', j);
109  tmp_name.erase(j, i - j);
110 
111  return tmp_name;
112  }
113 }
114 
115 // Compare elements in a list of (BaseType *)s and return true if there are
116 // no duplicate elements, otherwise return false.
117 
118 bool
119 unique_names(vector<BaseType *> l, const string &var_name,
120  const string &type_name, string &msg)
121 {
122  // copy the identifier names to a vector
123  vector<string> names(l.size());
124 
125  int nelem = 0;
126  typedef std::vector<BaseType *>::const_iterator citer ;
127  for (citer i = l.begin(); i != l.end(); i++) {
128  assert(*i);
129  names[nelem++] = (*i)->name();
130  DBG(cerr << "NAMES[" << nelem - 1 << "]=" << names[nelem-1] << endl);
131  }
132 
133  // sort the array of names
134  sort(names.begin(), names.end());
135 
136 #ifdef DODS_DEBUG2
137  cout << "unique:" << endl;
138  for (int ii = 0; ii < nelem; ++ii)
139  cout << "NAMES[" << ii << "]=" << names[ii] << endl;
140 #endif
141 
142  // sort the array of names
143  sort(names.begin(), names.end());
144 
145 #ifdef DODS_DEBUG2
146  cout << "unique:" << endl;
147  for (int ii = 0; ii < nelem; ++ii)
148  cout << "NAMES[" << ii << "]=" << names[ii] << endl;
149 #endif
150 
151  // look for any instance of consecutive names that are ==
152  for (int j = 1; j < nelem; ++j) {
153  if (names[j-1] == names[j]) {
154  ostringstream oss;
155  oss << "The variable `" << names[j]
156  << "' is used more than once in " << type_name << " `"
157  << var_name << "'";
158  msg = oss.str();
159 
160  return false;
161  }
162  }
163 
164  return true;
165 }
166 
167 const char *
169 {
170  return LIBDAP_ROOT;
171 }
172 
173 extern "C"
174  const char *
176 {
177  return PACKAGE_VERSION;
178 }
179 
180 extern "C"
181  const char *
183 {
184  return PACKAGE_NAME;
185 }
186 
187 // Since Server4 can get compressed responses using Tomcat, bail on this
188 // software (which complicates building under Win32). It can be turned on
189 // for use with Server3 in configure.ac.
190 
191 #if COMPRESSION_FOR_SERVER3
192 
193 // Return true if the program deflate exists and is executable by user, group
194 // and world. If this returns false the caller should assume that server
195 // filter programs won't be able to find the deflate program and thus won't
196 // be able to compress the return document.
197 // NB: this works because this function uses the same rules as compressor()
198 // (which follows) to look for deflate. 2/11/98 jhrg
199 
200 bool
202 {
203  DBG(cerr << "Entering deflate_exists...");
204 
205  int status = false;
206  struct stat buf;
207 
208 #ifdef WIN32
209  string deflate = (string)libdap_root() + "\\bin\\deflate";
210 #else
211  string deflate = (string)libdap_root() + "/sbin/deflate";
212 #endif
213 
214  // Check that the file exists...
215  // First look for deflate using DODS_ROOT (compile-time constant subsumed
216  // by an environment variable) and if that fails in the CWD which finds
217  // the program when it is in the same directory as the dispatch script
218  // and other server components. 2/11/98 jhrg
219  status = (stat(deflate.c_str(), &buf) == 0)
220 #ifdef WIN32
221  || (stat(".\\deflate", &buf) == 0);
222 #else
223  || (stat("./deflate", &buf) == 0);
224 #endif
225 
226  // and that it can be executed.
227 #ifdef WIN32
228  status &= (buf.st_mode & _S_IEXEC);
229 #else
230  status &= buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH);
231 #endif
232  DBG(cerr << " returning " << (status ? "true." : "false.") << endl);
233  return (status != 0);
234 }
235 
236 FILE *
237 compressor(FILE *output, int &childpid)
238 {
239 #ifdef WIN32
240  // There is no such thing as a "fork" under win32. This makes it so that
241  // we have to juggle handles more aggressively. This code hasn't been
242  // tested and shown to work as of 07/2000.
243  int pid, data[2];
244  int hStdIn, hStdOut;
245 
246  if (_pipe(data, 512, O_BINARY | O_NOINHERIT) < 0) {
247  cerr << "Could not create IPC channel for compressor process"
248  << endl;
249  return NULL;
250  }
251 
252 
253  // This sets up for the child process, but it has to be reversed for the
254  // parent after the spawn takes place.
255 
256  // Store stdin, stdout so we have something to restore to
257  hStdIn = _dup(_fileno(stdin));
258  hStdOut = _dup(_fileno(stdout));
259 
260  // Child is to read from read end of pipe
261  if (_dup2(data[0], _fileno(stdin)) != 0) {
262  cerr << "dup of child stdin failed" << endl;
263  return NULL;
264  }
265  // Child is to write its's stdout to file
266  if (_dup2(_fileno(output), _fileno(stdout)) != 0) {
267  cerr << "dup of child stdout failed" << endl;
268  return NULL;
269  }
270 
271  // Spawn child process
272  string deflate = "deflate.exe";
273  if ((pid = _spawnlp(_P_NOWAIT, deflate.c_str(), deflate.c_str(),
274  "-c", "5", "-s", NULL)) < 0) {
275  cerr << "Could not spawn to create compressor process" << endl;
276  return NULL;
277  }
278 
279  // Restore stdin, stdout for parent and close duplicate copies
280  if (_dup2(hStdIn, _fileno(stdin)) != 0) {
281  cerr << "dup of stdin failed" << endl;
282  return NULL;
283  }
284  if (_dup2(hStdOut, _fileno(stdout)) != 0) {
285  cerr << "dup of stdout failed" << endl;
286  return NULL;
287  }
288  close(hStdIn);
289  close(hStdOut);
290 
291  // Tell the parent that it reads from the opposite end of the
292  // place where the child writes.
293  close(data[0]);
294  FILE *input = fdopen(data[1], "w");
295  setbuf(input, 0);
296  childpid = pid;
297  return input;
298 
299 #else
300  FILE *ret_file = NULL ;
301 
302  int pid, data[2];
303 
304  if (pipe(data) < 0) {
305  cerr << "Could not create IPC channel for compressor process"
306  << endl;
307  return NULL;
308  }
309 
310  if ((pid = fork()) < 0) {
311  cerr << "Could not fork to create compressor process" << endl;
312  return NULL;
313  }
314 
315  // The parent process closes the write end of the Pipe, and creates a
316  // FILE * using fdopen(). The FILE * is used by the calling program to
317  // access the read end of the Pipe.
318 
319  if (pid > 0) { // Parent, pid is that of the child
320  close(data[0]);
321  ret_file = fdopen(data[1], "w");
322  setbuf(ret_file, 0);
323  childpid = pid;
324  }
325  else { // Child
326  close(data[1]);
327  dup2(data[0], 0); // Read from the pipe...
328  dup2(fileno(output), 1); // Write to the FILE *output.
329 
330  DBG(cerr << "Opening compression stream." << endl);
331 
332  // First try to run deflate using DODS_ROOT (the value read from the
333  // DODS_ROOT environment variable takes precedence over the value set
334  // at build time. If that fails, try the CWD.
335  string deflate = (string)libdap_root() + "/sbin/deflate";
336  (void) execl(deflate.c_str(), "deflate", "-c", "5", "-s", NULL);
337  (void) execl("./deflate", "deflate", "-c", "5", "-s", NULL);
338  cerr << "Warning: Could not start compressor!" << endl;
339  cerr << "defalte should be in DODS_ROOT/etc or in the CWD!"
340  << endl;
341  _exit(127); // Only here if an error occurred.
342  }
343 
344  return ret_file ;
345 #endif
346 }
347 
348 #endif // COMPRESSION_FOR_SERVER3
349 
350 // This function returns a pointer to the system time formated for an httpd
351 // log file.
352 
353 string
355 {
356  time_t TimBin;
357 
358  if (time(&TimBin) == (time_t) - 1)
359  return string("time() error");
360  else {
361  string TimStr = ctime(&TimBin);
362  return TimStr.substr(0, TimStr.size() - 2); // remove the \n
363  }
364 }
365 
366 void
367 downcase(string &s)
368 {
369  for (unsigned int i = 0; i < s.length(); i++)
370  s[i] = tolower(s[i]);
371 }
372 
373 bool
374 is_quoted(const string &s)
375 {
376  return (!s.empty() && s[0] == '\"' && s[s.length()-1] == '\"');
377 }
378 
379 string
380 remove_quotes(const string &s)
381 {
382  if (is_quoted(s))
383  return s.substr(1, s.length() - 2);
384  else
385  return s;
386 }
387 
388 #ifdef WIN32
389 // Sometimes need to buffer within an iostream under win32 when
390 // we want the output to go to a FILE *. This is because
391 // it's not possible to associate an ofstream with a FILE *
392 // under the Standard ANSI C++ Library spec. Unix systems
393 // don't follow the spec in this regard.
394 void flush_stream(iostream ios, FILE *out)
395 {
396  int nbytes;
397  char buffer[512];
398 
399  ios.get(buffer, 512, NULL);
400  while ((nbytes = ios.gcount()) > 0) {
401  fwrite(buffer, 1, nbytes, out);
402  ios.get(buffer, 512, NULL);
403  }
404 
405  return;
406 }
407 #endif
408 
409 // Jose Garcia
410 void
411 append_long_to_string(long val, int base, string &str_val)
412 {
413  // The array digits contains 36 elements which are the
414  // posible valid digits for out bases in the range
415  // [2,36]
416  char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
417  // result of val / base
418  ldiv_t r;
419 
420  if (base > 36 || base < 2) {
421  // no conversion if wrong base
422  std::invalid_argument ex("The parameter base has an invalid value.");
423  throw ex;
424  }
425  if (val < 0)
426  str_val += '-';
427  r = ldiv(labs(val), base);
428 
429  // output digits of val/base first
430  if (r.quot > 0)
431  append_long_to_string(r.quot, base, str_val);
432 
433  // output last digit
434 
435  str_val += digits[(int)r.rem];
436 }
437 
438 // base defaults to 10
439 string
440 long_to_string(long val, int base)
441 {
442  string s;
443  append_long_to_string(val, base, s);
444  return s;
445 }
446 
447 // Jose Garcia
448 void append_double_to_string(const double &num, string &str)
449 {
450  // s having 100 characters should be enough for sprintf to do its job.
451  // I want to banish all instances of sprintf. 10/5/2001 jhrg
452  ostringstream oss;
453  oss.precision(9);
454  oss << num;
455  str += oss.str();
456 }
457 
458 string
459 double_to_string(const double &num)
460 {
461  string s;
462  append_double_to_string(num, s);
463  return s;
464 }
465 
466 // Get the version number of the core software. Defining this means that
467 // clients of the DAP don't have to rely on config.h for the version
468 // number.
469 string
471 {
472  return (string)"OPeNDAP DAP/" + libdap_version() + ": compiled on " + __DATE__ + ":" + __TIME__ ;
473 }
474 
475 // Given a pathname, return the file at the end of the path. This is used
476 // when reporting errors (maybe other times, too) to keep the server from
477 // revealing too much about its organization when sending error responses
478 // back to clients. 10/11/2000 jhrg
479 // MT-safe. 08/05/02 jhrg
480 
481 #ifdef WIN32
482 static const char path_sep[] =
483  {"\\"
484  };
485 #else
486 static const char path_sep[] =
487  {"/"
488  };
489 #endif
490 
491 string
492 path_to_filename(string path)
493 {
494  string::size_type pos = path.rfind(path_sep);
495 
496  return (pos == string::npos) ? path : path.substr(++pos);
497 }
498 
503 string
504 file_to_string(FILE *fp)
505 {
506  rewind(fp);
507  ostringstream oss;
508  char c;
509  while (fread(&c, 1, 1, fp))
510  oss << c;
511  return oss.str();
512 }
513 
516 
522 bool
523 size_ok(unsigned int sz, unsigned int nelem)
524 {
525  return (sz > 0 && nelem < UINT_MAX / sz);
526 }
527 
544 bool
545 pathname_ok(const string &path, bool strict)
546 {
547  if (path.length() > 255)
548  return false;
549 
550  Regex name("[-0-9A-z_./]+");
551  if (!strict)
552  name = "[:print:]+";
553 
554  string::size_type len = path.length();
555  int result = name.match(path.c_str(), len);
556  // Protect against casting too big an uint to int
557  // if LEN is bigger than the max int32, the second test can't work
558  if (len > INT_MAX || result != static_cast<int>(len))
559  return false;
560 
561  return true;
562 }
563 
565 
566 } // namespace libdap
567 
void downcase(string &s)
Definition: util.cc:367
#define not_used
Definition: config.h:850
string prune_spaces(const string &name)
Definition: util.cc:96
bool size_ok(unsigned int sz, unsigned int nelem)
sanitize the size of an array. Test for integer overflow when dynamically allocating an array...
Definition: util.cc:523
#define DBG(x)
Definition: debug.h:58
bool pathname_ok(const string &path, bool strict)
Does the string name a potentailly valid pathname? Test the given pathname to verfiy that it is a val...
Definition: util.cc:545
#define PACKAGE_NAME
Definition: config.h:595
string file_to_string(FILE *fp)
Definition: util.cc:504
string path_to_filename(string path)
Definition: util.cc:492
bool is_quoted(const string &s)
Definition: util.cc:374
string systime()
Definition: util.cc:354
const char * libdap_root()
Definition: util.cc:168
void append_long_to_string(long val, int base, string &str_val)
Definition: util.cc:411
bool deflate_exists()
string long_to_string(long val, int base)
Definition: util.cc:440
string dap_version()
Definition: util.cc:470
string double_to_string(const double &num)
Definition: util.cc:459
string remove_quotes(const string &s)
Definition: util.cc:380
#define LIBDAP_ROOT
Definition: config.h:570
bool unique_names(vector< BaseType * > l, const string &var_name, const string &type_name, string &msg)
Definition: util.cc:119
#define PACKAGE_VERSION
Definition: config.h:607
const char * libdap_version()
Definition: util.cc:175
FILE * compressor(FILE *output, int &childpid)
void append_double_to_string(const double &num, string &str)
Definition: util.cc:448
const char * libdap_name()
Definition: util.cc:182