diff options
Diffstat (limited to 'support/pkgcheck')
-rw-r--r-- | support/pkgcheck/CHANGES.md | 17 | ||||
-rw-r--r-- | support/pkgcheck/Cargo.toml | 63 | ||||
-rwxr-xr-x | support/pkgcheck/bin/pkgcheck | bin | 7266008 -> 8028168 bytes | |||
-rw-r--r-- | support/pkgcheck/docs/errorsd.tex | 154 | ||||
-rw-r--r-- | support/pkgcheck/docs/pkgcheck.pdf | bin | 89893 -> 90348 bytes | |||
-rw-r--r-- | support/pkgcheck/docs/title.tex | 2 | ||||
-rw-r--r-- | support/pkgcheck/docs/warningsd.tex | 53 | ||||
-rw-r--r-- | support/pkgcheck/src/gparser.rs | 1 | ||||
-rw-r--r-- | support/pkgcheck/src/linkcheck.rs | 4 | ||||
-rw-r--r-- | support/pkgcheck/src/main.rs | 338 | ||||
-rw-r--r-- | support/pkgcheck/src/messages/errorsd.rs | 26 | ||||
-rw-r--r-- | support/pkgcheck/src/messages/fatald.rs | 1 | ||||
-rw-r--r-- | support/pkgcheck/src/messages/informationd.rs | 1 | ||||
-rw-r--r-- | support/pkgcheck/src/messages/mod.rs | 76 | ||||
-rw-r--r-- | support/pkgcheck/src/messages/warningsd.rs | 19 | ||||
-rw-r--r-- | support/pkgcheck/src/recode.rs | 4 | ||||
-rw-r--r-- | support/pkgcheck/src/utils.rs | 46 |
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 Binary files differindex abaea81efc..293c66dd08 100755 --- a/support/pkgcheck/bin/pkgcheck +++ b/support/pkgcheck/bin/pkgcheck 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 Binary files differindex 5d5259bab4..149531d21a 100644 --- a/support/pkgcheck/docs/pkgcheck.pdf +++ b/support/pkgcheck/docs/pkgcheck.pdf 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(); +} |