libdap++ Updated for version 3.8.2
|
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