summaryrefslogtreecommitdiff
path: root/support
diff options
context:
space:
mode:
Diffstat (limited to 'support')
-rw-r--r--support/pkgcheck/CHANGES.md17
-rw-r--r--support/pkgcheck/Cargo.toml63
-rwxr-xr-xsupport/pkgcheck/bin/pkgcheckbin7266008 -> 8028168 bytes
-rw-r--r--support/pkgcheck/docs/errorsd.tex154
-rw-r--r--support/pkgcheck/docs/pkgcheck.pdfbin89893 -> 90348 bytes
-rw-r--r--support/pkgcheck/docs/title.tex2
-rw-r--r--support/pkgcheck/docs/warningsd.tex53
-rw-r--r--support/pkgcheck/src/gparser.rs1
-rw-r--r--support/pkgcheck/src/linkcheck.rs4
-rw-r--r--support/pkgcheck/src/main.rs338
-rw-r--r--support/pkgcheck/src/messages/errorsd.rs26
-rw-r--r--support/pkgcheck/src/messages/fatald.rs1
-rw-r--r--support/pkgcheck/src/messages/informationd.rs1
-rw-r--r--support/pkgcheck/src/messages/mod.rs76
-rw-r--r--support/pkgcheck/src/messages/warningsd.rs19
-rw-r--r--support/pkgcheck/src/recode.rs4
-rw-r--r--support/pkgcheck/src/utils.rs46
17 files changed, 539 insertions, 266 deletions
diff --git a/support/pkgcheck/CHANGES.md b/support/pkgcheck/CHANGES.md
index 3f1414d6bf..c6985a7d06 100644
--- a/support/pkgcheck/CHANGES.md
+++ b/support/pkgcheck/CHANGES.md
@@ -6,6 +6,23 @@
# Changes
+
+## 2024-06-11 (3.2.0)
+### Added
+ - add generation of completion support for nushell
+ - add a new warning message W0011 if a file in the package tree
+ has a future modification time
+ - add Thumbs.db as temporary file
+ - (Experimental) for l3backend-dev and l3kernel-dev use the real directory name
+ latex-dev/l3backend resp. latex-dev/l3kernel when checking path names in the
+ TDS archive
+ - new E0042 error when there are filenames with different letter cases in a TDS archive
+ - new E0043 error when a symlink is found in the TDS archive
+### Changed
+ - small improvement in src/recode.rs due to clippy suggestions
+ - small code reorganization in check_tds_archive()
+ - crates update
+
## 2022-12-26 (3.1.0)
### Added
- now checking for hard links which generate a warning message
diff --git a/support/pkgcheck/Cargo.toml b/support/pkgcheck/Cargo.toml
index c765d91590..b35bb8a7e4 100644
--- a/support/pkgcheck/Cargo.toml
+++ b/support/pkgcheck/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "pkgcheck"
-version = "3.1.0"
+version = "3.2.0"
authors = ["Manfred Lotz <manfred@ctan.org>"]
description = "Command-line tool to check packages uploaded to CTAN."
license = "MIT/Apache-2.0"
@@ -17,52 +17,74 @@ panic = "abort"
lto = true
[dependencies]
-blake3 = "1.3"
+blake3 = "1.5"
clap = { version = "4", features = ["derive"] }
clap_complete = "4"
+clap_complete_nushell = "4.5"
colored = "2"
-config = "0.13"
+config = "0.14"
escargot = "0.5"
fern = "0.6"
glob = "0.3"
-home = "0.5.3"
-once_cell = "1.13"
+home = "0.5.9"
+once_cell = "1.19"
#lazy_static = "1"
-linkify = "0.9"
+linkify = "0.10"
log = "0.4"
num_cpus = "1"
-openssl-probe = "0.1.4"
-pest = "2.3"
-pest_derive = "2.3"
+openssl-probe = "0.1.5"
+pest = "2.7"
+pest_derive = "2.7"
regex = "1"
-reqwest = { version = "0.11", features = ["blocking", "gzip"] }
+reqwest = { version = "0.12", features = ["blocking", "gzip"] }
rustc-hash = "1.1"
scoped_threadpool = "0.1"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"
-tempfile = "3.1"
+tempfile = "3.8"
threadpool = "1.8"
tokio = { version = "1", features = ["full"] }
unbytify = "0.2"
-unicode-bom = "1"
-url = "2.1"
+unicode-bom = "2"
+url = "2.4"
walkdir = "2.3"
-zip = { version = "^0.6", default-features = false, features = ["deflate"] }
+##zip = { version = "^0.6", default-features = false, features = ["deflate"] }
+zip = { version = "^2.1", default-features = false, features = ["deflate"] }
[dev-dependencies]
assert_cmd = "2"
-predicates = "2"
+predicates = "3"
[package.metadata.deb]
section = "utility"
depends = ""
priority = "optional"
assets = [
- ["target/x86_64-unknown-linux-musl/release/pkgcheck", "usr/bin/", "755"],
- ["LICENSE-MIT", "usr/share/doc/pkgcheck/", "644"],
- ["LICENSE-APACHE", "usr/share/doc/pkgcheck/", "644"],
- ["README.md", "usr/share/doc/pkgcheck/README", "644"],
- ["docs/pkgcheck.pdf", "usr/share/doc/pkgcheck/pkgcheck.pdf", "644"],
+ [
+ "target/x86_64-unknown-linux-musl/release/pkgcheck",
+ "usr/bin/",
+ "755",
+ ],
+ [
+ "LICENSE-MIT",
+ "usr/share/doc/pkgcheck/",
+ "644",
+ ],
+ [
+ "LICENSE-APACHE",
+ "usr/share/doc/pkgcheck/",
+ "644",
+ ],
+ [
+ "README.md",
+ "usr/share/doc/pkgcheck/README",
+ "644",
+ ],
+ [
+ "docs/pkgcheck.pdf",
+ "usr/share/doc/pkgcheck/pkgcheck.pdf",
+ "644",
+ ],
]
extended-description = """\
A checker for uploaded packages to CTAN
@@ -71,7 +93,6 @@ A checker for uploaded packages to CTAN
changelog = "CHANGES.md"
-
[package.metadata.rpm]
package = "pkgcheck"
diff --git a/support/pkgcheck/bin/pkgcheck b/support/pkgcheck/bin/pkgcheck
index abaea81efc..293c66dd08 100755
--- a/support/pkgcheck/bin/pkgcheck
+++ b/support/pkgcheck/bin/pkgcheck
Binary files differ
diff --git a/support/pkgcheck/docs/errorsd.tex b/support/pkgcheck/docs/errorsd.tex
index 7fb3509791..c37a082641 100644
--- a/support/pkgcheck/docs/errorsd.tex
+++ b/support/pkgcheck/docs/errorsd.tex
@@ -1,6 +1,5 @@
-\hypertarget{e0001----bad-characters-in-file-name}{%
\subsection{E0001 -\/- Bad characters in file
-name}\label{e0001----bad-characters-in-file-name}}
+name}\label{e0001----bad-characters-in-file-name}
File name should not contain non-ascii characters. Additionally, file
names should not contain control characters or other characters which
@@ -9,9 +8,8 @@ may have a special meaning for UNIX shells.
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#nounixspecialcharacters}
-\hypertarget{e0002----file-permissions}{%
\subsection{E0002 -\/- File
-Permissions}\label{e0002----file-permissions}}
+Permissions}\label{e0002----file-permissions}
Files submitted to CTAN should be world readable.
@@ -21,16 +19,14 @@ be marked as such.
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#filepermissions}
-\hypertarget{e0003----readme-is-not-a-text-file}{%
\subsection{E0003 -\/- README is not a text
-file}\label{e0003----readme-is-not-a-text-file}}
+file}\label{e0003----readme-is-not-a-text-file}
The \texttt{README} file specified in the error message must be a text
-file but it isn't.
+file but it isn\textquotesingle t.
-\hypertarget{e0004----empty-directory-not-allowed}{%
\subsection{E0004 -\/- Empty directory not
-allowed}\label{e0004----empty-directory-not-allowed}}
+allowed}\label{e0004----empty-directory-not-allowed}
Empty directories are considered as rubbish, and are usually not
accepted as part of a package, neither in the package tree nor in the
@@ -39,9 +35,8 @@ TDS zip archive.
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#noemptyfiles}
-\hypertarget{e0005----empty-files-not-allowed}{%
\subsection{E0005 -\/- Empty files not
-allowed}\label{e0005----empty-files-not-allowed}}
+allowed}\label{e0005----empty-files-not-allowed}
Empty files are considered as rubbish, and are usually not accepted as
part of a package.
@@ -49,9 +44,8 @@ part of a package.
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#noemptyfiles}
-\hypertarget{e0006----hidden-directories-not-allowed}{%
\subsection{E0006 -\/- Hidden directories not
-allowed}\label{e0006----hidden-directories-not-allowed}}
+allowed}\label{e0006----hidden-directories-not-allowed}
A package should not contain hidden directories, neither in the package
tree nor in the TDS zip archive.
@@ -59,9 +53,8 @@ tree nor in the TDS zip archive.
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#noauxfiles}
-\hypertarget{e0007----hidden-files-not-allowed}{%
\subsection{E0007 -\/- Hidden files not
-allowed}\label{e0007----hidden-files-not-allowed}}
+allowed}\label{e0007----hidden-files-not-allowed}
A package should not contain hidden files, neither in the package tree
nor in the TDS zip archive.
@@ -69,9 +62,8 @@ nor in the TDS zip archive.
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#noauxfiles}
-\hypertarget{e0008----temporary-file-detected}{%
\subsection{E0008 -\/- Temporary file
-detected}\label{e0008----temporary-file-detected}}
+detected}\label{e0008----temporary-file-detected}
A temporary file was detected. These are typically files created by TeX
\& friends and should not be part of a package.
@@ -81,9 +73,8 @@ Temporary files will also be detected in a TDS zip archive.
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#noauxfiles}
-\hypertarget{e0009----package-doesnt-contain-a-readme-file}{%
-\subsection{E0009 -\/- Package doesn't contain a README
-file}\label{e0009----package-doesnt-contain-a-readme-file}}
+\subsection{E0009 -\/- Package doesn\textquotesingle t contain a README
+file}\label{e0009----package-doesnt-contain-a-readme-file}
A package must contain at least one of \texttt{README},
\texttt{README.md} or \texttt{README.txt} file.
@@ -91,15 +82,13 @@ A package must contain at least one of \texttt{README},
For more details refer to:
\url{http://mirrors.ibiblio.org/CTAN/help/ctan/CTAN-upload-addendum.html\#readme}
-\hypertarget{e0010----broken-symlink-detected}{%
\subsection{E0010 -\/- Broken symlink
-detected}\label{e0010----broken-symlink-detected}}
+detected}\label{e0010----broken-symlink-detected}
A broken symlink was detected.
-\hypertarget{e0011----wrong-permission-for-directory}{%
\subsection{E0011 -\/- Wrong permission for
-directory}\label{e0011----wrong-permission-for-directory}}
+directory}\label{e0011----wrong-permission-for-directory}
Directories should have rwx for the owner and at least \texttt{r-x} for
others (i.e. world readable).
@@ -107,9 +96,8 @@ others (i.e. world readable).
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#filepermissions}
-\hypertarget{e0012----crlf-line-endings-detected}{%
\subsection{E0012 -\/- CRLF line endings
-detected}\label{e0012----crlf-line-endings-detected}}
+detected}\label{e0012----crlf-line-endings-detected}
The file specified in the error message contains CRLF line endings. Text
files should have UNIX style line endings.
@@ -117,37 +105,32 @@ files should have UNIX style line endings.
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#crlf}
-\hypertarget{e0013----socket-special-fie-detected}{%
\subsection{E0013 -\/- Socket special fie
-detected}\label{e0013----socket-special-fie-detected}}
+detected}\label{e0013----socket-special-fie-detected}
The file specified in the error message is a socket special file which
is not allowed.
-\hypertarget{e0014----fifo-special-file-detected}{%
\subsection{E0014 -\/- Fifo special file
-detected}\label{e0014----fifo-special-file-detected}}
+detected}\label{e0014----fifo-special-file-detected}
The file specified in the error message is a fifo special file which is
not allowed.
-\hypertarget{e0015----bloch-device-file-detected}{%
\subsection{E0015 -\/- Bloch device file
-detected}\label{e0015----bloch-device-file-detected}}
+detected}\label{e0015----bloch-device-file-detected}
The file specified in the error message is a block device file which is
not allowed.
-\hypertarget{e0016----character-device-file-detected}{%
\subsection{E0016 -\/- Character device file
-detected}\label{e0016----character-device-file-detected}}
+detected}\label{e0016----character-device-file-detected}
The file specified in the error message is a character device file which
is not allowed.
-\hypertarget{e0017----pdf-document-is-in-error}{%
\subsection{E0017 -\/- PDF document is in
-error}\label{e0017----pdf-document-is-in-error}}
+error}\label{e0017----pdf-document-is-in-error}
The PDF document mentioned in the message is in error.
@@ -167,20 +150,18 @@ Syntax Error: Couldn't find trailer dictionary
Syntax Error: Couldn't read xref table
\end{verbatim}
-\hypertarget{e0018----unwanted-directory-detected}{%
\subsection{E0018 -\/- Unwanted directory
-detected}\label{e0018----unwanted-directory-detected}}
+detected}\label{e0018----unwanted-directory-detected}
A directory was detected which should not be part of a package. Example:
\texttt{\_\_MACOSX}
-\hypertarget{e0019----generated-file-detected}{%
\subsection{E0019 -\/- Generated file
-detected}\label{e0019----generated-file-detected}}
+detected}\label{e0019----generated-file-detected}
-In order to avoid redundancy we don't want to have included files in a
-package which easily can be generated from other files in the
-submission.
+In order to avoid redundancy we don\textquotesingle t want to have
+included files in a package which easily can be generated from other
+files in the submission.
Exceptions are the \texttt{README} files of the package, i.e.
\texttt{README}, \texttt{README.md} or \texttt{README.txt},
@@ -192,10 +173,9 @@ directory tree.
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#nogeneratedfiles}
-\hypertarget{e0020----unwanted-directory-detected-in-the-top-level-directory-in-tds-zip-archive}{%
\subsection{E0020 -\/- Unwanted directory detected in the top level
directory in TDS zip
-archive}\label{e0020----unwanted-directory-detected-in-the-top-level-directory-in-tds-zip-archive}}
+archive}\label{e0020----unwanted-directory-detected-in-the-top-level-directory-in-tds-zip-archive}
The name of a top level directory of a TDS archive must be one of those
listed here: \texttt{asymptote}, \texttt{bibtex}, \texttt{chktex},
@@ -208,30 +188,26 @@ listed here: \texttt{asymptote}, \texttt{bibtex}, \texttt{chktex},
Any other other directory at the top level is an error.
-\hypertarget{e0021----error-when-reading-a-file}{%
\subsection{E0021 -\/- Error when reading a
-file}\label{e0021----error-when-reading-a-file}}
+file}\label{e0021----error-when-reading-a-file}
An error was encountered when reading the file specified in the message.
-\hypertarget{e0022----check-of-an-url-in-a-readme-file-failed}{%
\subsection{E0022 -\/- Check of an URL in a README file
-failed}\label{e0022----check-of-an-url-in-a-readme-file-failed}}
+failed}\label{e0022----check-of-an-url-in-a-readme-file-failed}
URL checking is in effect. An error occcurred when trying to retrieve an
URL which was found in the specified \texttt{README} file.
-\hypertarget{e0023----follow-up-error-when-trying-to-read-a-directory-with-insufficient-permissions}{%
\subsection{E0023 -\/- Follow up error when trying to read a directory
with insufficient
-permissions}\label{e0023----follow-up-error-when-trying-to-read-a-directory-with-insufficient-permissions}}
+permissions}\label{e0023----follow-up-error-when-trying-to-read-a-directory-with-insufficient-permissions}
Error which is a follow-up error. For instance, when a directory could
not be read.
-\hypertarget{e0024----tds-zip-archive-has-wrong-permissions}{%
\subsection{E0024 -\/- TDS zip archive has wrong
-permissions}\label{e0024----tds-zip-archive-has-wrong-permissions}}
+permissions}\label{e0024----tds-zip-archive-has-wrong-permissions}
The TDS zip archive should have at least \texttt{r-\/-} for the owner
and at least \texttt{r-\/-} for others (i.e. world readable).
@@ -239,37 +215,34 @@ and at least \texttt{r-\/-} for others (i.e. world readable).
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#filepermissions}
-\hypertarget{e0025----duplicate-names-when-ignoring-letter-case-for-files-or-directories}{%
\subsection{E0025 -\/- Duplicate names when ignoring letter case for
files or
-directories}\label{e0025----duplicate-names-when-ignoring-letter-case-for-files-or-directories}}
+directories}\label{e0025----duplicate-names-when-ignoring-letter-case-for-files-or-directories}
As there are operating systems which do not distinguish between
-\texttt{myfile} and \texttt{MYFILE} we don't want to have file names in
-a directory which are the same after converting to lower case.
+\texttt{myfile} and \texttt{MYFILE} we don\textquotesingle t want to
+have file names in a directory which are the same after converting to
+lower case.
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#filenames}
-\hypertarget{e0026----files-not-in-tds-or-different-in-tds-and-non-install-tree}{%
\subsection{E0026 -\/- Files not in TDS or different in TDS and
non-install
-tree}\label{e0026----files-not-in-tds-or-different-in-tds-and-non-install-tree}}
+tree}\label{e0026----files-not-in-tds-or-different-in-tds-and-non-install-tree}
The file mentioned in the error message is either not existing in the
TDS zip archive, or it is different to the one in the non-install tree
-\hypertarget{e0027----an-io-error-occurred}{%
\subsection{E0027 -\/- An I/O error
-occurred}\label{e0027----an-io-error-occurred}}
+occurred}\label{e0027----an-io-error-occurred}
Some kind of I/O error occurred. If you believe there is an error in
\texttt{pkgcheck} please contact the author.
-\hypertarget{e0028----a-path-name-in-a-tds-zip-archive-must-contain-the-package-name}{%
\subsection{E0028 -\/- A path name in a TDS zip archive must contain the
package
-name}\label{e0028----a-path-name-in-a-tds-zip-archive-must-contain-the-package-name}}
+name}\label{e0028----a-path-name-in-a-tds-zip-archive-must-contain-the-package-name}
The path names in a TDS zip archive must contain the package name.
@@ -283,9 +256,8 @@ source/latex/somepkg/somepkg.dtx
...
\end{verbatim}
-\hypertarget{e0029----readme-file--encoding-with-bom-detected}{%
\subsection{\texorpdfstring{E0029 -\/- README file: encoding with BOM
-detected}{E0029 -\/- README file: encoding with BOM detected}}\label{e0029----readme-file--encoding-with-bom-detected}}
+detected}{E0029 -\/- README file: encoding with BOM detected}}\label{e0029----readme-file--encoding-with-bom-detected}
A README file should be either ASCII or UTF-8 without BOM(byte order
mark)
@@ -293,55 +265,48 @@ mark)
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#readme}
-\hypertarget{e0030----a-symlink-was-found-which-points-outside-of-the-package-directory-tree}{%
\subsection{E0030 -\/- A symlink was found which points outside of the
package directory
-tree}\label{e0030----a-symlink-was-found-which-points-outside-of-the-package-directory-tree}}
+tree}\label{e0030----a-symlink-was-found-which-points-outside-of-the-package-directory-tree}
A symlink must not point to a file or directory outside of the package
directory tree.
-\hypertarget{e0031----file-name-contains-invalid-utf-8-characters}{%
\subsection{E0031 -\/- File name contains invalid UTF-8
-character(s)}\label{e0031----file-name-contains-invalid-utf-8-characters}}
+character(s)}\label{e0031----file-name-contains-invalid-utf-8-characters}
A file name contains invalid UTF-8 character(s).
-\hypertarget{e0033----error-when-unpacking-tds-archive}{%
\subsection{E0033 -\/- Error when unpacking tds
-archive}\label{e0033----error-when-unpacking-tds-archive}}
+archive}\label{e0033----error-when-unpacking-tds-archive}
In order to investigate the contents of the TDS zip archive
\texttt{pkgcheck} unpacks the TDS zip archive to a temporary location
which failed for the reason given in the error message.
-\hypertarget{e0034----unwanted-file-detected-in-the-top-level-directory-in-tds-zip-archive}{%
\subsection{E0034 -\/- Unwanted file detected in the top level directory
in TDS zip
-archive}\label{e0034----unwanted-file-detected-in-the-top-level-directory-in-tds-zip-archive}}
+archive}\label{e0034----unwanted-file-detected-in-the-top-level-directory-in-tds-zip-archive}
A top level directory of a TDS archive should only contain certain
directories but no files.
-\hypertarget{e0035----unwanted-tds-archive-detected-in-package-directory-tree}{%
\subsection{E0035 -\/- Unwanted TDS archive detected in package
directory
-tree}\label{e0035----unwanted-tds-archive-detected-in-package-directory-tree}}
+tree}\label{e0035----unwanted-tds-archive-detected-in-package-directory-tree}
A package directory should not contain a TDS zip archive.
-\hypertarget{e0036----dtxins-files-found-in-wrong-directory-in-tds-zip-archive}{%
\subsection{E0036 -\/- .dtx/.ins files found in wrong directory in TDS
zip
-archive}\label{e0036----dtxins-files-found-in-wrong-directory-in-tds-zip-archive}}
+archive}\label{e0036----dtxins-files-found-in-wrong-directory-in-tds-zip-archive}
In a TDS zip archive a \texttt{.dtx} resp. \texttt{.ins} file must be in
a subdirectory of either of \texttt{source/} or \texttt{doc/} top level
directories.
-\hypertarget{e0037----cr-line-endings-detected}{%
\subsection{E0037 -\/- CR line endings
-detected}\label{e0037----cr-line-endings-detected}}
+detected}\label{e0037----cr-line-endings-detected}
The file specified in the error message contains CR line endings. Text
files should have UNIX style line endings.
@@ -349,10 +314,9 @@ files should have UNIX style line endings.
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#crlf}
-\hypertarget{e0038----file-has-inconsistent-line-endings-cr-x-lf-y-crlf-z}{%
\subsection{E0038 -\/- File has inconsistent line endings: CR: x, LF: y,
CRLF:
-z}\label{e0038----file-has-inconsistent-line-endings-cr-x-lf-y-crlf-z}}
+z}\label{e0038----file-has-inconsistent-line-endings-cr-x-lf-y-crlf-z}
The file specified in the error message contains CR line endings. Text
files should have UNIX style line endings.
@@ -360,16 +324,14 @@ files should have UNIX style line endings.
For more details refer to:
\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#crlf}
-\hypertarget{e0039----no-doc-directory-found-in-the-top-level-directory-of-the-tds-zip-archive}{%
\subsection{E0039 -\/- No doc/ directory found in the top level
directory of the TDS zip
-archive}\label{e0039----no-doc-directory-found-in-the-top-level-directory-of-the-tds-zip-archive}}
+archive}\label{e0039----no-doc-directory-found-in-the-top-level-directory-of-the-tds-zip-archive}
A TDS zip archive is required to contain a top level directory doc/.
-\hypertarget{e0040----too-few-top-level-directories-in-the-tds-zip-archive}{%
\subsection{E0040 -\/- Too few top level directories in the TDS zip
-archive}\label{e0040----too-few-top-level-directories-in-the-tds-zip-archive}}
+archive}\label{e0040----too-few-top-level-directories-in-the-tds-zip-archive}
The top level directory of a TDS zip archive must contain at least a
\texttt{doc} directory and one or more of the following directories:
@@ -383,16 +345,26 @@ The top level directory of a TDS zip archive must contain at least a
Any other other directory at the top level is an error.
-\hypertarget{e0041----one-or-more-map-file-found-for-the-package-but-none-of-them-is-in-a-path-starting-with-fontsmapdvips}{%
\subsection{E0041 -\/- One or more map file found for the package but
none of them is in a path starting with
-fonts/map/dvips}\label{e0041----one-or-more-map-file-found-for-the-package-but-none-of-them-is-in-a-path-starting-with-fontsmapdvips}}
+fonts/map/dvips}\label{e0041----one-or-more-map-file-found-for-the-package-but-none-of-them-is-in-a-path-starting-with-fontsmapdvips}
At least one map file was found which was not in a path starting with
\texttt{fonts/map/dvips}.
-\hypertarget{e0042----config-file--doesnt-exist}{%
-\subsection{\texorpdfstring{E0042 -\/- Config file doesn't
-exist}{E0042 -\/- Config file doesn't exist}}\label{e0042----config-file--doesnt-exist}}
+\subsection{E0042 -\/- TDS zip archive: duplicate names when ignoring
+letter case for files or
+directories}\label{e0042----tds-zip-archive-duplicate-names-when-ignoring-letter-case-for-files-or-directories}
-The config file specified at the command line doesn't exist.
+As there are operating systems which do not distinguish between
+\texttt{myfile} and \texttt{MYFILE} we don\textquotesingle t want to
+have file names in a directory which are the same after converting to
+lower case.
+
+For more details refer to:
+\url{http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html\#filenames}
+
+\subsection{\texorpdfstring{E0043 -\/- Symlink found in TDS zip
+archive}{E0043 -\/- Symlink found in TDS zip archive}}\label{e0043----symlink--found-in-tds-zip-archive}
+
+The TDS zip archive contained a symlink which is not allowed.
diff --git a/support/pkgcheck/docs/pkgcheck.pdf b/support/pkgcheck/docs/pkgcheck.pdf
index 5d5259bab4..149531d21a 100644
--- a/support/pkgcheck/docs/pkgcheck.pdf
+++ b/support/pkgcheck/docs/pkgcheck.pdf
Binary files differ
diff --git a/support/pkgcheck/docs/title.tex b/support/pkgcheck/docs/title.tex
index 8dcca045d1..9745306ef3 100644
--- a/support/pkgcheck/docs/title.tex
+++ b/support/pkgcheck/docs/title.tex
@@ -1 +1 @@
-\title{pkgcheck Utility, v3.1.0}
+\title{pkgcheck Utility, v3.2.0}
diff --git a/support/pkgcheck/docs/warningsd.tex b/support/pkgcheck/docs/warningsd.tex
index 24667fc2f8..5e1565ef48 100644
--- a/support/pkgcheck/docs/warningsd.tex
+++ b/support/pkgcheck/docs/warningsd.tex
@@ -1,23 +1,20 @@
-\hypertarget{w0001----archive-as-package-file-detected}{%
\subsection{W0001 -\/- Archive as package file
-detected}\label{w0001----archive-as-package-file-detected}}
+detected}\label{w0001----archive-as-package-file-detected}
Usually a CTAN package should not contain archives. An exception are
situations where, for example, the source code of a package is kept in a
separate zip archive.
-\hypertarget{w0002----duplicate-files-detected}{%
\subsection{W0002 -\/- Duplicate files
-detected}\label{w0002----duplicate-files-detected}}
+detected}\label{w0002----duplicate-files-detected}
Duplicate files were detected which are listed right after this message.
The message is a warning message as something like this could not be
seen as an error in general.
-\hypertarget{w0003----same-named-files-detected-in-the-package-tree}{%
\subsection{W0003 -\/- Same named files detected in the package
-tree}\label{w0003----same-named-files-detected-in-the-package-tree}}
+tree}\label{w0003----same-named-files-detected-in-the-package-tree}
We like to have unique file names over the whole package directory tree.
When we discover same named files we report it as a warning. Common
@@ -28,9 +25,8 @@ names like \texttt{README}, \texttt{README.txt}, \texttt{README.md},
For more details refer to:
\url{http://mirror.utexas.edu/ctan/help/ctan/CTAN-upload-addendum.html\#uniquefilenames}
-\hypertarget{w0004-----encoding-with-bom-detected}{%
\subsection{\texorpdfstring{W0004 -\/- encoding with BOM
-detected}{W0004 -\/- encoding with BOM detected}}\label{w0004-----encoding-with-bom-detected}}
+detected}{W0004 -\/- encoding with BOM detected}}\label{w0004-----encoding-with-bom-detected}
A UTF encoded package file contains a BOM (byte order mark). Currently,
we issues a warning.
@@ -38,33 +34,29 @@ we issues a warning.
Nevertheless, the CTAN team discourages uses of BOM. Please be aware,
that in some future time this could be reagarded as an error.
-\hypertarget{w0005----very-large-file--with-size-size-detected-in-package}{%
\subsection{\texorpdfstring{W0005 -\/- Very large file with size
\texttt{\textless{}size\textgreater{}} detected in
-package}{W0005 -\/- Very large file with size \textless size\textgreater{} detected in package}}\label{w0005----very-large-file--with-size-size-detected-in-package}}
+package}{W0005 -\/- Very large file with size \textless size\textgreater{} detected in package}}\label{w0005----very-large-file--with-size-size-detected-in-package}
(Experimental) We issue the message if there is a file is larger than
40MiB in the package directory tree.
-\hypertarget{w0006----very-large-file-with-size-size-detected-in-tds-zip-archive}{%
\subsection{\texorpdfstring{W0006 -\/- Very large file with size
\texttt{\textless{}size\textgreater{}} detected in TDS zip
-archive}{W0006 -\/- Very large file with size \textless size\textgreater{} detected in TDS zip archive}}\label{w0006----very-large-file-with-size-size-detected-in-tds-zip-archive}}
+archive}{W0006 -\/- Very large file with size \textless size\textgreater{} detected in TDS zip archive}}\label{w0006----very-large-file-with-size-size-detected-in-tds-zip-archive}
(Experimental) We issue the message if there is a file larger than 40MiB
in the TDS zip archive.
-\hypertarget{w0007----empty-directory-detected-in-the-tds-zip-archive}{%
\subsection{W0007 -\/- Empty directory detected in the TDS zip
-archive}\label{w0007----empty-directory-detected-in-the-tds-zip-archive}}
+archive}\label{w0007----empty-directory-detected-in-the-tds-zip-archive}
Empty directories in a TDS zip archive are discouraged. As they usually
don\textquotesingle t create errors in the distribution we issue a
warning only.
-\hypertarget{w0008----windows-file-has-unix-line-endings}{%
\subsection{W0008 -\/- Windows file has Unix line
-endings}\label{w0008----windows-file-has-unix-line-endings}}
+endings}\label{w0008----windows-file-has-unix-line-endings}
A Windows file with Unix line endings was detected.
@@ -82,19 +74,36 @@ We regard a file as a Windows file if its name ends with:
\texttt{.reg}
\end{itemize}
-\hypertarget{w0009----replacing-----with-the-same-from-config-file}{%
-\subsection{\texorpdfstring{W0009 -\/- Replacing -\textgreater{} ` with
-the same from config
-file",}{W0009 -\/- Replacing -\textgreater{} ` with the same from config file",}}\label{w0009----replacing-----with-the-same-from-config-file}}
+\subsection{\texorpdfstring{W0009 -\/- Replacing
+\texttt{\textless{}pkgname\textgreater{}\ -\textgreater{}\ \textless{}tpkg\textgreater{}}
+with the same from config
+file",}{W0009 -\/- Replacing \textless pkgname\textgreater{} -\textgreater{} \textless tpkg\textgreater{} with the same from config file",}}\label{w0009----replacing-pkgname---tpkg-with-the-same-from-config-file}
This message can only show up if \texttt{pkgcheck} got called with
\texttt{-\/-config}. Indicates that an entry in the \texttt{pkgcheck}
config file does the same as the hard-coded entry. This helps to keep a
clean config file.
-\hypertarget{w0010----hardlinks-detected-with-inode-}{%
\subsection{\texorpdfstring{W0010 -\/- Hardlinks detected with inode
-}{W0010 -\/- Hardlinks detected with inode }}\label{w0010----hardlinks-detected-with-inode-}}
+}{W0010 -\/- Hardlinks detected with inode }}\label{w0010----hardlinks-detected-with-inode-}
Hardlinks found in the package directory tree. The inode number will be
displayed
+
+\subsection{\texorpdfstring{W0011 -\/- has an mtime in the future by
+seconds, or \textless hours, minutes,
+seconds\textgreater{}}{W0011 -\/- has an mtime in the future by seconds, or \textless hours, minutes, seconds\textgreater{}}}\label{w0011-----has-an-mtime-in-the-future-by--seconds-or-hours-minutes-seconds}
+
+The file has a future modification time. This is most probably caused by
+the archiver tool which doesn\textquotesingle t pay attention to the
+timezone when adding the file to the archive
+
+The future time will be displayed in
+
+\begin{itemize}
+\tightlist
+\item
+ seconds
+\item
+ and hours, minutes and seconds
+\end{itemize}
diff --git a/support/pkgcheck/src/gparser.rs b/support/pkgcheck/src/gparser.rs
index 4c10ba9f7a..136f6f41b1 100644
--- a/support/pkgcheck/src/gparser.rs
+++ b/support/pkgcheck/src/gparser.rs
@@ -1,4 +1,3 @@
-
use log::*;
use pest::Parser;
diff --git a/support/pkgcheck/src/linkcheck.rs b/support/pkgcheck/src/linkcheck.rs
index ba0cd73cea..bd61239ef2 100644
--- a/support/pkgcheck/src/linkcheck.rs
+++ b/support/pkgcheck/src/linkcheck.rs
@@ -121,7 +121,7 @@ fn check_link(url: &str, fname: &str, urlhash: &Arc<Mutex<UrlHash>>, print_all:
match check_link_inner(&url, true) {
UrlStatus::UrlOk => {
let mut urlhash = urlhash.lock().unwrap();
- if let Some(mut hs) = urlhash.get_mut(&url) {
+ if let Some(hs) = urlhash.get_mut(&url) {
if print_all {
for p in hs.paths.iter() {
print_ok(super::ARGS.no_colors, &url, p);
@@ -132,7 +132,7 @@ fn check_link(url: &str, fname: &str, urlhash: &Arc<Mutex<UrlHash>>, print_all:
}
UrlStatus::UrlError(e) => {
let mut urlhash = urlhash.lock().unwrap();
- if let Some(mut hs) = urlhash.get_mut(&url) {
+ if let Some(hs) = urlhash.get_mut(&url) {
for p in hs.paths.iter() {
e0022!(p, e);
}
diff --git a/support/pkgcheck/src/main.rs b/support/pkgcheck/src/main.rs
index 9ff8390f9c..6ad786dcd9 100644
--- a/support/pkgcheck/src/main.rs
+++ b/support/pkgcheck/src/main.rs
@@ -26,10 +26,10 @@ use std::fmt::Display;
use std::str;
use utils::*;
+use scoped_threadpool::Pool;
use serde::{Deserialize, Serialize};
-use std::os::unix::fs::MetadataExt;
use std::borrow::Cow;
-use scoped_threadpool::Pool;
+use std::os::unix::fs::MetadataExt;
use tempfile::Builder;
@@ -54,16 +54,92 @@ use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
use rustc_hash::{FxHashMap, FxHashSet};
+use std::time::SystemTime;
use std::fmt::Arguments;
use std::sync::mpsc::{channel, Sender};
-use clap::{Command, CommandFactory, Parser, ValueHint};
-use clap_complete::{generate, Generator, Shell};
+use clap::builder::PossibleValue;
+use clap::{Command, CommandFactory, Parser, ValueEnum, ValueHint};
+//use clap_complete::{generate, Generator, Shell};
+use clap_complete::{shells, Generator};
+use clap_complete_nushell::Nushell;
#[cfg(unix)]
use walkdir::{DirEntry, WalkDir};
+/// Shell with auto-generated completion script available.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+pub enum Shell {
+ /// Bourne Again SHell (bash)
+ Bash,
+ /// Elvish shell
+ Elvish,
+ /// Friendly Interactive SHell (fish)
+ Fish,
+ /// PowerShell
+ PowerShell,
+ /// Z SHell (zsh)
+ Zsh,
+ /// Nu shell (nu)
+ Nu,
+}
+
+impl Generator for Shell {
+ fn file_name(&self, name: &str) -> String {
+ match self {
+ Shell::Bash => shells::Bash.file_name(name),
+ Shell::Elvish => shells::Elvish.file_name(name),
+ Shell::Fish => shells::Fish.file_name(name),
+ Shell::PowerShell => shells::PowerShell.file_name(name),
+ Shell::Zsh => shells::Zsh.file_name(name),
+ Shell::Nu => Nushell.file_name(name),
+ }
+ }
+
+ fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::Write) {
+ match self {
+ Shell::Bash => shells::Bash.generate(cmd, buf),
+ Shell::Elvish => shells::Elvish.generate(cmd, buf),
+ Shell::Fish => shells::Fish.generate(cmd, buf),
+ Shell::PowerShell => shells::PowerShell.generate(cmd, buf),
+ Shell::Zsh => shells::Zsh.generate(cmd, buf),
+ Shell::Nu => Nushell.generate(cmd, buf),
+ }
+ }
+}
+
+// Hand-rolled so it can work even when `derive` feature is disabled
+impl ValueEnum for Shell {
+ fn value_variants<'a>() -> &'a [Self] {
+ &[
+ Shell::Bash,
+ Shell::Elvish,
+ Shell::Fish,
+ Shell::PowerShell,
+ Shell::Zsh,
+ Shell::Nu,
+ ]
+ }
+
+ fn to_possible_value<'a>(&self) -> Option<PossibleValue> {
+ Some(match self {
+ Shell::Bash => PossibleValue::new("bash"),
+ Shell::Elvish => PossibleValue::new("elvish"),
+ Shell::Fish => PossibleValue::new("fish"),
+ Shell::PowerShell => PossibleValue::new("powershell"),
+ Shell::Zsh => PossibleValue::new("zsh"),
+ Shell::Nu => PossibleValue::new("nu"),
+ })
+ }
+}
+
+fn is_future_mtime(now: SystemTime, mtime: SystemTime) -> bool {
+ mtime > now
+ //mtime > now + Duration::new(1800, 0)
+}
+
fn format_message(message: &Arguments, no_color: bool) -> Cow<'static, str> {
let msg_str = format!("{}", message);
if msg_str.starts_with(' ') {
@@ -99,7 +175,7 @@ pub struct PathExceptions {
fn get_config_file_name() -> Option<String> {
if let Some(config_file) = &ARGS.config_file {
if Path::new(&config_file).exists() {
- return Some(config_file.to_string())
+ return Some(config_file.to_string());
} else {
f0008!(config_file);
std::process::exit(1);
@@ -124,6 +200,8 @@ fn read_yaml_config() -> FxHashMap<String, String> {
for (p, q) in [
("armtex", "armenian"),
("babel-base", "babel"),
+ ("l3backend-dev", "latex-dev/l3backend"),
+ ("l3kernel-dev", "latex-dev/l3kernel"),
("latex-amsmath", "latex"),
("latex-amsmath-dev", "latex-dev"),
("latex-base", "latex"),
@@ -148,7 +226,10 @@ fn read_yaml_config() -> FxHashMap<String, String> {
let data = match fs::read_to_string(&config_filename) {
Ok(str) => str,
- Err(e) => { f0009!(&config_filename, e); std::process::exit(1); }
+ Err(e) => {
+ f0009!(&config_filename, e);
+ std::process::exit(1);
+ }
};
let path_exceptions = serde_yaml::from_str::<PathExceptions>(&data);
@@ -171,7 +252,10 @@ fn read_yaml_config() -> FxHashMap<String, String> {
}
pb
}
- Err(e) => { f0010!(e); std::process::exit(1);},
+ Err(e) => {
+ f0010!(e);
+ std::process::exit(1);
+ }
};
pkg_replacements
}
@@ -277,7 +361,11 @@ fn check_readme(dir_entry: &str, is_readme: &ReadmeKind, ft: &filemagic::Mimetyp
return false;
}
filemagic::Mimetype::Bom(b) => {
- e0029!(msg_name, b.as_ref());
+ // wegmit e0029!(msg_name, b.as_ref());
+ e0029!(
+ msg_name,
+ <unicode_bom::Bom as std::convert::AsRef<str>>::as_ref(b)
+ );
return false;
}
filemagic::Mimetype::Text(_le) => match File::open(dir_entry) {
@@ -329,55 +417,58 @@ fn _get_devno(entry: &DirEntry) -> u64 {
#[derive(Parser, Debug, PartialEq)]
#[clap(author, version, about, long_about = None)]
+#[command(arg_required_else_help(true))]
struct Args {
- #[clap(short = 'I', long = "ignore-dupes", help = "Ignore dupes")]
+ #[arg(short = 'I', long = "ignore-dupes", help = "Ignore dupes")]
ignore_dupes: bool,
- #[clap(long = "ignore-same-named", help = "Ignore same-named files")]
+ #[arg(long = "ignore-same-named", help = "Ignore same-named files")]
ignore_same_named: bool,
- #[clap(short = 'v', long = "verbose", help = "Verbose operation?")]
+ #[arg(short = 'v', long = "verbose", help = "Verbose operation?")]
verbose: bool,
- #[clap(short = 'L', long = "correct-le", help = "Correct line endings")]
+ #[arg(short = 'L', long = "correct-le", help = "Correct line endings")]
correct_le: bool,
- #[clap(short = 'C', long = "correct-perms", help = "Correct permissions")]
+ #[arg(short = 'C', long = "correct-perms", help = "Correct permissions")]
correct_perms: bool,
- #[clap(long = "no-colors", help = "Don't display messages in color")]
+ #[arg(long = "no-colors", help = "Don't display messages in color")]
no_colors: bool,
- #[clap(long = "urlcheck", help = "Check URLs found in README files")]
+ #[arg(long = "urlcheck", help = "Check URLs found in README files")]
urlcheck: bool,
- #[clap(short = 'T', long = "tds-zip", help = "tds zip archive", group = "tds", value_hint = ValueHint::FilePath)]
+ #[arg(short = 'T', long = "tds-zip", help = "tds zip archive", group = "tds", value_hint = ValueHint::FilePath)]
tds_zip: Option<String>,
- #[clap(
+ #[arg(
short = 'e',
long = "explain",
help = "Explain error or warning message",
group = "only_one"
)]
explain: Option<String>,
- #[clap(
+ #[arg(
long = "explain-all",
help = "Explains all error or warning messages",
group = "only_one"
)]
explain_all: bool,
- #[clap(long = "generate-completion", group = "only_one", value_enum)]
+ #[arg(long = "generate-completion", group = "only_one", value_enum)]
generator: Option<Shell>,
- #[clap(
+ #[arg(
long = "show-temp-endings",
help = "Show file endings for temporary files",
group = "only_one"
)]
show_tmp_endings: bool,
- #[clap(short = 'd', long = "package-dir", help = "Package directory", value_hint = ValueHint::DirPath)]
+ #[arg(short = 'd', long = "package-dir", help = "Package directory", value_hint = ValueHint::DirPath)]
pkg_dir: Option<String>,
- #[clap(long = "config-file", help = "Specify config file to use", value_hint = ValueHint::FilePath)]
+ #[arg(long = "config-file", help = "Specify config file to use", value_hint = ValueHint::FilePath)]
config_file: Option<String>,
}
// In the pas we took care to avoid visiting a single inode twice, which takes care of (false positive) hardlinks.
// Now we want to know if there is a hardlink in the package directory
#[cfg(unix)]
-fn check_inode(set: &mut FxHashMap<(u64, u64), Vec<String>>, filename: &str, meta: &Metadata) {
- set.entry((get_devno(meta), meta.ino())).or_insert_with(Vec::new).push(filename.to_string());
+fn check_inode(set: &mut FxHashMap<(u64, u64), Vec<String>>, filename: &str, meta: &Metadata) {
+ set.entry((get_devno(meta), meta.ino()))
+ .or_default()
+ .push(filename.to_string());
}
#[cfg(not(unix))]
@@ -387,6 +478,8 @@ fn check_inode(_: &mut FxHashSet<u64>, _: &Metadata) -> bool {
static ARGS: Lazy<Args> = Lazy::new(Args::parse);
static ERROR_OCCURRED: AtomicBool = AtomicBool::new(false);
+//Get the current time
+static NOW: Lazy<SystemTime> = Lazy::new(SystemTime::now);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DPath {
@@ -434,14 +527,15 @@ impl DupPath {
type DupHashes = FxHashMap<(u64, Vec<u8>), DupPath>;
fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
- generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
+ //generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
+ clap_complete::generate(gen, cmd, "pkgcheck", &mut std::io::stdout());
}
fn main() {
let _ = setup_logger(ARGS.no_colors);
// read yaml config file if one is given explicitly or implicitly
- let pkg_replace : FxHashMap<String, String> = read_yaml_config();
+ let pkg_replace: FxHashMap<String, String> = read_yaml_config();
match &ARGS.explain {
None => (),
@@ -716,9 +810,16 @@ fn check_tds_archive_name(tds_zip: &Option<String>) -> Option<String> {
// }
// }
-fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_replace: &FxHashMap<String, String>) {
+fn check_tds_archive(
+ pkg_name: &str,
+ tds_zip: &str,
+ hashes: &DupHashes,
+ pkg_replace: &FxHashMap<String, String>,
+) {
i0003!(tds_zip);
+ let mut lcnames: FxHashMap<PathBuf, Vec<(PathBuf, FileKind)>> = FxHashMap::default();
+
let dir_entry = Path::new(tds_zip);
let p = get_perms(dir_entry);
if !owner_has(p, 4) || !others_have(p, 4) || x_bit_set(p) {
@@ -731,12 +832,6 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
let ut = Utils::new(utils::CheckType::Tds);
- let real_pkg_name = if let Some(real_name) = pkg_replace.get(pkg_name) {
- real_name
- } else {
- pkg_name
- };
-
let tmp_dir = match Builder::new().prefix("pkgcheck").tempdir() {
Ok(tdir) => tdir,
Err(e) => {
@@ -777,7 +872,7 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
let path = dir_entry.path().to_path_buf();
let sizeref = &mut sizes;
- sizeref.entry(fsize).or_insert_with(Vec::new).push(path);
+ sizeref.entry(fsize).or_default().push(path);
};
let mut map_files_found = false;
@@ -846,6 +941,15 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
}
};
+ // let mtime = meta.modified().unwrap();
+ // if is_future_mtime(*NOW, mtime) {
+ // let diff = mtime.duration_since(*NOW).unwrap();
+ // println!(
+ // "{} has an mtime in the future by {} seconds",
+ // &file_name,
+ // diff.as_secs()
+ // );
+ // }
let ft = get_filetype(&dir_entry);
if let FType::Error(e) = ft {
@@ -853,12 +957,27 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
continue;
}
+ // this is the path name without the temporary part
+ // from unpacking the TDS zip archive
let dir_entry_display = if dir_entry.depth() == 0 {
&dir_entry_str[tmp_dir_offset - 1..]
} else {
&dir_entry_str[tmp_dir_offset..]
};
+ let filetype = match ft {
+ FType::Directory => FileKind::Directory,
+ FType::Regular => FileKind::File,
+ FType::Symlink => {
+ e0043!(dir_entry_display);
+ continue;
+ }
+ _ => panic!(
+ "Unexpected file type for {} in zip archive",
+ dir_entry_display
+ ),
+ };
+ register_duplicate_filename(&mut lcnames, dir_entry_display, filetype);
ut.check_for_temporary_file(dir_entry_display);
// In the top level directory of a TDS zip archive
@@ -912,11 +1031,14 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
}
// if the path doesn't contain a man page...
- if !dir_entry_str.contains("/man/") {
- let pkg_name_s = format!("/{}/", real_pkg_name);
- // ...then we want to have the package name in the path
- if !dir_entry_str.contains(&pkg_name_s) {
- e0028!(real_pkg_name, dir_entry_display);
+ if !dir_entry_str.contains("/man/") && !dir_entry_str.contains(pkg_name) {
+ if let Some(real_name) = pkg_replace.get(pkg_name) {
+ let pkg_name_s = format!("/{}/", real_name);
+ if !dir_entry_str.contains(&pkg_name_s) {
+ e0028!(pkg_name_s, dir_entry_display);
+ }
+ } else {
+ e0028!(pkg_name, dir_entry_display);
}
}
@@ -952,10 +1074,7 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
let hashref = &mut tds_hashes;
scope.execute(move || {
for (size, path, hash) in rx.iter() {
- hashref
- .entry((size, hash))
- .or_insert_with(Vec::new)
- .push(path);
+ hashref.entry((size, hash)).or_default().push(path);
}
});
@@ -974,6 +1093,7 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
e0026!(p);
}
}
+ print_casefolding_tds(&lcnames);
}
fn get_extension_from_filename(filename: &str) -> Option<&str> {
@@ -1073,6 +1193,21 @@ enum ReadmeKind {
Symlink(String),
}
+fn register_duplicate_filename(
+ lcnames: &mut FxHashMap<PathBuf, Vec<(PathBuf, FileKind)>>,
+ dir_entry: &str,
+ fk: FileKind,
+) {
+ let lc_dir_entry_str = dir_entry.to_lowercase();
+ if let Some(_dir_name) = filename(dir_entry) {
+ // let lcnref = &mut lcnames;
+ lcnames
+ .entry(PathBuf::from(lc_dir_entry_str))
+ .or_default()
+ .push((PathBuf::from(&dir_entry), fk));
+ }
+}
+
fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
let mut lcnames: FxHashMap<PathBuf, Vec<(PathBuf, FileKind)>> = FxHashMap::default();
@@ -1125,6 +1260,12 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
// above in the definition of dir_entry_str
let file_name = dir_entry.file_name().to_str().unwrap().to_string();
+ let mtime = meta.modified().unwrap();
+ if is_future_mtime(*NOW, mtime) {
+ let diff = mtime.duration_since(*NOW).unwrap();
+ w0011!(&file_name, diff.as_secs(), &utils::format_duration(&diff));
+ }
+
// we check for weird stuff like socket files aso.
let ft = get_filetype(&dir_entry);
if found_unwanted_filetype(dir_entry_str, &ft) {
@@ -1136,6 +1277,7 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
// 1. dealing with symlinks
if ft == FType::Symlink {
match get_symlink(&dir_entry) {
+ // broken symlink
Ok(None) => {
e0010!(&dir_entry_str);
continue;
@@ -1147,22 +1289,18 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
Ok(Some(p)) => {
let pd: String =
p.canonicalize().unwrap().to_string_lossy().to_string();
+ // symlink pointing to outside of the package directory tree
if !pd.starts_with(&root_absolute) {
e0030!(&dir_entry_str, p.display());
continue;
}
- let lc_dir_entry_str = dir_entry_str.to_lowercase();
if let Some(_dir_name) = filename(dir_entry_str) {
- let lcnref = &mut lcnames;
- lcnref
- .entry(PathBuf::from(lc_dir_entry_str))
- .or_insert_with(Vec::new)
- .push((
- PathBuf::from(&dir_entry_str),
- //FileKind::Symlink(&dir_entry_str.into()),
- FileKind::Symlink(pd.clone()),
- ));
+ register_duplicate_filename(
+ &mut lcnames,
+ dir_entry_str,
+ FileKind::Symlink(pd.clone()),
+ );
}
if is_readme(&file_name) {
readme_found = true;
@@ -1184,14 +1322,13 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
// 2. dealing with directories
if ft == FType::Directory {
- let lc_dir_entry_str = dir_entry_str.to_lowercase();
+ //let lc_dir_entry_str = dir_entry_str.to_lowercase();
if let Some(_dir_name) = filename(dir_entry_str) {
- let lcnref = &mut lcnames;
-
- lcnref
- .entry(PathBuf::from(lc_dir_entry_str))
- .or_insert_with(Vec::new)
- .push((PathBuf::from(dir_entry_str), FileKind::Directory));
+ register_duplicate_filename(
+ &mut lcnames,
+ dir_entry_str,
+ FileKind::Directory,
+ );
}
if !owner_has(p, 5) || !others_have(p, 5) {
@@ -1222,7 +1359,7 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
doubleref
.entry(PathBuf::from(file_name))
- .or_insert_with(Vec::new)
+ .or_default()
.push(PathBuf::from(&dir_entry_str));
}
@@ -1258,14 +1395,7 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
));
}
- let lc_dir_entry_str = dir_entry_str.to_lowercase();
-
- let lcnref = &mut lcnames;
-
- lcnref
- .entry(PathBuf::from(lc_dir_entry_str))
- .or_insert_with(Vec::new)
- .push((PathBuf::from(&dir_entry_str), FileKind::File));
+ register_duplicate_filename(&mut lcnames, dir_entry_str, FileKind::File);
}
Err(e) => {
@@ -1293,7 +1423,7 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
let sizeref = &mut sizes;
let path = path.clone();
- sizeref.entry(fsize).or_insert_with(Vec::new).push(path);
+ sizeref.entry(fsize).or_default().push(path);
};
for (path, (meta, _file_name, is_readme)) in file_names.iter() {
let dir_entry_str = match path.to_str() {
@@ -1362,38 +1492,37 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
}
fmm => error!("Should not occur: {} has {:?}", dir_entry_str, fmm),
},
- Some(_) | None => {
- match ft {
- filemagic::Mimetype::Text(LineEnding::Crlf) => {
- e0012!(&dir_entry_str);
- if ARGS.correct_le {
- fix_inconsistent_le(dir_entry_str);
- }
+ Some(_) | None => match ft {
+ filemagic::Mimetype::Text(LineEnding::Crlf) => {
+ e0012!(&dir_entry_str);
+ if ARGS.correct_le {
+ fix_inconsistent_le(dir_entry_str);
}
- filemagic::Mimetype::Text(LineEnding::Cr) => {
- e0037!(&dir_entry_str);
- if ARGS.correct_le {
- fix_inconsistent_le(dir_entry_str);
- }
+ }
+ filemagic::Mimetype::Text(LineEnding::Cr) => {
+ e0037!(&dir_entry_str);
+ if ARGS.correct_le {
+ fix_inconsistent_le(dir_entry_str);
}
- filemagic::Mimetype::Text(LineEnding::Mixed(0, 0, 0)) => (),
- filemagic::Mimetype::Text(LineEnding::Mixed(cr, lf, crlf)) => {
- //println!(">>>{}: {:?} {},{},{}", &dir_entry_str, ft, x, y, z);
- e0038!(&dir_entry_str, cr, lf, crlf);
- if ARGS.correct_le {
- fix_inconsistent_le(dir_entry_str);
- }
+ }
+ filemagic::Mimetype::Text(LineEnding::Mixed(0, 0, 0)) => (),
+ filemagic::Mimetype::Text(LineEnding::Mixed(cr, lf, crlf)) => {
+ e0038!(&dir_entry_str, cr, lf, crlf);
+ if ARGS.correct_le {
+ fix_inconsistent_le(dir_entry_str);
}
- filemagic::Mimetype::Text(LineEnding::Lf) => (),
- fmm => error!("Should not occur: {} has {:?}", dir_entry_str, fmm),
}
- }
+ filemagic::Mimetype::Text(LineEnding::Lf) => (),
+ fmm => error!("Should not occur: {} has {:?}", dir_entry_str, fmm),
+ },
}
}
filemagic::Mimetype::Bom(b) => {
- //println!("{}: {} with BOM detected", dir_entry_str, b.as_ref());
- w0004!(&dir_entry_str, b.as_ref());
+ w0004!(
+ &dir_entry_str,
+ <unicode_bom::Bom as std::convert::AsRef<str>>::as_ref(&b)
+ );
check_and_correct_perms(dir_entry_str, p);
}
filemagic::Mimetype::Binary | filemagic::Mimetype::Script(_) => {
@@ -1462,10 +1591,7 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
let hashref = &mut hashes;
scope.execute(move || {
for (size, path, hash) in rx.iter() {
- hashref
- .entry((size, hash))
- .or_insert_with(DupPath::new)
- .push(path);
+ hashref.entry((size, hash)).or_default().push(path);
}
});
@@ -1489,18 +1615,30 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
}
fn print_hardlinks(hashes: &FxHashMap<(u64, u64), Vec<String>>) {
- for ((_devid,inode), eles) in hashes.iter() {
+ for ((_devid, inode), eles) in hashes.iter() {
if eles.len() > 1 {
w0010!(inode);
for hfile in eles.iter() {
info!(" >>> {}", &hfile);
}
+ }
+ }
+}
+fn print_casefolding_tds(hashes: &FxHashMap<PathBuf, Vec<(PathBuf, FileKind)>>) {
+ for (k, eles) in hashes.iter() {
+ // println!("pcf_tds: {:?}, {:?}", k, &eles);
+ if eles.len() == 1 {
+ continue;
}
+ e0042!(k.display());
+
+ for (p, ty) in eles {
+ info!(" >>> {} ({})", p.display(), ty);
+ }
}
}
-
fn print_casefolding(hashes: &FxHashMap<PathBuf, Vec<(PathBuf, FileKind)>>) {
for (k, eles) in hashes.iter() {
//println!("pcf: {:?}, {:?}", k, &eles);
diff --git a/support/pkgcheck/src/messages/errorsd.rs b/support/pkgcheck/src/messages/errorsd.rs
index 39a8ccb357..532f0df371 100644
--- a/support/pkgcheck/src/messages/errorsd.rs
+++ b/support/pkgcheck/src/messages/errorsd.rs
@@ -528,3 +528,29 @@ fonts/map/dvips.
"#
)
}
+
+pub fn e0042d() {
+ error!(
+ r#"
+E0042 -- TDS zip archive: duplicate names when ignoring letter case for files or directories
+
+As there are operating systems which do not distinguish between myfile
+and MYFILE we don't want to have file names in a directory which are the
+same after converting to lower case.
+
+For more details refer to:
+http://mirror.ctan.org/help/ctan/CTAN-upload-addendum.html#filenames
+"#
+ )
+}
+
+pub fn e0043d() {
+ error!(
+ r#"
+E0043 -- Symlink found in TDS zip archive
+
+The TDS zip archive contained a symlink which is not allowed.
+"#
+ )
+}
+
diff --git a/support/pkgcheck/src/messages/fatald.rs b/support/pkgcheck/src/messages/fatald.rs
index 0558612c72..24ece06bad 100644
--- a/support/pkgcheck/src/messages/fatald.rs
+++ b/support/pkgcheck/src/messages/fatald.rs
@@ -107,4 +107,3 @@ rerun.
"#
)
}
-
diff --git a/support/pkgcheck/src/messages/informationd.rs b/support/pkgcheck/src/messages/informationd.rs
index 0eca9849a0..7a87378ad0 100644
--- a/support/pkgcheck/src/messages/informationd.rs
+++ b/support/pkgcheck/src/messages/informationd.rs
@@ -127,4 +127,3 @@ pkgcheck.yml config file like in the following example
"#
)
}
-
diff --git a/support/pkgcheck/src/messages/mod.rs b/support/pkgcheck/src/messages/mod.rs
index 721f27ff7b..265babc961 100644
--- a/support/pkgcheck/src/messages/mod.rs
+++ b/support/pkgcheck/src/messages/mod.rs
@@ -2,11 +2,11 @@ mod errorsd;
mod fatald;
mod informationd;
mod warningsd;
-use colored::Colorize;
use self::errorsd::*;
use self::fatald::*;
use self::informationd::*;
use self::warningsd::*;
+use colored::Colorize;
macro_rules! error_occurred {
() => {
@@ -14,7 +14,6 @@ macro_rules! error_occurred {
};
}
-
macro_rules! no_colors {
() => {
$crate::ARGS.no_colors
@@ -33,7 +32,10 @@ macro_rules! fatal {
macro_rules! f0001 {
() => {{
error_occurred!();
- eprintln!("{} Specify a directory to check (use option -d)", fatal!("F0001"),);
+ eprintln!(
+ "{} Specify a directory to check (use option -d)",
+ fatal!("F0001"),
+ );
}};
}
@@ -42,7 +44,8 @@ macro_rules! f0002 {
error_occurred!();
eprintln!(
"{} Specified directory {} does not exist. Exiting...",
- fatal!("F0002"), $fmt1
+ fatal!("F0002"),
+ $fmt1
);
}};
}
@@ -52,7 +55,8 @@ macro_rules! f0003 {
error_occurred!();
eprintln!(
"{} Specified TDS archive {} does not exist or is no file. Exiting...",
- fatal!("F0003"), $fmt1
+ fatal!("F0003"),
+ $fmt1
);
}};
}
@@ -62,7 +66,8 @@ macro_rules! f0004 {
error_occurred!();
eprintln!(
"{} File {} specified as TDS archive is no zip archive. Exiting...",
- fatal!("F0004"), $fmt1
+ fatal!("F0004"),
+ $fmt1
);
}};
}
@@ -72,7 +77,8 @@ macro_rules! f0005 {
error_occurred!();
eprintln!(
"{} Bad filename {} for the tds.zip archive. Exiting...",
- fatal!("F0005"), $fmt1
+ fatal!("F0005"),
+ $fmt1
);
}};
}
@@ -104,7 +110,8 @@ macro_rules! f0008 {
error_occurred!();
eprintln!(
"{} Config file {} doesn't exist. Exiting...",
- fatal!("F0007"), $fmt
+ fatal!("F0007"),
+ $fmt
);
}};
}
@@ -114,7 +121,9 @@ macro_rules! f0009 {
error_occurred!();
eprintln!(
"{} Error reading config file {}: {} Exiting...",
- fatal!("F0009"), $fmt1, $fmt2
+ fatal!("F0009"),
+ $fmt1,
+ $fmt2
);
}};
}
@@ -124,7 +133,8 @@ macro_rules! f0010 {
error_occurred!();
eprintln!(
"{} Config file's content could not be read properly: {} Exiting...",
- fatal!("F0010"), $fmt
+ fatal!("F0010"),
+ $fmt
);
}};
}
@@ -369,7 +379,10 @@ macro_rules! e0026 {
macro_rules! e0027 {
($fmt1:expr, $fmt2:expr) => {{
error_occurred!();
- error!("{} {}: An I/O error occurred -> {}", "E0027", $fmt1, $fmt2);
+ error!(
+ "{} {}: An I/O error occurred -> {}",
+ "E0027", $fmt1, $fmt2
+ );
}};
}
@@ -494,6 +507,22 @@ macro_rules! e0041 {
}};
}
+macro_rules! e0042 {
+ ($fmt:expr) => {{
+ error_occurred!();
+ error!(
+ "{} TDS zip archive: duplicate names when ignoring letter case for: {}",
+ "E0042", $fmt
+ );
+ }};
+}
+
+macro_rules! e0043 {
+ ($fmt:expr) => {{
+ error_occurred!();
+ error!("{} Symlink {} found in TDS zip archive", "E0043", $fmt);
+ }};
+}
macro_rules! w0001 {
($fmt:expr) => {{
@@ -569,9 +598,7 @@ macro_rules! w0009 {
error_occurred!();
warn!(
"{} Replacing `{} -> {}` with the same from config file",
- "W0009",
- $fmt1,
- $fmt2
+ "W0009", $fmt1, $fmt2
)
}};
}
@@ -582,6 +609,15 @@ macro_rules! w0010 {
}};
}
+macro_rules! w0011 {
+ ($fmt1:expr, $fmt2:expr, $fmt3:expr) => {{
+ warn!(
+ "{} {} has an mtime in the future by {} seconds, or {}",
+ "W0011", $fmt1, $fmt2, $fmt3
+ )
+ }};
+}
+
macro_rules! i0002 {
($fmt:expr) => {{
info!(
@@ -638,11 +674,7 @@ macro_rules! i0009 {
error_occurred!();
warn!(
"{} Updating entry `{} -> {}` with `{} -> {}` from config file",
- "I0009",
- $fmt1,
- $fmt2,
- $fmt1,
- $fmt3
+ "I0009", $fmt1, $fmt2, $fmt1, $fmt3
)
}};
}
@@ -701,6 +733,8 @@ pub fn explains(err: &str) {
"E0039" => e0039d(),
"E0040" => e0040d(),
"E0041" => e0041d(),
+ "E0042" => e0042d(),
+ "E0043" => e0043d(),
// "I0001" => i0001d!(),
"I0001" => i0001d(),
@@ -723,6 +757,7 @@ pub fn explains(err: &str) {
"W0008" => w0008d(),
"W0009" => w0009d(),
"W0010" => w0010d(),
+ "W0011" => w0011d(),
e => eprintln!(
"{} Unknown error code `{}` specified with option -e resp. --explain. Exiting...",
@@ -785,6 +820,8 @@ pub fn explains_all() {
explains("E0039");
explains("E0040");
explains("E0041");
+ explains("E0042");
+ explains("E0043");
explains("I0001");
explains("I0002");
@@ -806,4 +843,5 @@ pub fn explains_all() {
explains("W0008");
explains("W0009");
explains("W0010");
+ explains("W0011");
}
diff --git a/support/pkgcheck/src/messages/warningsd.rs b/support/pkgcheck/src/messages/warningsd.rs
index 6fe37835cf..176fc1d2de 100644
--- a/support/pkgcheck/src/messages/warningsd.rs
+++ b/support/pkgcheck/src/messages/warningsd.rs
@@ -111,7 +111,7 @@ We regard a file as a Windows file if its name ends with:
pub fn w0009d() {
warn!(
r#"
-W0009 -- Replacing -> ` with the same from config file",
+W0009 -- Replacing <pkgname> -> <tpkg> with the same from config file",
This message can only show up if pkgcheck got called with --config.
Indicates that an entry in the pkgcheck config file does the same as the
@@ -131,3 +131,20 @@ displayed
)
}
+pub fn w0011d() {
+ warn!(
+ r#"
+W0011 -- has an mtime in the future by seconds, or <hours, minutes, seconds>
+
+The file has a future modification time. This is most probably caused by
+the archiver tool which doesn't pay attention to the timezone when
+adding the file to the archive
+
+The future time will be displayed in
+
+- seconds
+- and hours, minutes and seconds
+"#
+ )
+}
+
diff --git a/support/pkgcheck/src/recode.rs b/support/pkgcheck/src/recode.rs
index 827da9587b..4d71ac5467 100644
--- a/support/pkgcheck/src/recode.rs
+++ b/support/pkgcheck/src/recode.rs
@@ -74,7 +74,7 @@ pub fn wrong_line_endings2crlf(fname: &str) -> Result<(), io::Error> {
};
// write back
- match hdl_out.write(&another_vec) {
+ match hdl_out.write_all(&another_vec) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
@@ -102,7 +102,7 @@ pub fn wrong_line_endings2lf(fname: &str) -> Result<(), io::Error> {
};
// write back
- match hdl_out.write(&another_vec) {
+ match hdl_out.write_all(&another_vec) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
diff --git a/support/pkgcheck/src/utils.rs b/support/pkgcheck/src/utils.rs
index b311453b66..e934c6d9d9 100644
--- a/support/pkgcheck/src/utils.rs
+++ b/support/pkgcheck/src/utils.rs
@@ -5,16 +5,17 @@ use std::fs::read_link;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::path::PathBuf;
+use std::time::Duration;
use walkdir::DirEntry;
use once_cell::sync::Lazy; // 1.3.1
use regex::Regex;
use std::borrow::Cow;
-use std::io;
use std::fs::File;
+use std::io;
use std::process::Command;
-use zip::result::ZipResult;
use std::sync::atomic::Ordering;
+use zip::result::ZipResult;
use std::fs;
@@ -121,7 +122,6 @@ impl Utils {
}
pub fn check_for_temporary_file(&self, dir_entry_str: &str) {
-
static RE: Lazy<Regex> = Lazy::new(regex_temporary_file_endings);
if RE.is_match(dir_entry_str) {
@@ -269,7 +269,7 @@ pub fn temp_file_endings() -> Vec<(String, String)> {
(".xref", "htlatex related"),
(".xray", "dump of \\show output"),
("~", "a file name ending with ~ (tilde) is temporary anyway"),
- // ( ".lyx~", "LyX related backup file" ),
+ ("Thumbs.db", "thumbnails file in Windows"),
];
v.into_iter()
@@ -474,6 +474,16 @@ pub fn dirname(entry: &str) -> Option<&str> {
}
#[test]
+fn test_format_duration() {
+ assert!(format_duration(&Duration::new(5, 0)) == String::from("5sec"));
+ assert!(format_duration(&Duration::new(105, 0)) == String::from("1min 45sec"));
+ assert!(format_duration(&Duration::new(3801, 0)) == String::from("1h 3min 21sec"));
+ assert!(format_duration(&Duration::new(25449, 0)) == String::from("7h 4min 9sec"));
+ assert!(format_duration(&Duration::new(108245, 0)) == String::from("1d 6h 4min 5sec"));
+ assert!(format_duration(&Duration::new(0, 0)) == String::from("0sec"));
+}
+
+#[test]
fn test_filename() {
assert!(filename("/etc/fstab") == Some("fstab"));
assert!(filename("fstab") == Some("fstab"));
@@ -482,6 +492,7 @@ fn test_filename() {
assert!(filename("/") == None);
}
+// We return the right part of a path name if it does not end with a `/`
pub fn filename(entry: &str) -> Option<&str> {
if entry.ends_with('/') {
return None;
@@ -502,3 +513,30 @@ pub fn basename(path: &str) -> Cow<str> {
None => path.into(),
}
}
+
+pub fn format_duration(duration: &Duration) -> String {
+ let seconds = duration.as_secs();
+ let days = seconds / 86400;
+ let hours = (seconds % 86400) / 3600;
+ let minutes = (seconds % 3600) / 60;
+ let seconds = seconds % 60;
+
+ let mut result = String::new();
+ if days > 0 {
+ result.push_str(&format!("{}d ", days));
+ }
+
+ if hours > 0 {
+ result.push_str(&format!("{}h ", hours));
+ }
+
+ if minutes > 0 {
+ result.push_str(&format!("{}min ", minutes));
+ }
+
+ if seconds > 0 || result.is_empty() {
+ result.push_str(&format!("{}sec", seconds));
+ }
+
+ return result.trim().to_string();
+}