mirror of
https://github.com/akatrevorjay/edid-generator.git
synced 2026-01-15 23:50:28 +01:00
Simplify
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -35,4 +35,6 @@
|
|||||||
*.c
|
*.c
|
||||||
*.bin
|
*.bin
|
||||||
*.ihex
|
*.ihex
|
||||||
|
*.o.pre
|
||||||
|
*.o.post
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,5 @@
|
|||||||
#define ESTABLISHED_TIMING2_BITS 0x08 /* Bit 3 -> 1024x768 @60 Hz */
|
#define ESTABLISHED_TIMING2_BITS 0x08 /* Bit 3 -> 1024x768 @60 Hz */
|
||||||
#define HSYNC_POL 0
|
#define HSYNC_POL 0
|
||||||
#define VSYNC_POL 0
|
#define VSYNC_POL 0
|
||||||
#define CRC 0x55
|
|
||||||
|
|
||||||
#include "edid.S"
|
#include "edid.S"
|
||||||
|
|||||||
@@ -39,6 +39,5 @@
|
|||||||
/* No ESTABLISHED_TIMINGx_BITS */
|
/* No ESTABLISHED_TIMINGx_BITS */
|
||||||
#define HSYNC_POL 1
|
#define HSYNC_POL 1
|
||||||
#define VSYNC_POL 1
|
#define VSYNC_POL 1
|
||||||
#define CRC 0xa0
|
|
||||||
|
|
||||||
#include "edid.S"
|
#include "edid.S"
|
||||||
|
|||||||
@@ -39,6 +39,5 @@
|
|||||||
/* No ESTABLISHED_TIMINGx_BITS */
|
/* No ESTABLISHED_TIMINGx_BITS */
|
||||||
#define HSYNC_POL 1
|
#define HSYNC_POL 1
|
||||||
#define VSYNC_POL 1
|
#define VSYNC_POL 1
|
||||||
#define CRC 0x9d
|
|
||||||
|
|
||||||
#include "edid.S"
|
#include "edid.S"
|
||||||
|
|||||||
@@ -39,6 +39,5 @@
|
|||||||
/* No ESTABLISHED_TIMINGx_BITS */
|
/* No ESTABLISHED_TIMINGx_BITS */
|
||||||
#define HSYNC_POL 1
|
#define HSYNC_POL 1
|
||||||
#define VSYNC_POL 1
|
#define VSYNC_POL 1
|
||||||
#define CRC 0x26
|
|
||||||
|
|
||||||
#include "edid.S"
|
#include "edid.S"
|
||||||
|
|||||||
@@ -39,6 +39,5 @@
|
|||||||
/* No ESTABLISHED_TIMINGx_BITS */
|
/* No ESTABLISHED_TIMINGx_BITS */
|
||||||
#define HSYNC_POL 1
|
#define HSYNC_POL 1
|
||||||
#define VSYNC_POL 1
|
#define VSYNC_POL 1
|
||||||
#define CRC 0x05
|
|
||||||
|
|
||||||
#include "edid.S"
|
#include "edid.S"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* 3840x2160: 3840x2160 594.000 3840 4016 4104 4400 2160 2168 2178 2250 +hsync +vsync */
|
/* 3840x2160: Modeline "3840x2160" 594.000 3840 4016 4104 4400 2160 2168 2178 2250 +hsync +vsync */
|
||||||
#define HSYNC_POL 1
|
#define HSYNC_POL 1
|
||||||
#define CRC 0x9b
|
|
||||||
#define VSYNC_POL 1
|
#define VSYNC_POL 1
|
||||||
#define YPULSE (63+10)
|
#define YPULSE (63+10)
|
||||||
#define XPULSE 88
|
#define XPULSE 88
|
||||||
|
|||||||
@@ -36,6 +36,5 @@
|
|||||||
#define ESTABLISHED_TIMING1_BITS 0x01 /* Bit 0: 800x600 @ 60Hz */
|
#define ESTABLISHED_TIMING1_BITS 0x01 /* Bit 0: 800x600 @ 60Hz */
|
||||||
#define HSYNC_POL 1
|
#define HSYNC_POL 1
|
||||||
#define VSYNC_POL 1
|
#define VSYNC_POL 1
|
||||||
#define CRC 0xc2
|
|
||||||
|
|
||||||
#include "edid.S"
|
#include "edid.S"
|
||||||
|
|||||||
58
HOWTO.txt
58
HOWTO.txt
@@ -1,58 +0,0 @@
|
|||||||
In the good old days when graphics parameters were configured explicitly
|
|
||||||
in a file called xorg.conf, even broken hardware could be managed.
|
|
||||||
|
|
||||||
Today, with the advent of Kernel Mode Setting, a graphics board is
|
|
||||||
either correctly working because all components follow the standards -
|
|
||||||
or the computer is unusable, because the screen remains dark after
|
|
||||||
booting or it displays the wrong area. Cases when this happens are:
|
|
||||||
- The graphics board does not recognize the monitor.
|
|
||||||
- The graphics board is unable to detect any EDID data.
|
|
||||||
- The graphics board incorrectly forwards EDID data to the driver.
|
|
||||||
- The monitor sends no or bogus EDID data.
|
|
||||||
- A KVM sends its own EDID data instead of querying the connected monitor.
|
|
||||||
Adding the kernel parameter "nomodeset" helps in most cases, but causes
|
|
||||||
restrictions later on.
|
|
||||||
|
|
||||||
As a remedy for such situations, the kernel configuration item
|
|
||||||
CONFIG_DRM_LOAD_EDID_FIRMWARE was introduced. It allows to provide an
|
|
||||||
individually prepared or corrected EDID data set in the /lib/firmware
|
|
||||||
directory from where it is loaded via the firmware interface. The code
|
|
||||||
(see drivers/gpu/drm/drm_edid_load.c) contains built-in data sets for
|
|
||||||
commonly used screen resolutions (800x600, 1024x768, 1280x1024, 1600x1200,
|
|
||||||
1680x1050, 1920x1080) as binary blobs, but the kernel source tree does
|
|
||||||
not contain code to create these data. In order to elucidate the origin
|
|
||||||
of the built-in binary EDID blobs and to facilitate the creation of
|
|
||||||
individual data for a specific misbehaving monitor, commented sources
|
|
||||||
and a Makefile environment are given here.
|
|
||||||
|
|
||||||
To create binary EDID and C source code files from the existing data
|
|
||||||
material, simply type "make".
|
|
||||||
|
|
||||||
If you want to create your own EDID file, copy the file 1024x768.S,
|
|
||||||
replace the settings with your own data and add a new target to the
|
|
||||||
Makefile. Please note that the EDID data structure expects the timing
|
|
||||||
values in a different way as compared to the standard X11 format.
|
|
||||||
|
|
||||||
X11:
|
|
||||||
HTimings: hdisp hsyncstart hsyncend htotal
|
|
||||||
VTimings: vdisp vsyncstart vsyncend vtotal
|
|
||||||
|
|
||||||
EDID:
|
|
||||||
#define XPIX hdisp
|
|
||||||
#define XBLANK htotal-hdisp
|
|
||||||
#define XOFFSET hsyncstart-hdisp
|
|
||||||
#define XPULSE hsyncend-hsyncstart
|
|
||||||
|
|
||||||
#define YPIX vdisp
|
|
||||||
#define YBLANK vtotal-vdisp
|
|
||||||
#define YOFFSET (63+(vsyncstart-vdisp))
|
|
||||||
#define YPULSE (63+(vsyncend-vsyncstart))
|
|
||||||
|
|
||||||
The CRC value in the last line
|
|
||||||
#define CRC 0x55
|
|
||||||
also is a bit tricky. After a first version of the binary data set is
|
|
||||||
created, it must be checked with the "edid-decode" utility which will
|
|
||||||
most probably complain about a wrong CRC. Fortunately, the utility also
|
|
||||||
displays the correct CRC which must then be inserted into the source
|
|
||||||
file. After the make procedure is repeated, the EDID data set is ready
|
|
||||||
to be used.
|
|
||||||
22
Makefile
22
Makefile
@@ -10,22 +10,28 @@ CODE := $(patsubst %.S, %.c, $(SOURCES))
|
|||||||
all: $(BIN) $(IHEX) $(CODE)
|
all: $(BIN) $(IHEX) $(CODE)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o *.bin.ihex *.bin *.c
|
rm -f *.o *.crc *.bin *.bin.ihex *.c
|
||||||
|
|
||||||
%.o: %.S
|
%.o: %.S
|
||||||
cc -c $^
|
cc -c -DCRC="0x00" -o $@ $^
|
||||||
|
|
||||||
%.bin: %.o
|
%.bin.nocrc: %.o
|
||||||
objcopy -Obinary $^ $@
|
objcopy -Obinary $^ $@
|
||||||
|
|
||||||
%.bin.ihex: %.o
|
%.crc: %.bin.nocrc
|
||||||
|
cat $^ | edid-decode \
|
||||||
|
| sed -ne 's/^Checksum: 0x\w\+ (should be \(0x\w\+\))$$/\1/p' >$@
|
||||||
|
|
||||||
|
%.p: %.crc %.S
|
||||||
|
cc -c -DCRC="$$(cat $*.crc)" -o $@ $*.S
|
||||||
|
|
||||||
|
%.bin: %.p
|
||||||
|
objcopy -Obinary $^ $@
|
||||||
|
|
||||||
|
%.bin.ihex: %.p
|
||||||
objcopy -Oihex $^ $@
|
objcopy -Oihex $^ $@
|
||||||
dos2unix $@ 2>/dev/null
|
dos2unix $@ 2>/dev/null
|
||||||
|
|
||||||
%.c: %.bin
|
%.c: %.bin
|
||||||
@echo "{" >$@; hexdump -f hex $^ >>$@; echo "};" >>$@
|
@echo "{" >$@; hexdump -f hex $^ >>$@; echo "};" >>$@
|
||||||
|
|
||||||
%.crc: %.bin
|
|
||||||
./compute-crc $^ >$@
|
|
||||||
echo $(MAKE) $^
|
|
||||||
|
|
||||||
|
|||||||
65
README.md
65
README.md
@@ -1,2 +1,65 @@
|
|||||||
# edid-generator
|
edid-generator
|
||||||
|
==============
|
||||||
|
|
||||||
Hackerswork to generate an EDID binary file from given Xorg Modelines
|
Hackerswork to generate an EDID binary file from given Xorg Modelines
|
||||||
|
|
||||||
|
An extension of the awesome work provided in the Linux kernel documentation (in `docs/EDID`).
|
||||||
|
|
||||||
|
Simplifies the process greatly by allowing you to use a standard modeline as well as automatically calculating the CRC
|
||||||
|
and applying it to the resulting image.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install zsh edid-decode automake
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
If you don't have a `<mode>.S` prepared yet, generate one using a file containing Xorg Modelines. Lines that do not
|
||||||
|
contain modelines are ignored, so you can just read right from `xorg.conf`.
|
||||||
|
|
||||||
|
```s
|
||||||
|
./modeline2edid /etc/X11/xorg.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also just read from `stdin` the way you'd expect:
|
||||||
|
|
||||||
|
```s
|
||||||
|
./modeline2edid
|
||||||
|
# or explicitly:
|
||||||
|
./modeline2edid -
|
||||||
|
```
|
||||||
|
|
||||||
|
After this creates your `<name>.S` files for each modeline it encounters, simply `make`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
The end result, providing all goes well, is a glorious EDID bin image for each mode you gave to it. A `<name>.S` file
|
||||||
|
is templated, and then `make` is invoked to compile it into `<name>.bin`. It's actually compiled twice; once with an
|
||||||
|
invalid CRC in order to generate said CRC to enter it into the template, after which we recompile, hence glorious bins.
|
||||||
|
|
||||||
|
NOTE: If you use a ratio other than 16:9, you'll need to specify it at the end of the modeline.as `ratio=4:3`.
|
||||||
|
Ratios are hard defined in `edid.S`, so if you are trying to do something non-standard you'll need to add it.
|
||||||
|
|
||||||
|
Why?
|
||||||
|
----
|
||||||
|
|
||||||
|
Many monitors and TVs (both high and low end) provide invalid EDID data. After dealing with this for years, I wanted to
|
||||||
|
automate this process.
|
||||||
|
|
||||||
|
The final straw was when I bought a Sceptre 4K tv for ~$225 and ran into a long series of hurdles to get it to operate
|
||||||
|
as expected at `3840x2160@60`. After doing this enough times, I had to automate it or I was going to go crazy.
|
||||||
|
|
||||||
|
I used this to quickly iterate while troubleshhooting, finally it's all working from KMS all the way down to X!
|
||||||
|
|
||||||
|
(Via `drm_kms_helper.edid_firmware=DP-1:edid/blah.bin``if you're interested. I'm using radeon + intel, with nvidia you
|
||||||
|
have to specify it in `xorg.conf`/`xorg.conf.d` as they don't yet support KMS for the fb console yet; their beta
|
||||||
|
drivers, 367 at the time of writing, only support using KMS for the xorg server.)
|
||||||
|
|
||||||
|
Sometimes I hate being such a perfectionist. Keep in mind this project was made in a couple hours, I certainly didn't
|
||||||
|
attempt to polish it in the least ;)
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# A tad gross, but hey, it works.
|
|
||||||
cat "$bin_fn" | edid-decode | sed -ne 's/^Checksum: 0x\w\+ (should be \(0x\w\+\))$/\1/p'
|
|
||||||
173
modeline2edid
173
modeline2edid
@@ -1,148 +1,97 @@
|
|||||||
#!/bin/zsh
|
#!/bin/zsh
|
||||||
setopt errexit errreturn
|
setopt errexit errreturn
|
||||||
setopt sourcetrace verbose
|
#setopt xtrace
|
||||||
setopt xtrace
|
|
||||||
|
|
||||||
|
template-S() {
|
||||||
|
[[ ${(L)1} = mode(|line) ]] || return 1
|
||||||
|
echo "-- Found naughty unicorn: $@"
|
||||||
|
shift 1
|
||||||
|
|
||||||
munge-x11-modeline() {
|
local name="${1//\"}"
|
||||||
local args=() arg
|
[[ -z "$name" ]] && echo "Could not parse modeline: $@" >&2 && return 1
|
||||||
for arg in "$@"; do
|
local fn="${name}.S"
|
||||||
[[ "$arg" != \"*\" ]] || arg="${arg:1:-1}"
|
|
||||||
args+=("$arg")
|
|
||||||
done
|
|
||||||
set -- ${(@)args}
|
|
||||||
|
|
||||||
[[ "$1" != "Modeline" ]] || shift 1
|
local -F pixel_clock_mhz=$2
|
||||||
name="$1" pixel_clock_mhz="$2"; shift 2
|
local -i pixel_clock_khz=$((pixel_clock_mhz * 1000))
|
||||||
hdisp="$1" hsyncstart="$2" hsyncend="$3" htotal="$4"; shift 4
|
shift 2
|
||||||
vdisp="$1" vsyncstart="$2" vsyncend="$3" vtotal="$4"; shift 4
|
|
||||||
|
|
||||||
fn="${name}.S"
|
local -i hdisp="$1" hsyncstart="$2" hsyncend="$3" htotal="$4"; shift 4
|
||||||
bin_fn="${name}.bin"
|
local -i vdisp="$1" vsyncstart="$2" vsyncend="$3" vtotal="$4"; shift 4
|
||||||
|
|
||||||
hsync_polarity=0
|
local -i hsync_polarity=0 vsync_polarity=0 dpi=96 vfreq_hz=60
|
||||||
vsync_polarity=0
|
local edid_version="1.3" ratio="16:9" # TODO calc ratio
|
||||||
ratio="16:9" # todo calc
|
|
||||||
dpi="96"
|
|
||||||
edid_version="1.3"
|
|
||||||
vfreq_hz="60"
|
|
||||||
crc="0x00"
|
|
||||||
|
|
||||||
local arg
|
local arg
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "${(L)arg}" in
|
case "${(L)arg}" in
|
||||||
*hsync) [[ "${arg:1:1}" == "-" ]] || hsync_polarity=1 ;;
|
[-+]hsync) [[ "${arg:1:1}" == "-" ]] || hsync_polarity=1 ;;
|
||||||
*vsync) [[ "${arg:1:1}" == "-" ]] || vsync_polarity=1 ;;
|
[-+]vsync) [[ "${arg:1:1}" == "-" ]] || vsync_polarity=1 ;;
|
||||||
ratio=*|xy_ratio=*) ratio="${arg#*=}" ;;
|
ratio=*|xy_ratio=*) ratio="${arg#*=}" ;;
|
||||||
dpi=*) dpi="${arg#*=}" ;;
|
dpi=*) dpi="${arg#*=}" ;;
|
||||||
edid_version=*) edid_version="${arg#*=}" ;;
|
edid_version=*) edid_version="${arg#*=}" ;;
|
||||||
vfreq=*|vfreq_hz=*) vfreq_hz="${arg#*=}" ;;
|
vfreq=*|vfreq_hz=*) vfreq_hz="${arg#*=}" ;;
|
||||||
crc=*) crc="${arg#*=}" ;;
|
*) echo "Ignoring unknown modeline option passed: '$arg'" >&2 ;;
|
||||||
*) echo "Unknown modeline option passed: $arg" >&2 ;;
|
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
}
|
|
||||||
|
|
||||||
gen-S-defines() {
|
|
||||||
[[ $# -eq 0 ]] || munge-x11-modeline "$@"
|
|
||||||
local -i pixel_clock_khz=$((pixel_clock_mhz * 1000))
|
|
||||||
|
|
||||||
|
local -A defines
|
||||||
defines=(
|
defines=(
|
||||||
TIMING_NAME "${(qqq)name}"
|
TIMING_NAME "${(qqq)name}"
|
||||||
|
|
||||||
CLOCK "$pixel_clock_khz"
|
CLOCK "$pixel_clock_khz"
|
||||||
XPIX "$hdisp"
|
XPIX "$hdisp"
|
||||||
XBLANK "$((htotal - hdisp))"
|
XBLANK "$((htotal - hdisp))"
|
||||||
XOFFSET "$((hsyncstart - hdisp))"
|
XOFFSET "$((hsyncstart - hdisp))"
|
||||||
XPULSE "$((hsyncend - hsyncstart))"
|
XPULSE "$((hsyncend - hsyncstart))"
|
||||||
|
|
||||||
YPIX "$vdisp"
|
YPIX "$vdisp"
|
||||||
YBLANK "$((vtotal - vdisp))"
|
YBLANK "$((vtotal - vdisp))"
|
||||||
YOFFSET "(63+$((vsyncstart - vdisp)))"
|
YOFFSET "(63+$((vsyncstart - vdisp)))"
|
||||||
YPULSE "(63+$((vsyncend - vsyncstart)))"
|
YPULSE "(63+$((vsyncend - vsyncstart)))"
|
||||||
|
|
||||||
VERSION "${edid_version%%.*}"
|
VERSION "${edid_version%%.*}"
|
||||||
REVISION "${edid_version#*.}"
|
REVISION "${edid_version#*.}"
|
||||||
|
|
||||||
XY_RATIO "XY_RATIO_${(U)ratio//:/_}"
|
XY_RATIO "XY_RATIO_${(U)ratio//:/_}"
|
||||||
DPI "$dpi"
|
DPI "$dpi"
|
||||||
VFREQ "$vfreq_hz"
|
VFREQ "$vfreq_hz"
|
||||||
HSYNC_POL "$hsync_polarity"
|
HSYNC_POL "$hsync_polarity"
|
||||||
VSYNC_POL "$vsync_polarity"
|
VSYNC_POL "$vsync_polarity"
|
||||||
CRC "$crc"
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
template-S() {
|
local -a lines=("/* $name: $REPLY */")
|
||||||
local -a lines=("/* $name: $* */")
|
|
||||||
local k
|
local k
|
||||||
for k in ${(k)defines}; do
|
for k in ${(k)defines}; do
|
||||||
lines+=("#define $k ${defines[$k]}")
|
lines+=("#define $k ${defines[$k]}")
|
||||||
done
|
done
|
||||||
lines+=('#include "edid.S"')
|
lines+=('#include "edid.S"')
|
||||||
|
|
||||||
echo "${(j:\n:)lines[@]}" | tee "$fn"
|
echo "${(j:\n:)lines[@]}" > "$fn"
|
||||||
echo "Wrote $fn" >&2
|
echo "Wrote $fn"
|
||||||
}
|
}
|
||||||
|
|
||||||
compile() {
|
local f=${1:-'-'}
|
||||||
rm -fv $bin_fn
|
[[ $f != '-' ]] || f="/dev/stdin"
|
||||||
echo "Compiling $bin_fn" >&2
|
|
||||||
local i=0
|
|
||||||
while true; do
|
|
||||||
let i++ || :
|
|
||||||
make >/dev/null 2>&1 || :
|
|
||||||
! test -f "$bin_fn" || break
|
|
||||||
[[ $i -lt 100 ]] || (echo "Failed to compute checksum for $bin_fn" >&2; exit 1)
|
|
||||||
done
|
|
||||||
echo "Compiled $bin_fn in $i iterations" >&2
|
|
||||||
}
|
|
||||||
|
|
||||||
modeline2edid() {
|
if [[ -z "$f" || "$f" == "-h" ]]; then
|
||||||
local name
|
self=${0:t}
|
||||||
local -A defines
|
cat >&2 <<-EOF
|
||||||
gen-S-defines "$@"
|
Modeline2EDID, version forever 0.0.1
|
||||||
[[ -n "$name" ]] || (echo "Didn't get a name? The hell?" >&2 && exit 1)
|
Help:
|
||||||
|
$self -h
|
||||||
# Template and compile
|
Parse modelines from stdin:
|
||||||
template-S "$@"
|
$self
|
||||||
compile
|
$self -
|
||||||
|
Parse modelines from a file (eg xorg.conf)
|
||||||
# Fix CRC
|
$self FILENAME
|
||||||
local crc=$(${0:h}/compute-crc)
|
EOF
|
||||||
# we're done if we don't have a crc to fix
|
exit 1
|
||||||
[[ -n "$crc" ]] || return
|
fi
|
||||||
|
|
||||||
# Re-template and compile with proper CRC
|
|
||||||
defines[CRC]=$crc template-S "$@"
|
|
||||||
compile
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
main() {
|
|
||||||
zmodload -i zsh/zutil
|
|
||||||
|
|
||||||
# support single argument filename
|
|
||||||
[[ -z $1 || $1 = -* ]] || set -- -f "$@"
|
|
||||||
|
|
||||||
local o_file=()
|
|
||||||
zparseopts -K -D -E -- f:o_file
|
|
||||||
local f=${o_file[-1]}
|
|
||||||
|
|
||||||
case $f in
|
|
||||||
-|) f="/dev/stdin" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -z "$f" ]; then
|
|
||||||
echo "Usage:" >&2
|
|
||||||
echo "$0 [-f] FILENAME Parse modelines out of a file (eg xorg.conf) or '-' for stdin." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Searching for unicorns in $f" >&2
|
|
||||||
while read; do
|
|
||||||
echo "-- Found a unicorn: $REPLY" >&2
|
|
||||||
modeline2edid $=REPLY
|
|
||||||
done < $f
|
|
||||||
}
|
|
||||||
|
|
||||||
|
echo "Searching for runaway unicorns in '$f'"
|
||||||
|
while read; do
|
||||||
|
# trim
|
||||||
|
REPLY=($=REPLY)
|
||||||
|
[[ -n "$REPLY" ]] || continue
|
||||||
|
template-S ${(@)REPLY} || :
|
||||||
|
done < $f
|
||||||
|
|||||||
Reference in New Issue
Block a user