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 16731 2007-06-22 18:56:39Z jimg $"
00040     };
00041 
00042 #include <stdio.h>
00043 #include <stdlib.h>
00044 #include <assert.h>
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>
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 "debug.h"
00077 
00078 
00079 using namespace std;
00080 
00081 // Remove spaces from the start of a URL and from the start of any constraint
00082 // expression it contains. 4/7/98 jhrg
00083 
00092 string
00093 prune_spaces(const string &name)
00094 {
00095     // If the URL does not even have white space return.
00096     if (name.find_first_of(' ') == name.npos)
00097         return name;
00098     else {
00099         // Strip leading spaces from http://...
00100         unsigned int i = name.find_first_not_of(' ');
00101         string tmp_name = name.substr(i);
00102 
00103         // Strip leading spaces from constraint part (following `?').
00104         unsigned int j = tmp_name.find('?') + 1;
00105         i = tmp_name.find_first_not_of(' ', j);
00106         tmp_name.erase(j, i - j);
00107 
00108         return tmp_name;
00109     }
00110 }
00111 
00112 // Compare elements in a list of (BaseType *)s and return true if there are
00113 // no duplicate elements, otherwise return false.
00114 
00115 bool
00116 unique_names(vector<BaseType *> l, const string &var_name,
00117              const string &type_name, string &msg)
00118 {
00119     // copy the identifier names to a vector
00120     vector<string> names(l.size());
00121 
00122     int nelem = 0;
00123     typedef std::vector<BaseType *>::const_iterator citer ;
00124     for (citer i = l.begin(); i != l.end(); i++) {
00125         assert(*i);
00126         names[nelem++] = (*i)->name();
00127         DBG(cerr << "NAMES[" << nelem - 1 << "]=" << names[nelem-1] << endl);
00128     }
00129 
00130     // sort the array of names
00131     sort(names.begin(), names.end());
00132 
00133 #ifdef DODS_DEBUG2
00134     cout << "unique:" << endl;
00135     for (int ii = 0; ii < nelem; ++ii)
00136         cout << "NAMES[" << ii << "]=" << names[ii] << endl;
00137 #endif
00138 
00139     // sort the array of names
00140     sort(names.begin(), names.end());
00141 
00142 #ifdef DODS_DEBUG2
00143     cout << "unique:" << endl;
00144     for (int ii = 0; ii < nelem; ++ii)
00145         cout << "NAMES[" << ii << "]=" << names[ii] << endl;
00146 #endif
00147 
00148     // look for any instance of consecutive names that are ==
00149     for (int j = 1; j < nelem; ++j) {
00150         if (names[j-1] == names[j]) {
00151             ostringstream oss;
00152             oss << "The variable `" << names[j]
00153             << "' is used more than once in " << type_name << " `"
00154             << var_name << "'";
00155             msg = oss.str();
00156 
00157             return false;
00158         }
00159     }
00160 
00161     return true;
00162 }
00163 
00164 // This function is used to allocate memory for, and initialize, a new XDR
00165 // pointer. It sets the stream associated with the (XDR *) to STREAM.
00166 //
00167 // NB: STREAM is not one of the C++/libg++ iostream classes; it is a (FILE
00168 // *).
00169 
00170 //  These func's moved to xdrutil_ppc.* under the PPC as explained there
00171 #ifndef __POWERPC__
00172 XDR *
00173 new_xdrstdio(FILE *stream, enum xdr_op xop)
00174 {
00175     XDR *xdr = new XDR;
00176 
00177     xdrstdio_create(xdr, stream, xop);
00178 
00179     return xdr;
00180 }
00181 
00182 XDR *
00183 set_xdrstdio(XDR *xdr, FILE *stream, enum xdr_op xop)
00184 {
00185     xdrstdio_create(xdr, stream, xop);
00186 
00187     return xdr;
00188 }
00189 
00190 // Delete an XDR pointer allocated using the above function. Do not close the
00191 // associated FILE pointer.
00192 
00193 void
00194 delete_xdrstdio(XDR *xdr)
00195 {
00196     xdr_destroy(xdr);
00197 
00198     delete xdr; xdr = 0;
00199 }
00200 #endif
00201 
00202 // This function is used to en/decode Str and Url type variables. It is
00203 // defined as extern C since it is passed via function pointers to routines
00204 // in the xdr library where it is executed. This function is defined so
00205 // that Str and Url have an en/decoder which takes exactly two argumnets: an
00206 // XDR * and a string reference.
00207 //
00208 // NB: this function is *not* used for arrays (i.e., it is not the function
00209 // referenced by BaseType's _xdr_coder field when the object is a Str or Url.
00210 // Also note that \e max_str_len is an obese number but that really does not
00211 // matter; xdr_string() would never actually allocate that much memory unless
00212 // a string that size was sent from the server.
00213 //
00214 // Returns: XDR's bool_t; TRUE if no errors are detected, FALSE
00215 // otherwise. The formal parameter BUF is modified as a side effect.
00216 
00217 extern "C" bool_t
00218 xdr_str(XDR *xdrs, string &buf)
00219 {
00220     DBG(cerr << "In xdr_str, xdrs: " << xdrs << endl);
00221 
00222     switch (xdrs->x_op) {
00223     case XDR_ENCODE: { // BUF is a pointer to a (string *)
00224             const char *out_tmp = buf.c_str();
00225 
00226             return xdr_string(xdrs, (char **)&out_tmp, max_str_len);
00227         }
00228 
00229     case XDR_DECODE: {
00230             char *in_tmp = NULL;
00231 
00232             bool_t stat = xdr_string(xdrs, &in_tmp, max_str_len);
00233             if (!stat)
00234                 return stat;
00235 
00236             buf = in_tmp;
00237 
00238             free(in_tmp);
00239 
00240             return stat;
00241         }
00242 
00243     default:
00244         assert(false);
00245         return 0;
00246     }
00247 }
00248 
00249 const char *
00250 libdap_root()
00251 {
00252     char *libdap_root = 0;
00253     return ((libdap_root = getenv("LIBDAP_ROOT")) ? libdap_root : LIBDAP_ROOT);
00254 }
00255 
00256 extern "C"
00257     const char *
00258     libdap_version()
00259 {
00260     return PACKAGE_VERSION;
00261 }
00262 
00263 extern "C"
00264     const char *
00265     libdap_name()
00266 {
00267     return PACKAGE_NAME;
00268 }
00269 
00270 // Since Server4 can get compressed responses using Tomcat, bail on this
00271 // software (which complicates building under Win32). It can be turned on
00272 // for use with Server3 in configure.ac.
00273 
00274 #if COMPRESSION_FOR_SERVER3
00275 
00276 // Return true if the program deflate exists and is executable by user, group
00277 // and world. If this returns false the caller should assume that server
00278 // filter programs won't be able to find the deflate program and thus won't
00279 // be able to compress the return document.
00280 // NB: this works because this function uses the same rules as compressor()
00281 // (which follows) to look for deflate. 2/11/98 jhrg
00282 
00283 bool
00284 deflate_exists()
00285 {
00286     DBG(cerr << "Entering deflate_exists...");
00287 
00288     int status = false;
00289     struct stat buf;
00290 
00291 #ifdef WIN32
00292     string deflate = (string)libdap_root() + "\\bin\\deflate";
00293 #else
00294     string deflate = (string)libdap_root() + "/sbin/deflate";
00295 #endif
00296 
00297     // Check that the file exists...
00298     // First look for deflate using DODS_ROOT (compile-time constant subsumed
00299     // by an environment variable) and if that fails in the CWD which finds
00300     // the program when it is in the same directory as the dispatch script
00301     // and other server components. 2/11/98 jhrg
00302     status = (stat(deflate.c_str(), &buf) == 0)
00303 #ifdef WIN32
00304              || (stat(".\\deflate", &buf) == 0);
00305 #else
00306              || (stat("./deflate", &buf) == 0);
00307 #endif
00308 
00309     // and that it can be executed.
00310 #ifdef WIN32
00311     status &= (buf.st_mode & _S_IEXEC);
00312 #else
00313     status &= buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH);
00314 #endif
00315     DBG(cerr << " returning " << (status ? "true." : "false.") << endl);
00316     return (status != 0);
00317 }
00318 
00319 FILE *
00320 compressor(FILE *output, int &childpid)
00321 {
00322 #ifdef WIN32
00323     //  There is no such thing as a "fork" under win32. This makes it so that
00324     //  we have to juggle handles more aggressively. This code hasn't been
00325     //  tested and shown to work as of 07/2000.
00326     int pid, data[2];
00327     int hStdIn, hStdOut;
00328 
00329     if (_pipe(data, 512, O_BINARY | O_NOINHERIT) < 0) {
00330         cerr << "Could not create IPC channel for compressor process"
00331         << endl;
00332         return NULL;
00333     }
00334 
00335 
00336     // This sets up for the child process, but it has to be reversed for the
00337     // parent after the spawn takes place.
00338 
00339     // Store stdin, stdout so we have something to restore to
00340     hStdIn  = _dup(_fileno(stdin));
00341     hStdOut = _dup(_fileno(stdout));
00342 
00343     // Child is to read from read end of pipe
00344     if (_dup2(data[0], _fileno(stdin)) != 0) {
00345         cerr << "dup of child stdin failed" << endl;
00346         return NULL;
00347     }
00348     // Child is to write its's stdout to file
00349     if (_dup2(_fileno(output), _fileno(stdout)) != 0) {
00350         cerr << "dup of child stdout failed" << endl;
00351         return NULL;
00352     }
00353 
00354     // Spawn child process
00355     string deflate = "deflate.exe";
00356     if ((pid = _spawnlp(_P_NOWAIT, deflate.c_str(), deflate.c_str(),
00357                         "-c", "5", "-s", NULL)) < 0) {
00358         cerr << "Could not spawn to create compressor process" << endl;
00359         return NULL;
00360     }
00361 
00362     // Restore stdin, stdout for parent and close duplicate copies
00363     if (_dup2(hStdIn, _fileno(stdin)) != 0) {
00364         cerr << "dup of stdin failed" << endl;
00365         return NULL;
00366     }
00367     if (_dup2(hStdOut, _fileno(stdout)) != 0) {
00368         cerr << "dup of stdout failed" << endl;
00369         return NULL;
00370     }
00371     close(hStdIn);
00372     close(hStdOut);
00373 
00374     // Tell the parent that it reads from the opposite end of the
00375     // place where the child writes.
00376     close(data[0]);
00377     FILE *input = fdopen(data[1], "w");
00378     setbuf(input, 0);
00379     childpid = pid;
00380     return input;
00381 
00382 #else
00383     FILE *ret_file = NULL ;
00384 
00385     int pid, data[2];
00386 
00387     if (pipe(data) < 0) {
00388         cerr << "Could not create IPC channel for compressor process"
00389         << endl;
00390         return NULL;
00391     }
00392 
00393     if ((pid = fork()) < 0) {
00394         cerr << "Could not fork to create compressor process" << endl;
00395         return NULL;
00396     }
00397 
00398     // The parent process closes the write end of the Pipe, and creates a
00399     // FILE * using fdopen(). The FILE * is used by the calling program to
00400     // access the read end of the Pipe.
00401 
00402     if (pid > 0) {   // Parent, pid is that of the child
00403         close(data[0]);
00404         ret_file = fdopen(data[1], "w");
00405         setbuf(ret_file, 0);
00406         childpid = pid;
00407     }
00408     else {   // Child
00409         close(data[1]);
00410         dup2(data[0], 0); // Read from the pipe...
00411         dup2(fileno(output), 1); // Write to the FILE *output.
00412 
00413         DBG(cerr << "Opening compression stream." << endl);
00414 
00415         // First try to run deflate using DODS_ROOT (the value read from the
00416         // DODS_ROOT environment variable takes precedence over the value set
00417         // at build time. If that fails, try the CWD.
00418         string deflate = (string)libdap_root() + "/sbin/deflate";
00419         (void) execl(deflate.c_str(), "deflate", "-c",  "5", "-s", NULL);
00420         (void) execl("./deflate", "deflate", "-c",  "5", "-s", NULL);
00421         cerr << "Warning: Could not start compressor!" << endl;
00422         cerr << "defalte should be in DODS_ROOT/etc or in the CWD!"
00423         << endl;
00424         _exit(127);  // Only here if an error occurred.
00425     }
00426 
00427     return ret_file ;
00428 #endif
00429 }
00430 
00431 #endif // COMPRESSION_FOR_SERVER3
00432 
00433 // This function returns a pointer to the system time formated for an httpd
00434 // log file.
00435 
00436 string
00437 systime()
00438 {
00439     time_t TimBin;
00440 
00441     if (time(&TimBin) == (time_t) - 1)
00442         return string("time() error");
00443     else {
00444         string TimStr = ctime(&TimBin);
00445         return TimStr.substr(0, TimStr.size() - 2); // remove the \n
00446     }
00447 }
00448 
00449 void
00450 downcase(string &s)
00451 {
00452     for (unsigned int i = 0; i < s.length(); i++)
00453         s[i] = tolower(s[i]);
00454 }
00455 
00456 #ifdef WIN32
00457 //  Sometimes need to buffer within an iostream under win32 when
00458 //  we want the output to go to a FILE *.  This is because
00459 //  it's not possible to associate an ofstream with a FILE *
00460 //  under the Standard ANSI C++ Library spec.  Unix systems
00461 //  don't follow the spec in this regard.
00462 void flush_stream(iostream ios, FILE *out)
00463 {
00464     int nbytes;
00465     char buffer[512];
00466 
00467     ios.get(buffer, 512, NULL);
00468     while ((nbytes = ios.gcount()) > 0) {
00469         fwrite(buffer, 1, nbytes, out);
00470         ios.get(buffer, 512, NULL);
00471     }
00472 
00473     return;
00474 }
00475 #endif
00476 
00477 // Jose Garcia
00478 void
00479 append_long_to_string(long val, int base, string &str_val)
00480 {
00481     // The array digits contains 36 elements which are the
00482     // posible valid digits for out bases in the range
00483     // [2,36]
00484     char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
00485     // result of val / base
00486     ldiv_t r;
00487 
00488     if (base > 36 || base < 2) {
00489         // no conversion if wrong base
00490         std::invalid_argument ex("The parameter base has an invalid value.");
00491         throw ex;
00492     }
00493     if (val < 0)
00494         str_val += '-';
00495     r = ldiv(labs(val), base);
00496 
00497     // output digits of val/base first
00498     if (r.quot > 0)
00499         append_long_to_string(r.quot, base, str_val);
00500 
00501     // output last digit
00502 
00503     str_val += digits[(int)r.rem];
00504 }
00505 
00506 // base defaults to 10
00507 string
00508 long_to_string(long val, int base)
00509 {
00510     string s;
00511     append_long_to_string(val, base, s);
00512     return s;
00513 }
00514 
00515 // Jose Garcia
00516 void append_double_to_string(const double &num, string &str)
00517 {
00518     // s having 100 characters should be enough for sprintf to do its job.
00519     // I want to banish all instances of sprintf. 10/5/2001 jhrg
00520     ostringstream oss;
00521     oss.precision(9);
00522     oss << num;
00523     str += oss.str();
00524 }
00525 
00526 string
00527 double_to_string(const double &num)
00528 {
00529     string s;
00530     append_double_to_string(num, s);
00531     return s;
00532 }
00533 
00534 // Get the version number of the core software. Defining this means that
00535 // clients of the DAP don't have to rely on config.h for the version
00536 // number.
00537 string
00538 dap_version()
00539 {
00540     return (string)"OPeNDAP DAP/" + libdap_version() + ": compiled on " + __DATE__ + ":" + __TIME__ ;
00541 }
00542 
00543 // Given a pathname, return the file at the end of the path. This is used
00544 // when reporting errors (maybe other times, too) to keep the server from
00545 // revealing too much about its organization when sending error responses
00546 // back to clients. 10/11/2000 jhrg
00547 // MT-safe. 08/05/02 jhrg
00548 
00549 #ifdef WIN32
00550 static const char path_sep[] =
00551     {"\\"
00552     };
00553 #else
00554 static const char path_sep[] =
00555     {"/"
00556     };
00557 #endif
00558 
00559 string
00560 path_to_filename(string path)
00561 {
00562     string::size_type pos = path.rfind(path_sep);
00563 
00564     return (pos == string::npos) ? path : path.substr(++pos);
00565 }
00566 
00567 // Look around for a reasonable place to put a temporary file. Check first
00568 // the value of the TMPDIR env var. If that does not yeild a path that's
00569 // writable (as defined by access(..., W_OK|R_OK)) then look at P_tmpdir (as
00570 // defined in stdio.h. If both come up empty, then use `./'.
00571 //
00572 // This function allocates storage using new. The caller must delete the char
00573 // array.
00574 char *
00575 get_tempfile_template(char *file_template)
00576 {
00577     char *c;
00578 #ifdef WIN32
00579     if (getenv("TEMP") && (access(getenv("TEMP"), 6) == 0))
00580         c = getenv("TEMP");
00581     else if (getenv("TMP"))
00582         c = getenv("TMP");
00583 #else
00584     if (getenv("TMPDIR") && (access(getenv("TMPDIR"), W_OK | R_OK) == 0))
00585         c = getenv("TMPDIR");
00586 #ifdef P_tmpdir
00587     else if (access(P_tmpdir, W_OK | R_OK) == 0)
00588         c = P_tmpdir;
00589 #endif
00590 #endif
00591     else
00592         c = ".";
00593 
00594     char *temp = new char[strlen(c) + strlen(file_template) + 2];
00595     strcpy(temp, c);
00596     strcat(temp, "/");
00597 
00598     strcat(temp, file_template);
00599 
00600     return temp;
00601 }
00602 
00608 #ifndef WIN32
00609 FILE *
00610 get_temp_file(char *temp)
00611 {
00612     int fd = mkstemp(temp);
00613     if (fd < 0)
00614         return 0;
00615     FILE *tmp = fdopen(fd, "a+");
00616     return tmp;
00617 }
00618 #endif
00619 
00624 string
00625 file_to_string(FILE *fp)
00626 {
00627     rewind(fp);
00628     ostringstream oss;
00629     char c;
00630     while (fread(&c, 1, 1, fp))
00631         oss << c;
00632     return oss.str();
00633 }

Generated on Wed Jun 27 12:56:39 2007 for libdap++ by  doxygen 1.4.7