libdap++ Updated for version 3.8.2

util.cc

Go to the documentation of this file.
00001 
00002 // -*- mode: c++; c-basic-offset:4 -*-
00003 
00004 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
00005 // Access Protocol.
00006 
00007 // Copyright (c) 2002,2003 OPeNDAP, Inc.
00008 // Author: James Gallagher <jgallagher@opendap.org>
00009 //
00010 // This library is free software; you can redistribute it and/or
00011 // modify it under the terms of the GNU Lesser General Public
00012 // License as published by the Free Software Foundation; either
00013 // version 2.1 of the License, or (at your option) any later version.
00014 //
00015 // This library is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 // Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public
00021 // License along with this library; if not, write to the Free Software
00022 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00023 //
00024 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
00025 
00026 // (c) COPYRIGHT URI/MIT 1994-1999
00027 // Please read the full copyright statement in the file COPYRIGHT_URI.
00028 //
00029 // Authors:
00030 //      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>
00031 
00032 // Utility functions used by the api.
00033 //
00034 // jhrg 9/21/94
00035 
00036 #include "config.h"
00037 
00038 static char rcsid[] not_used =
00039     {"$Id: util.cc 21699 2009-11-05 00:06:01Z jimg $"
00040     };
00041 
00042 #include <cassert>
00043 #include <cstring>
00044 
00045 #include <ctype.h>
00046 #ifndef TM_IN_SYS_TIME
00047 #include <time.h>
00048 #else
00049 #include <sys/time.h>
00050 #endif
00051 
00052 #ifndef WIN32
00053 #include <unistd.h>    // for stat
00054 #else
00055 #include <io.h>
00056 #include <fcntl.h>
00057 #include <process.h>
00058 #endif
00059 
00060 #include <sys/types.h>
00061 #include <sys/stat.h>
00062 
00063 #include <string>
00064 #include <sstream>
00065 #include <vector>
00066 #include <algorithm>
00067 #include <stdexcept>
00068 
00069 #include "BaseType.h"
00070 #include "Str.h"
00071 #include "Url.h"
00072 #include "Sequence.h"
00073 #include "Error.h"
00074 #include "parser.h"
00075 #include "util.h"
00076 #include "GNURegex.h"
00077 #include "debug.h"
00078 
00079 
00080 using namespace std;
00081 
00082 namespace libdap {
00083 
00084 // Remove spaces from the start of a URL and from the start of any constraint
00085 // expression it contains. 4/7/98 jhrg
00086 
00095 string
00096 prune_spaces(const string &name)
00097 {
00098     // If the URL does not even have white space return.
00099     if (name.find_first_of(' ') == name.npos)
00100         return name;
00101     else {
00102         // Strip leading spaces from http://...
00103         unsigned int i = name.find_first_not_of(' ');
00104         string tmp_name = name.substr(i);
00105 
00106         // Strip leading spaces from constraint part (following `?').
00107         unsigned int j = tmp_name.find('?') + 1;
00108         i = tmp_name.find_first_not_of(' ', j);
00109         tmp_name.erase(j, i - j);
00110 
00111         return tmp_name;
00112     }
00113 }
00114 
00115 // Compare elements in a list of (BaseType *)s and return true if there are
00116 // no duplicate elements, otherwise return false.
00117 
00118 bool
00119 unique_names(vector<BaseType *> l, const string &var_name,
00120              const string &type_name, string &msg)
00121 {
00122     // copy the identifier names to a vector
00123     vector<string> names(l.size());
00124 
00125     int nelem = 0;
00126     typedef std::vector<BaseType *>::const_iterator citer ;
00127     for (citer i = l.begin(); i != l.end(); i++) {
00128         assert(*i);
00129         names[nelem++] = (*i)->name();
00130         DBG(cerr << "NAMES[" << nelem - 1 << "]=" << names[nelem-1] << endl);
00131     }
00132 
00133     // sort the array of names
00134     sort(names.begin(), names.end());
00135 
00136 #ifdef DODS_DEBUG2
00137     cout << "unique:" << endl;
00138     for (int ii = 0; ii < nelem; ++ii)
00139         cout << "NAMES[" << ii << "]=" << names[ii] << endl;
00140 #endif
00141 
00142     // sort the array of names
00143     sort(names.begin(), names.end());
00144 
00145 #ifdef DODS_DEBUG2
00146     cout << "unique:" << endl;
00147     for (int ii = 0; ii < nelem; ++ii)
00148         cout << "NAMES[" << ii << "]=" << names[ii] << endl;
00149 #endif
00150 
00151     // look for any instance of consecutive names that are ==
00152     for (int j = 1; j < nelem; ++j) {
00153         if (names[j-1] == names[j]) {
00154             ostringstream oss;
00155             oss << "The variable `" << names[j]
00156             << "' is used more than once in " << type_name << " `"
00157             << var_name << "'";
00158             msg = oss.str();
00159 
00160             return false;
00161         }
00162     }
00163 
00164     return true;
00165 }
00166 
00167 const char *
00168 libdap_root()
00169 {
00170     return LIBDAP_ROOT;
00171 }
00172 
00173 extern "C"
00174     const char *
00175     libdap_version()
00176 {
00177     return PACKAGE_VERSION;
00178 }
00179 
00180 extern "C"
00181     const char *
00182     libdap_name()
00183 {
00184     return PACKAGE_NAME;
00185 }
00186 
00187 // Since Server4 can get compressed responses using Tomcat, bail on this
00188 // software (which complicates building under Win32). It can be turned on
00189 // for use with Server3 in configure.ac.
00190 
00191 #if COMPRESSION_FOR_SERVER3
00192 
00193 // Return true if the program deflate exists and is executable by user, group
00194 // and world. If this returns false the caller should assume that server
00195 // filter programs won't be able to find the deflate program and thus won't
00196 // be able to compress the return document.
00197 // NB: this works because this function uses the same rules as compressor()
00198 // (which follows) to look for deflate. 2/11/98 jhrg
00199 
00200 bool
00201 deflate_exists()
00202 {
00203     DBG(cerr << "Entering deflate_exists...");
00204 
00205     int status = false;
00206     struct stat buf;
00207 
00208 #ifdef WIN32
00209     string deflate = (string)libdap_root() + "\\bin\\deflate";
00210 #else
00211     string deflate = (string)libdap_root() + "/sbin/deflate";
00212 #endif
00213 
00214     // Check that the file exists...
00215     // First look for deflate using DODS_ROOT (compile-time constant subsumed
00216     // by an environment variable) and if that fails in the CWD which finds
00217     // the program when it is in the same directory as the dispatch script
00218     // and other server components. 2/11/98 jhrg
00219     status = (stat(deflate.c_str(), &buf) == 0)
00220 #ifdef WIN32
00221              || (stat(".\\deflate", &buf) == 0);
00222 #else
00223              || (stat("./deflate", &buf) == 0);
00224 #endif
00225 
00226     // and that it can be executed.
00227 #ifdef WIN32
00228     status &= (buf.st_mode & _S_IEXEC);
00229 #else
00230     status &= buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH);
00231 #endif
00232     DBG(cerr << " returning " << (status ? "true." : "false.") << endl);
00233     return (status != 0);
00234 }
00235 
00236 FILE *
00237 compressor(FILE *output, int &childpid)
00238 {
00239 #ifdef WIN32
00240     //  There is no such thing as a "fork" under win32. This makes it so that
00241     //  we have to juggle handles more aggressively. This code hasn't been
00242     //  tested and shown to work as of 07/2000.
00243     int pid, data[2];
00244     int hStdIn, hStdOut;
00245 
00246     if (_pipe(data, 512, O_BINARY | O_NOINHERIT) < 0) {
00247         cerr << "Could not create IPC channel for compressor process"
00248         << endl;
00249         return NULL;
00250     }
00251 
00252 
00253     // This sets up for the child process, but it has to be reversed for the
00254     // parent after the spawn takes place.
00255 
00256     // Store stdin, stdout so we have something to restore to
00257     hStdIn  = _dup(_fileno(stdin));
00258     hStdOut = _dup(_fileno(stdout));
00259 
00260     // Child is to read from read end of pipe
00261     if (_dup2(data[0], _fileno(stdin)) != 0) {
00262         cerr << "dup of child stdin failed" << endl;
00263         return NULL;
00264     }
00265     // Child is to write its's stdout to file
00266     if (_dup2(_fileno(output), _fileno(stdout)) != 0) {
00267         cerr << "dup of child stdout failed" << endl;
00268         return NULL;
00269     }
00270 
00271     // Spawn child process
00272     string deflate = "deflate.exe";
00273     if ((pid = _spawnlp(_P_NOWAIT, deflate.c_str(), deflate.c_str(),
00274                         "-c", "5", "-s", NULL)) < 0) {
00275         cerr << "Could not spawn to create compressor process" << endl;
00276         return NULL;
00277     }
00278 
00279     // Restore stdin, stdout for parent and close duplicate copies
00280     if (_dup2(hStdIn, _fileno(stdin)) != 0) {
00281         cerr << "dup of stdin failed" << endl;
00282         return NULL;
00283     }
00284     if (_dup2(hStdOut, _fileno(stdout)) != 0) {
00285         cerr << "dup of stdout failed" << endl;
00286         return NULL;
00287     }
00288     close(hStdIn);
00289     close(hStdOut);
00290 
00291     // Tell the parent that it reads from the opposite end of the
00292     // place where the child writes.
00293     close(data[0]);
00294     FILE *input = fdopen(data[1], "w");
00295     setbuf(input, 0);
00296     childpid = pid;
00297     return input;
00298 
00299 #else
00300     FILE *ret_file = NULL ;
00301 
00302     int pid, data[2];
00303 
00304     if (pipe(data) < 0) {
00305         cerr << "Could not create IPC channel for compressor process"
00306         << endl;
00307         return NULL;
00308     }
00309 
00310     if ((pid = fork()) < 0) {
00311         cerr << "Could not fork to create compressor process" << endl;
00312         return NULL;
00313     }
00314 
00315     // The parent process closes the write end of the Pipe, and creates a
00316     // FILE * using fdopen(). The FILE * is used by the calling program to
00317     // access the read end of the Pipe.
00318 
00319     if (pid > 0) {   // Parent, pid is that of the child
00320         close(data[0]);
00321         ret_file = fdopen(data[1], "w");
00322         setbuf(ret_file, 0);
00323         childpid = pid;
00324     }
00325     else {   // Child
00326         close(data[1]);
00327         dup2(data[0], 0); // Read from the pipe...
00328         dup2(fileno(output), 1); // Write to the FILE *output.
00329 
00330         DBG(cerr << "Opening compression stream." << endl);
00331 
00332         // First try to run deflate using DODS_ROOT (the value read from the
00333         // DODS_ROOT environment variable takes precedence over the value set
00334         // at build time. If that fails, try the CWD.
00335         string deflate = (string)libdap_root() + "/sbin/deflate";
00336         (void) execl(deflate.c_str(), "deflate", "-c",  "5", "-s", NULL);
00337         (void) execl("./deflate", "deflate", "-c",  "5", "-s", NULL);
00338         cerr << "Warning: Could not start compressor!" << endl;
00339         cerr << "defalte should be in DODS_ROOT/etc or in the CWD!"
00340         << endl;
00341         _exit(127);  // Only here if an error occurred.
00342     }
00343 
00344     return ret_file ;
00345 #endif
00346 }
00347 
00348 #endif // COMPRESSION_FOR_SERVER3
00349 
00350 // This function returns a pointer to the system time formated for an httpd
00351 // log file.
00352 
00353 string
00354 systime()
00355 {
00356     time_t TimBin;
00357 
00358     if (time(&TimBin) == (time_t) - 1)
00359         return string("time() error");
00360     else {
00361         string TimStr = ctime(&TimBin);
00362         return TimStr.substr(0, TimStr.size() - 2); // remove the \n
00363     }
00364 }
00365 
00366 void
00367 downcase(string &s)
00368 {
00369     for (unsigned int i = 0; i < s.length(); i++)
00370         s[i] = tolower(s[i]);
00371 }
00372 
00373 bool
00374 is_quoted(const string &s)
00375 {
00376     return (!s.empty() && s[0] == '\"' && s[s.length()-1] == '\"');
00377 }
00378 
00379 string
00380 remove_quotes(const string &s)
00381 {
00382     if (is_quoted(s))
00383         return s.substr(1, s.length() - 2);
00384     else
00385         return s;
00386 }
00387 
00388 #ifdef WIN32
00389 //  Sometimes need to buffer within an iostream under win32 when
00390 //  we want the output to go to a FILE *.  This is because
00391 //  it's not possible to associate an ofstream with a FILE *
00392 //  under the Standard ANSI C++ Library spec.  Unix systems
00393 //  don't follow the spec in this regard.
00394 void flush_stream(iostream ios, FILE *out)
00395 {
00396     int nbytes;
00397     char buffer[512];
00398 
00399     ios.get(buffer, 512, NULL);
00400     while ((nbytes = ios.gcount()) > 0) {
00401         fwrite(buffer, 1, nbytes, out);
00402         ios.get(buffer, 512, NULL);
00403     }
00404 
00405     return;
00406 }
00407 #endif
00408 
00409 // Jose Garcia
00410 void
00411 append_long_to_string(long val, int base, string &str_val)
00412 {
00413     // The array digits contains 36 elements which are the
00414     // posible valid digits for out bases in the range
00415     // [2,36]
00416     char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
00417     // result of val / base
00418     ldiv_t r;
00419 
00420     if (base > 36 || base < 2) {
00421         // no conversion if wrong base
00422         std::invalid_argument ex("The parameter base has an invalid value.");
00423         throw ex;
00424     }
00425     if (val < 0)
00426         str_val += '-';
00427     r = ldiv(labs(val), base);
00428 
00429     // output digits of val/base first
00430     if (r.quot > 0)
00431         append_long_to_string(r.quot, base, str_val);
00432 
00433     // output last digit
00434 
00435     str_val += digits[(int)r.rem];
00436 }
00437 
00438 // base defaults to 10
00439 string
00440 long_to_string(long val, int base)
00441 {
00442     string s;
00443     append_long_to_string(val, base, s);
00444     return s;
00445 }
00446 
00447 // Jose Garcia
00448 void append_double_to_string(const double &num, string &str)
00449 {
00450     // s having 100 characters should be enough for sprintf to do its job.
00451     // I want to banish all instances of sprintf. 10/5/2001 jhrg
00452     ostringstream oss;
00453     oss.precision(9);
00454     oss << num;
00455     str += oss.str();
00456 }
00457 
00458 string
00459 double_to_string(const double &num)
00460 {
00461     string s;
00462     append_double_to_string(num, s);
00463     return s;
00464 }
00465 
00466 // Get the version number of the core software. Defining this means that
00467 // clients of the DAP don't have to rely on config.h for the version
00468 // number.
00469 string
00470 dap_version()
00471 {
00472     return (string)"OPeNDAP DAP/" + libdap_version() + ": compiled on " + __DATE__ + ":" + __TIME__ ;
00473 }
00474 
00475 // Given a pathname, return the file at the end of the path. This is used
00476 // when reporting errors (maybe other times, too) to keep the server from
00477 // revealing too much about its organization when sending error responses
00478 // back to clients. 10/11/2000 jhrg
00479 // MT-safe. 08/05/02 jhrg
00480 
00481 #ifdef WIN32
00482 static const char path_sep[] =
00483     {"\\"
00484     };
00485 #else
00486 static const char path_sep[] =
00487     {"/"
00488     };
00489 #endif
00490 
00491 string
00492 path_to_filename(string path)
00493 {
00494     string::size_type pos = path.rfind(path_sep);
00495 
00496     return (pos == string::npos) ? path : path.substr(++pos);
00497 }
00498 
00503 string
00504 file_to_string(FILE *fp)
00505 {
00506     rewind(fp);
00507     ostringstream oss;
00508     char c;
00509     while (fread(&c, 1, 1, fp))
00510         oss << c;
00511     return oss.str();
00512 }
00513 
00516 
00522 bool
00523 size_ok(unsigned int sz, unsigned int nelem)
00524 {
00525     return (sz > 0 && nelem < UINT_MAX / sz);
00526 }
00527 
00544 bool
00545 pathname_ok(const string &path, bool strict)
00546 {
00547     if (path.length() > 255)
00548         return false;
00549 
00550     Regex name("[-0-9A-z_./]+");
00551     if (!strict)
00552         name = "[:print:]+";
00553 
00554     string::size_type len = path.length();
00555     int result = name.match(path.c_str(), len);
00556     // Protect against casting too big an uint to int
00557     // if LEN is bigger than the max int32, the second test can't work
00558     if (len > INT_MAX || result != static_cast<int>(len))
00559         return false;
00560 
00561     return true;
00562 }
00563 
00565 
00566 } // namespace libdap
00567