summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2023-01-04 23:22:48 +0000
committerKarl Berry <karl@freefriends.org>2023-01-04 23:22:48 +0000
commitd6a9805ec5c502c43bbbfce3cb0d5bbd6bb4e758 (patch)
tree664ec597a6372d4131a0e2b60e9697888eefdfba
parent02d10a61d06b998bad3984a37e8ec40abed76d69 (diff)
xput also in bindir
git-svn-id: svn://tug.org/texlive/trunk@65465 c570f23f-e606-0410-a88d-b1316a301751
-rw-r--r--Build/source/texk/texlive/linked_scripts/Makefile.am1
-rw-r--r--Build/source/texk/texlive/linked_scripts/Makefile.in1
-rw-r--r--Build/source/texk/texlive/linked_scripts/scripts.lst1
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/xput/xput520
l---------Master/bin/aarch64-linux/xput1
l---------Master/bin/amd64-freebsd/xput1
l---------Master/bin/amd64-netbsd/xput1
l---------Master/bin/armhf-linux/xput1
l---------Master/bin/i386-cygwin/xput1
l---------Master/bin/i386-freebsd/xput1
l---------Master/bin/i386-linux/xput1
l---------Master/bin/i386-netbsd/xput1
l---------Master/bin/i386-solaris/xput1
l---------Master/bin/universal-darwin/xput1
l---------Master/bin/x86_64-cygwin/xput1
l---------Master/bin/x86_64-darwinlegacy/xput1
l---------Master/bin/x86_64-linux/xput1
l---------Master/bin/x86_64-linuxmusl/xput1
l---------Master/bin/x86_64-solaris/xput1
-rwxr-xr-xMaster/tlpkg/libexec/ctan2tds2
-rw-r--r--Master/tlpkg/tlpsrc/xput.tlpsrc1
21 files changed, 540 insertions, 1 deletions
diff --git a/Build/source/texk/texlive/linked_scripts/Makefile.am b/Build/source/texk/texlive/linked_scripts/Makefile.am
index 157368683e2..963669255a7 100644
--- a/Build/source/texk/texlive/linked_scripts/Makefile.am
+++ b/Build/source/texk/texlive/linked_scripts/Makefile.am
@@ -99,6 +99,7 @@ texmf_shell_scripts = \
tikztosvg/tikztosvg \
typeoutfileinfo/typeoutfileinfo.sh \
wordcount/wordcount.sh \
+ xput/xput \
xput/xputserver
texmf_other_scripts = \
diff --git a/Build/source/texk/texlive/linked_scripts/Makefile.in b/Build/source/texk/texlive/linked_scripts/Makefile.in
index d474578f96e..5589afd5521 100644
--- a/Build/source/texk/texlive/linked_scripts/Makefile.in
+++ b/Build/source/texk/texlive/linked_scripts/Makefile.in
@@ -313,6 +313,7 @@ texmf_shell_scripts = \
tikztosvg/tikztosvg \
typeoutfileinfo/typeoutfileinfo.sh \
wordcount/wordcount.sh \
+ xput/xput \
xput/xputserver
texmf_other_scripts = \
diff --git a/Build/source/texk/texlive/linked_scripts/scripts.lst b/Build/source/texk/texlive/linked_scripts/scripts.lst
index 5d7f62926b1..35b860a8f13 100644
--- a/Build/source/texk/texlive/linked_scripts/scripts.lst
+++ b/Build/source/texk/texlive/linked_scripts/scripts.lst
@@ -40,6 +40,7 @@ texosquery/texosquery-jre8.sh
tikztosvg/tikztosvg
typeoutfileinfo/typeoutfileinfo.sh
wordcount/wordcount.sh
+xput/xput
xput/xputserver
a2ping/a2ping.pl
accfonts/mkt1font
diff --git a/Build/source/texk/texlive/linked_scripts/xput/xput b/Build/source/texk/texlive/linked_scripts/xput/xput
new file mode 100755
index 00000000000..57203c7a5da
--- /dev/null
+++ b/Build/source/texk/texlive/linked_scripts/xput/xput
@@ -0,0 +1,520 @@
+#!/bin/sh
+#
+# Copyright (C) 2022 by Friedemann Bartels
+#
+# This file may be distributed and/or modified under the
+# conditions of the LaTeX Project Public License, either
+# version 1.3c of this license or (at your option) any later
+# version. The latest version of this license is in:
+#
+# http://www.latex-project.org/lppl.txt
+#
+# and version 1.3c or later is part of all distributions of
+# LaTeX version 2008/05/04 or later.
+#
+
+version=1.0.2
+
+OK="$( tput setaf 2 )•$( tput sgr0 )"
+FAILED="$( tput setaf 1 )$( tput bold )x$( tput sgr0 )"
+NEW="$( tput setaf 3 )$( tput bold )+$( tput sgr0 )"
+REMOVED="$( tput setaf 5 )$( tput bold )-$( tput sgr0 )"
+ERROR="$( tput setaf 1 )$( tput bold )!$( tput sgr0 )"
+
+_playok() {
+ if [ -f /System/Library/Sounds/Glass.aiff ]; then
+ afplay /System/Library/Sounds/Glass.aiff >/dev/null 2>&1
+ fi
+}
+
+_playhm() {
+ if [ -f /System/Library/Sounds/Basso.aiff ]; then
+ afplay /System/Library/Sounds/Basso.aiff >/dev/null 2>&1
+ fi
+}
+
+_playerror() {
+ if [ -f /System/Library/Sounds/Sosumi.aiff ]; then
+ afplay /System/Library/Sounds/Sosumi.aiff >/dev/null 2>&1
+ fi
+}
+
+_gettime() {
+ if command -v gdate &> /dev/null; then
+ echo $( gdate +%s%3N )
+ else
+ if command -v date &> /dev/null; then
+ milliseconds=$( date +%3N )
+ if [ $milliseconds = 3N ]; then
+ echo $(( SECONDS * 1000 ))
+ else
+ echo $( date +%s%3N )
+ fi
+ else
+ echo $(( SECONDS * 1000 ))
+ fi
+ fi
+}
+
+_starttimer() {
+ starttime=$( _gettime )
+}
+
+_stoptimer() {
+ endtime=$( _gettime )
+
+ elapsedtime=$(( endtime - starttime ))
+ elapsedseconds=$(( elapsedtime / 1000 ))
+ elapsedmilliseconds=$(( elapsedtime % 1000 ))
+ elapsed=$elapsedseconds.$( printf "%03d" "$elapsedmilliseconds" )
+}
+
+test() {
+ _starttimer
+ tmpdir=$( mktemp -d 2>/dev/null )/textest$( date "+%Y%m%d%H%M%S" )$RANDOM
+ mkdir $tmpdir
+
+ if [ $3 = true ] && [ -d cache ]; then
+ rm -rf cache
+ fi
+
+ okcount=0
+ failedcount=0
+ newcount=0
+ removedcount=0
+ fileokcount=0
+ filefailedcount=0
+ defaultfiller=23
+
+ echo ""
+ for entry in *$5*.tex
+ do
+ if [ $entry != "*$5*.tex" ]; then
+ name=$( echo $entry | sed -e 's/\.tex$//g' )
+ if [ ${name:0:1} != _ ]; then
+ printf "$( tput sgr0 )$name"
+
+ runtwice=0
+ if [ "${name:$(( ${#name} - 2 )):2}" = "@2" ]; then
+ runtwice=1
+ fi
+
+ if [ "$4" != "xelatex" ]; then
+ name="$name.$4"
+ fi
+
+ passed=1
+ rm -f $name.failed.*
+
+ cp $entry $tmpdir/$name.tex
+ shell=""
+ if [ $1 = true ]; then
+ shell="--shell-escape"
+ fi
+ $4 -interaction=batchmode $shell --output-directory $tmpdir $tmpdir/$name.tex > /dev/null
+ if [ $? -eq 1 ]; then
+ passed=0
+ fi
+ if [ $runtwice -eq 1 ]; then
+ $4 -interaction=batchmode $shell --output-directory $tmpdir $tmpdir/$name.tex > /dev/null
+ if [ $? -eq 1 ]; then
+ passed=0
+ fi
+ fi
+
+ magick convert -density $2 -quiet $tmpdir/$name.pdf $tmpdir/$name.png
+ if [ -f "$tmpdir/$name.png" ]; then
+ mv $tmpdir/$name.png $tmpdir/$name-0.png
+ fi
+
+ if [ -f "$name.approved.pdf" ]; then
+ magick convert -density $2 -quiet $name.approved.pdf $tmpdir/$name.approved.png
+ if [ -f "$tmpdir/$name.approved.png" ]; then
+ mv $tmpdir/$name.approved.png $tmpdir/$name.approved-0.png
+ fi
+ fi
+
+ if [ $passed -eq 0 ]; then
+ filler=$(( defaultfiller - ${#name} - 2 ))
+ else
+ filler=$(( defaultfiller - ${#name} ))
+ fi
+ while [ 0 -le $filler ]; do
+ printf " "
+ filler=$(( filler - 1 ))
+ done
+ if [ $passed -eq 0 ]; then
+ printf " $ERROR"
+ fi
+
+ index=0
+ while [ -f "$tmpdir/$name-$index.png" ]; do
+ if [ -f "$tmpdir/$name.approved-$index.png" ]; then
+ changedpixels=$( magick compare -quiet -metric AE $tmpdir/$name-$index.png $tmpdir/$name.approved-$index.png null: 2>&1 )
+ if [ $changedpixels -eq 0 ]; then
+ okcount=$(( okcount + 1 ))
+ printf " $OK"
+ else
+ failedcount=$(( failedcount + 1 ))
+ passed=0
+ printf " $FAILED"
+ magick compare -quiet $tmpdir/$name-$index.png $tmpdir/$name.approved-$index.png $name.failed.$(( index + 1 )).png
+ if [ -f "$tmpdir/$name.pdf" ]; then
+ mv $tmpdir/$name.pdf $name.failed.pdf
+ fi
+ fi
+ else
+ newcount=$(( newcount + 1 ))
+ passed=0
+ printf " $NEW"
+ mv $tmpdir/$name-$index.png $name.failed.$(( index + 1 )).png
+ if [ -f "$tmpdir/$name.pdf" ]; then
+ mv $tmpdir/$name.pdf $name.failed.pdf
+ fi
+ fi
+ index=$(( index + 1 ))
+ if [ `expr $index % 20` -eq 0 ] && [ -f "$tmpdir/$name-$index.png" ]; then
+ echo ""
+ filler=$defaultfiller
+ while [ 0 -le $filler ]; do
+ printf " "
+ filler=$(( filler - 1 ))
+ done
+ fi
+ done
+ while [ -f "$tmpdir/$name.approved-$index.png" ]; do
+ removedcount=$(( removedcount + 1 ))
+ passed=0
+ printf " $REMOVED"
+ mv $tmpdir/$name.approved-$index.png $name.failed.$(( index + 1 )).png
+ if [ -f "$tmpdir/$name.pdf" ]; then
+ mv $tmpdir/$name.pdf $name.failed.pdf
+ fi
+ index=$(( index + 1 ))
+ if [ `expr $index % 20` -eq 0 ] && [ -f "$tmpdir/$name.approved-$index.png" ]; then
+ echo ""
+ filler=$defaultfiller
+ while [ 0 -le $filler ]; do
+ printf " "
+ filler=$(( filler - 1 ))
+ done
+ fi
+ done
+
+ if [ $passed -eq 1 ]; then
+ fileokcount=$(( fileokcount + 1 ))
+ else
+ filefailedcount=$(( filefailedcount + 1 ))
+ fi
+
+ echo ""
+ fi
+ fi
+ done
+
+ rm -rf $tmpdir
+ _stoptimer
+ seconds=$( printf "%.1f" "$elapsed" )
+
+ filecount=$(( fileokcount + filefailedcount ))
+
+ if [ $filecount -gt 0 ]; then
+ echo ""
+ fi
+ if [ $filecount -eq $fileokcount ]; then
+ if [ $filecount -eq 0 ]; then
+ _playhm &
+ echo "$( tput sgr0 )Tested 0 files 🧐."
+ else
+ _playok &
+ echo "$( tput sgr0 )Successfully tested $filecount files in $seconds seconds 🎉."
+ fi
+ echo ""
+ else
+ _playerror &
+ echo "$( tput sgr0 )Tested $filecount files in $seconds seconds."
+ echo ""
+
+ if [ $fileokcount -gt 0 ]; then
+ filler=$(( 6 + ${#filecount} - ${#fileokcount} ))
+ while [ 0 -le $filler ]; do
+ printf " "
+ filler=$(( filler - 1 ))
+ done
+ printf "$( tput setaf 2 )$fileokcount OK$( tput sgr0 )"
+ echo ""
+ fi
+ if [ $filefailedcount -gt 0 ]; then
+ filler=$(( 6 + ${#filecount} - ${#filefailedcount} ))
+ while [ 0 -le $filler ]; do
+ printf " "
+ filler=$(( filler - 1 ))
+ done
+ printf "$( tput setaf 1 )$filefailedcount Failed$( tput sgr0 )"
+ echo ""
+ fi
+ echo ""
+ exit 1
+ fi
+}
+
+approve() {
+ filecount=0
+
+ suffix=""
+ if [ "$1" != "xelatex" ]; then
+ suffix=".$1"
+ fi
+
+ echo ""
+ for entry in *$2*$suffix.failed.pdf
+ do
+ if [ $entry != "*$2*$suffix.failed.pdf" ]; then
+ filecount=$(( filecount + 1 ))
+ name=$( echo $entry | sed -e "s/$suffix.failed.pdf//" )
+ echo $name
+ rm -f $name$suffix.approved.pdf
+ if [ -f "$name.aux" ]; then
+ cp $name$suffix.failed.pdf $name$suffix.approved.pdf
+ else
+ mv $name$suffix.failed.pdf $name$suffix.approved.pdf
+ fi
+
+ rm -f $name$suffix.failed.*
+ fi
+ done
+
+ if [ $filecount -gt 0 ]; then
+ echo ""
+ fi
+ if [ $filecount -eq 1 ]; then
+ echo "Approved 1 file."
+ else
+ echo "Approved $filecount files."
+ fi
+ echo ""
+}
+
+compare() {
+ filea=$2
+ fileb=$3
+ tmpdir=$( mktemp -d 2>/dev/null)/textest$( date "+%Y%m%d%H%M%S" )$RANDOM
+ mkdir $tmpdir
+
+ namea=$( echo $filea | sed -e 's/\.\///g' | sed -e 's/\.pdf$//g' )
+ nameb=$( echo $fileb | sed -e 's/\.\///g' | sed -e 's/\.pdf$//g' )
+ basenamea=$(basename $namea)
+ basenameb=$(basename $nameb)
+
+ rm -f $namea.diff.*.png
+
+ spacer=""
+ if [ -f "$filea" ]; then
+ if [ -f "$fileb" ]; then
+ magick convert -density $1 -quiet $filea $tmpdir/$basenamea.png
+ if [ -f "$tmpdir/$basenamea.png" ]; then
+ mv $tmpdir/$basenamea.png $tmpdir/$basenamea-0.png
+ fi
+ magick convert -density $1 -quiet $fileb $tmpdir/$basenameb.png
+ if [ -f "$tmpdir/$basenameb.png" ]; then
+ mv $tmpdir/$basenameb.png $tmpdir/$basenameb-0.png
+ fi
+
+ index=0
+ while [ -f "$tmpdir/$basenamea-$index.png" ]; do
+ if [ -f "$tmpdir/$basenameb-$index.png" ]; then
+ changedpixels=$( magick compare -quiet -metric AE $tmpdir/$basenamea-$index.png $tmpdir/$basenameb-$index.png null: 2>&1 )
+ if [ $changedpixels -eq 0 ]; then
+ printf "$spacer$OK"
+ spacer=" "
+ else
+ printf "$spacer$FAILED"
+ spacer=" "
+ magick compare -quiet $tmpdir/$basenamea-$index.png $tmpdir/$basenameb-$index.png $namea.diff.$(( index + 1 )).png
+ fi
+ else
+ printf "$spacer$NEW"
+ spacer=" "
+ fi
+ index=$(( index + 1 ))
+ done
+ while [ -f "$tmpdir/$basenameb-$index.png" ]; do
+ printf "$spacer$REMOVED"
+ spacer=" "
+ index=$(( index + 1 ))
+ done
+ echo ""
+ else
+ echo "File $fileb not found."
+ fi
+ else
+ echo "File $filea not found."
+ fi
+
+ rm -rf $tmpdir
+}
+
+perf() {
+ if [ -f $4 ]; then
+ tmpdir=$( mktemp -d 2>/dev/null )/textestperf$( date "+%Y%m%d%H%M%S" )$RANDOM
+ mkdir $tmpdir
+ basename=$(basename $4)
+
+ if [ $1 = true ] && [ -d cache ]; then
+ rm -rf cache
+ fi
+
+ cp $4 $tmpdir
+ shell=""
+ if [ $2 = true ]; then
+ shell="--shell-escape"
+ fi
+ _starttimer
+ $3 -interaction=batchmode $shell --output-directory $tmpdir $tmpdir/$basename > /dev/null
+ _stoptimer
+ echo "$elapsed"
+
+ rm -rf $tmpdir
+ else
+ echo "File $4 not found."
+ fi
+}
+
+usage() {
+ cat <<HELP_USAGE
+usage: xput test [-cs] [-e ENGINE] [-d DENSITY] [PATTERN]
+ xput approve [-e ENGINE] [PATTERN]
+ xput compare [-d DENSITY] FILE FILE
+ xput perf [-cs] [-e ENGINE] FILE
+ xput --help
+ xput --version
+
+ -c clear cache
+ -s enable shell escape
+ -d DENSITY density in ppi (default 72)
+ -e ENGINE latex engine (default xelatex)
+
+ Running \`xput test\` creates pdf files for all tex files matching the
+ pattern and performs a visual regression test. If no pattern is specified,
+ all files in the current directory are tested except for files whose names
+ begin with an underscore.
+
+ Running \`xput approve\` replaces the reference files with the test files.
+
+ Running \`xput compare\` creates a visual diff for two pdf files.
+
+ Running \`xput perf\` measures the time for creating a pdf file from the
+ given tex file.
+
+ Running \`xput --help\` returns this man page.
+
+ Running \`xput --version\` returns the Xput version.
+HELP_USAGE
+}
+
+case "$1" in
+test)
+ shift
+
+ density=72
+ shell=false
+ clear=false
+ engine=xelatex
+
+ while getopts ":e:d:sc" arg; do
+ case "${arg}" in
+ c)
+ clear=true
+ ;;
+ d)
+ density="${OPTARG}"
+ ;;
+ e)
+ engine="${OPTARG}"
+ ;;
+ s)
+ shell=true
+ ;;
+ ?)
+ echo "Invalid option: -${OPTARG}."
+ ;;
+ esac
+ done
+
+ shift "$(( OPTIND - 1 ))"
+
+ test $shell $density $clear $engine $1
+ ;;
+approve)
+ shift
+ engine=xelatex
+
+ while getopts ":e:" arg; do
+ case "${arg}" in
+ e)
+ engine="${OPTARG}"
+ ;;
+ ?)
+ echo "Invalid option: -${OPTARG}."
+ ;;
+ esac
+ done
+
+ shift "$(( OPTIND - 1 ))"
+
+ approve $engine $1
+ ;;
+compare)
+ shift
+
+ density=72
+
+ while getopts ":d:" arg; do
+ case "${arg}" in
+ d)
+ density="${OPTARG}"
+ ;;
+ ?)
+ echo "Invalid option: -${OPTARG}."
+ ;;
+ esac
+ done
+
+ shift "$(( OPTIND - 1 ))"
+
+ compare $density $1 $2
+ ;;
+perf)
+ shift
+
+ shell=false
+ clear=false
+ engine=xelatex
+
+ while getopts ":e:sc" arg; do
+ case "${arg}" in
+ c)
+ clear=true
+ ;;
+ e)
+ engine="${OPTARG}"
+ ;;
+ s)
+ shell=true
+ ;;
+ ?)
+ echo "Invalid option: -${OPTARG}."
+ ;;
+ esac
+ done
+
+ shift "$(( OPTIND - 1 ))"
+
+ perf $clear $shell $engine $1
+ ;;
+--version)
+ echo Xput $version
+ ;;
+*)
+ usage
+esac
diff --git a/Master/bin/aarch64-linux/xput b/Master/bin/aarch64-linux/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/aarch64-linux/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/amd64-freebsd/xput b/Master/bin/amd64-freebsd/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/amd64-freebsd/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/amd64-netbsd/xput b/Master/bin/amd64-netbsd/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/amd64-netbsd/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/armhf-linux/xput b/Master/bin/armhf-linux/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/armhf-linux/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/i386-cygwin/xput b/Master/bin/i386-cygwin/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/i386-cygwin/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/i386-freebsd/xput b/Master/bin/i386-freebsd/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/i386-freebsd/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/i386-linux/xput b/Master/bin/i386-linux/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/i386-linux/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/i386-netbsd/xput b/Master/bin/i386-netbsd/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/i386-netbsd/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/i386-solaris/xput b/Master/bin/i386-solaris/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/i386-solaris/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/universal-darwin/xput b/Master/bin/universal-darwin/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/universal-darwin/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/x86_64-cygwin/xput b/Master/bin/x86_64-cygwin/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/x86_64-cygwin/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/x86_64-darwinlegacy/xput b/Master/bin/x86_64-darwinlegacy/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/x86_64-darwinlegacy/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/x86_64-linux/xput b/Master/bin/x86_64-linux/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/x86_64-linux/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/x86_64-linuxmusl/xput b/Master/bin/x86_64-linuxmusl/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/x86_64-linuxmusl/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/bin/x86_64-solaris/xput b/Master/bin/x86_64-solaris/xput
new file mode 120000
index 00000000000..95fb47cf1ca
--- /dev/null
+++ b/Master/bin/x86_64-solaris/xput
@@ -0,0 +1 @@
+../../texmf-dist/scripts/xput/xput \ No newline at end of file
diff --git a/Master/tlpkg/libexec/ctan2tds b/Master/tlpkg/libexec/ctan2tds
index 8087b10e413..e1f540463c5 100755
--- a/Master/tlpkg/libexec/ctan2tds
+++ b/Master/tlpkg/libexec/ctan2tds
@@ -3679,7 +3679,7 @@ $standardttf = '\.ttf|\.TTC';
'wordcount' => '\.sh$',
'wheretotrim' => '\.pl$',
'xindex' => 'xindex\.lua$',
- 'xput' => 'xputserver$',
+ 'xput' => 'xput(server)?$',
'yplan' => 'yplan$',
);
diff --git a/Master/tlpkg/tlpsrc/xput.tlpsrc b/Master/tlpkg/tlpsrc/xput.tlpsrc
index ba0631802cf..f7b3a88e919 100644
--- a/Master/tlpkg/tlpsrc/xput.tlpsrc
+++ b/Master/tlpkg/tlpsrc/xput.tlpsrc
@@ -1 +1,2 @@
+binpattern f bin/${ARCH}/${PKGNAME}
binpattern f bin/${ARCH}/${PKGNAME}server