Skip to content

Commit 75a7497

Browse files
committed
disable --rm on -o command
make it more similar to -c (aka `stdout`) convention.
1 parent 41682e6 commit 75a7497

File tree

4 files changed

+110
-66
lines changed

4 files changed

+110
-66
lines changed

programs/fileio.c

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs,
612612
if (!prefs->overwrite) {
613613
if (g_display_prefs.displayLevel <= 1) {
614614
/* No interaction possible */
615-
DISPLAY("zstd: %s already exists; not overwritten \n",
615+
DISPLAYLEVEL(1, "zstd: %s already exists; not overwritten \n",
616616
dstFileName);
617617
return NULL;
618618
}
@@ -723,7 +723,7 @@ int FIO_checkFilenameCollisions(const char** filenameTable, unsigned nbFiles) {
723723

724724
filenameTableSorted = (const char**) malloc(sizeof(char*) * nbFiles);
725725
if (!filenameTableSorted) {
726-
DISPLAY("Unable to malloc new str array, not checking for name collisions\n");
726+
DISPLAYLEVEL(1, "Allocation error during filename collision checking \n");
727727
return 1;
728728
}
729729

@@ -740,7 +740,7 @@ int FIO_checkFilenameCollisions(const char** filenameTable, unsigned nbFiles) {
740740
prevElem = filenameTableSorted[0];
741741
for (u = 1; u < nbFiles; ++u) {
742742
if (strcmp(prevElem, filenameTableSorted[u]) == 0) {
743-
DISPLAY("WARNING: Two files have same filename: %s\n", prevElem);
743+
DISPLAYLEVEL(2, "WARNING: Two files have same filename: %s\n", prevElem);
744744
}
745745
prevElem = filenameTableSorted[u];
746746
}
@@ -823,41 +823,59 @@ static void FIO_adjustMemLimitForPatchFromMode(FIO_prefs_t* const prefs,
823823
FIO_setMemLimit(prefs, (unsigned)maxSize);
824824
}
825825

826-
/* FIO_removeMultiFilesWarning() :
826+
/* FIO_multiFilesConcatWarning() :
827+
* This function handles logic when processing multiple files with -o or -c, displaying the appropriate warnings/prompts.
827828
* Returns 1 if the console should abort, 0 if console should proceed.
828-
* This function handles logic when processing multiple files with -o, displaying the appropriate warnings/prompts.
829829
*
830-
* If -f is specified, or there is just 1 file, zstd will always proceed as usual.
831-
* If --rm is specified, there will be a prompt asking for user confirmation.
832-
* If -f is specified with --rm, zstd will proceed as usual
833-
* If -q is specified with --rm, zstd will abort pre-emptively
834-
* If neither flag is specified, zstd will prompt the user for confirmation to proceed.
835-
* If --rm is not specified, then zstd will print a warning to the user (which can be silenced with -q).
836-
* Note : --rm in combination with stdout is not allowed.
830+
* If output is stdout or test mode is active, check that `--rm` disabled.
831+
*
832+
* If there is just 1 file to process, zstd will proceed as usual.
833+
* If each file get processed into its own separate destination file, proceed as usual.
834+
*
835+
* When multiple files are processed into a single output,
836+
* display a warning message, then disable --rm if it's set.
837+
*
838+
* If -f is specified or if output is stdout, just proceed.
839+
* If output is set with -o, prompt for confirmation.
837840
*/
838-
static int FIO_removeMultiFilesWarning(FIO_ctx_t* const fCtx, const FIO_prefs_t* const prefs, const char* outFileName, int displayLevelCutoff)
841+
static int FIO_multiFilesConcatWarning(const FIO_ctx_t* fCtx, FIO_prefs_t* prefs, const char* outFileName, int displayLevelCutoff)
839842
{
840-
int error = 0;
841-
if (fCtx->nbFilesTotal > 1 && !prefs->overwrite) {
842-
if (g_display_prefs.displayLevel <= displayLevelCutoff) {
843-
if (prefs->removeSrcFile) {
844-
DISPLAYLEVEL(1, "zstd: Aborting... not deleting files and processing into dst: %s\n", outFileName);
845-
error = 1;
846-
}
847-
} else {
848-
if (!strcmp(outFileName, stdoutmark)) {
849-
DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into stdout. \n");
850-
} else {
851-
DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into a single output file: %s \n", outFileName);
852-
}
853-
DISPLAYLEVEL(2, "The concatenated output CANNOT regenerate original file names nor directory structure. \n")
854-
if (prefs->removeSrcFile) {
855-
assert(fCtx->hasStdoutOutput == 0); /* not possible : never erase source files when output == stdout */
856-
error = (g_display_prefs.displayLevel > displayLevelCutoff) && UTIL_requireUserConfirmation("This is a destructive operation. Proceed? (y/n): ", "Aborting...", "yY", fCtx->hasStdinInput);
857-
}
858-
}
843+
if (fCtx->hasStdoutOutput) assert(prefs->removeSrcFile == 0);
844+
if (prefs->testMode) {
845+
assert(prefs->removeSrcFile == 0);
846+
return 0;
859847
}
860-
return error;
848+
849+
if (fCtx->nbFilesTotal == 1) return 0;
850+
assert(fCtx->nbFilesTotal > 1);
851+
852+
if (!outFileName) return 0;
853+
854+
if (fCtx->hasStdoutOutput) {
855+
DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into stdout. \n");
856+
} else {
857+
DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into a single output file: %s \n", outFileName);
858+
}
859+
DISPLAYLEVEL(2, "The concatenated output CANNOT regenerate original file names nor directory structure. \n")
860+
861+
/* multi-input into single output : --rm is not allowed */
862+
if (prefs->removeSrcFile) {
863+
DISPLAYLEVEL(2, "Since it's a destructive operation, input files will not be removed. \n");
864+
prefs->removeSrcFile = 0;
865+
}
866+
867+
if (fCtx->hasStdoutOutput) return 0;
868+
if (prefs->overwrite) return 0;
869+
870+
/* multiple files concatenated into single destination file using -o without -f */
871+
if (g_display_prefs.displayLevel <= displayLevelCutoff) {
872+
/* quiet mode => no prompt => fail automatically */
873+
DISPLAYLEVEL(1, "Concatenating multiple processed inputs into a single output loses file metadata. \n");
874+
DISPLAYLEVEL(1, "Aborting. \n");
875+
return 1;
876+
}
877+
/* normal mode => prompt */
878+
return UTIL_requireUserConfirmation("Proceed? (y/n): ", "Aborting...", "yY", fCtx->hasStdinInput);
861879
}
862880

863881
static ZSTD_inBuffer setInBuffer(const void* buf, size_t s, size_t pos)
@@ -1767,9 +1785,9 @@ FIO_compressFilename_srcFile(FIO_ctx_t* const fCtx,
17671785
&srcFileStat, compressionLevel);
17681786
AIO_ReadPool_closeFile(ress.readCtx);
17691787

1770-
if ( prefs->removeSrcFile /* --rm */
1771-
&& result == 0 /* success */
1772-
&& strcmp(srcFileName, stdinmark) /* exception : don't erase stdin */
1788+
if ( prefs->removeSrcFile /* --rm */
1789+
&& result == 0 /* success */
1790+
&& strcmp(srcFileName, stdinmark) /* exception : don't erase stdin */
17731791
) {
17741792
/* We must clear the handler, since after this point calling it would
17751793
* delete both the source and destination files.
@@ -1791,7 +1809,8 @@ checked_index(const char* options[], size_t length, size_t index) {
17911809

17921810
#define INDEX(options, index) checked_index((options), sizeof(options) / sizeof(char*), (size_t)(index))
17931811

1794-
void FIO_displayCompressionParameters(const FIO_prefs_t* prefs) {
1812+
void FIO_displayCompressionParameters(const FIO_prefs_t* prefs)
1813+
{
17951814
static const char* formatOptions[5] = {ZSTD_EXTENSION, GZ_EXTENSION, XZ_EXTENSION,
17961815
LZMA_EXTENSION, LZ4_EXTENSION};
17971816
static const char* sparseOptions[3] = {" --no-sparse", "", " --sparse"};
@@ -1920,7 +1939,7 @@ int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
19201939
assert(outFileName != NULL || suffix != NULL);
19211940
if (outFileName != NULL) { /* output into a single destination (stdout typically) */
19221941
FILE *dstFile;
1923-
if (FIO_removeMultiFilesWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
1942+
if (FIO_multiFilesConcatWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
19241943
FIO_freeCResources(&ress);
19251944
return 1;
19261945
}
@@ -2742,7 +2761,7 @@ FIO_decompressMultipleFilenames(FIO_ctx_t* const fCtx,
27422761
dRess_t ress = FIO_createDResources(prefs, dictFileName);
27432762

27442763
if (outFileName) {
2745-
if (FIO_removeMultiFilesWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
2764+
if (FIO_multiFilesConcatWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
27462765
FIO_freeDResources(ress);
27472766
return 1;
27482767
}

programs/util.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ int UTIL_requireUserConfirmation(const char* prompt, const char* abortMsg,
121121
ch = getchar();
122122
result = 0;
123123
if (strchr(acceptableLetters, ch) == NULL) {
124-
UTIL_DISPLAY("%s", abortMsg);
124+
UTIL_DISPLAY("%s \n", abortMsg);
125125
result = 1;
126126
}
127127
/* flush the rest */

programs/zstdcli.c

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ static const char* lastNameFromPath(const char* path)
339339

340340
static void errorOut(const char* msg)
341341
{
342-
DISPLAY("%s \n", msg); exit(1);
342+
DISPLAYLEVEL(1, "%s \n", msg); exit(1);
343343
}
344344

345345
/*! readU32FromCharChecked() :
@@ -786,13 +786,13 @@ static unsigned init_nbThreads(void) {
786786
} else { \
787787
argNb++; \
788788
if (argNb >= argCount) { \
789-
DISPLAY("error: missing command argument \n"); \
789+
DISPLAYLEVEL(1, "error: missing command argument \n"); \
790790
CLEAN_RETURN(1); \
791791
} \
792792
ptr = argv[argNb]; \
793793
assert(ptr != NULL); \
794794
if (ptr[0]=='-') { \
795-
DISPLAY("error: command cannot be separated from its argument by another command \n"); \
795+
DISPLAYLEVEL(1, "error: command cannot be separated from its argument by another command \n"); \
796796
CLEAN_RETURN(1); \
797797
} } }
798798

@@ -859,6 +859,7 @@ int main(int argCount, const char* argv[])
859859

860860
FIO_prefs_t* const prefs = FIO_createPreferences();
861861
FIO_ctx_t* const fCtx = FIO_createContext();
862+
FIO_progressSetting_e progress = FIO_ps_auto;
862863
zstd_operation_mode operation = zom_compress;
863864
ZSTD_compressionParameters compressionParams;
864865
int cLevel = init_cLevel();
@@ -898,7 +899,7 @@ int main(int argCount, const char* argv[])
898899
(void)recursive; (void)cLevelLast; /* not used when ZSTD_NOBENCH set */
899900
(void)memLimit;
900901
assert(argCount >= 1);
901-
if ((filenames==NULL) || (file_of_names==NULL)) { DISPLAY("zstd: allocation error \n"); exit(1); }
902+
if ((filenames==NULL) || (file_of_names==NULL)) { DISPLAYLEVEL(1, "zstd: allocation error \n"); exit(1); }
902903
programName = lastNameFromPath(programName);
903904
#ifdef ZSTD_MULTITHREAD
904905
nbWorkers = init_nbThreads();
@@ -999,8 +1000,8 @@ int main(int argCount, const char* argv[])
9991000
if (!strcmp(argument, "--rsyncable")) { rsyncable = 1; continue; }
10001001
if (!strcmp(argument, "--compress-literals")) { literalCompressionMode = ZSTD_ps_enable; continue; }
10011002
if (!strcmp(argument, "--no-compress-literals")) { literalCompressionMode = ZSTD_ps_disable; continue; }
1002-
if (!strcmp(argument, "--no-progress")) { FIO_setProgressSetting(FIO_ps_never); continue; }
1003-
if (!strcmp(argument, "--progress")) { FIO_setProgressSetting(FIO_ps_always); continue; }
1003+
if (!strcmp(argument, "--no-progress")) { progress = FIO_ps_never; continue; }
1004+
if (!strcmp(argument, "--progress")) { progress = FIO_ps_always; continue; }
10041005
if (!strcmp(argument, "--exclude-compressed")) { FIO_setExcludeCompressedFile(prefs, 1); continue; }
10051006
if (!strcmp(argument, "--fake-stdin-is-console")) { UTIL_fakeStdinIsConsole(); continue; }
10061007
if (!strcmp(argument, "--fake-stdout-is-console")) { UTIL_fakeStdoutIsConsole(); continue; }
@@ -1057,7 +1058,7 @@ int main(int argCount, const char* argv[])
10571058
if (longCommandWArg(&argument, "--output-dir-flat")) {
10581059
NEXT_FIELD(outDirName);
10591060
if (strlen(outDirName) == 0) {
1060-
DISPLAY("error: output dir cannot be empty string (did you mean to pass '.' instead?)\n");
1061+
DISPLAYLEVEL(1, "error: output dir cannot be empty string (did you mean to pass '.' instead?)\n");
10611062
CLEAN_RETURN(1);
10621063
}
10631064
continue;
@@ -1073,7 +1074,7 @@ int main(int argCount, const char* argv[])
10731074
if (longCommandWArg(&argument, "--output-dir-mirror")) {
10741075
NEXT_FIELD(outMirroredDirName);
10751076
if (strlen(outMirroredDirName) == 0) {
1076-
DISPLAY("error: output dir cannot be empty string (did you mean to pass '.' instead?)\n");
1077+
DISPLAYLEVEL(1, "error: output dir cannot be empty string (did you mean to pass '.' instead?)\n");
10771078
CLEAN_RETURN(1);
10781079
}
10791080
continue;
@@ -1349,7 +1350,7 @@ int main(int argCount, const char* argv[])
13491350
int const ret = FIO_listMultipleFiles((unsigned)filenames->tableSize, filenames->fileNames, g_displayLevel);
13501351
CLEAN_RETURN(ret);
13511352
#else
1352-
DISPLAY("file information is not supported \n");
1353+
DISPLAYLEVEL(1, "file information is not supported \n");
13531354
CLEAN_RETURN(1);
13541355
#endif
13551356
}
@@ -1480,24 +1481,29 @@ int main(int argCount, const char* argv[])
14801481

14811482
if (showDefaultCParams) {
14821483
if (operation == zom_decompress) {
1483-
DISPLAY("error : can't use --show-default-cparams in decompression mode \n");
1484+
DISPLAYLEVEL(1, "error : can't use --show-default-cparams in decompression mode \n");
14841485
CLEAN_RETURN(1);
14851486
}
14861487
}
14871488

14881489
if (dictFileName != NULL && patchFromDictFileName != NULL) {
1489-
DISPLAY("error : can't use -D and --patch-from=# at the same time \n");
1490+
DISPLAYLEVEL(1, "error : can't use -D and --patch-from=# at the same time \n");
14901491
CLEAN_RETURN(1);
14911492
}
14921493

14931494
if (patchFromDictFileName != NULL && filenames->tableSize > 1) {
1494-
DISPLAY("error : can't use --patch-from=# on multiple files \n");
1495+
DISPLAYLEVEL(1, "error : can't use --patch-from=# on multiple files \n");
14951496
CLEAN_RETURN(1);
14961497
}
14971498

1498-
/* No status message in pipe mode (stdin - stdout) */
1499+
/* No status message by default when output is stdout */
14991500
hasStdout = outFileName && !strcmp(outFileName,stdoutmark);
1500-
if ((hasStdout || !UTIL_isConsole(stderr)) && (g_displayLevel==2)) g_displayLevel=1;
1501+
if (hasStdout && (g_displayLevel==2)) g_displayLevel=1;
1502+
1503+
/* when stderr is not the console, do not pollute it with status updates
1504+
* Note : the below code actually also silence more stuff, including completion report. */
1505+
if (!UTIL_isConsole(stderr) && (g_displayLevel==2)) g_displayLevel=1;
1506+
FIO_setProgressSetting(progress);
15011507

15021508
/* don't remove source files when output is stdout */;
15031509
if (hasStdout && removeSrcFile) {
@@ -1569,7 +1575,7 @@ int main(int argCount, const char* argv[])
15691575
operationResult = FIO_compressMultipleFilenames(fCtx, prefs, filenames->fileNames, outMirroredDirName, outDirName, outFileName, suffix, dictFileName, cLevel, compressionParams);
15701576
#else
15711577
(void)contentSize; (void)suffix; (void)adapt; (void)rsyncable; (void)ultra; (void)cLevel; (void)ldmFlag; (void)literalCompressionMode; (void)targetCBlockSize; (void)streamSrcSize; (void)srcSizeHint; (void)ZSTD_strategyMap; (void)useRowMatchFinder; /* not used when ZSTD_NOCOMPRESS set */
1572-
DISPLAY("Compression not supported \n");
1578+
DISPLAYLEVEL(1, "Compression not supported \n");
15731579
#endif
15741580
} else { /* decompression or test */
15751581
#ifndef ZSTD_NODECOMPRESS
@@ -1579,7 +1585,7 @@ int main(int argCount, const char* argv[])
15791585
operationResult = FIO_decompressMultipleFilenames(fCtx, prefs, filenames->fileNames, outMirroredDirName, outDirName, outFileName, dictFileName);
15801586
}
15811587
#else
1582-
DISPLAY("Decompression not supported \n");
1588+
DISPLAYLEVEL(1, "Decompression not supported \n");
15831589
#endif
15841590
}
15851591

tests/playTests.sh

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ println "\n===> file removal"
387387
zstd -f --rm tmp
388388
test ! -f tmp # tmp should no longer be present
389389
zstd -f -d --rm tmp.zst
390-
test ! -f tmp.zst # tmp.zst should no longer be present
390+
test ! -f tmp.zst # tmp.zst should no longer be present
391391
println "test: --rm is disabled when output is stdout"
392392
test -f tmp
393393
zstd --rm tmp -c > $INTOVOID
@@ -396,6 +396,20 @@ zstd -f --rm tmp -c > $INTOVOID
396396
test -f tmp # tmp shall still be there
397397
zstd -f tmp -c > $INTOVOID --rm
398398
test -f tmp # tmp shall still be there
399+
println "test: --rm is disabled when multiple inputs are concatenated into a single output"
400+
cp tmp tmp2
401+
zstd --rm tmp tmp2 -c > $INTOVOID
402+
test -f tmp
403+
test -f tmp2
404+
rm -f tmp3.zst
405+
echo 'y' | zstd -v tmp tmp2 -o tmp3.zst --rm # prompt for confirmation
406+
test -f tmp
407+
test -f tmp2
408+
zstd -f tmp tmp2 -o tmp3.zst --rm # just warns, no prompt
409+
test -f tmp
410+
test -f tmp2
411+
zstd -q tmp tmp2 -o tmp3.zst --rm && die "should refuse to concatenate"
412+
399413
println "test : should quietly not remove non-regular file"
400414
println hello > tmp
401415
zstd tmp -f -o "$DEVDEVICE" 2>tmplog > "$INTOVOID"
@@ -437,34 +451,35 @@ println hello > tmp1
437451
println world > tmp2
438452
zstd tmp1 tmp2 -o "$INTOVOID" -f
439453
zstd tmp1 tmp2 -c | zstd -t
440-
zstd tmp1 tmp2 -o tmp.zst
454+
echo 'y' | zstd -v tmp1 tmp2 -o tmp.zst
441455
test ! -f tmp1.zst
442456
test ! -f tmp2.zst
443457
zstd tmp1 tmp2
444458
zstd -t tmp1.zst tmp2.zst
445459
zstd -dc tmp1.zst tmp2.zst
446460
zstd tmp1.zst tmp2.zst -o "$INTOVOID" -f
447-
zstd -d tmp1.zst tmp2.zst -o tmp
461+
echo 'y' | zstd -v -d tmp1.zst tmp2.zst -o tmp
448462
touch tmpexists
449463
zstd tmp1 tmp2 -f -o tmpexists
450464
zstd tmp1 tmp2 -q -o tmpexists && die "should have refused to overwrite"
451465
println gooder > tmp_rm1
452466
println boi > tmp_rm2
453467
println worldly > tmp_rm3
454-
echo 'y' | zstd tmp_rm1 tmp_rm2 -v -o tmp_rm3.zst --rm # tests the warning prompt for --rm with multiple inputs into once source
455-
test ! -f tmp_rm1
456-
test ! -f tmp_rm2
468+
echo 'y' | zstd -v tmp_rm1 tmp_rm2 -v -o tmp_rm3.zst
469+
test -f tmp_rm1
470+
test -f tmp_rm2
457471
cp tmp_rm3.zst tmp_rm4.zst
458-
echo 'Y' | zstd -d tmp_rm3.zst tmp_rm4.zst -v -o tmp_rm_out --rm
459-
test ! -f tmp_rm3.zst
460-
test ! -f tmp_rm4.zst
472+
echo 'Y' | zstd -v -d tmp_rm3.zst tmp_rm4.zst -v -o tmp_rm_out --rm
473+
test -f tmp_rm3.zst
474+
test -f tmp_rm4.zst
461475
println gooder > tmpexists1
462476
zstd tmpexists1 tmpexists -c --rm -f > $INTOVOID
463-
464477
# Bug: PR #972
465478
if [ "$?" -eq 139 ]; then
466479
die "should not have segfaulted"
467480
fi
481+
test -f tmpexists1
482+
test -f tmpexists
468483
println "\n===> multiple files and shell completion "
469484
datagen -s1 > tmp1 2> $INTOVOID
470485
datagen -s2 -g100K > tmp2 2> $INTOVOID
@@ -1172,6 +1187,10 @@ zstd -t tmp3 && die "bad file not detected !" # detects 0-sized files as bad
11721187
println "test --rm and --test combined "
11731188
zstd -t --rm tmp1.zst
11741189
test -f tmp1.zst # check file is still present
1190+
cp tmp1.zst tmp2.zst
1191+
zstd -t tmp1.zst tmp2.zst --rm
1192+
test -f tmp1.zst # check file is still present
1193+
test -f tmp2.zst # check file is still present
11751194
split -b16384 tmp1.zst tmpSplit.
11761195
zstd -t tmpSplit.* && die "bad file not detected !"
11771196
datagen | zstd -c | zstd -t

0 commit comments

Comments
 (0)