PSI sics-cvs-psi-2008-10-02
This commit is contained in:
247
napi4.c
247
napi4.c
@@ -3,7 +3,7 @@
|
||||
|
||||
Application Program Interface (HDF4) Routines
|
||||
|
||||
Copyright (C) 1997-2002 Mark Koennecke, Przemek Klosowski
|
||||
Copyright (C) 1997-2006 Mark Koennecke, Przemek Klosowski
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
@@ -64,13 +64,50 @@ extern void *NXpData;
|
||||
assert(pRes->iNXID == NXSIGNATURE);
|
||||
return pRes;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static int findNapiClass(pNexusFile pFile, int groupRef, NXname nxclass)
|
||||
{
|
||||
NXname classText, linkClass;
|
||||
int32 tags[2], attID, linkID, groupID;
|
||||
|
||||
groupID = Vattach(pFile->iVID,groupRef,"r");
|
||||
Vgetclass(groupID, classText);
|
||||
if(strcmp(classText,"NAPIlink") != 0)
|
||||
{
|
||||
/* normal group */
|
||||
strcpy(nxclass,classText);
|
||||
Vdetach(groupID);
|
||||
return groupRef;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* code for linked renamed groups */
|
||||
attID = Vfindattr(groupID,"NAPIlink");
|
||||
if(attID >= 0)
|
||||
{
|
||||
Vgetattr(groupID,attID, tags);
|
||||
linkID = Vattach(pFile->iVID,tags[1],"r");
|
||||
Vgetclass(linkID, linkClass);
|
||||
Vdetach(groupID);
|
||||
Vdetach(linkID);
|
||||
strcpy(nxclass,linkClass);
|
||||
return tags[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* this allows for finding the NAPIlink group in NXmakenamedlink */
|
||||
strcpy(nxclass,classText);
|
||||
Vdetach(groupID);
|
||||
return groupRef;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int32 NXIFindVgroup (pNexusFile pFile, CONSTCHAR *name, CONSTCHAR *nxclass)
|
||||
{
|
||||
int32 iNew, iRef, iTag;
|
||||
int iN, i;
|
||||
int iN, i, status;
|
||||
int32 *pArray = NULL;
|
||||
NXname pText;
|
||||
|
||||
@@ -93,17 +130,16 @@ extern void *NXpData;
|
||||
for (i = 0; i < iN; i++) {
|
||||
iNew = Vattach (pFile->iVID, pArray[i], "r");
|
||||
Vgetname (iNew, pText);
|
||||
Vdetach(iNew);
|
||||
if (strcmp (pText, name) == 0) {
|
||||
Vgetclass (iNew, pText);
|
||||
pArray[i] = findNapiClass(pFile,pArray[i],pText);
|
||||
if (strcmp (pText, nxclass) == 0) {
|
||||
/* found ! */
|
||||
Vdetach (iNew);
|
||||
iNew = pArray[i];
|
||||
free (pArray);
|
||||
return iNew;
|
||||
}
|
||||
}
|
||||
Vdetach (iNew);
|
||||
}
|
||||
/* nothing found */
|
||||
free (pArray);
|
||||
@@ -115,15 +151,13 @@ extern void *NXpData;
|
||||
if (iTag == DFTAG_VG) {
|
||||
iNew = Vattach (pFile->iVID, iRef, "r");
|
||||
Vgetname (iNew, pText);
|
||||
Vdetach(iNew);
|
||||
if (strcmp (pText, name) == 0) {
|
||||
Vgetclass (iNew, pText);
|
||||
iRef = findNapiClass(pFile,iRef, pText);
|
||||
if (strcmp (pText, nxclass) == 0) {
|
||||
/* found ! */
|
||||
Vdetach (iNew);
|
||||
return iRef;
|
||||
}
|
||||
}
|
||||
Vdetach (iNew);
|
||||
}
|
||||
} /* end for */
|
||||
} /* end else */
|
||||
@@ -264,8 +298,15 @@ extern void *NXpData;
|
||||
if (pFile->iCurrentSDS != 0) { /* SDS level */
|
||||
iRet = SDgetinfo (pFile->iCurrentSDS, pNam, &iRank, iDim, &iType,
|
||||
&iAtt);
|
||||
} else { /* global level */
|
||||
iRet = SDfileinfo (pFile->iSID, &iData, &iAtt);
|
||||
} else {
|
||||
if(pFile->iCurrentVG == 0){
|
||||
/* global level */
|
||||
iRet = SDfileinfo (pFile->iSID, &iData, &iAtt);
|
||||
} else {
|
||||
/* group attribute */
|
||||
iRet = Vnattrs(pFile->iCurrentVG);
|
||||
iAtt = iRet;
|
||||
}
|
||||
}
|
||||
if (iRet < 0) {
|
||||
NXIReportError (NXpData, "ERROR: HDF cannot read attribute numbers");
|
||||
@@ -325,7 +366,7 @@ extern void *NXpData;
|
||||
{
|
||||
pNexusFile pNew = NULL;
|
||||
char pBuffer[512];
|
||||
char *time_puffer;
|
||||
char *time_puffer = NULL;
|
||||
char HDF_VERSION[64];
|
||||
uint32 lmajor, lminor, lrelease;
|
||||
int32 am1=0;
|
||||
@@ -350,8 +391,6 @@ extern void *NXpData;
|
||||
}
|
||||
memset (pNew, 0, sizeof (NexusFile));
|
||||
|
||||
time_puffer = NXIformatNeXusTime();
|
||||
|
||||
#if WRITE_OLD_IDENT /* not used at moment */
|
||||
/*
|
||||
* write something that can be used by OLE
|
||||
@@ -402,6 +441,8 @@ extern void *NXpData;
|
||||
return NX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
time_puffer = NXIformatNeXusTime();
|
||||
if (am == NXACC_CREATE || am == NXACC_CREATE4) {
|
||||
if (SDsetattr(pNew->iSID, "file_name", DFNT_CHAR8, strlen(filename), (char*)filename) < 0) {
|
||||
NXIReportError (NXpData, "ERROR: HDF failed to store file_name attribute ");
|
||||
@@ -415,9 +456,11 @@ extern void *NXpData;
|
||||
free(time_puffer);
|
||||
return NX_ERROR;
|
||||
}
|
||||
free(time_puffer);
|
||||
}
|
||||
}
|
||||
if (time_puffer != NULL) {
|
||||
free(time_puffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise we try to create the file two times which makes HDF
|
||||
@@ -534,8 +577,6 @@ extern void *NXpData;
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
NXstatus NX4opengroup (NXhandle fid, CONSTCHAR *name, CONSTCHAR *nxclass)
|
||||
{
|
||||
pNexusFile pFile;
|
||||
@@ -566,7 +607,6 @@ extern void *NXpData;
|
||||
NXIKillDir (pFile);
|
||||
return NX_OK;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
|
||||
|
||||
@@ -661,6 +701,11 @@ extern void *NXpData;
|
||||
{
|
||||
type=DFNT_FLOAT64;
|
||||
}
|
||||
else
|
||||
{
|
||||
NXIReportError (NXpData, "ERROR: invalid type in NX4makedata");
|
||||
return NX_ERROR;
|
||||
}
|
||||
|
||||
if (rank <= 0) {
|
||||
sprintf (pBuffer, "ERROR: invalid rank specified for SDS %s",
|
||||
@@ -787,6 +832,11 @@ extern void *NXpData;
|
||||
{
|
||||
type=DFNT_FLOAT64;
|
||||
}
|
||||
else
|
||||
{
|
||||
NXIReportError (NXpData, "ERROR: invalid datatype in NX4compmakedata");
|
||||
return NX_ERROR;
|
||||
}
|
||||
|
||||
if (rank <= 0) {
|
||||
sprintf (pBuffer, "ERROR: invalid rank specified for SDS %s",
|
||||
@@ -877,7 +927,7 @@ extern void *NXpData;
|
||||
else
|
||||
{
|
||||
NXIReportError (NXpData, "Unknown compression method!");
|
||||
NX_ERROR;
|
||||
return NX_ERROR;
|
||||
}
|
||||
/* link into Vgroup, if in one */
|
||||
if (pFile->iCurrentVG != 0) {
|
||||
@@ -900,7 +950,7 @@ extern void *NXpData;
|
||||
pNexusFile pFile;
|
||||
int32 iRank, iAtt, iType, iRet;
|
||||
int32 iSize[MAX_VAR_DIMS];
|
||||
int compress_typei;
|
||||
int compress_typei = COMP_CODE_NONE;
|
||||
NXname pBuffer;
|
||||
char pError[512];
|
||||
comp_info compstruct;
|
||||
@@ -962,7 +1012,7 @@ extern void *NXpData;
|
||||
NXstatus NX4opendata (NXhandle fid, CONSTCHAR *name)
|
||||
{
|
||||
pNexusFile pFile;
|
||||
int32 iNew;
|
||||
int32 iNew, attID, tags[2];
|
||||
char pBuffer[256];
|
||||
int iRet;
|
||||
|
||||
@@ -986,10 +1036,19 @@ extern void *NXpData;
|
||||
}
|
||||
/* clear pending attribute directories first */
|
||||
NXIKillAttDir (pFile);
|
||||
|
||||
/* open the SDS */
|
||||
|
||||
/* open the SDS, thereby watching for linked SDS under a different name */
|
||||
iNew = SDreftoindex (pFile->iSID, iNew);
|
||||
pFile->iCurrentSDS = SDselect (pFile->iSID, iNew);
|
||||
attID = SDfindattr(pFile->iCurrentSDS,"NAPIlink");
|
||||
if(attID >= 0)
|
||||
{
|
||||
SDreadattr(pFile->iCurrentSDS,attID, tags);
|
||||
SDendaccess(pFile->iCurrentSDS);
|
||||
iNew = SDreftoindex (pFile->iSID, tags[1]);
|
||||
pFile->iCurrentSDS = SDselect (pFile->iSID, iNew);
|
||||
}
|
||||
|
||||
if (pFile->iCurrentSDS < 0) {
|
||||
NXIReportError (NXpData, "ERROR: HDF error opening SDS");
|
||||
pFile->iCurrentSDS = 0;
|
||||
@@ -1105,15 +1164,25 @@ extern void *NXpData;
|
||||
{
|
||||
type=DFNT_FLOAT64;
|
||||
}
|
||||
else
|
||||
{
|
||||
NXIReportError (NXpData, "ERROR: Invalid data type for HDF attribute");
|
||||
return NX_ERROR;
|
||||
}
|
||||
if (pFile->iCurrentSDS != 0) {
|
||||
/* SDS attribute */
|
||||
iRet = SDsetattr (pFile->iCurrentSDS, (char*)name, (int32)type,
|
||||
(int32)datalen, data);
|
||||
} else {
|
||||
/* global attribute */
|
||||
iRet = SDsetattr (pFile->iSID, (char*)name, (int32)type,
|
||||
(int32)datalen, data);
|
||||
|
||||
if(pFile->iCurrentVG == 0){
|
||||
/* global attribute */
|
||||
iRet = SDsetattr (pFile->iSID, (char*)name, (int32)type,
|
||||
(int32)datalen, data);
|
||||
} else {
|
||||
/* group attribute */
|
||||
iRet = Vsetattr(pFile->iCurrentVG, (char *)name, (int32) type,
|
||||
(int32)datalen,data);
|
||||
}
|
||||
}
|
||||
iType = type;
|
||||
if (iRet < 0) {
|
||||
@@ -1218,15 +1287,66 @@ extern void *NXpData;
|
||||
return NX_ERROR;
|
||||
}
|
||||
Vaddtagref(pFile->iCurrentVG, sLink->iTag, sLink->iRef);
|
||||
length = strlen(sLink->targetPath);
|
||||
if(sLink->iTag == DFTAG_SDG || sLink->iTag == DFTAG_NDG ||
|
||||
sLink->iTag == DFTAG_SDS)
|
||||
{
|
||||
dataID = SDreftoindex(pFile->iSID,sLink->iRef);
|
||||
dataID = SDselect(pFile->iSID,dataID);
|
||||
length = strlen(sLink->targetPath);
|
||||
SDsetattr(dataID,name,type,length,sLink->targetPath);
|
||||
SDendaccess(dataID);
|
||||
}
|
||||
else
|
||||
{
|
||||
dataID = Vattach(pFile->iVID,sLink->iRef,"w");
|
||||
Vsetattr(dataID, (char *)name, type, (int32) length, sLink->targetPath);
|
||||
Vdetach(dataID);
|
||||
}
|
||||
return NX_OK;
|
||||
}
|
||||
/* ------------------------------------------------------------------- */
|
||||
|
||||
|
||||
NXstatus NX4makenamedlink (NXhandle fid, CONSTCHAR* newname, NXlink* sLink)
|
||||
{
|
||||
pNexusFile pFile;
|
||||
int32 iVG, iRet, dataID, type = DFNT_CHAR8, length, dataType = NX_CHAR,
|
||||
rank = 1, attType = NX_INT32;
|
||||
int iDim[1];
|
||||
char name[] = "target";
|
||||
int tags[2];
|
||||
|
||||
pFile = NXIassert (fid);
|
||||
|
||||
if (pFile->iCurrentVG == 0) { /* root level, can not link here */
|
||||
return NX_ERROR;
|
||||
}
|
||||
|
||||
tags[0] = sLink->iTag;
|
||||
tags[1] = sLink->iRef;
|
||||
|
||||
length = strlen(sLink->targetPath);
|
||||
if(sLink->iTag == DFTAG_SDG || sLink->iTag == DFTAG_NDG ||
|
||||
sLink->iTag == DFTAG_SDS)
|
||||
{
|
||||
iDim[0] = 1;
|
||||
NX4makedata(fid,newname, dataType,rank,iDim);
|
||||
NX4opendata(fid,newname);
|
||||
NX4putattr(fid,"NAPIlink",tags, 2, attType);
|
||||
NX4closedata(fid);
|
||||
dataID = SDreftoindex(pFile->iSID,sLink->iRef);
|
||||
dataID = SDselect(pFile->iSID,dataID);
|
||||
SDsetattr(dataID,name,type,length,sLink->targetPath);
|
||||
SDendaccess(dataID);
|
||||
} else {
|
||||
NX4makegroup(fid,newname,"NAPIlink");
|
||||
NX4opengroup(fid,newname,"NAPIlink");
|
||||
NX4putattr(fid,"NAPIlink",tags, 2, attType);
|
||||
NX4closegroup(fid);
|
||||
dataID = Vattach(pFile->iVID,sLink->iRef,"w");
|
||||
Vsetattr(dataID, (char *)name, type, (int32) length, sLink->targetPath);
|
||||
Vdetach(dataID);
|
||||
}
|
||||
return NX_OK;
|
||||
}
|
||||
|
||||
@@ -1236,8 +1356,7 @@ extern void *NXpData;
|
||||
{
|
||||
pNexusFile pFile;
|
||||
pFile = NXIassert (fid);
|
||||
printf("HDF4 link: iTag = %ld, iRef = %ld, target=\"%s\"\n",
|
||||
sLink->iTag, sLink->iRef, sLink->targetPath);
|
||||
printf("HDF4 link: iTag = %ld, iRef = %ld, target=\"%s\"\n", sLink->iTag, sLink->iRef, sLink->targetPath);
|
||||
return NX_OK;
|
||||
}
|
||||
|
||||
@@ -1272,6 +1391,10 @@ extern void *NXpData;
|
||||
ac = NXACC_READ;
|
||||
}else if(pFile->iAccess[0] == 'w') {
|
||||
ac = NXACC_RDWR;
|
||||
} else {
|
||||
NXIReportError (NXpData,
|
||||
"ERROR: NX4flush failed to determine file access mode");
|
||||
return NX_ERROR;
|
||||
}
|
||||
pCopy = (char *)malloc((strlen(pFileName)+10)*sizeof(char));
|
||||
if(!pCopy) {
|
||||
@@ -1345,11 +1468,13 @@ extern void *NXpData;
|
||||
return NX_EOD;
|
||||
}
|
||||
}
|
||||
|
||||
/* Next case: end of directory */
|
||||
if (iCurDir >= pFile->iStack[pFile->iStackPtr].iNDir) {
|
||||
NXIKillDir (pFile);
|
||||
return NX_EOD;
|
||||
}
|
||||
|
||||
/* Next case: we have data! supply it and increment counter */
|
||||
if (pFile->iCurrentVG == 0) { /* root level */
|
||||
iTemp = Vattach (pFile->iVID,
|
||||
@@ -1359,10 +1484,10 @@ extern void *NXpData;
|
||||
return NX_ERROR;
|
||||
}
|
||||
Vgetname (iTemp, name);
|
||||
Vgetclass (iTemp, nxclass);
|
||||
Vdetach (iTemp);
|
||||
findNapiClass(pFile, pFile->iStack[pFile->iStackPtr].iRefDir[iCurDir], nxclass);
|
||||
*datatype = DFTAG_VG;
|
||||
pFile->iStack[pFile->iStackPtr].iCurDir++;
|
||||
Vdetach (iTemp);
|
||||
return NX_OK;
|
||||
} else { /* in Vgroup */
|
||||
if (pFile->iStack[iStackPtr].iTagDir[iCurDir] == DFTAG_VG) {/* Vgroup */
|
||||
@@ -1373,7 +1498,8 @@ extern void *NXpData;
|
||||
return NX_ERROR;
|
||||
}
|
||||
Vgetname (iTemp, name);
|
||||
Vgetclass (iTemp, nxclass);
|
||||
Vdetach(iTemp);
|
||||
findNapiClass(pFile, pFile->iStack[pFile->iStackPtr].iRefDir[iCurDir], nxclass);
|
||||
*datatype = DFTAG_VG;
|
||||
pFile->iStack[pFile->iStackPtr].iCurDir++;
|
||||
Vdetach (iTemp);
|
||||
@@ -1511,7 +1637,7 @@ extern void *NXpData;
|
||||
{
|
||||
pNexusFile pFile;
|
||||
int iRet;
|
||||
int32 iPType, iCount;
|
||||
int32 iPType, iCount, count;
|
||||
|
||||
pFile = NXIassert (fileid);
|
||||
|
||||
@@ -1528,9 +1654,16 @@ extern void *NXpData;
|
||||
return NX_EOD;
|
||||
}
|
||||
/* well, there must be data to copy */
|
||||
if (pFile->iCurrentSDS == 0) { /* global attribute */
|
||||
iRet = SDattrinfo (pFile->iSID, pFile->iAtt.iCurDir,
|
||||
pName, &iPType, &iCount);
|
||||
if (pFile->iCurrentSDS == 0) {
|
||||
if(pFile->iCurrentVG == 0) {
|
||||
/* global attribute */
|
||||
iRet = SDattrinfo (pFile->iSID, pFile->iAtt.iCurDir,
|
||||
pName, &iPType, &iCount);
|
||||
}else {
|
||||
/* group attribute */
|
||||
iRet = Vattrinfo(pFile->iCurrentVG, pFile->iAtt.iCurDir,
|
||||
pName, &iPType, &iCount, &count);
|
||||
}
|
||||
} else {
|
||||
iRet = SDattrinfo (pFile->iCurrentSDS, pFile->iAtt.iCurDir,
|
||||
pName, &iPType, &iCount);
|
||||
@@ -1552,7 +1685,7 @@ extern void *NXpData;
|
||||
NXstatus NX4getattr (NXhandle fid, char *name, void *data, int* datalen, int* iType)
|
||||
{
|
||||
pNexusFile pFile;
|
||||
int32 iNew, iType32;
|
||||
int32 iNew, iType32, count;
|
||||
void *pData = NULL;
|
||||
int32 iLen, iRet;
|
||||
int type;
|
||||
@@ -1604,8 +1737,13 @@ extern void *NXpData;
|
||||
/* SDS attribute */
|
||||
iNew = SDfindattr (pFile->iCurrentSDS, name);
|
||||
} else {
|
||||
/* global attribute */
|
||||
iNew = SDfindattr (pFile->iSID, name);
|
||||
if(pFile->iCurrentVG == 0){
|
||||
/* global attribute */
|
||||
iNew = SDfindattr (pFile->iSID, name);
|
||||
} else {
|
||||
/* group attribute */
|
||||
iNew = Vfindattr(pFile->iCurrentVG, name);
|
||||
}
|
||||
}
|
||||
if (iNew < 0) {
|
||||
sprintf (pBuffer, "ERROR: attribute %s not found", name);
|
||||
@@ -1617,7 +1755,12 @@ extern void *NXpData;
|
||||
if (pFile->iCurrentSDS != 0) {
|
||||
iRet = SDattrinfo (pFile->iCurrentSDS, iNew, pNam, &iType32, &iLen);
|
||||
} else {
|
||||
iRet = SDattrinfo (pFile->iSID, iNew, pNam, &iType32, &iLen);
|
||||
if(pFile->iCurrentVG == 0){
|
||||
iRet = SDattrinfo (pFile->iSID, iNew, pNam, &iType32, &iLen);
|
||||
} else {
|
||||
iRet = Vattrinfo(pFile->iCurrentVG,iNew,pNam,&iType32,&count,
|
||||
&iLen);
|
||||
}
|
||||
}
|
||||
if (iRet < 0) {
|
||||
sprintf (pBuffer, "ERROR: HDF could not read attribute info");
|
||||
@@ -1637,7 +1780,11 @@ extern void *NXpData;
|
||||
if (pFile->iCurrentSDS != 0) {
|
||||
iRet = SDreadattr (pFile->iCurrentSDS, iNew, pData);
|
||||
} else {
|
||||
iRet = SDreadattr (pFile->iSID, iNew, pData);
|
||||
if(pFile->iCurrentVG == 0){
|
||||
iRet = SDreadattr (pFile->iSID, iNew, pData);
|
||||
} else {
|
||||
iRet = Vgetattr(pFile->iCurrentVG, iNew, pData);
|
||||
}
|
||||
}
|
||||
if (iRet < 0) {
|
||||
sprintf (pBuffer, "ERROR: HDF could not read attribute data");
|
||||
@@ -1646,7 +1793,8 @@ extern void *NXpData;
|
||||
}
|
||||
/* copy data to caller */
|
||||
memset (data, 0, *datalen);
|
||||
if ((*datalen <= iLen) && (*iType == DFNT_UINT8 || *iType == DFNT_CHAR8 || *iType == DFNT_UCHAR8)) {
|
||||
if ((*datalen <= iLen) &&
|
||||
(*iType == DFNT_UINT8 || *iType == DFNT_CHAR8 || *iType == DFNT_UCHAR8)) {
|
||||
iLen = *datalen - 1;
|
||||
}
|
||||
memcpy (data, pData, iLen);
|
||||
@@ -1670,8 +1818,14 @@ extern void *NXpData;
|
||||
if (pFile->iCurrentSDS != 0) { /* SDS level */
|
||||
iRet = SDgetinfo (pFile->iCurrentSDS, pNam, &iRank, iDim, &iType,
|
||||
&iAtt);
|
||||
} else { /* global level */
|
||||
iRet = SDfileinfo (pFile->iSID, &iData, &iAtt);
|
||||
} else {
|
||||
if(pFile->iCurrentVG == 0){
|
||||
/* global level */
|
||||
iRet = SDfileinfo (pFile->iSID, &iData, &iAtt);
|
||||
} else {
|
||||
iRet = Vnattrs(pFile->iCurrentVG);
|
||||
iAtt = iRet;
|
||||
}
|
||||
}
|
||||
if (iRet < 0) {
|
||||
NXIReportError (NXpData, "NX_ERROR: HDF cannot read attribute numbers");
|
||||
@@ -1793,6 +1947,7 @@ void NX4assignFunctions(pNexusFunction fHandle)
|
||||
fHandle->nxputslab=NX4putslab;
|
||||
fHandle->nxgetdataID=NX4getdataID;
|
||||
fHandle->nxmakelink=NX4makelink;
|
||||
fHandle->nxmakenamedlink=NX4makenamedlink;
|
||||
fHandle->nxgetdata=NX4getdata;
|
||||
fHandle->nxgetinfo=NX4getinfo;
|
||||
fHandle->nxgetnextentry=NX4getnextentry;
|
||||
|
||||
Reference in New Issue
Block a user