@@ -204,7 +204,7 @@ unsigned ZSTD_isFrame(const void* buffer, size_t size)
|
|||||||
/** ZSTD_frameHeaderSize() :
|
/** ZSTD_frameHeaderSize() :
|
||||||
* srcSize must be >= ZSTD_frameHeaderSize_prefix.
|
* srcSize must be >= ZSTD_frameHeaderSize_prefix.
|
||||||
* @return : size of the Frame Header */
|
* @return : size of the Frame Header */
|
||||||
static size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize)
|
size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize)
|
||||||
{
|
{
|
||||||
if (srcSize < ZSTD_frameHeaderSize_prefix) return ERROR(srcSize_wrong);
|
if (srcSize < ZSTD_frameHeaderSize_prefix) return ERROR(srcSize_wrong);
|
||||||
{ BYTE const fhd = ((const BYTE*)src)[4];
|
{ BYTE const fhd = ((const BYTE*)src)[4];
|
||||||
|
|||||||
@@ -461,6 +461,11 @@ ZSTDLIB_API unsigned long long ZSTD_getFrameContentSize(const void *src, size_t
|
|||||||
* however it does mean that all frame data must be present and valid. */
|
* however it does mean that all frame data must be present and valid. */
|
||||||
ZSTDLIB_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
|
ZSTDLIB_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
|
||||||
|
|
||||||
|
/*! ZSTD_frameHeaderSize() :
|
||||||
|
* `src` should point to the start of a ZSTD frame
|
||||||
|
* `srcSize` must be >= ZSTD_frameHeaderSize_prefix.
|
||||||
|
* @return : size of the Frame Header */
|
||||||
|
size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize);
|
||||||
|
|
||||||
/***************************************
|
/***************************************
|
||||||
* Context memory usage
|
* Context memory usage
|
||||||
|
|||||||
@@ -91,6 +91,7 @@
|
|||||||
* Macros
|
* Macros
|
||||||
***************************************/
|
***************************************/
|
||||||
#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
|
#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
|
||||||
|
#define DISPLAYOUT(...) fprintf(stdout, __VA_ARGS__)
|
||||||
#define DISPLAYLEVEL(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
|
#define DISPLAYLEVEL(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
|
||||||
static int g_displayLevel = 2; /* 0 : no display; 1: errors; 2 : + result + interaction + warnings; 3 : + progression; 4 : + information */
|
static int g_displayLevel = 2; /* 0 : no display; 1: errors; 2 : + result + interaction + warnings; 3 : + progression; 4 : + information */
|
||||||
void FIO_setNotificationLevel(unsigned level) { g_displayLevel=level; }
|
void FIO_setNotificationLevel(unsigned level) { g_displayLevel=level; }
|
||||||
@@ -862,6 +863,222 @@ int FIO_compressFilename(const char* dstFileName, const char* srcFileName,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int numActualFrames;
|
||||||
|
int numSkippableFrames;
|
||||||
|
unsigned long long decompressedSize;
|
||||||
|
int decompUnavailable;
|
||||||
|
unsigned long long compressedSize;
|
||||||
|
int usesCheck;
|
||||||
|
} fileInfo_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads information from file, stores in *info
|
||||||
|
* if successful, returns 0, returns 1 for frame analysis error, returns 2 for file not compressed with zstd
|
||||||
|
*/
|
||||||
|
static int getFileInfo(fileInfo_t* info, const char* inFileName){
|
||||||
|
int detectError = 0;
|
||||||
|
FILE* const srcFile = FIO_openSrcFile(inFileName);
|
||||||
|
if (srcFile == NULL) {
|
||||||
|
DISPLAY("Error: could not open source file %s\n", inFileName);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
info->compressedSize = (unsigned long long)UTIL_getFileSize(inFileName);
|
||||||
|
/* begin analyzing frame */
|
||||||
|
for ( ; ; ) {
|
||||||
|
BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
|
||||||
|
size_t const numBytesRead = fread(headerBuffer, 1, sizeof(headerBuffer), srcFile);
|
||||||
|
if (numBytesRead < ZSTD_frameHeaderSize_min) {
|
||||||
|
if (feof(srcFile) && numBytesRead == 0 && info->compressedSize > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (feof(srcFile)) {
|
||||||
|
DISPLAY("Error: reached end of file with incomplete frame\n");
|
||||||
|
detectError = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DISPLAY("Error: did not reach end of file but ran out of frames\n");
|
||||||
|
detectError = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
U32 const magicNumber = MEM_readLE32(headerBuffer);
|
||||||
|
if (magicNumber == ZSTD_MAGICNUMBER) {
|
||||||
|
U64 const frameContentSize = ZSTD_getFrameContentSize(headerBuffer, numBytesRead);
|
||||||
|
if (frameContentSize == ZSTD_CONTENTSIZE_ERROR || frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN) {
|
||||||
|
info->decompUnavailable = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
info->decompressedSize += frameContentSize;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
/* move to the end of the frame header */
|
||||||
|
size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead);
|
||||||
|
if (ZSTD_isError(headerSize)) {
|
||||||
|
DISPLAY("Error: could not determine frame header size\n");
|
||||||
|
detectError = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int const ret = fseek(srcFile, ((long)headerSize)-((long)numBytesRead), SEEK_CUR);
|
||||||
|
if (ret != 0) {
|
||||||
|
DISPLAY("Error: could not move to end of frame header\n");
|
||||||
|
detectError = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip the rest of the blocks in the frame */
|
||||||
|
{
|
||||||
|
int lastBlock = 0;
|
||||||
|
do {
|
||||||
|
BYTE blockHeaderBuffer[3];
|
||||||
|
U32 blockHeader;
|
||||||
|
int blockSize;
|
||||||
|
size_t const readBytes = fread(blockHeaderBuffer, 1, 3, srcFile);
|
||||||
|
if (readBytes != 3) {
|
||||||
|
DISPLAY("There was a problem reading the block header\n");
|
||||||
|
detectError = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
blockHeader = MEM_readLE24(blockHeaderBuffer);
|
||||||
|
lastBlock = blockHeader & 1;
|
||||||
|
blockSize = blockHeader >> 3;
|
||||||
|
{
|
||||||
|
int const ret = fseek(srcFile, blockSize, SEEK_CUR);
|
||||||
|
if (ret != 0) {
|
||||||
|
DISPLAY("Error: could not skip to end of block\n");
|
||||||
|
detectError = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (lastBlock != 1);
|
||||||
|
|
||||||
|
if (detectError) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
/* check if checksum is used */
|
||||||
|
BYTE const frameHeaderDescriptor = headerBuffer[4];
|
||||||
|
int const contentChecksumFlag = (frameHeaderDescriptor & (1 << 2)) >> 2;
|
||||||
|
if (contentChecksumFlag) {
|
||||||
|
int const ret = fseek(srcFile, 4, SEEK_CUR);
|
||||||
|
info->usesCheck = 1;
|
||||||
|
if (ret != 0) {
|
||||||
|
DISPLAY("Error: could not skip past checksum\n");
|
||||||
|
detectError = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info->numActualFrames++;
|
||||||
|
}
|
||||||
|
else if (magicNumber == ZSTD_MAGIC_SKIPPABLE_START) {
|
||||||
|
BYTE frameSizeBuffer[4];
|
||||||
|
size_t const readBytes = fread(frameSizeBuffer, 1, 4, srcFile);
|
||||||
|
if (readBytes != 4) {
|
||||||
|
DISPLAY("There was an error reading skippable frame size");
|
||||||
|
detectError = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
U32 const frameSize = MEM_readLE32(frameSizeBuffer);
|
||||||
|
int const ret = LONG_SEEK(srcFile, frameSize, SEEK_CUR);
|
||||||
|
if (ret != 0) {
|
||||||
|
DISPLAY("Error: could not find end of skippable frame\n");
|
||||||
|
detectError = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info->numSkippableFrames++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
detectError = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(srcFile);
|
||||||
|
return detectError;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void displayInfo(const char* inFileName, fileInfo_t* info, int displayLevel){
|
||||||
|
double const compressedSizeMB = (double)info->compressedSize/(1 MB);
|
||||||
|
double const decompressedSizeMB = (double)info->decompressedSize/(1 MB);
|
||||||
|
const char* const checkString = (info->usesCheck ? "XXH64" : "None");
|
||||||
|
if (displayLevel <= 2) {
|
||||||
|
if (!info->decompUnavailable) {
|
||||||
|
double const ratio = (info->decompressedSize == 0) ? 0.0 : compressedSizeMB/decompressedSizeMB;
|
||||||
|
DISPLAYOUT("Skippable Non-Skippable Compressed Uncompressed Ratio Check Filename\n");
|
||||||
|
DISPLAYOUT("%9d %13d %7.2f MB %9.2f MB %5.3f %5s %s\n",
|
||||||
|
info->numSkippableFrames, info->numActualFrames, compressedSizeMB, decompressedSizeMB,
|
||||||
|
ratio, checkString, inFileName);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DISPLAYOUT("Skippable Non-Skippable Compressed Check Filename\n");
|
||||||
|
DISPLAYOUT("%9d %13d %7.2f MB %5s %s\n",
|
||||||
|
info->numSkippableFrames, info->numActualFrames, compressedSizeMB, checkString, inFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames);
|
||||||
|
DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames);
|
||||||
|
DISPLAYOUT("Compressed Size: %.2f MB (%llu B)\n", compressedSizeMB, info->compressedSize);
|
||||||
|
if (!info->decompUnavailable) {
|
||||||
|
DISPLAYOUT("Decompressed Size: %.2f MB (%llu B)\n", decompressedSizeMB, info->decompressedSize);
|
||||||
|
DISPLAYOUT("Ratio: %.4f\n", compressedSizeMB/decompressedSizeMB);
|
||||||
|
}
|
||||||
|
DISPLAYOUT("Check: %s\n", checkString);
|
||||||
|
DISPLAYOUT("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int FIO_listFile(const char* inFileName, int displayLevel, unsigned fileNo, unsigned numFiles){
|
||||||
|
/* initialize info to avoid warnings */
|
||||||
|
fileInfo_t info;
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
DISPLAYOUT("%s (%u/%u):\n", inFileName, fileNo, numFiles);
|
||||||
|
{
|
||||||
|
int const error = getFileInfo(&info, inFileName);
|
||||||
|
if (error == 1) {
|
||||||
|
/* display error, but provide output */
|
||||||
|
DISPLAY("An error occurred with getting file info\n");
|
||||||
|
}
|
||||||
|
else if (error == 2) {
|
||||||
|
DISPLAYOUT("File %s not compressed with zstd\n", inFileName);
|
||||||
|
if (displayLevel > 2) {
|
||||||
|
DISPLAYOUT("\n");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
displayInfo(inFileName, &info, displayLevel);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int displayLevel){
|
||||||
|
if (numFiles == 0) {
|
||||||
|
DISPLAYOUT("No files given\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DISPLAYOUT("===========================================\n");
|
||||||
|
DISPLAYOUT("Printing information about compressed files\n");
|
||||||
|
DISPLAYOUT("===========================================\n");
|
||||||
|
DISPLAYOUT("Number of files listed: %u\n", numFiles);
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
unsigned u;
|
||||||
|
for (u=0; u<numFiles;u++) {
|
||||||
|
error |= FIO_listFile(filenameTable[u], displayLevel, u+1, numFiles);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFiles,
|
int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFiles,
|
||||||
const char* suffix,
|
const char* suffix,
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ int FIO_compressFilename (const char* outfilename, const char* infilename, const
|
|||||||
@return : 0 == ok; 1 == pb with src file. */
|
@return : 0 == ok; 1 == pb with src file. */
|
||||||
int FIO_decompressFilename (const char* outfilename, const char* infilename, const char* dictFileName);
|
int FIO_decompressFilename (const char* outfilename, const char* infilename, const char* dictFileName);
|
||||||
|
|
||||||
|
int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int displayLevel);
|
||||||
|
|
||||||
/*-*************************************
|
/*-*************************************
|
||||||
* Multiple File functions
|
* Multiple File functions
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ the last one takes effect.
|
|||||||
* `--train FILEs`:
|
* `--train FILEs`:
|
||||||
Use FILEs as a training set to create a dictionary.
|
Use FILEs as a training set to create a dictionary.
|
||||||
The training set should contain a lot of small files (> 100).
|
The training set should contain a lot of small files (> 100).
|
||||||
|
* `-l`, `--list`:
|
||||||
|
Display information related to a zstd compressed file, such as size, ratio, and checksum.
|
||||||
|
Some of these fields may not be available.
|
||||||
|
This command can be augmented with the `-v` modifier.
|
||||||
|
|
||||||
### Operation modifiers
|
### Operation modifiers
|
||||||
|
|
||||||
|
|||||||
+9
-3
@@ -117,6 +117,7 @@ static int usage_advanced(const char* programName)
|
|||||||
DISPLAY( " -v : verbose mode; specify multiple times to increase verbosity\n");
|
DISPLAY( " -v : verbose mode; specify multiple times to increase verbosity\n");
|
||||||
DISPLAY( " -q : suppress warnings; specify twice to suppress errors too\n");
|
DISPLAY( " -q : suppress warnings; specify twice to suppress errors too\n");
|
||||||
DISPLAY( " -c : force write to standard output, even if it is the console\n");
|
DISPLAY( " -c : force write to standard output, even if it is the console\n");
|
||||||
|
DISPLAY( " -l : print information about zstd compressed files.\n");
|
||||||
#ifndef ZSTD_NOCOMPRESS
|
#ifndef ZSTD_NOCOMPRESS
|
||||||
DISPLAY( "--ultra : enable levels beyond %i, up to %i (requires more memory)\n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel());
|
DISPLAY( "--ultra : enable levels beyond %i, up to %i (requires more memory)\n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel());
|
||||||
#ifdef ZSTD_MULTITHREAD
|
#ifdef ZSTD_MULTITHREAD
|
||||||
@@ -148,6 +149,7 @@ static int usage_advanced(const char* programName)
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
DISPLAY( " -M# : Set a memory usage limit for decompression \n");
|
DISPLAY( " -M# : Set a memory usage limit for decompression \n");
|
||||||
|
DISPLAY( "--list : list information about a zstd compressed file \n");
|
||||||
DISPLAY( "-- : All arguments after \"--\" are treated as files \n");
|
DISPLAY( "-- : All arguments after \"--\" are treated as files \n");
|
||||||
#ifndef ZSTD_NODICT
|
#ifndef ZSTD_NODICT
|
||||||
DISPLAY( "\n");
|
DISPLAY( "\n");
|
||||||
@@ -310,7 +312,7 @@ static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
typedef enum { zom_compress, zom_decompress, zom_test, zom_bench, zom_train } zstd_operation_mode;
|
typedef enum { zom_compress, zom_decompress, zom_test, zom_bench, zom_train, zom_list } zstd_operation_mode;
|
||||||
|
|
||||||
#define CLEAN_RETURN(i) { operationResult = (i); goto _end; }
|
#define CLEAN_RETURN(i) { operationResult = (i); goto _end; }
|
||||||
|
|
||||||
@@ -401,6 +403,7 @@ int main(int argCount, const char* argv[])
|
|||||||
if (argument[1]=='-') {
|
if (argument[1]=='-') {
|
||||||
/* long commands (--long-word) */
|
/* long commands (--long-word) */
|
||||||
if (!strcmp(argument, "--")) { nextArgumentsAreFiles=1; continue; } /* only file names allowed from now on */
|
if (!strcmp(argument, "--")) { nextArgumentsAreFiles=1; continue; } /* only file names allowed from now on */
|
||||||
|
if (!strcmp(argument, "--list")) { operation=zom_list; continue; }
|
||||||
if (!strcmp(argument, "--compress")) { operation=zom_compress; continue; }
|
if (!strcmp(argument, "--compress")) { operation=zom_compress; continue; }
|
||||||
if (!strcmp(argument, "--decompress")) { operation=zom_decompress; continue; }
|
if (!strcmp(argument, "--decompress")) { operation=zom_decompress; continue; }
|
||||||
if (!strcmp(argument, "--uncompress")) { operation=zom_decompress; continue; }
|
if (!strcmp(argument, "--uncompress")) { operation=zom_decompress; continue; }
|
||||||
@@ -531,7 +534,7 @@ int main(int argCount, const char* argv[])
|
|||||||
argument++;
|
argument++;
|
||||||
memLimit = readU32FromChar(&argument);
|
memLimit = readU32FromChar(&argument);
|
||||||
break;
|
break;
|
||||||
|
case 'l': operation=zom_list; argument++; break;
|
||||||
#ifdef UTIL_HAS_CREATEFILELIST
|
#ifdef UTIL_HAS_CREATEFILELIST
|
||||||
/* recursive */
|
/* recursive */
|
||||||
case 'r': recursive=1; argument++; break;
|
case 'r': recursive=1; argument++; break;
|
||||||
@@ -672,7 +675,10 @@ int main(int argCount, const char* argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (operation == zom_list) {
|
||||||
|
int const ret = FIO_listMultipleFiles(filenameIdx, filenameTable, g_displayLevel);
|
||||||
|
CLEAN_RETURN(ret);
|
||||||
|
}
|
||||||
/* Check if benchmark is selected */
|
/* Check if benchmark is selected */
|
||||||
if (operation==zom_bench) {
|
if (operation==zom_bench) {
|
||||||
#ifndef ZSTD_NOBENCH
|
#ifndef ZSTD_NOBENCH
|
||||||
|
|||||||
@@ -537,6 +537,51 @@ fi
|
|||||||
|
|
||||||
rm tmp*
|
rm tmp*
|
||||||
|
|
||||||
|
$ECHO "\n**** zstd --list/-l single frame tests ****"
|
||||||
|
./datagen > tmp1
|
||||||
|
./datagen > tmp2
|
||||||
|
./datagen > tmp3
|
||||||
|
./datagen > tmp4
|
||||||
|
$ZSTD tmp*
|
||||||
|
$ZSTD -l *.zst
|
||||||
|
$ZSTD -lv *.zst
|
||||||
|
$ZSTD --list *.zst
|
||||||
|
$ZSTD --list -v *.zst
|
||||||
|
|
||||||
|
$ECHO "\n**** zstd --list/-l multiple frame tests ****"
|
||||||
|
cat tmp1.zst tmp2.zst > tmp12.zst
|
||||||
|
cat tmp3.zst tmp4.zst > tmp34.zst
|
||||||
|
cat tmp12.zst tmp34.zst > tmp1234.zst
|
||||||
|
cat tmp12.zst tmp4.zst > tmp124.zst
|
||||||
|
$ZSTD -l *.zst
|
||||||
|
$ZSTD -lv *.zst
|
||||||
|
$ZSTD --list *.zst
|
||||||
|
$ZSTD --list -v *.zst
|
||||||
|
|
||||||
|
$ECHO "\n**** zstd --list/-l error detection tests ****"
|
||||||
|
! $ZSTD -l tmp1 tmp1.zst
|
||||||
|
! $ZSTD --list tmp*
|
||||||
|
! $ZSTD -lv tmp1*
|
||||||
|
! $ZSTD --list -v tmp2 tmp23.zst
|
||||||
|
|
||||||
|
$ECHO "\n**** zstd --list/-l test with null files ****"
|
||||||
|
./datagen -g0 > tmp5
|
||||||
|
$ZSTD tmp5
|
||||||
|
! $ZSTD -l tmp5*
|
||||||
|
! $ZSTD -lv tmp5*
|
||||||
|
! $ZSTD --list tmp5*
|
||||||
|
! $ZSTD --list -v tmp5*
|
||||||
|
|
||||||
|
$ECHO "\n**** zstd --list/-l test with no frame content size ****"
|
||||||
|
echo -n '' > tmp6
|
||||||
|
$ZSTD tmp6
|
||||||
|
$ZSTD -l tmp6.zst
|
||||||
|
$ZSTD -lv tmp6.zst
|
||||||
|
$ZSTD --list tmp6.zst
|
||||||
|
$ZSTD --list -v tmp6.zst
|
||||||
|
|
||||||
|
rm tmp*
|
||||||
|
|
||||||
if [ "$1" != "--test-large-data" ]; then
|
if [ "$1" != "--test-large-data" ]; then
|
||||||
$ECHO "Skipping large data tests"
|
$ECHO "Skipping large data tests"
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
Reference in New Issue
Block a user