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

Generated on Thu Jun 7 00:27:24 2007 for libdap++ by  doxygen 1.5.2