mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-08-11 22:03:09 -06:00
Testing usvn
git-svn-id: svn://ultimatepp.org/upp/trunk@1342 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
255c218e23
commit
79a9e1fb12
88 changed files with 18578 additions and 16 deletions
662
uppdev/XLS/unit.cpp
Normal file
662
uppdev/XLS/unit.cpp
Normal file
|
|
@ -0,0 +1,662 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* This file is part of xlslib -- A multiplatform, C/C++ library
|
||||
* for dynamic generation of Excel(TM) files.
|
||||
*
|
||||
* xlslib 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* xlslib 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 xlslib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2004 Yeico S. A. de C. V.
|
||||
* Copyright 2008 David Hoerl
|
||||
*
|
||||
* $Source: /cvsroot/xlslib/xlslib/src/xlslib/unit.cpp,v $
|
||||
* $Revision: 1.3 $
|
||||
* $Author: dhoerl $
|
||||
* $Date: 2008/10/25 18:39:54 $
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* File description:
|
||||
*
|
||||
*
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include <XLS/systype.h>
|
||||
#include <XLS/unit.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace xlslib_core;
|
||||
|
||||
/*
|
||||
*********************************************************************************
|
||||
*********************************************************************************
|
||||
CUnit class implementation
|
||||
*********************************************************************************
|
||||
*********************************************************************************
|
||||
*/
|
||||
|
||||
// Default constructor
|
||||
CUnit::CUnit() : m_nSize(0), m_pData(0), m_nDataSize(0)
|
||||
//, m_ShadowUnit(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Default destructor
|
||||
CUnit::~CUnit ( )
|
||||
{
|
||||
if(m_pData /*&& !m_ShadowUnit*/)
|
||||
{
|
||||
delete[] m_pData;
|
||||
}
|
||||
|
||||
}
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
const unsigned8_t CUnit::DefaultInflateSize = 10;
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::SetAt(unsigned8_t newval, unsigned32_t index)
|
||||
{
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
|
||||
if(m_pData != NULL)
|
||||
{
|
||||
if (index < m_nDataSize)
|
||||
m_pData[index] = newval;
|
||||
else
|
||||
errcode = ERR_INVALID_INDEX;
|
||||
} else {
|
||||
errcode = ERR_DATASTORAGE_EMPTY;
|
||||
}
|
||||
|
||||
return errcode;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::AddValue16(unsigned16_t newval)
|
||||
{
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
|
||||
if(AddValue8(BYTE_0(newval))) errcode = GENERAL_ERROR;
|
||||
if(AddValue8(BYTE_1(newval))) errcode = GENERAL_ERROR;
|
||||
|
||||
return errcode;
|
||||
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::AddValue32(unsigned32_t newval)
|
||||
{
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
|
||||
if(AddValue8(BYTE_0(newval))) errcode = GENERAL_ERROR;
|
||||
if(AddValue8(BYTE_1(newval))) errcode = GENERAL_ERROR;
|
||||
if(AddValue8(BYTE_2(newval))) errcode = GENERAL_ERROR;
|
||||
if(AddValue8(BYTE_3(newval))) errcode = GENERAL_ERROR;
|
||||
|
||||
return errcode;
|
||||
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::AddValue64(unsigned64_t newval)
|
||||
{
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
|
||||
if(AddValue8(BYTE_0(newval))) errcode = GENERAL_ERROR;
|
||||
if(AddValue8(BYTE_1(newval))) errcode = GENERAL_ERROR;
|
||||
if(AddValue8(BYTE_2(newval))) errcode = GENERAL_ERROR;
|
||||
if(AddValue8(BYTE_3(newval))) errcode = GENERAL_ERROR;
|
||||
|
||||
if(AddValue8(BYTE_4(newval))) errcode = GENERAL_ERROR;
|
||||
if(AddValue8(BYTE_5(newval))) errcode = GENERAL_ERROR;
|
||||
if(AddValue8(BYTE_6(newval))) errcode = GENERAL_ERROR;
|
||||
if(AddValue8(BYTE_7(newval))) errcode = GENERAL_ERROR;
|
||||
|
||||
return errcode;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::SetValueAt(signed16_t newval, unsigned32_t index)
|
||||
{
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
|
||||
if(SetAt(BYTE_0(newval), index )) errcode = GENERAL_ERROR;
|
||||
if(SetAt(BYTE_1(newval), index+1)) errcode = GENERAL_ERROR;
|
||||
|
||||
return errcode;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::SetValueAt(signed32_t newval, unsigned32_t index)
|
||||
{
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
|
||||
if(SetAt(BYTE_0(newval), index )) errcode = GENERAL_ERROR;
|
||||
if(SetAt(BYTE_1(newval), index+1)) errcode = GENERAL_ERROR;
|
||||
if(SetAt(BYTE_2(newval), index+2)) errcode = GENERAL_ERROR;
|
||||
if(SetAt(BYTE_3(newval), index+3)) errcode = GENERAL_ERROR;
|
||||
|
||||
return errcode;
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::GetValue16From(signed16_t* val, unsigned32_t index)
|
||||
{
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
|
||||
*val = (signed16_t)(operator[](index) +
|
||||
operator[](index+1)*0x0100);
|
||||
|
||||
return errcode;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::GetValue32From(signed32_t* val, unsigned32_t index)
|
||||
{
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
|
||||
*val = (signed16_t)(operator[](index) *0x0000001 +
|
||||
operator[](index+1)*0x0000100 +
|
||||
operator[](index+2)*0x0010000 +
|
||||
operator[](index+2)*0x1000000 );
|
||||
return errcode;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::GetAt(signed8_t* data, unsigned32_t index)
|
||||
{
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
|
||||
if(m_pData != NULL)
|
||||
{
|
||||
if (index < m_nDataSize)
|
||||
{
|
||||
*data = m_pData[index];
|
||||
} else {
|
||||
errcode = ERR_INVALID_INDEX;
|
||||
}
|
||||
} else {
|
||||
errcode = ERR_DATASTORAGE_EMPTY;
|
||||
}
|
||||
|
||||
return errcode;
|
||||
|
||||
}
|
||||
/************************************************
|
||||
************************************************/
|
||||
/*
|
||||
signed8_t CUnit::GetData(unsigned8_t** ppdata, unsigned32_t from, unsigned32_t to )
|
||||
{
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
|
||||
// Simply make the external pointer point to the local storage data
|
||||
*ppdata = m_pData;
|
||||
|
||||
return errcode;
|
||||
}
|
||||
*/
|
||||
/************************************************
|
||||
************************************************/
|
||||
signed8_t CUnit::AddDataArray (const unsigned8_t* newdata, size_t size)
|
||||
{
|
||||
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
size_t spaceleft = m_nSize - m_nDataSize;
|
||||
|
||||
if(spaceleft < size) // allocate more space if new tobeadded array won't fit
|
||||
{
|
||||
Inflate(size-spaceleft+1);
|
||||
}
|
||||
|
||||
if(newdata != NULL)
|
||||
{
|
||||
for(unsigned32_t i=0; i<size; i++)
|
||||
m_pData[m_nDataSize++] = newdata[i];
|
||||
} else {
|
||||
//No data to add. Do nothing
|
||||
}
|
||||
|
||||
return errcode;
|
||||
}
|
||||
|
||||
signed8_t CUnit::AddFixedDataArray (const unsigned8_t value, size_t size)
|
||||
{
|
||||
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
size_t spaceleft = m_nSize - m_nDataSize;
|
||||
|
||||
if(spaceleft < size) // allocate more space if new tobeadded array won't fit
|
||||
{
|
||||
Inflate(size-spaceleft+1);
|
||||
}
|
||||
|
||||
// The following can be a memset
|
||||
for(unsigned32_t i=0; i<size; i++)
|
||||
m_pData[m_nDataSize++] = value;
|
||||
|
||||
|
||||
return errcode;
|
||||
}
|
||||
signed8_t AddDataArray (const unsigned16_t* newdata, size_t size);
|
||||
signed8_t AddFixedDataArray (const unsigned16_t value, size_t size);
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
signed8_t CUnit::RemoveTrailData (unsigned32_t remove_size)
|
||||
{
|
||||
/*
|
||||
total_to_remove = (m_nSize - m_nDataSize) - remove_size;
|
||||
size of temp_data = m_nSize - total_to_remove = m_nDataSize + remove_size
|
||||
*/
|
||||
unsigned32_t temp_size = m_nDataSize + remove_size;
|
||||
unsigned8_t* temp_data = new unsigned8_t[temp_size];
|
||||
|
||||
if(temp_data != NULL)
|
||||
{
|
||||
for(unsigned32_t i=0; i<temp_size; i++)
|
||||
temp_data[i] = m_pData[i];
|
||||
} else {
|
||||
return GENERAL_ERROR;
|
||||
}
|
||||
|
||||
m_nDataSize = temp_size;
|
||||
m_nSize = m_nDataSize;
|
||||
delete[] m_pData;
|
||||
m_pData = temp_data;
|
||||
|
||||
return NO_ERRORS;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::SetArrayAt(const unsigned8_t* newdata, size_t size, unsigned32_t index)
|
||||
{
|
||||
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
size_t spaceleft = m_nSize - index;
|
||||
|
||||
if(spaceleft < size) // allocate more space if new tobeadded array won't fit
|
||||
{
|
||||
Inflate(size-spaceleft);
|
||||
}
|
||||
|
||||
if(newdata != NULL)
|
||||
{
|
||||
for(unsigned32_t i=0; i<size; i++)
|
||||
{
|
||||
/*
|
||||
// The following code adds needed space to the whole allocated array
|
||||
if (index > m_nDataSize) m_nDataSize = index+1;
|
||||
else if(index==m_nDataSize) m_nDataSize++;
|
||||
m_pData[index++] = newdata[i];
|
||||
*/
|
||||
// The following code truncates the array if it exceeds DataSize
|
||||
if(index==m_nDataSize) break;
|
||||
|
||||
m_pData[index++] = newdata[i];
|
||||
}
|
||||
} else {
|
||||
//No data to add. Do nothing
|
||||
}
|
||||
|
||||
return errcode;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::AddValue8(unsigned8_t newdata)
|
||||
{
|
||||
|
||||
if(m_nDataSize >= m_nSize)
|
||||
{
|
||||
Inflate();
|
||||
}
|
||||
|
||||
m_pData[m_nDataSize++] = newdata;
|
||||
|
||||
return NO_ERRORS;
|
||||
|
||||
}
|
||||
|
||||
#if VERSION_BIFF == VERSION_BIFF8
|
||||
#ifdef HAVE_ICONV
|
||||
void CUnit::Conv32to16(string& iconv_code, ustring& str1, u16string& str2)
|
||||
{
|
||||
size_t resultSize, inbytesleft, outbytesleft;
|
||||
const wchar_t *inbuf;
|
||||
iconv_t cd;
|
||||
unsigned16_t *outbuf, *origOutbuf;
|
||||
static unsigned16_t convFail[] = { 'i', 'c', 'o', 'n', 'v', ' ', 'f', 'a', 'i', 'l', 'e', 'd', '!' };
|
||||
|
||||
cd = iconv_open("UCS-2-INTERNAL", "wchar_t");
|
||||
// no need to test return code as we ALREADY did this when setting iconv_code in workbook
|
||||
|
||||
inbytesleft = str1.size() * sizeof(uchar_t);
|
||||
outbytesleft = inbytesleft * 4 * sizeof(unsigned16_t); // Unicode expansion 4 to 2 ???
|
||||
|
||||
inbuf = str1.c_str();
|
||||
origOutbuf = (unsigned16_t *)calloc(outbytesleft, 1);
|
||||
outbuf = origOutbuf;
|
||||
|
||||
resultSize = iconv(cd, (char **)&inbuf, &inbytesleft, (char **)&outbuf, &outbytesleft);
|
||||
iconv_close(cd);
|
||||
|
||||
if(resultSize == (size_t)-1) {
|
||||
str2 = convFail;
|
||||
} else {
|
||||
str2.assign(origOutbuf, outbuf - origOutbuf);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// for cases where we save a single byte
|
||||
signed8_t CUnit::AddUnicodeStrlen(unsigned8_t newdata)
|
||||
{
|
||||
return AddValue8(newdata);
|
||||
}
|
||||
// for cases where we save a single byte
|
||||
signed8_t CUnit::AddUnicodeStrlen(unsigned16_t newdata)
|
||||
{
|
||||
return AddValue16(newdata);
|
||||
}
|
||||
signed8_t CUnit::AddUnicodeString (const unsigned8_t* newdata, unsigned16_t size)
|
||||
{
|
||||
unsigned16_t strSize = 1 + size;
|
||||
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
size_t spaceleft = m_nSize - m_nDataSize;
|
||||
|
||||
if(spaceleft < strSize) // allocate more space if new tobeadded array won't fit
|
||||
{
|
||||
Inflate((size_t)(strSize-spaceleft+1));
|
||||
}
|
||||
|
||||
m_pData[m_nDataSize++] = 0x00; // character compression
|
||||
|
||||
if(newdata != NULL)
|
||||
{
|
||||
for(unsigned16_t i=0; i<size; i++)
|
||||
m_pData[m_nDataSize++] = newdata[i];
|
||||
} else {
|
||||
//No data to add. Do nothing
|
||||
}
|
||||
|
||||
return errcode;
|
||||
}
|
||||
signed8_t CUnit::AddUnicodeString (const unsigned16_t* newdata, unsigned16_t size)
|
||||
{
|
||||
unsigned16_t strSize = 1 + size*sizeof(unsigned16_t);
|
||||
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
size_t spaceleft = m_nSize - m_nDataSize;
|
||||
|
||||
if(spaceleft < strSize) // allocate more space if new tobeadded array won't fit
|
||||
{
|
||||
Inflate((size_t)(strSize-spaceleft+1));
|
||||
}
|
||||
|
||||
m_pData[m_nDataSize++] = 0x01; // UTF-16
|
||||
|
||||
if(newdata != NULL)
|
||||
{
|
||||
for(unsigned16_t i=0; i<size; i++) {
|
||||
unsigned16_t data;
|
||||
|
||||
data = newdata[i];
|
||||
|
||||
m_pData[m_nDataSize++] = data & 0xFF;
|
||||
m_pData[m_nDataSize++] = (data >> 8) & 0xFF;
|
||||
}
|
||||
} else {
|
||||
//No data to add. Do nothing
|
||||
}
|
||||
|
||||
return errcode;
|
||||
}
|
||||
#endif
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::Inflate(size_t increase)
|
||||
{
|
||||
signed8_t errcode = NO_ERRORS;
|
||||
|
||||
if (increase == 0)
|
||||
increase = CUnit::DefaultInflateSize;
|
||||
|
||||
// Create the new storage with increased size
|
||||
// and initialize it to 0.
|
||||
unsigned8_t* temp_storage = new unsigned8_t[m_nSize + increase];
|
||||
|
||||
if(temp_storage != NULL)
|
||||
{
|
||||
|
||||
memset(temp_storage, 0, (m_nSize+increase)*(sizeof(unsigned8_t)));
|
||||
// Copy data to the new storage
|
||||
memcpy(temp_storage, m_pData, m_nSize*sizeof(unsigned8_t));
|
||||
|
||||
// Update the size
|
||||
m_nSize += increase;
|
||||
|
||||
if (m_pData != NULL)
|
||||
delete []m_pData;
|
||||
|
||||
m_pData = temp_storage;
|
||||
|
||||
errcode = ERR_UNNABLE_TOALLOCATE_MEMORY;
|
||||
} else {
|
||||
// No errors... errcode already clean
|
||||
}
|
||||
|
||||
return errcode;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
unsigned8_t& CUnit::operator[] ( const unsigned32_t index )
|
||||
{
|
||||
if(index >= m_nDataSize)
|
||||
return m_pData[m_nDataSize];
|
||||
return m_pData[index];
|
||||
|
||||
}
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
CUnit& CUnit::operator= (CUnit& newvalue )
|
||||
{
|
||||
if(&newvalue != this)
|
||||
Init(newvalue.GetBuffer(), newvalue.GetSize(), newvalue.GetDataSize());
|
||||
|
||||
return *this;
|
||||
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
CUnit& CUnit::operator+= ( CUnit& from )
|
||||
{
|
||||
|
||||
if(&from != this)
|
||||
Append(from);
|
||||
else
|
||||
{
|
||||
CUnit shadow;
|
||||
shadow = from;
|
||||
Append(shadow);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
CUnit& CUnit::operator+= ( unsigned8_t from )
|
||||
{
|
||||
|
||||
AddValue8(from);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::Init (unsigned8_t* data, const size_t size, const unsigned32_t datasz)
|
||||
{
|
||||
|
||||
m_nSize = size;
|
||||
m_nDataSize = datasz;
|
||||
|
||||
m_pData = new unsigned8_t[m_nSize];
|
||||
|
||||
if(data)
|
||||
{
|
||||
memset(m_pData, 0, m_nSize*sizeof(unsigned8_t));
|
||||
// Copy data to the new storage
|
||||
memcpy(m_pData, data, m_nSize*sizeof(unsigned8_t));
|
||||
}
|
||||
|
||||
return NO_ERRORS;
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
signed8_t CUnit::Append (CUnit& newunit)
|
||||
{
|
||||
|
||||
if(AddDataArray(newunit.GetBuffer(), newunit.GetDataSize()) == NO_ERRORS)
|
||||
return NO_ERRORS;
|
||||
else
|
||||
return GENERAL_ERROR;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
signed8_t CUnit::InitFill (unsigned8_t data, unsigned32_t size)
|
||||
{
|
||||
if(m_pData)
|
||||
delete[] m_pData;
|
||||
|
||||
m_pData = new unsigned8_t[size];
|
||||
|
||||
if(m_pData)
|
||||
{
|
||||
memset(m_pData, data, size*sizeof(unsigned8_t));
|
||||
m_nSize = m_nDataSize = size;
|
||||
|
||||
return NO_ERRORS;
|
||||
} else {
|
||||
return GENERAL_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
size_t CUnit::GetSize (void)
|
||||
{
|
||||
return m_nSize;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
unsigned32_t CUnit::GetDataSize (void)
|
||||
{
|
||||
return m_nDataSize;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
|
||||
unsigned8_t* CUnit::GetBuffer (void)
|
||||
{
|
||||
return m_pData;
|
||||
}
|
||||
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
/*
|
||||
void CUnit::SetShadow(bool shadowval)
|
||||
{
|
||||
m_ShadowUnit = shadowval;
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/************************************************
|
||||
************************************************/
|
||||
/*
|
||||
void CUnit::CopyShadowUnit(unsigned8_t* data, unsigned32_t size)
|
||||
{
|
||||
m_Size = m_DataSize = size;
|
||||
m_pData = data;
|
||||
|
||||
SetShadow(true);
|
||||
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* $Log: unit.cpp,v $
|
||||
* Revision 1.3 2008/10/25 18:39:54 dhoerl
|
||||
* 2008
|
||||
*
|
||||
* Revision 1.2 2004/09/01 00:47:21 darioglz
|
||||
* + Modified to gain independence of target
|
||||
*
|
||||
* Revision 1.1.1.1 2004/08/27 16:31:53 darioglz
|
||||
* Initial Import.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue