escaping.cc
// -*- mode: c++; c-basic-offset:4 -*-
// This file is part of libdap, A C++ implementation of the OPeNDAP Data
// Access Protocol.
// Copyright (c) 2002,2003 OPeNDAP, Inc.
// Author: James Gallagher <jgallagher@opendap.org>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
// Copyright (c) 1996, California Institute of Technology.
// ALL RIGHTS RESERVED. U.S. Government Sponsorship acknowledged.
//
// Please read the full copyright notice in the file COPYRIGHT_URI
// in this directory.
//
// Author: Todd Karakashian, NASA/Jet Propulsion Laboratory
// Todd.K.Karakashian@jpl.nasa.gov
//
// $RCSfile: escaping.cc,v $ - Miscellaneous routines for OPeNDAP HDF server
//
// These two routines are for escaping/unescaping strings that are identifiers
// in DAP2
// id2www() -- escape (using WWW hex codes) non-allowable characters in a
// DAP2 identifier
// www2id() -- given an WWW hexcode escaped identifier, restore it
//
// These two routines are for escaping/unescaping strings storing attribute
// values. They use traditional octal escapes (\nnn) because they are
// intended to be viewed by a user
// escattr() -- escape (using traditional octal backslash) non-allowable
// characters in the value of a DAP2 attribute
// unescattr() -- given an octally escaped string, restore it
//
// These are routines used by the above, not intended to be called directly:
//
// hexstring()
// unhexstring()
// octstring()
// unoctstring()
//
// -Todd
#include "config.h"
#include <ctype.h>
#include <iomanip>
#include <string>
#include <sstream>
#include "GNURegex.h"
#include "Error.h"
#include "InternalErr.h"
//#define DODS_DEBUG
#include "debug.h"
using namespace std;
namespace libdap {
// The next four functions were originally defined static, but I removed that
// to make testing them (see generalUtilTest.cc) easier to write. 5/7/2001
// jhrg
string
hexstring(unsigned char val)
{
ostringstream buf;
buf << hex << setw(2) << setfill('0') << static_cast<unsigned int>(val);
return buf.str();
}
string
unhexstring(string s)
{
int val;
istringstream ss(s);
ss >> hex >> val;
char tmp_str[2];
tmp_str[0] = static_cast<char>(val);
tmp_str[1] = '\0';
return string(tmp_str);
}
string
octstring(unsigned char val)
{
ostringstream buf;
buf << oct << setw(3) << setfill('0')
<< static_cast<unsigned int>(val);
return buf.str();
}
string
unoctstring(string s)
{
int val;
istringstream ss(s);
ss >> oct >> val;
DBG(cerr << "unoctstring: " << val << endl);
char tmp_str[2];
tmp_str[0] = static_cast<char>(val);
tmp_str[1] = '\0';
return string(tmp_str);
}
string
id2www(string in, const string &allowable)
{
string::size_type i = 0;
DBG(cerr<<"Input string: [" << in << "]" << endl);
while ((i = in.find_first_not_of(allowable, i)) != string::npos) {
DBG(cerr<<"Found escapee: [" << in[i] << "]");
in.replace(i, 1, "%" + hexstring(in[i]));
DBGN(cerr<<" now the string is: " << in << endl);
i += 3;//i++;
}
return in;
}
string
id2www_ce(string in, const string &allowable)
{
return id2www(in, allowable);
}
string
www2id(const string &in, const string &escape, const string &except)
{
string::size_type i = 0;
string res = in;
while ((i = res.find_first_of(escape, i)) != string::npos) {
if (except.find(res.substr(i, 3)) != string::npos) {
i += 3;
continue;
}
res.replace(i, 3, unhexstring(res.substr(i + 1, 2)));
++i;
}
return res;
}
static string
entity(char c)
{
switch (c) {
case '>': return "&gt;";
case '<': return "&lt;";
case '&': return "&amp;";
case '\'': return "&apos;";
case '\"': return "&quot;";
default:
throw InternalErr(__FILE__, __LINE__, "Unrecognized character.");
}
}
// Assumption: There are always exactly two octal digits in the input
// and two hex digits in the result.
string
octal_to_hex(const string &octal_digits)
{
int val;
istringstream ss(octal_digits);
ss >> oct >> val;
ostringstream ds;
ds << hex << setw(2) << setfill('0') << val;
return ds.str();
}
string
id2xml(string in, const string &not_allowed)
{
string::size_type i = 0;
while ((i = in.find_first_of(not_allowed, i)) != string::npos) {
in.replace(i, 1, entity(in[i]));
++i;
}
#if 0
// Removed the encoding of octal escapes. This function is used by
// AttrTable to encode the stuff that is the value of the <value>
// element in the DDX. The problem is that some of the values are not
// valid UTF-8 and that makes a XML parser gag.; ticket 1512.
// jhrg 3/19/10
// OK, now scan for octal escape sequences like \\012 (where the '\'
// is itself escaped). This type of attribute value comes from the netCDF
// handler and maybe others. Assumption: The '\' will always appear as
// in its escaped form: '\\'. NB: Both backslashes must be escaped in the
// C++ string.
string octal_escape = "\\\\";
i = 0;
string::size_type length = in.length();
while ((i = in.find(octal_escape, i)) != string::npos) {
// Get the three octal digits following the '\\0'
string::size_type j = i + 2;
if (j + 1 >= length) // Check that we're not past the end
break;
string octal_digits = in.substr(j, 3);
// convert to a &#xdd; XML escape
string hex_escape = string("&#x");
hex_escape.append(octal_to_hex(octal_digits));
hex_escape.append(string(";"));
// replace the octal escape with an XML/hex escape
in.replace(i, 5, hex_escape);
// increment i
i += 6;
}
#endif
return in;
}
string
xml2id(string in)
{
string::size_type i = 0;
while ((i = in.find("&gt;", i)) != string::npos)
in.replace(i, 4, ">");
i = 0;
while ((i = in.find("&lt;", i)) != string::npos)
in.replace(i, 4, "<");
i = 0;
while ((i = in.find("&amp;", i)) != string::npos)
in.replace(i, 5, "&");
i = 0;
while ((i = in.find("&apos;", i)) != string::npos)
in.replace(i, 6, "'");
i = 0;
while ((i = in.find("&quot;", i)) != string::npos)
in.replace(i, 6, "\"");
return in;
}
string
esc2underscore(string s)
{
string::size_type pos;
while ((pos = s.find('%')) != string::npos)
s.replace(pos, 3, "_");
return s;
}
string
escattr(string s)
{
const string printable = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()_-+={[}]|\\:;<,>.?/'\"";
const string ESC = "\\";
const string DOUBLE_ESC = ESC + ESC;
const string QUOTE = "\"";
const string ESCQUOTE = ESC + QUOTE;
// escape \ with a second backslash
string::size_type ind = 0;
while ((ind = s.find(ESC, ind)) != s.npos) {
s.replace(ind, 1, DOUBLE_ESC);
ind += DOUBLE_ESC.length();
}
// escape non-printing characters with octal escape
ind = 0;
while ((ind = s.find_first_not_of(printable, ind)) != s.npos)
s.replace(ind, 1, ESC + octstring(s[ind]));
// escape " with backslash
ind = 0;
while ((ind = s.find(QUOTE, ind)) != s.npos) {
s.replace(ind, 1, ESCQUOTE);
ind += ESCQUOTE.length();
}
return s;
}
string
unescattr(string s)
{
Regex octal("\\\\[0-3][0-7][0-7]"); // matches 4 characters
Regex esc_quote("\\\\\""); // matches 3 characters
Regex esc_esc("\\\\\\\\"); // matches 2 characters
const string ESC = "\\";
const string QUOTE = "\"";
int matchlen;
unsigned int index;
DBG(cerr << "0XX" << s << "XXX" << endl);
// unescape any escaped backslashes
index = esc_esc.search(s.c_str(), s.length(), matchlen, 0);
while (index < s.length()) {
DBG(cerr << "1aXX" << s << "XXX index: " << index << endl);
s.replace(index, 2, ESC);
DBG(cerr << "1bXX" << s << "XXX index: " << index << endl);
index = esc_esc.search(s.c_str(), s.length(), matchlen, 0);
}
// unescape any escaped double quote characters
index = esc_quote.search(s.c_str(), s.length(), matchlen, 0);
while (index < s.length()) {
s.replace(index, 2, QUOTE);
DBG(cerr << "2XX" << s << "XXX index: " << index << endl);
index = esc_quote.search(s.c_str(), s.length(), matchlen, 0);
}
// unescape octal characters
index = octal.search(s.c_str(), s.length(), matchlen, 0);
while (index < s.length()) {
s.replace(index, 4, unoctstring(s.substr(index + 1, 3)));
DBG(cerr << "3XX" << s << "XXX index: " << index << endl);
index = octal.search(s.c_str(), s.length(), matchlen, 0);
}
DBG(cerr << "4XX" << s << "XXX" << endl);
return s;
}
string
munge_error_message(string msg)
{
// First, add enclosing quotes if needed.
if (*msg.begin() != '"')
msg.insert(msg.begin(), '"');
if (*(msg.end() - 1) != '"')
msg += "\"";
// Now escape any internal double quotes that aren't escaped.
string::iterator miter;
for (miter = msg.begin() + 1; miter != msg.end() - 1; miter++)
if (*miter == '"' && *(miter - 1) != '\\')
miter = msg.insert(miter, '\\');
return msg;
}
string
escape_double_quotes(string source)
{
string::size_type idx = 0;
while((idx = source.find('\"', idx)) != string::npos) {
source.replace(idx, 1, "\\\""); // a backslash and a double quote
idx += 2;
}
return source;
}
string
unescape_double_quotes(string source)
{
string::size_type idx = 0;
while((idx = source.find("\\\"", idx)) != string::npos) {
source.replace(idx, 2, "\""); // a backslash and a double quote
++idx;
}
return source;
}
} // namespace libdap
libdap::escattr
string escattr(string s)
Definition: escaping.cc:368
libdap::id2www_ce
string id2www_ce(string in, const string &allowable)
Definition: escaping.cc:178
libdap::id2xml
string id2xml(string in, const string &not_allowed)
Definition: escaping.cc:272
libdap::escape_double_quotes
string escape_double_quotes(string source)
Definition: escaping.cc:470
libdap
top level DAP object to house generic methods
Definition: AlarmHandler.h:35
libdap::Regex
Definition: GNURegex.h:35
libdap::esc2underscore
string esc2underscore(string s)
Definition: escaping.cc:354
libdap::unescattr
string unescattr(string s)
Definition: escaping.cc:407
libdap::www2id
string www2id(const string &in, const string &escape, const string &except)
Definition: escaping.cc:220
libdap::Regex::search
int search(const char *s, int len, int &matchlen, int pos=0)
How much of the string does the pattern match.
Definition: GNURegex.cc:147
libdap::xml2id
string xml2id(string in)
Definition: escaping.cc:322
libdap::id2www
string id2www(string in, const string &allowable)
Definition: escaping.cc:153
libdap::unescape_double_quotes
string unescape_double_quotes(string source)
Definition: escaping.cc:487