Windows2003-3790/inetcore/outlookexpress/wabw/vcard/versit/2.0/vcdll.cpp
2020-09-30 16:53:55 +02:00

685 lines
21 KiB
C++

/***************************************************************************
(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
Business Machines Corporation and Siemens Rolm Communications Inc.
For purposes of this license notice, the term Licensors shall mean,
collectively, Apple Computer, Inc., AT&T Corp., International
Business Machines Corporation and Siemens Rolm Communications Inc.
The term Licensor shall mean any of the Licensors.
Subject to acceptance of the following conditions, permission is hereby
granted by Licensors without the need for written agreement and without
license or royalty fees, to use, copy, modify and distribute this
software for any purpose.
The above copyright notice and the following four paragraphs must be
reproduced in all copies of this software and any software including
this software.
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
MODIFICATIONS.
IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.
The software is provided with RESTRICTED RIGHTS. Use, duplication, or
disclosure by the government are subject to restrictions set forth in
DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
***************************************************************************/
// vc32.cpp : Defines the initialization routines for the DLL.
//
#include "stdafx.h"
#include <fstream.h>
#include "vc32.h"
#include "vcdll.h"
#include "clist.h"
#include "vcard.h"
#include "msv.h"
#include "mime.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CVc32App
BEGIN_MESSAGE_MAP(CVc32App, CWinApp)
//{{AFX_MSG_MAP(CVc32App)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CVc32App construction
CVc32App::CVc32App()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
/////////////////////////////////////////////////////////////////////////////
// The one and only CVc32App object
CVc32App theApp;
typedef struct {
CVCard *vCard;
CVCPropEnumerator *pEnum; // if NULL, indicates a "one-shot" enum
CVCProp *prop;
CVCNode *node;
CList *pendingRemove;
} VCEnumPropPriv, *HVCEnumPropPriv;
typedef struct {
CList list;
CLISTPOSITION pos;
CVCNode *node;
HVCEnumPropPriv parentPropEnum;
} VCEnumListPriv, *HVCEnumListPriv;
typedef struct {
CVCProp *prop;
CVCNode *node;
} VCPendingRemove;
static HGLOBAL LoadFile(LPCSTR path);
static HVCEnumProp NewProp(
HVCEnumCard hVCEnum, LPCSTR lpszPropName, LPCSTR lpszType,
void *value, S32 size);
//---------------------------------------------------------------------------
// Exported functions
//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport HVCEnumCard VCGetFirstCardFromPath(LPCSTR lpszFileName)
{
HVCEnumListPriv hVCEnumPriv = new VCEnumListPriv;
CStdioFile file(lpszFileName, CFile::modeRead | CFile::typeBinary);
if (!Parse_Many_MSV_FromFile(&file, &hVCEnumPriv->list)
&& !Parse_Many_MIME_FromFile(&file, &hVCEnumPriv->list)) {
delete hVCEnumPriv; hVCEnumPriv = NULL;
} else {
hVCEnumPriv->pos = hVCEnumPriv->list.GetHeadPosition();
hVCEnumPriv->parentPropEnum = NULL;
hVCEnumPriv->node = NULL;
}
return (HVCEnumCard)hVCEnumPriv;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport HVCEnumCard VCGetFirstCardFromMem(HGLOBAL hGlobal)
{
HVCEnumListPriv hVCEnumPriv;
const char *data = (const char *)GlobalLock(hGlobal);
if (!data)
return (HVCEnumCard)NULL;
hVCEnumPriv = new VCEnumListPriv;
if (!Parse_Many_MSV(data, GlobalSize(hGlobal), &hVCEnumPriv->list)
&& !Parse_Many_MIME(data, GlobalSize(hGlobal), &hVCEnumPriv->list)) {
delete hVCEnumPriv; hVCEnumPriv = NULL;
} else {
hVCEnumPriv->pos = hVCEnumPriv->list.GetHeadPosition();
hVCEnumPriv->parentPropEnum = NULL;
hVCEnumPriv->node = NULL;
}
GlobalUnlock(hGlobal);
return (HVCEnumCard)hVCEnumPriv;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport HVCEnumCard VCGetNewCard()
{
HVCEnumListPriv hVCEnumPriv = new VCEnumListPriv;
CVCard *vCard = new CVCard;
CVCNode *root, *english;
CVCProp *prop;
vCard->AddObject(root = new CVCNode); // create root
root->AddProp(new CVCProp(vcRootObject)); // mark it so
english = root->AddObjectProp(vcBodyProp, vcBodyObject);
english->AddProp(prop = new CVCProp(vcLanguageProp));
prop->AddValue(new CVCValue(vcISO639Type, "en", (size_t)2));
hVCEnumPriv->list.AddTail(vCard);
hVCEnumPriv->pos = hVCEnumPriv->list.GetHeadPosition();
hVCEnumPriv->parentPropEnum = NULL;
hVCEnumPriv->node = NULL;
return (HVCEnumCard)hVCEnumPriv;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport DWORD VCGetNextCard(HVCEnumCard hVCEnum)
{
HVCEnumListPriv hVCEnumPriv = (HVCEnumListPriv)hVCEnum;
hVCEnumPriv->list.GetNext(hVCEnumPriv->pos);
if (hVCEnumPriv->pos == NULL)
return 0;
return 1;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport void VCGetCardClose(HVCEnumCard hVCEnum)
{
HVCEnumListPriv hVCEnumPriv = (HVCEnumListPriv)hVCEnum;
for (CLISTPOSITION pos = hVCEnumPriv->list.GetHeadPosition(); pos; ) {
CVCard* vCard = (CVCard*)hVCEnumPriv->list.GetNext(pos);
delete vCard;
}
delete hVCEnumPriv;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport HVCEnumProp VCGetFirstProp(HVCEnumCard hVCEnum)
{
HVCEnumListPriv hVCEnumListPriv = (HVCEnumListPriv)hVCEnum;
CVCard *vCard = (CVCard*)hVCEnumListPriv->list.GetAt(hVCEnumListPriv->pos);
CVCNode *body = vCard->FindBody();
HVCEnumPropPriv hVCEnumPriv = new VCEnumPropPriv;
hVCEnumPriv->pEnum = new CVCPropEnumerator(body);
hVCEnumPriv->pendingRemove = NULL;
hVCEnumPriv->vCard = vCard;
while ((hVCEnumPriv->prop = hVCEnumPriv->pEnum->NextProp(&(hVCEnumPriv->node)))) {
if (!hVCEnumPriv->prop->IsBool())
return (HVCEnumProp)hVCEnumPriv;
}
delete hVCEnumPriv->pEnum;
delete hVCEnumPriv;
return (HVCEnumProp)NULL;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport DWORD VCGetNextProp(HVCEnumProp hVCEnum)
{
HVCEnumPropPriv hVCEnumPriv = (HVCEnumPropPriv)hVCEnum;
if (!hVCEnumPriv->pEnum)
return FALSE;
while ((hVCEnumPriv->prop = hVCEnumPriv->pEnum->NextProp(&(hVCEnumPriv->node)))) {
if (!hVCEnumPriv->prop->IsBool())
return TRUE;
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport void VCGetPropClose(HVCEnumProp hVCEnum)
{
HVCEnumPropPriv hVCEnumPriv = (HVCEnumPropPriv)hVCEnum;
if (hVCEnumPriv->pEnum)
delete hVCEnumPriv->pEnum;
if (hVCEnumPriv->pendingRemove) {
for (CLISTPOSITION pos = hVCEnumPriv->pendingRemove->GetHeadPosition(); pos; ) {
VCPendingRemove *pr = (VCPendingRemove*)hVCEnumPriv->pendingRemove->GetNext(pos);
if (pr->prop->IsBool()) {
CList *props = pr->node->GetProps();
CLISTPOSITION pos2;
if ((pos2 = props->Find(pr->prop))) {
props->RemoveAt(pos2);
delete pr->prop;
}
} else {
CVCNode *parent = hVCEnumPriv->vCard->ParentForObject(pr->node);
CList *props = parent->GetProps();
for (CLISTPOSITION pos2 = props->GetHeadPosition(); pos2; ) {
CVCProp *prop = (CVCProp*)props->GetNext(pos2);
CVCValue *value = prop->FindValue(vcNextObjectType);
if (value && (CVCObject*)value->GetValue() == pr->node) {
props->RemoveAt(pos2 ? pos2->m_prev : props->GetTailPosition());
delete prop;
break;
}
}
}
delete pr;
}
delete hVCEnumPriv->pendingRemove;
}
delete hVCEnumPriv;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport HVCEnumBoolProp VCGetFirstBoolProp(HVCEnumProp hVCEnum)
{
HVCEnumPropPriv hVCEnumPropPriv = (HVCEnumPropPriv)hVCEnum;
HVCEnumListPriv hVCEnumPriv = new VCEnumListPriv;
CList *props = hVCEnumPropPriv->node->GetProps();
for (CLISTPOSITION pos = props->GetHeadPosition(); pos; ) {
CVCProp *prop = (CVCProp*)props->GetNext(pos);
if (prop->IsBool())
hVCEnumPriv->list.AddTail(prop);
}
if (hVCEnumPriv->list.GetCount() == 0) {
delete hVCEnumPriv; hVCEnumPriv = NULL;
} else {
hVCEnumPriv->pos = hVCEnumPriv->list.GetHeadPosition();
hVCEnumPriv->parentPropEnum = hVCEnumPropPriv;
hVCEnumPriv->node = hVCEnumPropPriv->node;
}
return (HVCEnumBoolProp)hVCEnumPriv;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport DWORD VCGetNextBoolProp(HVCEnumBoolProp hVCEnum)
{
HVCEnumListPriv hVCEnumPriv = (HVCEnumListPriv)hVCEnum;
hVCEnumPriv->list.GetNext(hVCEnumPriv->pos);
if (hVCEnumPriv->pos == NULL)
return 0;
return 1;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport void VCGetBoolPropClose(HVCEnumBoolProp hVCEnum)
{
HVCEnumListPriv hVCEnumPriv = (HVCEnumListPriv)hVCEnum;
delete hVCEnumPriv;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport void VCPropName(HVCEnumProp hVCEnum, LPSTR lpstr)
{
HVCEnumPropPriv hVCEnumPriv = (HVCEnumPropPriv)hVCEnum;
strcpy(lpstr, hVCEnumPriv->prop->GetName());
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport void VCBoolPropName(HVCEnumBoolProp hVCEnum, LPSTR lpstr)
{
HVCEnumListPriv hVCEnumPriv = (HVCEnumListPriv)hVCEnum;
CVCProp *prop = (CVCProp*)hVCEnumPriv->list.GetAt(hVCEnumPriv->pos);
strcpy(lpstr, prop->GetName());
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport int VCPropStringValue(HVCEnumProp hVCEnum, LPWSTR lpwstr, int len)
{
HVCEnumPropPriv hVCEnumPriv = (HVCEnumPropPriv)hVCEnum;
CVCValue *value = (CVCValue*)hVCEnumPriv->prop->FindValue(vcStrIdxType);
if (!value)
return 0;
LPCWSTR str = (LPCWSTR)value->GetValue();
int strlen = wcslen(str);
if (strlen + 1 > len)
return -(strlen + 1);
wcscpy(lpwstr, str);
return strlen;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport HGLOBAL VCPropBinaryValue(HVCEnumProp hVCEnum, LPCSTR lpszType)
{
HVCEnumPropPriv hVCEnumPriv = (HVCEnumPropPriv)hVCEnum;
CVCValue *value = (CVCValue*)hVCEnumPriv->prop->FindValue(lpszType);
long count;
HGLOBAL hGlobal;
U8 __huge *ptr;
if (!value)
return NULL;
count = value->GetSize();
if ((hGlobal = GlobalAlloc(GMEM_SHARE, count)) == NULL)
return hGlobal;
if ((ptr = (U8 __huge *)GlobalLock(hGlobal)) == NULL) {
GlobalUnlock(hGlobal);
return NULL;
}
_hmemcpy(ptr, (U8 __huge *)value->GetValue(), count);
GlobalUnlock(hGlobal);
return hGlobal;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport HVCEnumProp VCAddStringProp(
HVCEnumCard hVCEnum, LPCSTR lpszPropName, LPCWSTR value)
{
return NewProp(
hVCEnum, lpszPropName, vcStrIdxType, (void*)value,
(wcslen(value) + 1) * sizeof(wchar_t));
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport HVCEnumProp VCAddBinaryProp(
HVCEnumCard hVCEnum, LPCSTR lpszPropName, LPCSTR lpszType, HGLOBAL value)
{
HVCEnumProp hVCEnumProp;
U8 __huge *ptr;
if ((ptr = (U8 __huge *)GlobalLock(value)) == NULL)
return NULL;
hVCEnumProp = NewProp(
hVCEnum, lpszPropName, lpszType, (void*)ptr, GlobalSize(value));
GlobalUnlock(value);
return hVCEnumProp;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport DWORD VCSetStringProp(HVCEnumProp hVCEnum, LPCWSTR value)
{
HVCEnumPropPriv hVCEnumPriv = (HVCEnumPropPriv)hVCEnum;
CVCValue *val = (CVCValue*)hVCEnumPriv->prop->FindValue(vcStrIdxType);
if (!val)
return FALSE;
val->SetValue((void*)value, (wcslen(value) + 1) * sizeof(wchar_t));
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport DWORD VCSetBinaryProp(HVCEnumProp hVCEnum, LPCSTR lpszType, HGLOBAL value)
{
HVCEnumPropPriv hVCEnumPriv = (HVCEnumPropPriv)hVCEnum;
CVCValue *val = (CVCValue*)hVCEnumPriv->prop->FindValue(lpszType);
U8 __huge *ptr;
if (!val)
return FALSE;
if ((ptr = (U8 __huge *)GlobalLock(value)) == NULL)
return FALSE;
val->SetValue((void*)ptr, GlobalSize(value));
GlobalUnlock(value);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport DWORD VCAddBoolProp(LPCSTR lpszPropName, HVCEnumProp addTo)
{
HVCEnumPropPriv hVCEnumPriv = (HVCEnumPropPriv)addTo;
hVCEnumPriv->node->AddBoolProp(lpszPropName);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport void VCRemoveProp(HVCEnumProp hVCEnum)
{
HVCEnumPropPriv hVCEnumPriv = (HVCEnumPropPriv)hVCEnum;
VCPendingRemove *pr;
if (!hVCEnumPriv->pendingRemove)
hVCEnumPriv->pendingRemove = new CList;
pr = new VCPendingRemove;
pr->node = hVCEnumPriv->node;
pr->prop = hVCEnumPriv->prop;
hVCEnumPriv->pendingRemove->AddTail(pr);
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport void VCRemoveBoolProp(HVCEnumBoolProp hVCEnum)
{
HVCEnumListPriv hVCEnumPriv = (HVCEnumListPriv)hVCEnum;
CVCProp *prop = (CVCProp*)hVCEnumPriv->list.GetAt(hVCEnumPriv->pos);
VCPendingRemove *pr;
if (!hVCEnumPriv->parentPropEnum->pendingRemove)
hVCEnumPriv->parentPropEnum->pendingRemove = new CList;
pr = new VCPendingRemove;
pr->node = hVCEnumPriv->node;
pr->prop = prop;
hVCEnumPriv->parentPropEnum->pendingRemove->AddTail(pr);
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport void VCRemoveBoolPropByName(HVCEnumProp hVCEnum, LPCSTR name)
{
HVCEnumBoolProp bprop = VCGetFirstBoolProp(hVCEnum);
if (bprop) {
char propName[VC_PROPNAME_MAX];
VCBoolPropName(bprop, propName);
if (strcmp(propName, name) == 0)
VCRemoveBoolProp(bprop);
// process remaining props
while (VCGetNextBoolProp(bprop)) {
VCBoolPropName(bprop, propName);
if (strcmp(propName, name) == 0)
VCRemoveBoolProp(bprop);
}
VCGetBoolPropClose(bprop);
}
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport DWORD VCSaveCardToPath(LPCSTR path, HVCEnumCard hVCEnum)
{
HVCEnumListPriv hVCEnumPriv = (HVCEnumListPriv)hVCEnum;
CVCard *vCard = (CVCard*)hVCEnumPriv->list.GetAt(hVCEnumPriv->pos);
ofstream strm(path, ios::app);
vCard->Write(strm);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
DllExport HGLOBAL VCSaveCardToMem(HVCEnumCard hVCEnum)
{
char *tempname;
HGLOBAL hGlobal = NULL;
tempname = _tempnam(NULL, "CARD");
if (VCSaveCardToPath(tempname, hVCEnum)) {
hGlobal = LoadFile(tempname);
unlink(tempname);
}
free(tempname);
return hGlobal;
}
//---------------------------------------------------------------------------
// Private functions
//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
static HGLOBAL LoadFile(LPCSTR path)
{
fpos_t inLength;
DWORD count, numAlloc, num;
FILE *file;
U8 __huge *ptr;
BOOL error = FALSE;
HGLOBAL hGlobal = NULL;
file = fopen(path, "rb");
fseek(file, 0, SEEK_END);
fgetpos(file, &inLength);
fseek(file, 0, SEEK_SET);
count = (DWORD)inLength;
if ((hGlobal = GlobalAlloc(GMEM_SHARE, count)) == NULL)
goto Done;
if ((ptr = (U8 __huge *)GlobalLock(hGlobal)) == NULL) {
GlobalFree(hGlobal); hGlobal = NULL;
goto Done;
}
if ((numAlloc = GlobalSize(hGlobal)) > count)
memset(ptr + count, 0, (int)(numAlloc - count));
do {
num = min(count, 0x7FFF);
fread(ptr, 1, (size_t)num, file);
if (ferror(file))
error = TRUE;
else {
count -= num;
ptr += num;
}
} while (count && !error);
GlobalUnlock(hGlobal);
if (error) {
GlobalFree(hGlobal); hGlobal = NULL;
}
Done:
fclose(file);
return hGlobal;
}
static const char *groupAddr[] = {
vcExtAddressProp,
vcStreetAddressProp,
vcCityProp,
vcRegionProp,
vcPostalCodeProp,
vcCountryNameProp,
NULL
};
static const char *groupPersonName[] = {
vcFamilyNameProp,
vcGivenNameProp,
NULL
};
static const char *groupCompanyName[] = {
vcOrgNameProp,
vcOrgUnitProp,
NULL
};
static BOOL IsMemberOfGroup(const char *propName, const char **group)
{
while (*group) {
if (strcmp(propName, *group) == 0)
return TRUE;
group++;
}
return FALSE;
}
static CVCNode* FindGroup(CVCNode* body, const char **group)
{
CVCPropEnumerator enumerator(body);
CVCNode* node;
CVCProp* prop;
while ((prop = enumerator.NextProp(&node))) {
if (IsMemberOfGroup(prop->GetName(), group))
return node;
}
return NULL;
}
/////////////////////////////////////////////////////////////////////////////
static HVCEnumProp NewProp(
HVCEnumCard hVCEnum, LPCSTR lpszPropName, LPCSTR lpszType,
void *value, S32 size)
{
HVCEnumListPriv hVCEnumListPriv = (HVCEnumListPriv)hVCEnum;
CVCard *vCard = (CVCard*)hVCEnumListPriv->list.GetAt(hVCEnumListPriv->pos);
CVCNode *body = vCard->FindBody();
HVCEnumPropPriv hVCEnumPriv = new VCEnumPropPriv;
if (IsMemberOfGroup(lpszPropName, groupAddr))
hVCEnumPriv->node = FindGroup(body, groupAddr);
else if (IsMemberOfGroup(lpszPropName, groupPersonName))
hVCEnumPriv->node = FindGroup(body, groupPersonName);
else if (IsMemberOfGroup(lpszPropName, groupCompanyName))
hVCEnumPriv->node = FindGroup(body, groupCompanyName);
else
hVCEnumPriv->node = body->AddPart();
if (!hVCEnumPriv->node)
hVCEnumPriv->node = body->AddPart();
hVCEnumPriv->node->AddProp(
hVCEnumPriv->prop = new CVCProp(lpszPropName, lpszType, value, size));
hVCEnumPriv->pEnum = NULL;
hVCEnumPriv->pendingRemove = NULL;
hVCEnumPriv->vCard = vCard;
return (HVCEnumProp)hVCEnumPriv;
}
// Simple-minded conversion from UNICODE to char string
char *UI_CString(const wchar_t *u, char *dst)
{
char *str = dst;
while (*u) {
if (*u == 0x2028) {
*dst++ = '\r';
*dst++ = '\n';
u++;
} else
*dst++ = (char)*u++;
}
*dst = '\000';
return str;
}
CM_CFUNCTIONS
void Parse_Debug(const char *s)
{
::AfxTrace(s);
}
/*/////////////////////////////////////////////////////////////////////////*/
void msv_error(char *s)
{
if (++msv_numErrors <= 3) {
char buf[80];
sprintf(buf, "%s at line %d", s, msv_lineNum);
//TRACE1("%s\n", buf);
}
}
/*/////////////////////////////////////////////////////////////////////////*/
void mime_error(char *s)
{
if (++mime_numErrors <= 3) {
char buf[80];
sprintf(buf, "%s at line %d", s, mime_lineNum);
//TRACE1("%s\n", buf);
}
}
CM_END_CFUNCTIONS
/***************************************************************************/