mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2024-11-15 14:09:06 +00:00
56c6833220
This reverts commit fa3e9e7329
.
743 lines
20 KiB
C++
743 lines
20 KiB
C++
/*
|
|
Copyright (C) 2010 Andrew Church <achurch@achurch.org>
|
|
|
|
This file is part of Aquaria.
|
|
|
|
Aquaria is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
#pragma once
|
|
|
|
/*
|
|
* This class implements a lightweight version of the std::istringstream
|
|
* class used for parsing strings. std::istringstream (like many STL
|
|
* classes) requires a significant amount of overhead to implement its
|
|
* flexibility, and in some environments, the simple act of reading a
|
|
* floating-point value:
|
|
*
|
|
* std::istringstream is; float f; is >> f;
|
|
*
|
|
* can be 20-30 times (not a typo!) slower than the C equivalent:
|
|
*
|
|
* float f = strtof(s, &s);
|
|
*
|
|
* This class eschews most of std::istringstream's flexibility in favor of
|
|
* a more efficient implementation to improve performance when reading
|
|
* text-format data files.
|
|
*
|
|
* To create a new SimpleIStringStream, initialize it with or assign to it
|
|
* the string to be parsed:
|
|
*
|
|
* SimpleIStringStream is("123 4.5 -6.78e9"); // Alternative 1
|
|
* SimpleIStringStream is = "123 4.5 -6.78e9"; // Alternative 2
|
|
*
|
|
* If the string is a "char *" or "const char *" that is guaranteed to
|
|
* exist throughout the lifetime of the SimpleIStringStream instance, you
|
|
* can also use this more efficient constructor:
|
|
*
|
|
* SimpleIStringStream is("123 4.5 -6.78e9", SimpleIStringStream::REUSE);
|
|
*
|
|
* which avoids making a separate copy of the string buffer. (It is then
|
|
* incumbent upon the caller to ensure that the string actually remains
|
|
* valid throughout the SimpleIStringStream's lifetime.) Alternatively, if
|
|
* you have a dynamically-allocated "char *" buffer that you want the
|
|
* SimpleIStringStream to free for you when you're done with it, construct
|
|
* the object like this:
|
|
*
|
|
* SimpleIStringStream is(buffer, SimpleIStringStream::TAKE_OVER);
|
|
*
|
|
* In either of the latter two cases, you must not modify the string buffer
|
|
* while the SimpleIStringStream object exists.
|
|
*
|
|
* While it is possible to create a SimpleIStringStream using a std::string
|
|
* object, the object's contents will always be copied on creation for
|
|
* performance reasons; it is not possible to use the REUSE or TAKE_OVER
|
|
* flags with STL strings.
|
|
*
|
|
* The only operators (other than assignment) implemented by
|
|
* SimpleIStringStream are "bool()", for testing end-of-stream (in the same
|
|
* manner as std::istringstream), and ">>", the extraction operator. All
|
|
* arithmetic, std::string, and character extractors supported by
|
|
* std::istringstream except "long double" are also supported here, but not
|
|
* the C string or function extractors.
|
|
*/
|
|
|
|
#include "Base.h"
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* SISS_VERIFY: Verify the behavior of SimpleIStringStream by creating an
|
|
* istringstream alongside the local buffer and ensuring that it always
|
|
* extracts the same values returned by the SimpleIStringStream.
|
|
*/
|
|
// #define SISS_VERIFY
|
|
|
|
/*************************************************************************/
|
|
/*************************** Class definition ****************************/
|
|
/*************************************************************************/
|
|
|
|
class SimpleIStringStream {
|
|
|
|
public:
|
|
/* Reuse flag passed to StringStream(char *,int). */
|
|
enum {
|
|
/* Make a copy of the buffer (default action). */
|
|
COPY,
|
|
/* Use the passed-in string pointer as is. Requires the string
|
|
* pointer to remain valid over the life of this object. */
|
|
REUSE,
|
|
/* Take over the passed-in string buffer, which must have been
|
|
* allocated with new[]. The buffer will be deleted on object
|
|
* destruction. */
|
|
TAKE_OVER,
|
|
};
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* SimpleIStringStream(): Basic object constructor. No extraction is
|
|
* possible until a string has been assigned.
|
|
*/
|
|
inline SimpleIStringStream();
|
|
|
|
/**
|
|
* SimpleIStringStream(...): Create a new SimpleIStringStream from a
|
|
* C-style or STL string. Each of these constructors is exactly
|
|
* equivalent to creating a SimpleIString with the default constructor
|
|
* and then calling setString() with the same parameters.
|
|
*/
|
|
inline SimpleIStringStream(char *string, int reuse_flag = COPY);
|
|
inline SimpleIStringStream(const char *string, int reuse_flag = COPY);
|
|
inline SimpleIStringStream(std::string string);
|
|
|
|
/**
|
|
* ~SimpleIStringStream(): Object destructor.
|
|
*/
|
|
inline ~SimpleIStringStream();
|
|
|
|
/*------------------------------*/
|
|
|
|
/**
|
|
* setString(...): Set the string from which data will be extracted,
|
|
* replacing any existing string.
|
|
*
|
|
* [Parameters]
|
|
* string: String to use for extraction.
|
|
* reuse_flag: Flag indicating buffer reuse semantics for "char *"
|
|
* string buffers.
|
|
* [Return value]
|
|
* None
|
|
*/
|
|
inline void setString(char *string, int reuse_flag = COPY);
|
|
inline void setString(const char *string, int reuse_flag = COPY);
|
|
inline void setString(const std::string &string);
|
|
|
|
/**
|
|
* operator=(char *), operator=(const char *), operator=(std::string):
|
|
* Set the string from which data will be extracted. Exactly
|
|
* equivalent to setString(string).
|
|
*
|
|
* [Parameters]
|
|
* string: Assignment source
|
|
* [Return value]
|
|
* this
|
|
*/
|
|
inline SimpleIStringStream &operator=(char *string);
|
|
inline SimpleIStringStream &operator=(const char *string);
|
|
inline SimpleIStringStream &operator=(const std::string &string);
|
|
|
|
/**
|
|
* operator=(SimpleIStringStream): Make a copy of an existing stream.
|
|
* The input string buffer (if any) will always be copied, regardless
|
|
* of any reuse_flag passed to the original stream.
|
|
*
|
|
* [Parameters]
|
|
* stream: Assignment source
|
|
* [Return value]
|
|
* this
|
|
*/
|
|
inline SimpleIStringStream &operator=(const SimpleIStringStream &stream);
|
|
|
|
/*------------------------------*/
|
|
|
|
/**
|
|
* operator bool: Evaluate the stream as a boolean. Returns false if
|
|
* an error has occurred, true otherwise. (This allows the stream to
|
|
* be used in a while loop like "while (stream >> var)", in the same
|
|
* way as ordinary std::istringstream objects.)
|
|
*
|
|
* [Parameters]
|
|
* None
|
|
* [Return value]
|
|
* False if an error has occurred, else true
|
|
*/
|
|
inline operator bool() const;
|
|
|
|
/**
|
|
* operator>>: Extract a value from a stream. String extraction skips
|
|
* leading whitespace and stops at the first following whitespace
|
|
* character.
|
|
*
|
|
* [Parameters]
|
|
* target: Target for storing extracted value (right-hand operand)
|
|
* [Return value]
|
|
* this
|
|
*/
|
|
inline SimpleIStringStream &operator>>(bool &target);
|
|
inline SimpleIStringStream &operator>>(short &target);
|
|
inline SimpleIStringStream &operator>>(unsigned short &target);
|
|
inline SimpleIStringStream &operator>>(int &target);
|
|
inline SimpleIStringStream &operator>>(unsigned int &target);
|
|
inline SimpleIStringStream &operator>>(long &target);
|
|
inline SimpleIStringStream &operator>>(unsigned long &target);
|
|
inline SimpleIStringStream &operator>>(float &target);
|
|
inline SimpleIStringStream &operator>>(double &target);
|
|
inline SimpleIStringStream &operator>>(char &target);
|
|
inline SimpleIStringStream &operator>>(signed char &target);
|
|
inline SimpleIStringStream &operator>>(unsigned char &target);
|
|
inline SimpleIStringStream &operator>>(std::string &target);
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
private:
|
|
char *buffer; // The buffer we're parsing.
|
|
char *position; // Our current position in the buffer.
|
|
bool freeOnDestroy; // Should we free the buffer when we're destroyed?
|
|
bool error; // Current error status.
|
|
|
|
#ifdef SISS_VERIFY
|
|
std::istringstream std_is;
|
|
unsigned long ispos;
|
|
#endif
|
|
|
|
/* Is the given character a whitespace character? */
|
|
inline bool my_isspace(char c) const {
|
|
return c==' ' || c=='\t' || c=='\r' || c=='\n' || c=='\v';
|
|
};
|
|
|
|
/* Skip over leading whitespace. Assumes "position" is valid. */
|
|
inline void skip_spaces() {
|
|
while (my_isspace(*position)) {
|
|
position++;
|
|
}
|
|
}
|
|
|
|
}; // class SimpleIStringStream
|
|
|
|
/*************************************************************************/
|
|
/*********************** Inline method definitions ***********************/
|
|
/*************************************************************************/
|
|
|
|
/* Helper macros used with SISS_VERIFY to verify result values. "type" is
|
|
* the type of data being read, and "def_value" is the default value to
|
|
* test against when the stream reports an error. */
|
|
|
|
#ifdef SISS_VERIFY
|
|
|
|
extern void debugLog(const std::string &s);
|
|
|
|
#define VERIFY_SETUP \
|
|
char *old_position = position;
|
|
|
|
#define VERIFY(type,def_value) do { \
|
|
type test; \
|
|
std_is >> test; \
|
|
ispos += std_is.gcount(); \
|
|
if (std_is.fail()) { \
|
|
test = def_value; \
|
|
} \
|
|
if (test != target) { \
|
|
std::ostringstream os_target; os_target << target; \
|
|
std::ostringstream os_test; os_test << test; \
|
|
std::ostringstream os_offset; os_offset << (position - buffer); \
|
|
debugLog(std::string("SimpleIStringStream >> " #type ": MISMATCH") \
|
|
+ " (us=" + os_target.str() \
|
|
+ " STL=" + (std_is.fail() ? "<<FAIL>>" : os_test.str()) \
|
|
+ ") at: [" + std::string(old_position).substr(0,100) \
|
|
+ "] ([" + std::string(buffer).substr(0,100) + "] + " \
|
|
+ os_offset.str() + ")"); \
|
|
} \
|
|
} while (0)
|
|
|
|
#else
|
|
|
|
#define VERIFY_SETUP /*nothing*/
|
|
#define VERIFY(type,def_value) /*nothing*/
|
|
|
|
#endif
|
|
|
|
/*************************************************************************/
|
|
|
|
inline SimpleIStringStream::SimpleIStringStream()
|
|
{
|
|
buffer = NULL;
|
|
position = NULL;
|
|
freeOnDestroy = false;
|
|
error = false;
|
|
}
|
|
|
|
inline SimpleIStringStream::SimpleIStringStream(char *string, int reuse_flag)
|
|
{
|
|
freeOnDestroy = false; // Don't try to free the uninitialized pointer.
|
|
setString(string, reuse_flag);
|
|
}
|
|
|
|
inline SimpleIStringStream::SimpleIStringStream(const char *string, int reuse_flag)
|
|
{
|
|
freeOnDestroy = false;
|
|
setString(string, reuse_flag);
|
|
}
|
|
|
|
inline SimpleIStringStream::SimpleIStringStream(std::string string)
|
|
{
|
|
freeOnDestroy = false;
|
|
setString(string);
|
|
}
|
|
|
|
inline SimpleIStringStream::~SimpleIStringStream()
|
|
{
|
|
if (freeOnDestroy) {
|
|
delete[] buffer;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
inline void SimpleIStringStream::setString(char *string, int reuse_flag)
|
|
{
|
|
if (freeOnDestroy) {
|
|
delete[] buffer;
|
|
}
|
|
|
|
if (reuse_flag == REUSE) {
|
|
buffer = string;
|
|
freeOnDestroy = false;
|
|
} else if (reuse_flag == TAKE_OVER) {
|
|
buffer = string;
|
|
freeOnDestroy = true;
|
|
} else {
|
|
const size_t size = strlen(string) + 1;
|
|
buffer = new char[size];
|
|
freeOnDestroy = true;
|
|
if (buffer) {
|
|
memcpy(buffer, string, size);
|
|
}
|
|
}
|
|
position = buffer;
|
|
error = false;
|
|
#ifdef SISS_VERIFY
|
|
if (buffer) {
|
|
std_is.str(buffer);
|
|
std_is.clear();
|
|
ispos = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
inline void SimpleIStringStream::setString(const char *string, int reuse_flag)
|
|
{
|
|
if (freeOnDestroy) {
|
|
delete[] buffer;
|
|
}
|
|
|
|
if (reuse_flag == REUSE) {
|
|
/* We never actually write to the buffer, and we won't attempt to
|
|
* free it, so this cast is safe. */
|
|
buffer = const_cast<char *>(string);
|
|
freeOnDestroy = false;
|
|
} else {
|
|
/* It makes no sense to TAKE_OVER a const char *, since we could
|
|
* never free it, so we treat TAKE_OVER like COPY. */
|
|
const size_t size = strlen(string) + 1;
|
|
buffer = new char[size];
|
|
freeOnDestroy = true;
|
|
if (buffer) {
|
|
memcpy(buffer, string, size);
|
|
}
|
|
}
|
|
position = buffer;
|
|
error = false;
|
|
#ifdef SISS_VERIFY
|
|
if (buffer) {
|
|
std_is.str(buffer);
|
|
std_is.clear();
|
|
ispos = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
inline void SimpleIStringStream::setString(const std::string &string)
|
|
{
|
|
setString(string.c_str(), COPY);
|
|
}
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator=(char *string)
|
|
{
|
|
setString(string);
|
|
return *this;
|
|
}
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator=(const char *string)
|
|
{
|
|
setString(string);
|
|
return *this;
|
|
}
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator=(
|
|
const std::string &string)
|
|
{
|
|
setString(string);
|
|
return *this;
|
|
}
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator=(
|
|
const SimpleIStringStream &stream)
|
|
{
|
|
/* Watch out that we don't try to assign ourselves, or we'll raise a
|
|
* use-after-free bug. */
|
|
if (&stream == this) {
|
|
return *this;
|
|
}
|
|
|
|
setString(stream.buffer, COPY);
|
|
return *this;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
inline SimpleIStringStream::operator bool() const
|
|
{
|
|
#ifdef SISS_VERIFY
|
|
if (!error != bool(std_is)) {
|
|
std::ostringstream os_offset; os_offset << (position - buffer);
|
|
debugLog(std::string("SimpleIStringStream bool MISMATCH: us=")
|
|
+ (!error ? "true" : "false") + " STL="
|
|
+ (std_is ? "true" : "false") + " at: ["
|
|
+ std::string(position).substr(0,100) + "] (["
|
|
+ std::string(buffer).substr(0,100) + "] + "
|
|
+ os_offset.str() + ")");
|
|
}
|
|
#endif
|
|
return !error;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(bool &target)
|
|
{
|
|
if (position) {
|
|
char *old_position = position;
|
|
if (error) {
|
|
target = false;
|
|
} else {
|
|
skip_spaces();
|
|
unsigned long longval = strtoul(position, &position, 10);
|
|
target = (longval == 1);
|
|
error = (position == old_position || *position == 0);
|
|
}
|
|
VERIFY(bool, false);
|
|
} else {
|
|
target = false;
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/*----------------------------------*/
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(short &target)
|
|
{
|
|
if (position) {
|
|
char *old_position = position;
|
|
if (error) {
|
|
target = 0;
|
|
} else {
|
|
skip_spaces();
|
|
target = (short)strtol(position, &position, 0);
|
|
error = (position == old_position || *position == 0);
|
|
}
|
|
VERIFY(short, 0);
|
|
} else {
|
|
target = 0;
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(unsigned short &target)
|
|
{
|
|
if (position) {
|
|
char *old_position = position;
|
|
if (error) {
|
|
target = 0;
|
|
} else {
|
|
skip_spaces();
|
|
target = (unsigned short)strtoul(position, &position, 0);
|
|
error = (position == old_position || *position == 0);
|
|
}
|
|
VERIFY(unsigned short, 0);
|
|
} else {
|
|
target = 0;
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/*----------------------------------*/
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(int &target)
|
|
{
|
|
if (position) {
|
|
char *old_position = position;
|
|
if (error) {
|
|
target = 0;
|
|
} else {
|
|
skip_spaces();
|
|
target = (int)strtol(position, &position, 0);
|
|
error = (position == old_position || *position == 0);
|
|
}
|
|
VERIFY(int, 0);
|
|
} else {
|
|
target = 0;
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(unsigned int &target)
|
|
{
|
|
if (position) {
|
|
char *old_position = position;
|
|
if (error) {
|
|
target = 0;
|
|
} else {
|
|
skip_spaces();
|
|
target = (unsigned int)strtoul(position, &position, 0);
|
|
error = (position == old_position || *position == 0);
|
|
}
|
|
VERIFY(unsigned int, 0);
|
|
} else {
|
|
target = 0;
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/*----------------------------------*/
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(long &target)
|
|
{
|
|
if (position) {
|
|
char *old_position = position;
|
|
if (error) {
|
|
target = 0;
|
|
} else {
|
|
skip_spaces();
|
|
target = strtol(position, &position, 0);
|
|
error = (position == old_position || *position == 0);
|
|
}
|
|
VERIFY(long, 0);
|
|
} else {
|
|
target = 0;
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(unsigned long &target)
|
|
{
|
|
if (position) {
|
|
char *old_position = position;
|
|
if (error) {
|
|
target = 0;
|
|
} else {
|
|
skip_spaces();
|
|
target = strtoul(position, &position, 0);
|
|
error = (position == old_position || *position == 0);
|
|
}
|
|
VERIFY(unsigned long, 0);
|
|
} else {
|
|
target = 0;
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/*----------------------------------*/
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(float &target)
|
|
{
|
|
if (position) {
|
|
char *old_position = position;
|
|
if (error) {
|
|
target = 0;
|
|
} else {
|
|
skip_spaces();
|
|
target = strtof(position, &position);
|
|
error = (position == old_position || *position == 0);
|
|
}
|
|
VERIFY(float, 0);
|
|
} else {
|
|
target = 0;
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(double &target)
|
|
{
|
|
if (position) {
|
|
char *old_position = position;
|
|
if (error) {
|
|
target = 0;
|
|
} else {
|
|
skip_spaces();
|
|
target = strtod(position, &position);
|
|
error = (position == old_position || *position == 0);
|
|
}
|
|
VERIFY(double, 0);
|
|
} else {
|
|
target = 0;
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/*----------------------------------*/
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(char &target)
|
|
{
|
|
if (position) {
|
|
#ifdef SISS_VERIFY
|
|
char *old_position = position;
|
|
#endif
|
|
if (error) {
|
|
target = 0;
|
|
} else {
|
|
target = *position;
|
|
if (*position) {
|
|
position++;
|
|
error = (*position == 0);
|
|
} else {
|
|
error = true;
|
|
}
|
|
}
|
|
VERIFY(char, 0);
|
|
} else {
|
|
target = 0;
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(signed char &target)
|
|
{
|
|
if (position) {
|
|
#ifdef SISS_VERIFY
|
|
char *old_position = position;
|
|
#endif
|
|
if (error) {
|
|
target = 0;
|
|
} else {
|
|
target = *position;
|
|
if (*position) {
|
|
position++;
|
|
error = (*position == 0);
|
|
} else {
|
|
error = true;
|
|
}
|
|
}
|
|
VERIFY(signed char, 0);
|
|
} else {
|
|
target = 0;
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(unsigned char &target)
|
|
{
|
|
if (position) {
|
|
#ifdef SISS_VERIFY
|
|
char *old_position = position;
|
|
#endif
|
|
if (error) {
|
|
target = 0;
|
|
} else {
|
|
target = *position;
|
|
if (*position) {
|
|
position++;
|
|
error = (*position == 0);
|
|
} else {
|
|
error = true;
|
|
}
|
|
}
|
|
VERIFY(unsigned char, 0);
|
|
} else {
|
|
target = 0;
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/*----------------------------------*/
|
|
|
|
inline SimpleIStringStream &SimpleIStringStream::operator>>(std::string &target)
|
|
{
|
|
if (position) {
|
|
#ifdef SISS_VERIFY
|
|
char *old_position = position;
|
|
#endif
|
|
if (error) {
|
|
target = "";
|
|
} else {
|
|
skip_spaces();
|
|
if (!*position) {
|
|
target = "";
|
|
error = true;
|
|
} else {
|
|
char *start = position;
|
|
while (*position && !my_isspace(*position)) {
|
|
position++;
|
|
}
|
|
target.assign(start, position - start);
|
|
error = (*position == 0);
|
|
}
|
|
}
|
|
VERIFY(std::string, "");
|
|
} else {
|
|
target = "";
|
|
error = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
#undef VERIFY_SETUP
|
|
#undef VERIFY
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
/*
|
|
* Local variables:
|
|
* indent-tabs-mode: t
|
|
* tab-width: 4
|
|
* End:
|
|
*
|
|
* vim: shiftwidth=4:
|
|
*/
|