|
|
#include"kutil.h"
|
|
|
#include "ado.h"
|
|
|
#define debug true
|
|
|
#define ITK_err 919012
|
|
|
|
|
|
#include <chrono>
|
|
|
|
|
|
bool isExistValue(vector<char*> values, char* value)
|
|
|
{
|
|
|
for (auto referencedValue : values) {
|
|
|
if (strcmp(referencedValue, value) == 0)
|
|
|
{
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
std::vector<std::string> strSplit(const std::string& str, const std::string& pattern)
|
|
|
{
|
|
|
std::vector<std::string> resVec;
|
|
|
|
|
|
if ("" == str)
|
|
|
{
|
|
|
return resVec;
|
|
|
}
|
|
|
//方便截取最后一段数据
|
|
|
std::string strs = str + pattern;
|
|
|
|
|
|
size_t pos = strs.find(pattern);
|
|
|
size_t size = strs.size();
|
|
|
|
|
|
while (pos != std::string::npos)
|
|
|
{
|
|
|
std::string x = strs.substr(0, pos);
|
|
|
resVec.push_back(x);
|
|
|
strs = strs.substr(pos + pattern.size(), size);
|
|
|
pos = strs.find(pattern);
|
|
|
}
|
|
|
|
|
|
return resVec;
|
|
|
};
|
|
|
|
|
|
void strSplit(char* src, const char* separator, char** dest, int* num) {
|
|
|
|
|
|
vector<std::string> resultVector = strSplit(src, separator);
|
|
|
int count = 0;
|
|
|
for (auto result : resultVector) {
|
|
|
int nLen = strlen(result.c_str());
|
|
|
char* pCh = new char[nLen + 1];
|
|
|
strcpy(pCh, result.c_str());
|
|
|
dest[count] = pCh;
|
|
|
++count;
|
|
|
}
|
|
|
*num = count;
|
|
|
}
|
|
|
|
|
|
void CreateFile(const char* directory_path, const char* file_path) {
|
|
|
// 将目录路径转换为宽字符串
|
|
|
wchar_t w_directory_path[MAX_PATH];
|
|
|
MultiByteToWideChar(CP_UTF8, 0, directory_path, -1, w_directory_path, MAX_PATH);
|
|
|
|
|
|
// 检查文件夹是否存在,如果不存在则创建
|
|
|
DWORD dwAttrib = GetFileAttributes(w_directory_path);
|
|
|
if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
|
// 文件夹不存在,尝试创建
|
|
|
if (_wmkdir(w_directory_path) != 0) {
|
|
|
perror("Failed to create directory.");
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 将文件路径转换为宽字符串
|
|
|
wchar_t w_file_path[MAX_PATH];
|
|
|
MultiByteToWideChar(CP_UTF8, 0, file_path, -1, w_file_path, MAX_PATH);
|
|
|
|
|
|
// 检查文件是否存在,如果不存在则创建
|
|
|
FILE* file = fopen(file_path, "r");
|
|
|
if (file == NULL) {
|
|
|
// 文件不存在,尝试创建并打开(这里以追加模式打开)
|
|
|
file = fopen(file_path, "a+");
|
|
|
if (file == NULL) {
|
|
|
perror("Failed to create and open file.");
|
|
|
return;
|
|
|
}
|
|
|
fclose(file); // 关闭刚创建的文件
|
|
|
}
|
|
|
else {
|
|
|
fclose(file); // 如果文件已存在,则关闭打开的文件句柄
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int LY_ChildIsPickUp(EPM_action_message_t msg) {
|
|
|
int ifail = ITK_ok;
|
|
|
int argCnt = 0;
|
|
|
char* arg = NULL,
|
|
|
* argName = NULL,
|
|
|
* argValue = NULL;
|
|
|
|
|
|
// 属性名称(object_name)
|
|
|
char itemTypeName[1024] = "",
|
|
|
relationName[1024] = "",
|
|
|
itemChildTypeName[1024] = "",
|
|
|
itemChildRevValue[1024] = "",
|
|
|
releaseStatusName[1024] = "";
|
|
|
tag_t rootTask = NULLTAG;
|
|
|
|
|
|
|
|
|
const char* directory_path = "D:\\log";
|
|
|
const char* file_path = "D:\\log\\F4_MEP_Merge.log";
|
|
|
CreateFile(directory_path, file_path);
|
|
|
// Open log file in append mode
|
|
|
auto logFile = std::make_unique<std::ofstream>(file_path, std::ios::app);
|
|
|
if (!logFile->is_open()) {
|
|
|
return -1;
|
|
|
}
|
|
|
// Get current time
|
|
|
auto now = std::chrono::system_clock::now();
|
|
|
std::time_t time1 = std::chrono::system_clock::to_time_t(now);
|
|
|
std::string timeStr = std::ctime(&time1);
|
|
|
timeStr = timeStr.substr(0, timeStr.size() - 1);
|
|
|
*logFile << "[" << timeStr << "] ";
|
|
|
*logFile << "=========================依据对象类型提取其下特定版本/状态对象===================" << std::endl;
|
|
|
|
|
|
|
|
|
//获取参数的个数,以及它的值
|
|
|
argCnt = TC_number_of_arguments(msg.arguments);
|
|
|
if (argCnt > 0)
|
|
|
{
|
|
|
for (int i = 0; i < argCnt; i++)
|
|
|
{ //从下标为0的参数开始获取参数
|
|
|
arg = TC_next_argument(msg.arguments);
|
|
|
//获取参数的名称和值
|
|
|
// arg:入参
|
|
|
// argNam:出参(处理程序中的参数,key)
|
|
|
// argValue:出参(处理程序中的值,value)
|
|
|
ITKCALL(ifail = ITK_ask_argument_named_value((const char*)arg, &argName, &argValue));
|
|
|
if (stricmp(argName, "itemType") == 0)
|
|
|
{
|
|
|
if (argValue != NULL)
|
|
|
{
|
|
|
strcpy(itemTypeName, argValue);
|
|
|
}
|
|
|
}
|
|
|
else if (stricmp(argName, "Relation") == 0)
|
|
|
{
|
|
|
//strcmp("","");
|
|
|
if (argValue != NULL)
|
|
|
{
|
|
|
strcpy(relationName, argValue);
|
|
|
}
|
|
|
}
|
|
|
else if (stricmp(argName, "itemChildType") == 0) {
|
|
|
if (argValue != NULL)
|
|
|
{
|
|
|
strcpy(itemChildTypeName, argValue);
|
|
|
}
|
|
|
}
|
|
|
else if (stricmp(argName, "itemChildRev") == 0) {
|
|
|
if (argValue != NULL)
|
|
|
{
|
|
|
strcpy(itemChildRevValue, argValue);
|
|
|
}
|
|
|
}
|
|
|
else if (stricmp(argName, "releaseStatus") == 0) {
|
|
|
if (argValue != NULL)
|
|
|
{
|
|
|
strcpy(releaseStatusName, argValue);
|
|
|
}
|
|
|
}
|
|
|
MEM_free(argName);
|
|
|
MEM_free(argValue);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
int attachmentCount = 0;
|
|
|
tag_t* attachmentTags = NULL,
|
|
|
attachmentTag = NULLTAG;
|
|
|
//获取流程任务的根任务
|
|
|
ITKCALL(ifail = EPM_ask_root_task(msg.task, &rootTask));
|
|
|
//EEPM_target_attachment 目标, EPM_reference_attachment 引用
|
|
|
//获取流程目标下关联的对象
|
|
|
ITKCALL(ifail = EPM_ask_attachments(rootTask, EPM_target_attachment, &attachmentCount, &attachmentTags));
|
|
|
|
|
|
vector<char*> targetObjectStrings;
|
|
|
for (int i = 0; i < attachmentCount; i++)
|
|
|
{
|
|
|
char* targetString = NULL;
|
|
|
ITKCALL(ifail = AOM_ask_value_string(attachmentTags[i], "object_string", &targetString));
|
|
|
*logFile << "################# 已经存在的对象" << targetString << std::endl;
|
|
|
targetObjectStrings.push_back(targetString);
|
|
|
}
|
|
|
|
|
|
// 获取选项值 -itemChildType
|
|
|
*logFile << "============>开始获取选项值 -itemChildType" << std::endl;
|
|
|
vector<char*> itemchildTypeCheakVetor;
|
|
|
if (strstr(itemChildTypeName, ",") != NULL)
|
|
|
{
|
|
|
int vectorItemchildTypeCount = 0;
|
|
|
char** vectoritemchildTypeChar = new char* [64];
|
|
|
strSplit(itemChildTypeName, ",", vectoritemchildTypeChar, &vectorItemchildTypeCount);
|
|
|
for (int i = 0; i < vectorItemchildTypeCount; i++)
|
|
|
{
|
|
|
itemchildTypeCheakVetor.push_back(vectoritemchildTypeChar[i]);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
itemchildTypeCheakVetor.push_back(itemChildTypeName);
|
|
|
}
|
|
|
|
|
|
|
|
|
// 获取选项值 -releaseStatus
|
|
|
vector<char*> releaseStatusCheakVetor;
|
|
|
int vectorReleaseStatusCount = 0;
|
|
|
if (strstr(releaseStatusName, ",") != NULL)
|
|
|
{
|
|
|
char** vectorReleaseStatusChar = new char* [64];
|
|
|
strSplit(releaseStatusName, ",", vectorReleaseStatusChar, &vectorReleaseStatusCount);
|
|
|
for (int i = 0; i < vectorReleaseStatusCount; i++)
|
|
|
{
|
|
|
itemchildTypeCheakVetor.push_back(vectorReleaseStatusChar[i]);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
releaseStatusCheakVetor.push_back(releaseStatusName);
|
|
|
vectorReleaseStatusCount++;
|
|
|
}
|
|
|
*logFile << "============>开始获取选项值 -releaseStatus << num =" << vectorReleaseStatusCount << std::endl;
|
|
|
|
|
|
|
|
|
*logFile << "[" << timeStr << "] " << "************开始遍历所有的流程目标下的对象*************** 遍历次数:" << attachmentCount <<std::endl;
|
|
|
|
|
|
//遍历所有的流程目标下的对象
|
|
|
for (int i = 0; i < attachmentCount; i++) {
|
|
|
char* tagType = NULL;
|
|
|
char* tagName = NULL;
|
|
|
attachmentTag = attachmentTags[i];
|
|
|
|
|
|
ITKCALL(ifail = AOM_ask_value_string(attachmentTag, "object_type", &tagType));
|
|
|
ITKCALL(ifail = AOM_ask_value_string(attachmentTag, "object_name", &tagName));
|
|
|
*logFile << "[" << timeStr << "] " << "当前对象:" << tagName << std::endl;
|
|
|
/*
|
|
|
方法二:
|
|
|
vector<string> ans;
|
|
|
strSplit(itemTypeName,",",ans);*/
|
|
|
// -itemType ===> 判断是否是itemTypeName类型
|
|
|
bool isItemType = false;
|
|
|
if (strstr(itemTypeName, ",") != NULL)
|
|
|
{
|
|
|
int vectorValueCount = 0;
|
|
|
char** vectorValueChar = new char* [64];
|
|
|
strSplit(itemTypeName, ",", vectorValueChar, &vectorValueCount);
|
|
|
for (int i = 0; i < vectorValueCount; i++)
|
|
|
{
|
|
|
if (tc_strcmp(tagType, vectorValueChar[i]) == 0) {
|
|
|
isItemType = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else if (tc_strcmp(itemTypeName, tagType) == 0) {
|
|
|
isItemType = true;
|
|
|
}
|
|
|
MEM_free(tagType);
|
|
|
MEM_free(tagName);
|
|
|
if (isItemType) {
|
|
|
*logFile << "[" << timeStr << "] " << "判断是否是指定itemType对象:true" << std::endl;
|
|
|
}
|
|
|
else {
|
|
|
*logFile << "[" << timeStr << "] " << "判断是否是指定itemType对象:false" << std::endl;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
|
|
|
// -Relation ===> 获取当前对象的关系,并通过关系来查询它下面的对象,然后处理逻辑
|
|
|
if (strstr(relationName, ",") != NULL) {
|
|
|
*logFile << "[" << timeStr << "] " << "进入多值判断" << std::endl;
|
|
|
int vectorValueCount = 0;
|
|
|
char** vectorValueChar = new char* [64];
|
|
|
strSplit(relationName, ",", vectorValueChar, &vectorValueCount);
|
|
|
for (int i = 0; i < vectorValueCount; i++)
|
|
|
{
|
|
|
int count = 0;
|
|
|
// 通过handler中的值value获取其对象下的关系数组relationValues
|
|
|
tag_t* relationValues = NULLTAG;
|
|
|
ITKCALL(ifail = AOM_ask_value_tags(attachmentTag, vectorValueChar[i], &count, &relationValues));
|
|
|
|
|
|
*logFile << "[" << timeStr << "] " << "##########第" << i << "次遍历当前对象下与之关系的对象##########" << std::endl;
|
|
|
// 遍历当前关系的所有对象
|
|
|
if (count > 0) {
|
|
|
for (int j = 0; j < count; j++)
|
|
|
{
|
|
|
// 当前对象的类型 -itemChildType
|
|
|
char* tempTagType = NULL;
|
|
|
char* tempTagName = NULL;
|
|
|
char* tempTagString = NULL;
|
|
|
ITKCALL(ifail = AOM_ask_value_string(relationValues[j], "object_type", &tempTagType));
|
|
|
ITKCALL(ifail = AOM_ask_value_string(relationValues[j], "object_name", &tempTagName));
|
|
|
ITKCALL(ifail = AOM_ask_value_string(relationValues[j], "object_string", &tempTagString));
|
|
|
*logFile << "[" << timeStr << "] " << "======>当前对象:" << tempTagName << std::endl;
|
|
|
|
|
|
if (isExistValue(targetObjectStrings, tempTagString)) {
|
|
|
*logFile << "[" << timeStr << "] " << "!!!!!!!该对象已经存在目标流程下!!!!!!!" << tempTagName << std::endl;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
// -releaseStatus ==> 获取当前对象的发布状态
|
|
|
int isExist = 0;
|
|
|
bool isExistRelease = true;
|
|
|
// 当前对象的发布状态数量
|
|
|
int releaseNum = 0;
|
|
|
// 当前对象的发布状态数组
|
|
|
tag_t* innerTempReleaseValues = NULLTAG;
|
|
|
ITKCALL(ifail = AOM_ask_value_tags(relationValues[j], "release_status_list", &releaseNum, &innerTempReleaseValues));
|
|
|
*logFile << "[" << timeStr << "] " << "当前对象的release_status_list长度为:" << releaseNum << std::endl;
|
|
|
if (vectorReleaseStatusCount != 1 || !isExistValue(releaseStatusCheakVetor, "none")) {
|
|
|
|
|
|
// 遍历发布状态 ==> 判断是否符合要求
|
|
|
// -releaseStatus ===> 检查该对象的发布状态是否全部符合releaseStatus选项的值
|
|
|
if (releaseNum > 0) {
|
|
|
for (int k = 0; k < releaseNum; k++)
|
|
|
{
|
|
|
// 获取当前发布状态对象对应的属性
|
|
|
char* releaseProperty = NULL;
|
|
|
ITKCALL(ifail = AOM_ask_value_string(innerTempReleaseValues[k], "object_name", &releaseProperty));
|
|
|
*logFile << "[" << timeStr << "] " << "当前发布状态Release:" << releaseProperty << std::endl;
|
|
|
if (isExistValue(releaseStatusCheakVetor, releaseProperty))
|
|
|
{
|
|
|
isExist++;
|
|
|
}
|
|
|
MEM_free(releaseProperty);
|
|
|
}
|
|
|
if (isExist != vectorReleaseStatusCount) {
|
|
|
isExistRelease = false;
|
|
|
*logFile << "[" << timeStr << "] " << "=> 当前对象的发布状态不符合要求且首选项获取的发布数量为:" << vectorReleaseStatusCount << std::endl;
|
|
|
continue;
|
|
|
}
|
|
|
else {
|
|
|
*logFile << "[" << timeStr << "] " << "=> 当前对象的发布状态符合要求且首选项获取的发布数量为:" << vectorReleaseStatusCount << std::endl;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else if (vectorReleaseStatusCount == 1 && isExistValue(releaseStatusCheakVetor, "none") && releaseNum > 0) {
|
|
|
isExistRelease = false;
|
|
|
*logFile << "[" << timeStr << "] " << "=> 当前首选项为none,不提取,该对象发布状态数量为:" << vectorReleaseStatusCount << std::endl;
|
|
|
}
|
|
|
|
|
|
|
|
|
//-itemChildRev ===> 判断版本号不为空
|
|
|
bool isExistRev = false;
|
|
|
if (tc_strcmp(itemChildRevValue, "") != 0) {
|
|
|
// 获取对象版本号
|
|
|
char* tempItemId = NULL;
|
|
|
ITKCALL(ifail = AOM_ask_value_string(relationValues[j], "item_revision_id", &tempItemId));
|
|
|
if (tc_strcmp(itemChildRevValue, tempItemId) == 0) {
|
|
|
isExistRev = true;
|
|
|
*logFile << "[" << timeStr << "] " << "=> 当前对象的版本符合001要求" << std::endl;
|
|
|
}
|
|
|
else {
|
|
|
continue;
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
isExistRev = true;
|
|
|
}
|
|
|
|
|
|
// 判断 -itemChildType ===> 是否在给定的版本类型之中
|
|
|
if (isExistValue(itemchildTypeCheakVetor, tempTagType) && isExistRelease && isExistRev) {
|
|
|
*logFile << "[" << timeStr << "] " << "=> 当前对象在给定的版本类型之中" << std::endl;
|
|
|
int target = EPM_target_attachment;
|
|
|
// 提取到目标对象
|
|
|
ITKCALL(ifail = EPM_add_attachments(rootTask, 1, &relationValues[j], &target));
|
|
|
}
|
|
|
MEM_free(tempTagType);
|
|
|
MEM_free(innerTempReleaseValues);
|
|
|
}
|
|
|
}
|
|
|
MEM_free(relationValues);
|
|
|
}
|
|
|
}
|
|
|
else if (isItemType && strstr(relationName, ",") == NULL) {
|
|
|
*logFile << "[" << timeStr << "] " << "进入单值判断" << std::endl;
|
|
|
int count = 0;
|
|
|
// 通过handler中的值value获取其对象下的关系数组relationValues
|
|
|
tag_t* relationValues = NULLTAG;
|
|
|
ITKCALL(ifail = AOM_ask_value_tags(attachmentTag, relationName, &count, &relationValues));
|
|
|
|
|
|
*logFile << "[" << timeStr << "] " << "##########第" << i << "次遍历当前对象下与之关系的对象##########" << std::endl;
|
|
|
// 遍历当前关系的所有对象
|
|
|
if (count > 0) {
|
|
|
for (int j = 0; j < count; j++)
|
|
|
{
|
|
|
// 当前对象的类型 -itemChildType
|
|
|
char* tempTagType = NULL;
|
|
|
char* tempTagName = NULL;
|
|
|
char* tempTagString = NULL;
|
|
|
ITKCALL(ifail = AOM_ask_value_string(relationValues[j], "object_type", &tempTagType));
|
|
|
ITKCALL(ifail = AOM_ask_value_string(relationValues[j], "object_name", &tempTagName));
|
|
|
ITKCALL(ifail = AOM_ask_value_string(relationValues[j], "object_string", &tempTagString));
|
|
|
*logFile << "[" << timeStr << "] " << "======> ********当前遍历的对象的名字:" << tempTagString << std::endl;
|
|
|
|
|
|
if (isExistValue(targetObjectStrings, tempTagString)) {
|
|
|
*logFile << "[" << timeStr << "] " << "!!!!!!!该对象已经存在目标流程下!!!!!!!" << tempTagName << std::endl;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
// -releaseStatus ==> 获取当前对象的发布状态
|
|
|
int isExist = 0;
|
|
|
bool isExistRelease = true;
|
|
|
int releaseNum = 0;
|
|
|
tag_t* innerTempReleaseValues = NULLTAG;
|
|
|
ITKCALL(ifail = AOM_ask_value_tags(relationValues[j], "release_status_list", &releaseNum, &innerTempReleaseValues));
|
|
|
*logFile << "[" << timeStr << "] " << "当前对象的release_status_list长度为:" << releaseNum << std::endl;
|
|
|
if (vectorReleaseStatusCount != 1 || !isExistValue(releaseStatusCheakVetor, "none")) {
|
|
|
|
|
|
// 遍历发布状态 ==> 判断是否符合要求
|
|
|
// -releaseStatus ===> 检查该对象的发布状态是否全部符合releaseStatus选项的值
|
|
|
if (releaseNum > 0) {
|
|
|
for (int k = 0; k < releaseNum; k++)
|
|
|
{
|
|
|
// 获取当前发布状态对象对应的属性
|
|
|
char* releaseProperty = NULL;
|
|
|
ITKCALL(ifail = AOM_ask_value_string(innerTempReleaseValues[k], "object_name", &releaseProperty));
|
|
|
*logFile << "[" << timeStr << "] " << "当前发布状态Release:" << releaseProperty << std::endl;
|
|
|
if (isExistValue(releaseStatusCheakVetor, releaseProperty))
|
|
|
{
|
|
|
isExist++;
|
|
|
}
|
|
|
MEM_free(releaseProperty);
|
|
|
}
|
|
|
if (isExist != vectorReleaseStatusCount) {
|
|
|
isExistRelease = false;
|
|
|
*logFile << "[" << timeStr << "] " << "=> 当前对象的发布状态不符合要求且首选项获取的发布数量为:" << vectorReleaseStatusCount << std::endl;
|
|
|
}
|
|
|
else {
|
|
|
*logFile << "[" << timeStr << "] " << "=> 当前对象的发布状态符合要求且首选项获取的发布数量为:" << vectorReleaseStatusCount << std::endl;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else if (vectorReleaseStatusCount == 1 && isExistValue(releaseStatusCheakVetor, "none") && releaseNum > 0) {
|
|
|
isExistRelease = false;
|
|
|
*logFile << "[" << timeStr << "] " << "=> 当前首选项为none,不提取,该对象发布状态数量为:" << vectorReleaseStatusCount << std::endl;
|
|
|
}
|
|
|
|
|
|
|
|
|
//-itemChildRev ===> 判断版本号不为空
|
|
|
bool isExistRev = false;
|
|
|
if (tc_strcmp(itemChildRevValue, "") != 0) {
|
|
|
// 获取对象版本号
|
|
|
char* tempItemId = NULL;
|
|
|
ITKCALL(ifail = AOM_ask_value_string(relationValues[j], "item_revision_id", &tempItemId));
|
|
|
if (tc_strcmp(itemChildRevValue, tempItemId) == 0) {
|
|
|
isExistRev = true;
|
|
|
*logFile << "[" << timeStr << "] " << "=> 当前对象的版本符合001要求" << std::endl;
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
isExistRev = true;
|
|
|
}
|
|
|
|
|
|
// 判断 -itemChildType ===> 是否在给定的版本类型之中
|
|
|
if (isExistValue(itemchildTypeCheakVetor, tempTagType) && isExistRelease && isExistRev) {
|
|
|
*logFile << "[" << timeStr << "] " << "=> 当前对象在给定的版本类型之中" << std::endl;
|
|
|
int target = EPM_target_attachment;
|
|
|
// 提取到目标对象
|
|
|
ITKCALL(ifail = EPM_add_attachments(rootTask, 1, &relationValues[j], &target));
|
|
|
}
|
|
|
MEM_free(tempTagType);
|
|
|
MEM_free(innerTempReleaseValues);
|
|
|
}
|
|
|
}
|
|
|
MEM_free(relationValues);
|
|
|
}
|
|
|
}
|
|
|
MEM_free(attachmentTags);
|
|
|
return ifail;
|
|
|
} |