Blame util.cc

Packit a4aae4
// -*- mode: c++; c-basic-offset:4 -*-
Packit a4aae4
Packit a4aae4
// This file is part of libdap, A C++ implementation of the OPeNDAP Data
Packit a4aae4
// Access Protocol.
Packit a4aae4
Packit a4aae4
// Copyright (c) 2002,2003 OPeNDAP, Inc.
Packit a4aae4
// Author: James Gallagher <jgallagher@opendap.org>
Packit a4aae4
//
Packit a4aae4
// This library is free software; you can redistribute it and/or
Packit a4aae4
// modify it under the terms of the GNU Lesser General Public
Packit a4aae4
// License as published by the Free Software Foundation; either
Packit a4aae4
// version 2.1 of the License, or (at your option) any later version.
Packit a4aae4
//
Packit a4aae4
// This library is distributed in the hope that it will be useful,
Packit a4aae4
// but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a4aae4
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a4aae4
// Lesser General Public License for more details.
Packit a4aae4
//
Packit a4aae4
// You should have received a copy of the GNU Lesser General Public
Packit a4aae4
// License along with this library; if not, write to the Free Software
Packit a4aae4
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit a4aae4
//
Packit a4aae4
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
Packit a4aae4
Packit a4aae4
// (c) COPYRIGHT URI/MIT 1994-1999
Packit a4aae4
// Please read the full copyright statement in the file COPYRIGHT_URI.
Packit a4aae4
//
Packit a4aae4
// Authors:
Packit a4aae4
//      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>
Packit a4aae4
Packit a4aae4
// Utility functions used by the api.
Packit a4aae4
//
Packit a4aae4
// jhrg 9/21/94
Packit a4aae4
Packit a4aae4
#include "config.h"
Packit a4aae4
Packit a4aae4
#include <fstream>
Packit a4aae4
Packit a4aae4
#include <cassert>
Packit a4aae4
#include <cstring>
Packit a4aae4
#include <climits>
Packit a4aae4
#include <cstdlib>
Packit a4aae4
Packit a4aae4
#include <ctype.h>
Packit a4aae4
#ifndef TM_IN_SYS_TIME
Packit a4aae4
#include <time.h>
Packit a4aae4
#else
Packit a4aae4
#include <sys/time.h>
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#ifndef WIN32
Packit a4aae4
#include <unistd.h>    // for stat
Packit a4aae4
#else
Packit a4aae4
#include <io.h>
Packit a4aae4
#include <fcntl.h>
Packit a4aae4
#include <process.h>
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#include <sys/types.h>
Packit a4aae4
#include <sys/stat.h>
Packit a4aae4
Packit a4aae4
#include <string>
Packit a4aae4
#include <sstream>
Packit a4aae4
#include <vector>
Packit a4aae4
#include <algorithm>
Packit a4aae4
#include <stdexcept>
Packit a4aae4
Packit a4aae4
#include "BaseType.h"
Packit a4aae4
#include "Byte.h"
Packit a4aae4
#include "Int16.h"
Packit a4aae4
#include "Int32.h"
Packit a4aae4
#include "UInt16.h"
Packit a4aae4
#include "UInt32.h"
Packit a4aae4
#include "Float32.h"
Packit a4aae4
#include "Float64.h"
Packit a4aae4
#include "Str.h"
Packit a4aae4
#include "Array.h"
Packit a4aae4
Packit a4aae4
#include "Int64.h"
Packit a4aae4
#include "UInt64.h"
Packit a4aae4
#include "Int8.h"
Packit a4aae4
Packit a4aae4
#include "Error.h"
Packit a4aae4
Packit a4aae4
#include "util.h"
Packit a4aae4
#include "GNURegex.h"
Packit a4aae4
#include "debug.h"
Packit a4aae4
Packit a4aae4
using namespace std;
Packit a4aae4
Packit a4aae4
namespace libdap {
Packit a4aae4
Packit a4aae4
/** @brief Does this host use big-endian byte order? */
Packit a4aae4
bool is_host_big_endian()
Packit a4aae4
{
Packit a4aae4
#ifdef COMPUTE_ENDIAN_AT_RUNTIME
Packit a4aae4
Packit a4aae4
    dods_int16 i = 0x0100;
Packit a4aae4
    char *c = reinterpret_cast<char*>(&i);
Packit a4aae4
    return *c;
Packit a4aae4
Packit a4aae4
#else
Packit a4aae4
Packit a4aae4
#if WORDS_BIGENDIAN
Packit a4aae4
    return true;
Packit a4aae4
#else
Packit a4aae4
    return false;
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#endif
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Given a BaseType pointer, extract the string value it contains
Packit a4aae4
Packit a4aae4
 @param arg The BaseType pointer
Packit a4aae4
 @return A C++ string
Packit a4aae4
 @exception Error thrown if the referenced BaseType object does not contain
Packit a4aae4
 a DAP String. */
Packit a4aae4
string extract_string_argument(BaseType *arg)
Packit a4aae4
{
Packit a4aae4
    assert(arg);
Packit a4aae4
Packit a4aae4
    if (arg->type() != dods_str_c) throw Error(malformed_expr, "The function requires a string argument.");
Packit a4aae4
Packit a4aae4
    if (!arg->read_p())
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__,
Packit a4aae4
                "The CE Evaluator built an argument list where some constants held no values.");
Packit a4aae4
Packit a4aae4
    return static_cast<Str*>(arg)->value();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
template<class T> static void set_array_using_double_helper(Array *a, double *src, int src_len)
Packit a4aae4
{
Packit a4aae4
    assert(a);
Packit a4aae4
    assert(src);
Packit a4aae4
    assert(src_len > 0);
Packit a4aae4
Packit a4aae4
    vector<T> values(src_len);
Packit a4aae4
    for (int i = 0; i < src_len; ++i)
Packit a4aae4
        values[i] = (T) src[i];
Packit a4aae4
Packit a4aae4
    // This copies the values
Packit a4aae4
    a->set_value(values, src_len);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Given an array that holds some sort of numeric data, load it with values
Packit a4aae4
 using an array of doubles. This function makes several assumptions. First,
Packit a4aae4
 it assumes the caller really wants to put the doubles into whatever types
Packit a4aae4
 the array holds! Caveat emptor. Second, it assumes that if the size of
Packit a4aae4
 source (\e src) array is different than the destination (\e dest) the
Packit a4aae4
 caller has made a mistake. In that case it will throw an Error object.
Packit a4aae4
Packit a4aae4
 After setting the values, this method sets the \c read_p property for
Packit a4aae4
 \e dest. Setting \e read_p tells the serialization methods in libdap
Packit a4aae4
 that this variable already holds data values and, given that, the
Packit a4aae4
 serialization code will not try to read the values.
Packit a4aae4
Packit a4aae4
 @note Support for DAP4 added.
Packit a4aae4
 @param dest An Array. The values are written to this array, reusing
Packit a4aae4
 its storage. Existing values are lost.
Packit a4aae4
 @param src The source data.
Packit a4aae4
 @param src_len The number of elements in the \e src array.
Packit a4aae4
 @exception Error Thrown if \e dest is not a numeric-type array (Byte, ...,
Packit a4aae4
 Float64) or if the number of elements in \e src does not match the number
Packit a4aae4
 is \e dest. */
Packit a4aae4
void set_array_using_double(Array *dest, double *src, int src_len)
Packit a4aae4
{
Packit a4aae4
    assert(dest);
Packit a4aae4
    assert(src);
Packit a4aae4
    assert(src_len > 0);
Packit a4aae4
Packit a4aae4
    // Simple types are Byte, ..., Float64, String and Url.
Packit a4aae4
    if ((dest->type() == dods_array_c && !dest->var()->is_simple_type()) || dest->var()->type() == dods_str_c
Packit a4aae4
            || dest->var()->type() == dods_url_c)
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "The function requires a numeric-type array argument.");
Packit a4aae4
Packit a4aae4
    // Test sizes. Note that Array::length() takes any constraint into account
Packit a4aae4
    // when it returns the length. Even if this was removed, the 'helper'
Packit a4aae4
    // function this uses calls Vector::val2buf() which uses Vector::width()
Packit a4aae4
    // which in turn uses length().
Packit a4aae4
    if (dest->length() != src_len)
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__,
Packit a4aae4
                "The source and destination array sizes don't match (" + long_to_string(src_len) + " versus "
Packit a4aae4
                        + long_to_string(dest->length()) + ").");
Packit a4aae4
Packit a4aae4
    // The types of arguments that the CE Parser will build for numeric
Packit a4aae4
    // constants are limited to Uint32, Int32 and Float64. See ce_expr.y.
Packit a4aae4
    // Expanded to work for any numeric type so it can be used for more than
Packit a4aae4
    // just arguments.
Packit a4aae4
    switch (dest->var()->type()) {
Packit a4aae4
    case dods_byte_c:
Packit a4aae4
        set_array_using_double_helper<dods_byte>(dest, src, src_len);
Packit a4aae4
        break;
Packit a4aae4
    case dods_uint16_c:
Packit a4aae4
        set_array_using_double_helper<dods_uint16>(dest, src, src_len);
Packit a4aae4
        break;
Packit a4aae4
    case dods_int16_c:
Packit a4aae4
        set_array_using_double_helper<dods_int16>(dest, src, src_len);
Packit a4aae4
        break;
Packit a4aae4
    case dods_uint32_c:
Packit a4aae4
        set_array_using_double_helper<dods_uint32>(dest, src, src_len);
Packit a4aae4
        break;
Packit a4aae4
    case dods_int32_c:
Packit a4aae4
        set_array_using_double_helper<dods_int32>(dest, src, src_len);
Packit a4aae4
        break;
Packit a4aae4
    case dods_float32_c:
Packit a4aae4
        set_array_using_double_helper<dods_float32>(dest, src, src_len);
Packit a4aae4
        break;
Packit a4aae4
    case dods_float64_c:
Packit a4aae4
        set_array_using_double_helper<dods_float64>(dest, src, src_len);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
        // DAP4 support
Packit a4aae4
    case dods_uint8_c:
Packit a4aae4
        set_array_using_double_helper<dods_byte>(dest, src, src_len);
Packit a4aae4
        break;
Packit a4aae4
    case dods_int8_c:
Packit a4aae4
        set_array_using_double_helper<dods_int8>(dest, src, src_len);
Packit a4aae4
        break;
Packit a4aae4
    case dods_uint64_c:
Packit a4aae4
        set_array_using_double_helper<dods_uint64>(dest, src, src_len);
Packit a4aae4
        break;
Packit a4aae4
    case dods_int64_c:
Packit a4aae4
        set_array_using_double_helper<dods_int64>(dest, src, src_len);
Packit a4aae4
        break;
Packit a4aae4
    default:
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__,
Packit a4aae4
                "The argument list built by the CE parser contained an unsupported numeric type.");
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    // Set the read_p property.
Packit a4aae4
    dest->set_read_p(true);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
template<class T> static double *extract_double_array_helper(Array * a)
Packit a4aae4
{
Packit a4aae4
    assert(a);
Packit a4aae4
Packit a4aae4
    int length = a->length();
Packit a4aae4
Packit a4aae4
    vector<T> b(length);
Packit a4aae4
    a->value(&b[0]);    // Extract the values of 'a' to 'b'
Packit a4aae4
Packit a4aae4
    double *dest = new double[length];
Packit a4aae4
    for (int i = 0; i < length; ++i)
Packit a4aae4
        dest[i] = (double) b[i];
Packit a4aae4
Packit a4aae4
    return dest;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Given a pointer to an Array which holds a numeric type, extract the
Packit a4aae4
 * values and return in an array of doubles. This function allocates the
Packit a4aae4
 * array using 'new double[n]' so delete[] MUST be used when you are done
Packit a4aae4
 * the data.
Packit a4aae4
 *
Packit a4aae4
 * @note Support added for DAP4.
Packit a4aae4
 * @param a Extract value from this Array.
Packit a4aae4
 * @return A C++/C array of doubles.
Packit a4aae4
 */
Packit a4aae4
double *extract_double_array(Array * a)
Packit a4aae4
{
Packit a4aae4
    assert(a);
Packit a4aae4
Packit a4aae4
    // Simple types are Byte, ..., Float64, String and Url.
Packit a4aae4
    if ((a->type() == dods_array_c && !a->var()->is_simple_type()) || a->var()->type() == dods_str_c
Packit a4aae4
            || a->var()->type() == dods_url_c)
Packit a4aae4
        throw Error(malformed_expr, "The function requires a DAP numeric-type array argument.");
Packit a4aae4
Packit a4aae4
    if (!a->read_p())
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, string("The Array '") + a->name() + "'does not contain values.");
Packit a4aae4
Packit a4aae4
    // The types of arguments that the CE Parser will build for numeric
Packit a4aae4
    // constants are limited to Uint32, Int32 and Float64. See ce_expr.y.
Packit a4aae4
    // Expanded to work for any numeric type so it can be used for more than
Packit a4aae4
    // just arguments.
Packit a4aae4
    switch (a->var()->type()) {
Packit a4aae4
    case dods_byte_c:
Packit a4aae4
        return extract_double_array_helper<dods_byte>(a);
Packit a4aae4
    case dods_uint16_c:
Packit a4aae4
        return extract_double_array_helper<dods_uint16>(a);
Packit a4aae4
    case dods_int16_c:
Packit a4aae4
        return extract_double_array_helper<dods_int16>(a);
Packit a4aae4
    case dods_uint32_c:
Packit a4aae4
        return extract_double_array_helper<dods_uint32>(a);
Packit a4aae4
    case dods_int32_c:
Packit a4aae4
        return extract_double_array_helper<dods_int32>(a);
Packit a4aae4
    case dods_float32_c:
Packit a4aae4
        return extract_double_array_helper<dods_float32>(a);
Packit a4aae4
    case dods_float64_c:
Packit a4aae4
        // Should not be copying these values, just read them,
Packit a4aae4
        // but older code may depend on the return of this function
Packit a4aae4
        // being something that should be deleted, so leave this
Packit a4aae4
        // alone. jhrg 2/24/15
Packit a4aae4
        return extract_double_array_helper<dods_float64>(a);
Packit a4aae4
Packit a4aae4
        // Support for DAP4
Packit a4aae4
    case dods_uint8_c:
Packit a4aae4
        return extract_double_array_helper<dods_byte>(a);
Packit a4aae4
    case dods_int8_c:
Packit a4aae4
        return extract_double_array_helper<dods_int8>(a);
Packit a4aae4
    case dods_uint64_c:
Packit a4aae4
        return extract_double_array_helper<dods_uint64>(a);
Packit a4aae4
    case dods_int64_c:
Packit a4aae4
        return extract_double_array_helper<dods_int64>(a);
Packit a4aae4
    default:
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__,
Packit a4aae4
                "The argument list built by the CE parser contained an unsupported numeric type.");
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
// This helper function assumes 'dest' is the correct size. This should not
Packit a4aae4
// be called when the Array 'a' is a Float64, since the values are already
Packit a4aae4
// in a double array!
Packit a4aae4
template<class T> static void extract_double_array_helper(Array * a, vector<double> &dest)
Packit a4aae4
{
Packit a4aae4
    assert(a);
Packit a4aae4
    assert(dest.size() == (unsigned long )a->length());
Packit a4aae4
Packit a4aae4
    int length = a->length();
Packit a4aae4
Packit a4aae4
    vector<T> b(length);
Packit a4aae4
    a->value(&b[0]);    // Extract the values of 'a' to 'b'
Packit a4aae4
Packit a4aae4
    for (int i = 0; i < length; ++i)
Packit a4aae4
        dest[i] = (double) b[i];
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Given a pointer to an Array which holds a numeric type, extract the
Packit a4aae4
 * values and return in an array of doubles. This function allocates the
Packit a4aae4
 * array using 'new double[n]' so delete[] MUST be used when you are done
Packit a4aae4
 * the data.
Packit a4aae4
 *
Packit a4aae4
 * @note Before you call this function, you must call read on the array,
Packit a4aae4
 * or load the array with values and set the read_p property.
Packit a4aae4
 *
Packit a4aae4
 * @note Support added for DAP4.
Packit a4aae4
 * @param a Extract value from this Array.
Packit a4aae4
 * @param dest Put the values in this vector. A value-result parameter.
Packit a4aae4
 * @return A C++/C array of doubles.
Packit a4aae4
 */
Packit a4aae4
void extract_double_array(Array *a, vector<double> &dest)
Packit a4aae4
{
Packit a4aae4
    assert(a);
Packit a4aae4
Packit a4aae4
    // Simple types are Byte, ..., Float64, String and Url.
Packit a4aae4
    if ((a->type() == dods_array_c && !a->var()->is_simple_type()) || a->var()->type() == dods_str_c
Packit a4aae4
            || a->var()->type() == dods_url_c)
Packit a4aae4
        throw Error(malformed_expr, "The function requires a DAP numeric-type array argument.");
Packit a4aae4
Packit a4aae4
    if (!a->read_p())
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, string("The Array '") + a->name() + "' does not contain values.");
Packit a4aae4
Packit a4aae4
    dest.resize(a->length());
Packit a4aae4
Packit a4aae4
    // The types of arguments that the CE Parser will build for numeric
Packit a4aae4
    // constants are limited to Uint32, Int32 and Float64. See ce_expr.y.
Packit a4aae4
    // Expanded to work for any numeric type so it can be used for more than
Packit a4aae4
    // just arguments.
Packit a4aae4
    switch (a->var()->type()) {
Packit a4aae4
    case dods_byte_c:
Packit a4aae4
        return extract_double_array_helper<dods_byte>(a, dest);
Packit a4aae4
    case dods_uint16_c:
Packit a4aae4
        return extract_double_array_helper<dods_uint16>(a, dest);
Packit a4aae4
    case dods_int16_c:
Packit a4aae4
        return extract_double_array_helper<dods_int16>(a, dest);
Packit a4aae4
    case dods_uint32_c:
Packit a4aae4
        return extract_double_array_helper<dods_uint32>(a, dest);
Packit a4aae4
    case dods_int32_c:
Packit a4aae4
        return extract_double_array_helper<dods_int32>(a, dest);
Packit a4aae4
    case dods_float32_c:
Packit a4aae4
        return extract_double_array_helper<dods_float32>(a, dest);
Packit a4aae4
    case dods_float64_c:
Packit a4aae4
        return a->value(&dest[0]);      // no need to copy the values
Packit a4aae4
        // return extract_double_array_helper<dods_float64>(a, dest);
Packit a4aae4
Packit a4aae4
        // Support for DAP4
Packit a4aae4
    case dods_uint8_c:
Packit a4aae4
        return extract_double_array_helper<dods_byte>(a, dest);
Packit a4aae4
    case dods_int8_c:
Packit a4aae4
        return extract_double_array_helper<dods_int8>(a, dest);
Packit a4aae4
    case dods_uint64_c:
Packit a4aae4
        return extract_double_array_helper<dods_uint64>(a, dest);
Packit a4aae4
    case dods_int64_c:
Packit a4aae4
        return extract_double_array_helper<dods_int64>(a, dest);
Packit a4aae4
    default:
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__,
Packit a4aae4
                "The argument list built by the CE parser contained an unsupported numeric type.");
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Given a BaseType pointer, extract the numeric value it contains and return
Packit a4aae4
 it in a C++ double.
Packit a4aae4
Packit a4aae4
 @note Support for DAP4 types added.
Packit a4aae4
Packit a4aae4
 @param arg The BaseType pointer
Packit a4aae4
 @return A C++ double
Packit a4aae4
 @exception Error thrown if the referenced BaseType object does not contain
Packit a4aae4
 a DAP numeric value. */
Packit a4aae4
double extract_double_value(BaseType *arg)
Packit a4aae4
{
Packit a4aae4
    assert(arg);
Packit a4aae4
Packit a4aae4
    // Simple types are Byte, ..., Float64, String and Url.
Packit a4aae4
    if (!arg->is_simple_type() || arg->type() == dods_str_c || arg->type() == dods_url_c)
Packit a4aae4
        throw Error(malformed_expr, "The function requires a numeric-type argument.");
Packit a4aae4
Packit a4aae4
    if (!arg->read_p())
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__,
Packit a4aae4
                "The Evaluator built an argument list where some constants held no values.");
Packit a4aae4
Packit a4aae4
    // The types of arguments that the CE Parser will build for numeric
Packit a4aae4
    // constants are limited to Uint32, Int32 and Float64. See ce_expr.y.
Packit a4aae4
    // Expanded to work for any numeric type so it can be used for more than
Packit a4aae4
    // just arguments.
Packit a4aae4
    switch (arg->type()) {
Packit a4aae4
    case dods_byte_c:
Packit a4aae4
        return (double) (static_cast<Byte*>(arg)->value());
Packit a4aae4
    case dods_uint16_c:
Packit a4aae4
        return (double) (static_cast<UInt16*>(arg)->value());
Packit a4aae4
    case dods_int16_c:
Packit a4aae4
        return (double) (static_cast<Int16*>(arg)->value());
Packit a4aae4
    case dods_uint32_c:
Packit a4aae4
        return (double) (static_cast<UInt32*>(arg)->value());
Packit a4aae4
    case dods_int32_c:
Packit a4aae4
        return (double) (static_cast<Int32*>(arg)->value());
Packit a4aae4
    case dods_float32_c:
Packit a4aae4
        return (double) (static_cast<Float32*>(arg)->value());
Packit a4aae4
    case dods_float64_c:
Packit a4aae4
        return static_cast<Float64*>(arg)->value();
Packit a4aae4
Packit a4aae4
        // Support for DAP4 types.
Packit a4aae4
    case dods_uint8_c:
Packit a4aae4
        return (double) (static_cast<Byte*>(arg)->value());
Packit a4aae4
    case dods_int8_c:
Packit a4aae4
        return (double) (static_cast<Int8*>(arg)->value());
Packit a4aae4
    case dods_uint64_c:
Packit a4aae4
        return (double) (static_cast<UInt64*>(arg)->value());
Packit a4aae4
    case dods_int64_c:
Packit a4aae4
        return (double) (static_cast<Int64*>(arg)->value());
Packit a4aae4
Packit a4aae4
    default:
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__,
Packit a4aae4
                "The argument list built by the parser contained an unsupported numeric type.");
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
// Remove spaces from the start of a URL and from the start of any constraint
Packit a4aae4
// expression it contains. 4/7/98 jhrg
Packit a4aae4
Packit a4aae4
/** Removed spaces from the front of a URL and also from the front of the CE.
Packit a4aae4
 This function assumes that there are no holes in both the URL and the CE.
Packit a4aae4
 It will remove \e leading space, but not other spaces.
Packit a4aae4
Packit a4aae4
 @param name The URL to process
Packit a4aae4
 @return Returns a new string object that contains the pruned URL. */
Packit a4aae4
string prune_spaces(const string &name)
Packit a4aae4
{
Packit a4aae4
    // If the URL does not even have white space return.
Packit a4aae4
    if (name.find_first_of(' ') == name.npos)
Packit a4aae4
        return name;
Packit a4aae4
    else {
Packit a4aae4
        // Strip leading spaces from http://...
Packit a4aae4
        unsigned int i = name.find_first_not_of(' ');
Packit a4aae4
        string tmp_name = name.substr(i);
Packit a4aae4
Packit a4aae4
        // Strip leading spaces from constraint part (following `?').
Packit a4aae4
        unsigned int j = tmp_name.find('?') + 1;
Packit a4aae4
        i = tmp_name.find_first_not_of(' ', j);
Packit a4aae4
        tmp_name.erase(j, i - j);
Packit a4aae4
Packit a4aae4
        return tmp_name;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
// Compare elements in a list of (BaseType *)s and return true if there are
Packit a4aae4
// no duplicate elements, otherwise return false.
Packit a4aae4
Packit a4aae4
bool unique_names(vector<BaseType *> l, const string &var_name, const string &type_name, string &msg)
Packit a4aae4
{
Packit a4aae4
    // copy the identifier names to a vector
Packit a4aae4
    vector<string> names(l.size());
Packit a4aae4
Packit a4aae4
    int nelem = 0;
Packit a4aae4
    typedef std::vector<BaseType *>::const_iterator citer;
Packit a4aae4
    for (citer i = l.begin(); i != l.end(); i++) {
Packit a4aae4
        assert(*i);
Packit a4aae4
        names[nelem++] = (*i)->name();
Packit a4aae4
        DBG(cerr << "NAMES[" << nelem - 1 << "]=" << names[nelem-1] << endl);
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    // sort the array of names
Packit a4aae4
    sort(names.begin(), names.end());
Packit a4aae4
Packit a4aae4
    // sort the array of names
Packit a4aae4
    sort(names.begin(), names.end());
Packit a4aae4
Packit a4aae4
    // look for any instance of consecutive names that are ==
Packit a4aae4
    for (int j = 1; j < nelem; ++j) {
Packit a4aae4
        if (names[j - 1] == names[j]) {
Packit a4aae4
            ostringstream oss;
Packit a4aae4
            oss << "The variable `" << names[j] << "' is used more than once in " << type_name << " `" << var_name
Packit a4aae4
                    << "'";
Packit a4aae4
            msg = oss.str();
Packit a4aae4
Packit a4aae4
            return false;
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return true;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
const char *
Packit a4aae4
libdap_root()
Packit a4aae4
{
Packit a4aae4
    return LIBDAP_ROOT;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Return the version string for this package.
Packit a4aae4
 @note This function has C linkage so that it can be found using autoconf
Packit a4aae4
 tests.
Packit a4aae4
 @return The version string. */
Packit a4aae4
extern "C" const char *
Packit a4aae4
libdap_version()
Packit a4aae4
{
Packit a4aae4
    return PACKAGE_VERSION;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
extern "C" const char *
Packit a4aae4
libdap_name()
Packit a4aae4
{
Packit a4aae4
    return PACKAGE_NAME;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Use the system time() function to get the current time. Return a string,
Packit a4aae4
 * removing the trailing newline that time() includes in its response.
Packit a4aae4
 * @return A C++ string with the current system time as formatted by time()
Packit a4aae4
 */
Packit a4aae4
string systime()
Packit a4aae4
{
Packit a4aae4
    time_t TimBin;
Packit a4aae4
Packit a4aae4
    if (time(&TimBin) == (time_t) -1)
Packit a4aae4
        return string("time() error");
Packit a4aae4
    else {
Packit a4aae4
        char *ctime_value = ctime(&TimBin);
Packit a4aae4
        if (ctime_value) {
Packit a4aae4
            string TimStr = ctime_value;
Packit a4aae4
            return TimStr.substr(0, TimStr.size() - 2); // remove the \n
Packit a4aae4
        }
Packit a4aae4
        else
Packit a4aae4
            return "Unknown";
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Downcase the source string. This function modifies its argument.
Packit a4aae4
 * @param The string to modify
Packit a4aae4
 */
Packit a4aae4
void downcase(string &s)
Packit a4aae4
{
Packit a4aae4
    for (unsigned int i = 0; i < s.length(); i++)
Packit a4aae4
        s[i] = tolower(s[i]);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Is the string surrounded by double quotes?
Packit a4aae4
 * @param s The source string
Packit a4aae4
 * @reurn True if the string is quoted, false otherwise.
Packit a4aae4
 */
Packit a4aae4
bool is_quoted(const string &s)
Packit a4aae4
{
Packit a4aae4
    return (!s.empty() && s[0] == '\"' && s[s.length() - 1] == '\"');
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Return a new string that is not quoted. This will return a new string
Packit a4aae4
 * regardless of whether the source string is actualy quoted.
Packit a4aae4
 * @param s The source string
Packit a4aae4
 * @return A new string without quotes
Packit a4aae4
 */
Packit a4aae4
string remove_quotes(const string &s)
Packit a4aae4
{
Packit a4aae4
    if (is_quoted(s))
Packit a4aae4
        return s.substr(1, s.length() - 2);
Packit a4aae4
    else
Packit a4aae4
        return s;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Get the Type enumeration value which matches the given name. */
Packit a4aae4
Type get_type(const char *name)
Packit a4aae4
{
Packit a4aae4
    if (strcmp(name, "Byte") == 0) return dods_byte_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Char") == 0) return dods_char_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Int8") == 0) return dods_int8_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "UInt8") == 0) return dods_uint8_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Int16") == 0) return dods_int16_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "UInt16") == 0) return dods_uint16_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Int32") == 0) return dods_int32_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "UInt32") == 0) return dods_uint32_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Int64") == 0) return dods_int64_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "UInt64") == 0) return dods_uint64_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Float32") == 0) return dods_float32_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Float64") == 0) return dods_float64_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "String") == 0) return dods_str_c;
Packit a4aae4
Packit a4aae4
    // accept both spellings; this might be confusing since URL
Packit a4aae4
    // could be filtered through code and come out Url. Don't know...
Packit a4aae4
    // jhrg 8/15/13
Packit a4aae4
    if (strcmp(name, "Url") == 0 || strcmp(name, "URL") == 0) return dods_url_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Enum") == 0) return dods_enum_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Opaque") == 0) return dods_opaque_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Array") == 0) return dods_array_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Structure") == 0) return dods_structure_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Sequence") == 0) return dods_sequence_c;
Packit a4aae4
Packit a4aae4
    if (strcmp(name, "Grid") == 0) return dods_grid_c;
Packit a4aae4
Packit a4aae4
    return dods_null_c;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @brief Returns the type of the class instance as a string.
Packit a4aae4
 * Supports all DAP2 types and not the DAP4-only types. Also
Packit a4aae4
 * returns Url (DAP2) and not "URL" (DAP4) for the URL type.
Packit a4aae4
 * @param t The type code
Packit a4aae4
 * @return The type name in a string
Packit a4aae4
 */
Packit a4aae4
string D2type_name(Type t)
Packit a4aae4
{
Packit a4aae4
    switch (t) {
Packit a4aae4
    case dods_null_c:
Packit a4aae4
        return string("Null");
Packit a4aae4
    case dods_byte_c:
Packit a4aae4
        return string("Byte");
Packit a4aae4
    case dods_int16_c:
Packit a4aae4
        return string("Int16");
Packit a4aae4
    case dods_uint16_c:
Packit a4aae4
        return string("UInt16");
Packit a4aae4
    case dods_int32_c:
Packit a4aae4
        return string("Int32");
Packit a4aae4
    case dods_uint32_c:
Packit a4aae4
        return string("UInt32");
Packit a4aae4
    case dods_float32_c:
Packit a4aae4
        return string("Float32");
Packit a4aae4
    case dods_float64_c:
Packit a4aae4
        return string("Float64");
Packit a4aae4
    case dods_str_c:
Packit a4aae4
        return string("String");
Packit a4aae4
    case dods_url_c:
Packit a4aae4
        return string("Url");
Packit a4aae4
Packit a4aae4
    case dods_array_c:
Packit a4aae4
        return string("Array");
Packit a4aae4
    case dods_structure_c:
Packit a4aae4
        return string("Structure");
Packit a4aae4
    case dods_sequence_c:
Packit a4aae4
        return string("Sequence");
Packit a4aae4
    case dods_grid_c:
Packit a4aae4
        return string("Grid");
Packit a4aae4
Packit a4aae4
    default:
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "Unknown type.");
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @brief Returns the type of the class instance as a string.
Packit a4aae4
 * Supports all DAP4 types and not the DAP2-only types. Also
Packit a4aae4
 * returns URL (DAP4) and not "Url" (DAP2) for the URL type.
Packit a4aae4
 * @param t The type code
Packit a4aae4
 * @return The type name in a string
Packit a4aae4
 */
Packit a4aae4
string D4type_name(Type t)
Packit a4aae4
{
Packit a4aae4
    switch (t) {
Packit a4aae4
    case dods_null_c:
Packit a4aae4
        return string("Null");
Packit a4aae4
    case dods_byte_c:
Packit a4aae4
        return string("Byte");
Packit a4aae4
    case dods_char_c:
Packit a4aae4
        return string("Char");
Packit a4aae4
    case dods_int8_c:
Packit a4aae4
        return string("Int8");
Packit a4aae4
    case dods_uint8_c:
Packit a4aae4
        return string("UInt8");
Packit a4aae4
    case dods_int16_c:
Packit a4aae4
        return string("Int16");
Packit a4aae4
    case dods_uint16_c:
Packit a4aae4
        return string("UInt16");
Packit a4aae4
    case dods_int32_c:
Packit a4aae4
        return string("Int32");
Packit a4aae4
    case dods_uint32_c:
Packit a4aae4
        return string("UInt32");
Packit a4aae4
    case dods_int64_c:
Packit a4aae4
        return string("Int64");
Packit a4aae4
    case dods_uint64_c:
Packit a4aae4
        return string("UInt64");
Packit a4aae4
    case dods_enum_c:
Packit a4aae4
        return string("Enum");
Packit a4aae4
Packit a4aae4
    case dods_float32_c:
Packit a4aae4
        return string("Float32");
Packit a4aae4
    case dods_float64_c:
Packit a4aae4
        return string("Float64");
Packit a4aae4
Packit a4aae4
    case dods_str_c:
Packit a4aae4
        return string("String");
Packit a4aae4
    case dods_url_c:
Packit a4aae4
        return string("URL");
Packit a4aae4
Packit a4aae4
    case dods_opaque_c:
Packit a4aae4
        return string("Opaque");
Packit a4aae4
Packit a4aae4
    case dods_array_c:
Packit a4aae4
        return string("Array");
Packit a4aae4
Packit a4aae4
    case dods_structure_c:
Packit a4aae4
        return string("Structure");
Packit a4aae4
    case dods_sequence_c:
Packit a4aae4
        return string("Sequence");
Packit a4aae4
    case dods_group_c:
Packit a4aae4
        return string("Group");
Packit a4aae4
Packit a4aae4
    default:
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "Unknown type.");
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Return the type name. This function provides backward compatibility
Packit a4aae4
 * for older code that predates, and has not been ported to, DAP4. It
Packit a4aae4
 * is prejudiced toward DAP4, but if no D4 type name can be found, it
Packit a4aae4
 * types D2. If neither would return a type name, and InternalErr object
Packit a4aae4
 * is thrown.
Packit a4aae4
 *
Packit a4aae4
 * @param t The DAP2/DAP4 type
Packit a4aae4
 * @return A string naming the type, suitable for humans
Packit a4aae4
 * @exception InternalErr If not such type can be found
Packit a4aae4
 */
Packit a4aae4
string type_name(Type t)
Packit a4aae4
{
Packit a4aae4
    try {
Packit a4aae4
        return D4type_name(t);
Packit a4aae4
    }
Packit a4aae4
    catch (...) {
Packit a4aae4
        return D2type_name(t);
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Returns true if the instance is a numeric, string or URL
Packit a4aae4
 type variable.
Packit a4aae4
 @return True if the instance is a scalar numeric, String or URL variable,
Packit a4aae4
 False otherwise. Arrays (even of simple types) return False.
Packit a4aae4
 @see is_vector_type() */
Packit a4aae4
bool is_simple_type(Type t)
Packit a4aae4
{
Packit a4aae4
    switch (t) {
Packit a4aae4
Packit a4aae4
    case dods_byte_c:
Packit a4aae4
    case dods_char_c:
Packit a4aae4
Packit a4aae4
    case dods_int8_c:
Packit a4aae4
    case dods_uint8_c:
Packit a4aae4
Packit a4aae4
    case dods_int16_c:
Packit a4aae4
    case dods_uint16_c:
Packit a4aae4
    case dods_int32_c:
Packit a4aae4
    case dods_uint32_c:
Packit a4aae4
Packit a4aae4
    case dods_int64_c:
Packit a4aae4
    case dods_uint64_c:
Packit a4aae4
Packit a4aae4
    case dods_float32_c:
Packit a4aae4
    case dods_float64_c:
Packit a4aae4
    case dods_str_c:
Packit a4aae4
    case dods_url_c:
Packit a4aae4
    case dods_enum_c:
Packit a4aae4
    case dods_opaque_c:
Packit a4aae4
        return true;
Packit a4aae4
Packit a4aae4
    case dods_null_c:
Packit a4aae4
    case dods_array_c:
Packit a4aae4
    case dods_structure_c:
Packit a4aae4
    case dods_sequence_c:
Packit a4aae4
    case dods_grid_c:
Packit a4aae4
    case dods_group_c:
Packit a4aae4
    default:
Packit a4aae4
        return false;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return false;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Returns true if the instance is a vector (i.e., array) type
Packit a4aae4
 variable.
Packit a4aae4
 @return True if the instance is an Array, False otherwise. */
Packit a4aae4
bool is_vector_type(Type t)
Packit a4aae4
{
Packit a4aae4
    switch (t) {
Packit a4aae4
    case dods_null_c:
Packit a4aae4
    case dods_byte_c:
Packit a4aae4
    case dods_char_c:
Packit a4aae4
Packit a4aae4
    case dods_int8_c:
Packit a4aae4
    case dods_uint8_c:
Packit a4aae4
Packit a4aae4
    case dods_int16_c:
Packit a4aae4
    case dods_uint16_c:
Packit a4aae4
Packit a4aae4
    case dods_int32_c:
Packit a4aae4
    case dods_uint32_c:
Packit a4aae4
Packit a4aae4
    case dods_int64_c:
Packit a4aae4
    case dods_uint64_c:
Packit a4aae4
Packit a4aae4
    case dods_float32_c:
Packit a4aae4
    case dods_float64_c:
Packit a4aae4
Packit a4aae4
    case dods_str_c:
Packit a4aae4
    case dods_url_c:
Packit a4aae4
    case dods_enum_c:
Packit a4aae4
    case dods_opaque_c:
Packit a4aae4
        return false;
Packit a4aae4
Packit a4aae4
    case dods_array_c:
Packit a4aae4
        return true;
Packit a4aae4
Packit a4aae4
    case dods_structure_c:
Packit a4aae4
    case dods_sequence_c:
Packit a4aae4
    case dods_grid_c:
Packit a4aae4
    case dods_group_c:
Packit a4aae4
    default:
Packit a4aae4
        return false;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return false;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Returns true if the instance is a constructor (i.e., Structure,
Packit a4aae4
 Sequence or Grid) type variable.
Packit a4aae4
 @return True if the instance is a Structure, Sequence or Grid, False
Packit a4aae4
 otherwise. */
Packit a4aae4
bool is_constructor_type(Type t)
Packit a4aae4
{
Packit a4aae4
    switch (t) {
Packit a4aae4
    case dods_null_c:
Packit a4aae4
    case dods_byte_c:
Packit a4aae4
    case dods_char_c:
Packit a4aae4
Packit a4aae4
    case dods_int8_c:
Packit a4aae4
    case dods_uint8_c:
Packit a4aae4
Packit a4aae4
    case dods_int16_c:
Packit a4aae4
    case dods_uint16_c:
Packit a4aae4
    case dods_int32_c:
Packit a4aae4
    case dods_uint32_c:
Packit a4aae4
Packit a4aae4
    case dods_int64_c:
Packit a4aae4
    case dods_uint64_c:
Packit a4aae4
Packit a4aae4
    case dods_float32_c:
Packit a4aae4
    case dods_float64_c:
Packit a4aae4
    case dods_str_c:
Packit a4aae4
    case dods_url_c:
Packit a4aae4
    case dods_enum_c:
Packit a4aae4
    case dods_opaque_c:
Packit a4aae4
Packit a4aae4
    case dods_array_c:
Packit a4aae4
        return false;
Packit a4aae4
Packit a4aae4
    case dods_structure_c:
Packit a4aae4
    case dods_sequence_c:
Packit a4aae4
    case dods_grid_c:
Packit a4aae4
    case dods_group_c:
Packit a4aae4
    default:
Packit a4aae4
        return true;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return false;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Is this an integer type?
Packit a4aae4
 * @return True if the type holds an integer value, false otherwise.
Packit a4aae4
 */
Packit a4aae4
bool is_integer_type(Type t)
Packit a4aae4
{
Packit a4aae4
    switch (t) {
Packit a4aae4
    case dods_byte_c:
Packit a4aae4
    case dods_char_c:
Packit a4aae4
    case dods_int8_c:
Packit a4aae4
    case dods_uint8_c:
Packit a4aae4
    case dods_int16_c:
Packit a4aae4
    case dods_uint16_c:
Packit a4aae4
    case dods_int32_c:
Packit a4aae4
    case dods_uint32_c:
Packit a4aae4
    case dods_int64_c:
Packit a4aae4
    case dods_uint64_c:
Packit a4aae4
        return true;
Packit a4aae4
    default:
Packit a4aae4
        return false;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Does the directory exist?
Packit a4aae4
 *
Packit a4aae4
 * @param dir The pathname to test.
Packit a4aae4
 * @return True if the directory exists, false otherwise
Packit a4aae4
 */
Packit a4aae4
bool dir_exists(const string &dir)
Packit a4aae4
{
Packit a4aae4
    struct stat buf;
Packit a4aae4
Packit a4aae4
    return (stat(dir.c_str(), &buf) == 0) && (buf.st_mode & S_IFDIR);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
// Jose Garcia
Packit a4aae4
void append_long_to_string(long val, int base, string &str_val)
Packit a4aae4
{
Packit a4aae4
    // The array digits contains 36 elements which are the
Packit a4aae4
    // posible valid digits for out bases in the range
Packit a4aae4
    // [2,36]
Packit a4aae4
    char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Packit a4aae4
    // result of val / base
Packit a4aae4
    ldiv_t r;
Packit a4aae4
Packit a4aae4
    if (base > 36 || base < 2) {
Packit a4aae4
        // no conversion if wrong base
Packit a4aae4
        std::invalid_argument ex("The parameter base has an invalid value.");
Packit a4aae4
        throw ex;
Packit a4aae4
    }
Packit a4aae4
    if (val < 0) str_val += '-';
Packit a4aae4
    r = ldiv(labs(val), base);
Packit a4aae4
Packit a4aae4
    // output digits of val/base first
Packit a4aae4
    if (r.quot > 0) append_long_to_string(r.quot, base, str_val);
Packit a4aae4
Packit a4aae4
    // output last digit
Packit a4aae4
Packit a4aae4
    str_val += digits[(int) r.rem];
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
// base defaults to 10
Packit a4aae4
string long_to_string(long val, int base)
Packit a4aae4
{
Packit a4aae4
    string s;
Packit a4aae4
    append_long_to_string(val, base, s);
Packit a4aae4
    return s;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
// Jose Garcia
Packit a4aae4
void append_double_to_string(const double &num, string &str)
Packit a4aae4
{
Packit a4aae4
    // s having 100 characters should be enough for sprintf to do its job.
Packit a4aae4
    // I want to banish all instances of sprintf. 10/5/2001 jhrg
Packit a4aae4
    ostringstream oss;
Packit a4aae4
    oss.precision(9);
Packit a4aae4
    oss << num;
Packit a4aae4
    str += oss.str();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
string double_to_string(const double &num)
Packit a4aae4
{
Packit a4aae4
    string s;
Packit a4aae4
    append_double_to_string(num, s);
Packit a4aae4
    return s;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
// Given a pathname, return the file at the end of the path. This is used
Packit a4aae4
// when reporting errors (maybe other times, too) to keep the server from
Packit a4aae4
// revealing too much about its organization when sending error responses
Packit a4aae4
// back to clients. 10/11/2000 jhrg
Packit a4aae4
// MT-safe. 08/05/02 jhrg
Packit a4aae4
Packit a4aae4
#ifdef WIN32
Packit a4aae4
static const char path_sep[] =
Packit a4aae4
{   "\\"
Packit a4aae4
};
Packit a4aae4
#else
Packit a4aae4
static const char path_sep[] = { "/" };
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
/** Get the filename part from a path. This function can be used to return a
Packit a4aae4
 string that has the directory components stripped from a path. This is
Packit a4aae4
 useful when building error message strings.
Packit a4aae4
Packit a4aae4
 If WIN32 is defined, use '\' as the path separator, otherwise use '/' as
Packit a4aae4
 the path separator.
Packit a4aae4
Packit a4aae4
 @return A string containing only the filename given a path. */
Packit a4aae4
string path_to_filename(string path)
Packit a4aae4
{
Packit a4aae4
    string::size_type pos = path.rfind(path_sep);
Packit a4aae4
Packit a4aae4
    return (pos == string::npos) ? path : path.substr(++pos);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
#define CHECK_BIT( tab, bit ) ( tab[ (bit)/8 ] & (1<<( (bit)%8 )) )
Packit a4aae4
#define BITLISTSIZE 16 /* bytes used for [chars] in compiled expr */
Packit a4aae4
Packit a4aae4
/*
Packit a4aae4
 * globchars() - build a bitlist to check for character group match
Packit a4aae4
 */
Packit a4aae4
Packit a4aae4
static void globchars(const char *s, const char *e, char *b)
Packit a4aae4
{
Packit a4aae4
    int neg = 0;
Packit a4aae4
Packit a4aae4
    memset(b, '\0', BITLISTSIZE);
Packit a4aae4
Packit a4aae4
    if (*s == '^') neg++, s++;
Packit a4aae4
Packit a4aae4
    while (s < e) {
Packit a4aae4
        int c;
Packit a4aae4
Packit a4aae4
        if (s + 2 < e && s[1] == '-') {
Packit a4aae4
            for (c = s[0]; c <= s[2]; c++)
Packit a4aae4
                b[c / 8] |= (1 << (c % 8));
Packit a4aae4
            s += 3;
Packit a4aae4
        }
Packit a4aae4
        else {
Packit a4aae4
            c = *s++;
Packit a4aae4
            b[c / 8] |= (1 << (c % 8));
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    if (neg) {
Packit a4aae4
        int i;
Packit a4aae4
        for (i = 0; i < BITLISTSIZE; i++)
Packit a4aae4
            b[i] ^= 0377;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    /* Don't include \0 in either $[chars] or $[^chars] */
Packit a4aae4
Packit a4aae4
    b[0] &= 0376;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * glob:  match a string against a simple pattern
Packit a4aae4
 *
Packit a4aae4
 * Understands the following patterns:
Packit a4aae4
 *
Packit a4aae4
 *  *   any number of characters
Packit a4aae4
 *  ?   any single character
Packit a4aae4
 *  [a-z]   any single character in the range a-z
Packit a4aae4
 *  [^a-z]  any single character not in the range a-z
Packit a4aae4
 *  \x  match x
Packit a4aae4
 *
Packit a4aae4
 * @param c The pattern
Packit a4aae4
 * @param s The string
Packit a4aae4
 * @return 0 on success, -1 if the pattern is exhausted but there are
Packit a4aae4
 * characters remaining in the string and 1 if the pattern does not match
Packit a4aae4
 */
Packit a4aae4
int glob(const char *c, const char *s)
Packit a4aae4
{
Packit a4aae4
    if (!c || !s) return 1;
Packit a4aae4
Packit a4aae4
    char bitlist[BITLISTSIZE];
Packit a4aae4
    int i = 0;
Packit a4aae4
    for (;;) {
Packit a4aae4
        ++i;
Packit a4aae4
        switch (*c++) {
Packit a4aae4
        case '\0':
Packit a4aae4
            return *s ? -1 : 0;
Packit a4aae4
Packit a4aae4
        case '?':
Packit a4aae4
            if (!*s++) return i/*1*/;
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case '[': {
Packit a4aae4
            /* scan for matching ] */
Packit a4aae4
Packit a4aae4
            const char *here = c;
Packit a4aae4
            do {
Packit a4aae4
                if (!*c++) return i/*1*/;
Packit a4aae4
            } while (here == c || *c != ']');
Packit a4aae4
            c++;
Packit a4aae4
Packit a4aae4
            /* build character class bitlist */
Packit a4aae4
Packit a4aae4
            globchars(here, c, bitlist);
Packit a4aae4
Packit a4aae4
            if (!CHECK_BIT(bitlist, *(unsigned char * )s)) return i/*1*/;
Packit a4aae4
            s++;
Packit a4aae4
            break;
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        case '*': {
Packit a4aae4
            const char *here = s;
Packit a4aae4
Packit a4aae4
            while (*s)
Packit a4aae4
                s++;
Packit a4aae4
Packit a4aae4
            /* Try to match the rest of the pattern in a recursive */
Packit a4aae4
            /* call.  If the match fails we'll back up chars, retrying. */
Packit a4aae4
Packit a4aae4
            while (s != here) {
Packit a4aae4
                int r;
Packit a4aae4
Packit a4aae4
                /* A fast path for the last token in a pattern */
Packit a4aae4
Packit a4aae4
                r = *c ? glob(c, s) : *s ? -1 : 0;
Packit a4aae4
Packit a4aae4
                if (!r)
Packit a4aae4
                    return 0;
Packit a4aae4
                else if (r < 0) return i/*1*/;
Packit a4aae4
Packit a4aae4
                --s;
Packit a4aae4
            }
Packit a4aae4
            break;
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        case '\\':
Packit a4aae4
            /* Force literal match of next char. */
Packit a4aae4
Packit a4aae4
            if (!*c || *s++ != *c++) return i/*1*/;
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        default:
Packit a4aae4
            if (*s++ != c[-1]) return i/*1*/;
Packit a4aae4
            break;
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return 1;   // Should never get here; this quiets gcc's warning
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @name Security functions */
Packit a4aae4
//@{
Packit a4aae4
/** @brief sanitize the size of an array.
Packit a4aae4
 Test for integer overflow when dynamically allocating an array.
Packit a4aae4
 @param nelem Number of elements.
Packit a4aae4
 @param sz size of each element.
Packit a4aae4
 @return True if the \c nelem elements of \c sz size will overflow an array. */
Packit a4aae4
bool size_ok(unsigned int sz, unsigned int nelem)
Packit a4aae4
{
Packit a4aae4
    return (sz > 0 && nelem < UINT_MAX / sz);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Does the string name a potentially valid pathname?
Packit a4aae4
 Test the given pathname to verify that it is a valid name. We define this
Packit a4aae4
 as: Contains only printable characters; and Is less then 256 characters.
Packit a4aae4
 If \e strict is true, test that the pathname consists of only letters,
Packit a4aae4
 digits, and underscore, dash and dot characters instead of the more general
Packit a4aae4
 case where a pathname can be composed of any printable characters.
Packit a4aae4
Packit a4aae4
 @note Using this function does not guarantee that the path is valid, only
Packit a4aae4
 that the path \e could be valid. The intent is foil attacks where an
Packit a4aae4
 exploit is encoded in a string then passed to a library function. This code
Packit a4aae4
 does not address whether the pathname references a valid resource.
Packit a4aae4
Packit a4aae4
 @param path The pathname to test
Packit a4aae4
 @param strict Apply more restrictive tests (true by default)
Packit a4aae4
 @return true if the pathname consists of legal characters and is of legal
Packit a4aae4
 size, false otherwise. */
Packit a4aae4
bool pathname_ok(const string &path, bool strict)
Packit a4aae4
{
Packit a4aae4
    if (path.length() > 255) return false;
Packit a4aae4
Packit a4aae4
    Regex name("[-0-9A-z_./]+");
Packit a4aae4
    if (!strict) name = "[:print:]+";
Packit a4aae4
Packit a4aae4
    string::size_type len = path.length();
Packit a4aae4
    int result = name.match(path.c_str(), len);
Packit a4aae4
    // Protect against casting too big an uint to int
Packit a4aae4
    // if LEN is bigger than the max int32, the second test can't work
Packit a4aae4
    if (len > INT_MAX || result != static_cast<int>(len)) return false;
Packit a4aae4
Packit a4aae4
    return true;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
//@}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Get the version of the DAP library.
Packit a4aae4
 * @deprecated
Packit a4aae4
 */
Packit a4aae4
string dap_version()
Packit a4aae4
{
Packit a4aae4
    return (string) "OPeNDAP DAP/" + libdap_version() + ": compiled on " + __DATE__ + ":" + __TIME__;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Using the given template, open a temporary file using the given
Packit a4aae4
 * ofstream object. Uses mkstemp() in a 'safe' way.
Packit a4aae4
 *
Packit a4aae4
 * @param f Value-result parameter
Packit a4aae4
 * @param name_template The template used to name the temporary file.
Packit a4aae4
 * The template has the form templateXXXXXX where the six Xs will be
Packit a4aae4
 * overwritten.
Packit a4aae4
 * @param suffix If present, the template is 'templateXXXXXX.suffix'
Packit a4aae4
 * @return The new file's name.
Packit a4aae4
 * @exception Error if there is a problem.
Packit a4aae4
 */
Packit a4aae4
string open_temp_fstream(ofstream &f, const string &name_template, const string &suffix /* = "" */)
Packit a4aae4
{
Packit a4aae4
    vector<char> name;
Packit a4aae4
    copy(name_template.begin(), name_template.end(), back_inserter(name));
Packit a4aae4
    if (!suffix.empty())
Packit a4aae4
        copy(suffix.begin(), suffix.end(), back_inserter(name));
Packit a4aae4
    name.push_back('\0');
Packit a4aae4
Packit a4aae4
    // Use mkstemp to make and open the temp file atomically
Packit a4aae4
    int tmpfile = mkstemps(&name[0], suffix.length());
Packit a4aae4
    if (tmpfile == -1)
Packit a4aae4
        throw Error(internal_error, "Could not make a temporary file.");
Packit a4aae4
    // Open the file using C++ ofstream; get a C++ fstream object
Packit a4aae4
    f.open(&name[0]);
Packit a4aae4
    // Close the file descriptor; the file stays open because of the fstream object
Packit a4aae4
    close(tmpfile);
Packit a4aae4
    // Now test that the fstream object is valid
Packit a4aae4
    if (f.fail())
Packit a4aae4
        throw Error(internal_error, "Could not make a temporary file.");
Packit a4aae4
Packit a4aae4
    return string(&name[0]);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
Packit a4aae4
} // namespace libdap
Packit a4aae4