mirror of https://github.com/qpdf/qpdf.git
Detect input file = output file (fixes #29)
This commit is contained in:
parent
885b8781cc
commit
5993c3e83c
|
@ -1,5 +1,8 @@
|
|||
2017-07-29 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* Detect when input file and output file are the same and exit to
|
||||
avoid overwriting and losing input file. Fixes #29.
|
||||
|
||||
* When passing multiple inspection arguments, run --check first,
|
||||
and defer exit until after all the checks have been run. This
|
||||
makes it possible to force operations such as --show-xref to be
|
||||
|
|
|
@ -74,6 +74,9 @@ namespace QUtil
|
|||
QPDF_DLL
|
||||
qpdf_offset_t tell(FILE* stream);
|
||||
|
||||
QPDF_DLL
|
||||
bool same_file(char const* name1, char const* name2);
|
||||
|
||||
QPDF_DLL
|
||||
char* copy_string(std::string const&);
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
std::string
|
||||
|
@ -188,6 +189,55 @@ QUtil::tell(FILE* stream)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
QUtil::same_file(char const* name1, char const* name2)
|
||||
{
|
||||
if ((name1 == 0) || (strlen(name1) == 0) ||
|
||||
(name2 == 0) || (strlen(name2) == 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
HANDLE fh1 = CreateFile(name1, GENERIC_READ, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
HANDLE fh2 = CreateFile(name2, GENERIC_READ, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
BY_HANDLE_FILE_INFORMATION fi1;
|
||||
BY_HANDLE_FILE_INFORMATION fi2;
|
||||
bool same = false;
|
||||
if ((fh1 != INVALID_HANDLE_VALUE) &&
|
||||
(fh2 != INVALID_HANDLE_VALUE) &&
|
||||
GetFileInformationByHandle(fh1, &fi1) &&
|
||||
GetFileInformationByHandle(fh2, &fi2) &&
|
||||
(fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber) &&
|
||||
(fi1.nFileIndexLow == fi2.nFileIndexLow) &&
|
||||
(fi1.nFileIndexHigh == fi2.nFileIndexHigh))
|
||||
{
|
||||
same = true;
|
||||
}
|
||||
if (fh1 != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(fh1);
|
||||
}
|
||||
if (fh2 != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(fh2);
|
||||
}
|
||||
return same;
|
||||
#else
|
||||
struct stat st1;
|
||||
struct stat st2;
|
||||
if ((stat(name1, &st1) == 0) &&
|
||||
(stat(name2, &st2) == 0) &&
|
||||
(st1.st_ino == st2.st_ino) &&
|
||||
(st1.st_dev == st2.st_dev))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
char*
|
||||
QUtil::copy_string(std::string const& str)
|
||||
{
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
test
|
|
@ -35,3 +35,10 @@ quack1
|
|||
quack2
|
||||
quack3
|
||||
quack4
|
||||
----
|
||||
file1: -qutil.out-, file2: -./qutil.out-; same: 1: PASS
|
||||
file1: -qutil.out-, file2: -qutil.out-; same: 1: PASS
|
||||
file1: -qutil.out-, file2: -other-file-; same: 0: PASS
|
||||
file1: -qutil.out-, file2: --; same: 0: PASS
|
||||
file1: -qutil.out-, file2: -(null)-; same: 0: PASS
|
||||
file1: --, file2: -qutil.out-; same: 0: PASS
|
||||
|
|
|
@ -140,6 +140,36 @@ void get_whoami_test()
|
|||
print_whoami("a\\b\\c\\quack4.exe");
|
||||
}
|
||||
|
||||
void assert_same_file(char const* file1, char const* file2, bool expected)
|
||||
{
|
||||
bool actual = QUtil::same_file(file1, file2);
|
||||
std::cout << "file1: -" << (file1 ? file1 : "(null)") << "-, file2: -"
|
||||
<< (file2 ? file2 : "(null)") << "-; same: "
|
||||
<< actual << ": " << ((actual == expected) ? "PASS" : "FAIL")
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void same_file_test()
|
||||
{
|
||||
try
|
||||
{
|
||||
fclose(QUtil::safe_fopen("qutil.out", "r"));
|
||||
fclose(QUtil::safe_fopen("other-file", "r"));
|
||||
}
|
||||
catch (std::exception)
|
||||
{
|
||||
std::cout << "same_file_test expects to have qutil.out and other-file"
|
||||
" exist in the current directory\n";
|
||||
return;
|
||||
}
|
||||
assert_same_file("qutil.out", "./qutil.out", true);
|
||||
assert_same_file("qutil.out", "qutil.out", true);
|
||||
assert_same_file("qutil.out", "other-file", false);
|
||||
assert_same_file("qutil.out", "", false);
|
||||
assert_same_file("qutil.out", 0, false);
|
||||
assert_same_file("", "qutil.out", false);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
|
@ -155,6 +185,8 @@ int main(int argc, char* argv[])
|
|||
to_utf8_test();
|
||||
std::cout << "----" << std::endl;
|
||||
get_whoami_test();
|
||||
std::cout << "----" << std::endl;
|
||||
same_file_test();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
|
|
@ -1361,6 +1361,12 @@ int main(int argc, char* argv[])
|
|||
usage("no output file may be given for this option");
|
||||
}
|
||||
|
||||
if (QUtil::same_file(infilename, outfilename))
|
||||
{
|
||||
QTC::TC("qpdf", "qpdf same file error");
|
||||
usage("input file and output file are the same; this would cause input file to be lost");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
QPDF pdf;
|
||||
|
|
|
@ -284,3 +284,4 @@ QPDFWriter preserve unreferenced standard 0
|
|||
QPDFObjectHandle non-stream in parsecontent 0
|
||||
QPDFObjectHandle errors in parsecontent 0
|
||||
QPDF stream with non-space 0
|
||||
qpdf same file error 0
|
||||
|
|
|
@ -206,7 +206,7 @@ $td->runtest("remove page we don't have",
|
|||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- Miscellaneous Tests ---");
|
||||
$n_tests += 93;
|
||||
$n_tests += 94;
|
||||
|
||||
$td->runtest("qpdf version",
|
||||
{$td->COMMAND => "qpdf --version"},
|
||||
|
@ -641,6 +641,10 @@ $td->runtest("dump corrected bad xref",
|
|||
$td->EXIT_STATUS => 3},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
$td->runtest("don't overwrite self",
|
||||
{$td->COMMAND => "qpdf a.pdf a.pdf"},
|
||||
{$td->REGEXP => "input file and output file are the same.*",
|
||||
$td->EXIT_STATUS => 2});
|
||||
|
||||
show_ntests();
|
||||
# ----------
|
||||
|
|
Loading…
Reference in New Issue