First upload
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# Textereo: ASCII stereogram generator in AWK
|
||||
# Usage: [busybox] awk -f textereo.awk map.txt
|
||||
# Map format:
|
||||
# First line: [desired width] [maxpattern length]
|
||||
# Second line: alphabet (allowed characters to generate patterns from)
|
||||
# Next H lines: depth map, W digits from 0 to 7 without spaces
|
||||
# You can insert single zeros or empty lines into the map to adjust height
|
||||
#
|
||||
# Created by Luxferre in 2023, released into public domain
|
||||
|
||||
function dchar(str, pos) { # delete a character from str at position pos
|
||||
if(!pos) return str
|
||||
else return substr(str, 1, pos - 1) substr(str, pos + 1)
|
||||
}
|
||||
|
||||
function ichar(str, pos, c) { # insert a character c into str at position pos
|
||||
return substr(str, 1, pos - 1) c substr(str, pos)
|
||||
}
|
||||
|
||||
BEGIN { # we use 95-character subset from ASCII by default
|
||||
"date +%N"|getline rseed;srand(rseed) # init the PRNG
|
||||
getline # read the first line from the file to parse parameters
|
||||
WIDTH = int($1) # desired width of the image to generate
|
||||
PATLEN = int($2) # generated pattern length (must be between 8 and W/2)
|
||||
getline # read the full character set
|
||||
CHARSET = $0
|
||||
CHRANGE = length(CHARSET)
|
||||
if(WIDTH == 0) WIDTH = 78
|
||||
if(PATLEN < 8 || PATLEN > (WIDTH/2)) PATLEN = 10 # 10 to 15 is optimal
|
||||
}
|
||||
|
||||
{ # process the map itself
|
||||
j = 0 # set the secondary counter to store previous value
|
||||
FREEBUF = CHARSET # start with the full charset
|
||||
PATBUF = "" # start with empty pattern buffer
|
||||
mapline = $0 # cache the map line
|
||||
RWIDTH = length(mapline); # real pattern width
|
||||
tlen = (WIDTH - RWIDTH) / 2 # trailer AND header length
|
||||
for(i=0;i<tlen;i++) mapline = "0" mapline "0" # grow the line to the width
|
||||
if(length(mapline) > WIDTH) mapline = substr(mapline, 1, WIDTH)
|
||||
for(i=0;i<PATLEN;i++) { # fill in the pattern buffer
|
||||
do # we can repeat the characters in the pattern but not immediately
|
||||
pbc = substr(CHARSET, 1+int(rand()*CHRANGE), 1)
|
||||
while(pbc == j || !length(pbc)) # don't allow empty or repeating chars
|
||||
PATBUF = PATBUF pbc # concatenate the character to the pattern
|
||||
j = pbc # save as the previous value to j
|
||||
# delete this character from the unused buffer
|
||||
FREEBUF = dchar(FREEBUF, index(FREEBUF, pbc))
|
||||
}
|
||||
j = 0 # reset the secondary counter
|
||||
prevlen = PATLEN # cache the previous pattern length
|
||||
maplen = length(mapline)
|
||||
patpos = 0 # track pattern position
|
||||
for(i=0;++i<=maplen;) { # iterate over every character
|
||||
curlen = PATLEN - int(substr(mapline, i, 1)) # extract depth value and length
|
||||
delta = curlen - prevlen
|
||||
if(delta > 0) # add unused characters if decreasing depth value
|
||||
for(j=0;j<delta;j++) {
|
||||
ri = 1+int(rand()*length(FREEBUF)) # get random index
|
||||
rc = substr(FREEBUF, ri, 1) # retrieve the character
|
||||
FREEBUF = dchar(FREEBUF, ri) # delete it from the buffer
|
||||
PATBUF = ichar(PATBUF, 1+patpos, rc) # insert it here
|
||||
}
|
||||
else if(delta < 0) # remove characters if increasing depth value
|
||||
for(j=0;j<-delta;j++)
|
||||
PATBUF = dchar(PATBUF, 1+patpos)
|
||||
prevlen = curlen # update the previous length
|
||||
# now, output the current pattern character
|
||||
printf("%c", substr(PATBUF, 1 + (patpos % curlen), 1))
|
||||
patpos = (patpos + 1) % curlen # update pattern position
|
||||
}
|
||||
printf("\n")
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
# The Great Library of useful AWK functions
|
||||
# Fully POSIX-compatible but sometimes depends on other POSIX commands
|
||||
# Use with your programs like this:
|
||||
# LANG=C awk -f tgl.awk -f your_prog.awk [args]
|
||||
#
|
||||
# Current functionality:
|
||||
# * single character input: setterm, getchar
|
||||
# * ASCII and UTF-8 codepoint conversion: ord, wctomb, mbtowc
|
||||
# * loading binary files as decimal integers into arrays: loadbin
|
||||
# * saving binary files from arrays with decimal integers: savebin
|
||||
# * tangent and cotangent functions: tan, cotan
|
||||
# * signum, floor and ceiling functions: sign, floor, ceil
|
||||
# * test for native bitwise operation support: bw_native_support
|
||||
# * reimplementation of most bitwise operations (unsigned 32-bit):
|
||||
# - NOT: bw_compl
|
||||
# - AND: bw_and
|
||||
# - OR: bw_or
|
||||
# - XOR: bw_xor
|
||||
# - NAND: bw_nand
|
||||
# - NOR: bw_nor
|
||||
# - >>: bw_rshift
|
||||
# - <<: bw_lshift
|
||||
#
|
||||
# Created by Luxferre in 2023, released into public domain
|
||||
|
||||
# set/restore the terminal input mode using stty
|
||||
# usage: setterm(0|1|2|3)
|
||||
# 0 - restore the original terminal input mode
|
||||
# 1 - blocking single-character input with echo
|
||||
# 2 - blocking single-character input without echo
|
||||
# 3 - non-blocking single-character input without echo
|
||||
# in pipes, this function doesn't do anything
|
||||
# (but returns 0 since it's not an error)
|
||||
# otherwise an actual stty exit code is returned
|
||||
function setterm(mode, cmd) {
|
||||
if(system("stty >/dev/null 2>&1")) return 0 # exit code 0 means we're in a tty
|
||||
if(!TGL_TERMMODE) { # cache the original terminal input mode
|
||||
(cmd = "stty -g") | getline TGL_TERMMODE
|
||||
close(cmd)
|
||||
}
|
||||
if(mode == 1) cmd = "-icanon"
|
||||
else if(mode == 2) cmd = "-icanon -echo"
|
||||
else if(mode == 3) cmd = "-icanon time 0 min 0 -echo"
|
||||
else cmd = TGL_TERMMODE # restore the original mode
|
||||
return system("stty " cmd ">/dev/null 2>&1") # execute the stty command
|
||||
}
|
||||
|
||||
# getchar emulation using od
|
||||
# caches the read command for further usage
|
||||
# also able to capture null bytes, unlike read/printf approach
|
||||
# use in conjunction with setterm to achieve different input modes
|
||||
# setting LANG=C envvar is recommended, for GAWK it is required
|
||||
# usage: getchar() => integer
|
||||
function getchar(c) {
|
||||
if(!TGL_GCH_CMD) TGL_GCH_CMD = "od -tu1 -w1 -N1 -An -v" # first time usage
|
||||
TGL_GCH_CMD | getline c
|
||||
close(TGL_GCH_CMD)
|
||||
return int(c)
|
||||
}
|
||||
|
||||
# get the ASCII code of a character
|
||||
# setting LANG=C envvar is recommended, for GAWK it is required
|
||||
# usage: ord(c) => integer
|
||||
function ord(c, b) {
|
||||
# init char-to-ASCII mapping if it's not there yet
|
||||
if(!TGL_ORD["#"]) for(b=0;b<256;b++) TGL_ORD[sprintf("%c", b)] = b
|
||||
return int(TGL_ORD[c])
|
||||
}
|
||||
|
||||
# encode a single integer UTF-8 codepoint into a byte sequence in a string
|
||||
# setting LANG=C envvar is recommended, for GAWK it is required
|
||||
# usage: wctomb(code) => string
|
||||
# we can safely use the string type for all codepoints above 0 as all
|
||||
# multibyte sequences have a high bit set, so no null byte is there
|
||||
# for invalid codepoints, an empty string will be returned
|
||||
function wctomb(code, s) {
|
||||
code = int(code)
|
||||
if(code < 0 || code > 1114109) s = "" # invalid codepoint
|
||||
else if(code < 128) s = sprintf("%c", code) # single byte
|
||||
else if(code < 2048) # 2-byte sequence
|
||||
s = sprintf("%c%c", \
|
||||
192 + (int(code/64) % 32), \
|
||||
128 + (code % 64))
|
||||
else if(code < 65536) # 3-byte sequence
|
||||
s = sprintf("%c%c%c", \
|
||||
224 + (int(code/4096) % 16), \
|
||||
128 + (int(code/64) % 64), \
|
||||
128 + (code % 64))
|
||||
else # 4-byte sequence
|
||||
s = sprintf("%c%c%c%c", \
|
||||
240 + (int(code/262144) % 8), \
|
||||
128 + (int(code/4096) % 64), \
|
||||
128 + (int(code/64) % 64), \
|
||||
128 + (code % 64))
|
||||
return s
|
||||
}
|
||||
|
||||
# decode a byte string into a UTF-8 codepoint
|
||||
# setting LANG=C envvar is recommended, for GAWK it is required
|
||||
# usage: mbtowc(s) => integer
|
||||
# decoding stops on the first encountered invalid byte
|
||||
function mbtowc(s, len, code, b, pos) {
|
||||
len = length(s)
|
||||
code = 0
|
||||
for(pos=1;pos<=len;pos++) {
|
||||
code *= 64 # shift the code 6 bits left
|
||||
b = ord(substr(s, pos, 1))
|
||||
if(pos == 1) { # expect a single or header byte
|
||||
if(b < 128) {code = b; break} # it resolves into a single byte
|
||||
else if(b >= 192 && b < 224) # it's a header byte of 2-byte sequence
|
||||
code += b % 32
|
||||
else if(b >= 224 && b < 240) # it's a header byte of 3-byte sequence
|
||||
code += b % 16
|
||||
else if(b >= 240) # it's a header byte of 4-byte sequence
|
||||
code += b % 8
|
||||
else break # a trailer byte in the header position is invalid
|
||||
}
|
||||
else if(b >= 128 && b < 192) # it must be a trailer byte
|
||||
code += b % 64
|
||||
else break # a header byte in the trailer position is invalid
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
# load any binary file into an AWK array (0-indexed), depends on od
|
||||
# returns the resulting array length
|
||||
# usage: loadbin(fname, arr, len, wordsize) => integer
|
||||
# len parameter is optional, specifies how many bytes to read
|
||||
# (if 0 or unset, read everything)
|
||||
# wordsize parameter is optional, 1 byte by default
|
||||
# multibyte words are considered little-endian
|
||||
function loadbin(fname, arr, len, wordsize, cmd, i) {
|
||||
wordsize = int(wordsize)
|
||||
if(wordsize < 1) wordsize = 1
|
||||
len = int(len)
|
||||
i = (len > 0) ? (" -N" len " ") : ""
|
||||
cmd = "od -tu" wordsize " -An -w" wordsize i " -v \"" fname "\""
|
||||
# every line should be a single decimal integer (with some whitespace)
|
||||
i = 0
|
||||
while((cmd | getline) > 0) # read the next line from the stream
|
||||
if(NF) arr[i++] = int($1) # read the first and only field
|
||||
close(cmd) # close the od process
|
||||
return i
|
||||
}
|
||||
|
||||
# save an AWK array (0-indexed) into a binary file
|
||||
# setting LANG=C envvar is recommended, for GAWK it is required
|
||||
# returns the amount of written elements
|
||||
# usage: savebin(fname, arr, len, wordsize) => integer
|
||||
# wordsize parameter is optional, 1 byte by default
|
||||
# multibyte words are considered little-endian
|
||||
function savebin(fname, arr, len, wordsize, i, j) {
|
||||
wordsize = int(wordsize)
|
||||
if(wordsize < 1) wordsize = 1
|
||||
printf("") > fname # truncate the file and open the stream
|
||||
for(i=0;i<len;i++) {
|
||||
if(wordsize == 1) printf("%c", arr[i]) >> fname
|
||||
else # we have a multibyte word size
|
||||
for(j=0;j<wordsize;j++)
|
||||
printf("%c", int(arr[i]/2^(8*j))%256) >> fname
|
||||
}
|
||||
close(fname) # close the output file
|
||||
return i
|
||||
}
|
||||
|
||||
# the missing tangent/cotangent functions
|
||||
|
||||
function tan(x) {return sin(x)/cos(x)}
|
||||
function cotan(x) {return cos(x)/sin(x)}
|
||||
|
||||
# the missing sign/floor/ceil functions
|
||||
|
||||
function sign(x) {return x < 0 ? -1 : !!x}
|
||||
function floor(x, f) {
|
||||
f = int(x)
|
||||
if(x == f) return x
|
||||
else return x >= 0 ? f : (f - 1)
|
||||
}
|
||||
function ceil(x, f) {
|
||||
f = int(x)
|
||||
if(x == f) return x
|
||||
else return x >= 0 ? (f + 1) : f
|
||||
}
|
||||
|
||||
# Bitwise operations section
|
||||
|
||||
# test if the AWK engine has non-POSIX bitwise operation functions
|
||||
# (and, or, xor, compl, lshift, rshift) implemented natively:
|
||||
# if compl is missing, it will be concatenated with 1 and equal to 1
|
||||
# so the inverse of this condition will be the result
|
||||
function bw_native_support() {return (compl (1) != 1)}
|
||||
|
||||
# now, the implementation of the operations themselves
|
||||
# note that all complements are 32-bit and all operands must be non-negative
|
||||
|
||||
function bw_compl(a) {return 4294967295 - int(a)}
|
||||
function bw_lshift(a, b) {for(;b>0;b--) a = int(a/2);return a}
|
||||
function bw_rshift(a, b) {for(;b>0;b--) a *= 2;return int(a)}
|
||||
function bw_and(a, b, v, r) {
|
||||
v = 1; r = 0
|
||||
while(a > 0 || b > 0) {
|
||||
if((a%2) == 1 && (b%2) == 1) r += v
|
||||
a = int(a/2)
|
||||
b = int(b/2)
|
||||
v *= 2
|
||||
}
|
||||
return int(r)
|
||||
}
|
||||
function bw_or(a, b, v, r) {
|
||||
v = 1; r = 0
|
||||
while(a > 0 || b > 0) {
|
||||
if((a%2) == 1 || (b%2) == 1) r += v
|
||||
a = int(a/2)
|
||||
b = int(b/2)
|
||||
v *= 2
|
||||
}
|
||||
return int(r)
|
||||
}
|
||||
function bw_xor(a, b, v, r) {
|
||||
v = 1; r = 0
|
||||
while(a > 0 || b > 0) {
|
||||
if((a%2) != (b%2)) r += v
|
||||
a = int(a/2)
|
||||
b = int(b/2)
|
||||
v *= 2
|
||||
}
|
||||
return int(r)
|
||||
}
|
||||
function bw_nand(a, b) {return bw_compl(bw_and(a,b))}
|
||||
function bw_nor(a, b) {return bw_compl(bw_or(a,b))}
|
||||
|
||||
Reference in New Issue
Block a user