ufold

Прелама сваки UTF-8 улазни ред да стане у задату дужину
git clone https://git.sr.ht/~strahinja/ufold
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 3d3422956bf187bda11c79044b3c299fee705622
родитељ 6a1ac0ee692f3b642f5f89be7586d20ad4e1d391
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Thu,  2 May 2024 23:50:16 +0200

Migrate to POSIX make from redo; switch manpage to mdoc(7)

Signed-off-by: Страхиња Радић <contact@strahinja.org>

Diffstat:
MINSTALL | 42+++++++++++++++++++++---------------------
AMakefile | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dall.do | 2--
Dclean.do | 2--
Aconfig.mk | 22++++++++++++++++++++++
Dconfig.redo | 21---------------------
Ddate.do | 20--------------------
Ddefault.do | 10----------
Ddefault.gz.do | 3---
Ddefault.o.do | 5-----
Mdefs.h | 2+-
Ddo | 446-------------------------------------------------------------------------------
Ddo-test | 41-----------------------------------------
Dinstall.do | 12------------
Alib/makedate | 35+++++++++++++++++++++++++++++++++++
Alib/makeversion | 33+++++++++++++++++++++++++++++++++
Alib/mkpack | 12++++++++++++
Alib/replacepat | 5+++++
Alib/runtests | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dpack.do | 10----------
Drebuild.do | 2--
Dtest.do | 1-
Atests/001-generic.test | 16++++++++++++++++
Atests/002-spaces.test | 16++++++++++++++++
Atests/003-generic.test | 16++++++++++++++++
Atests/004-spaces.test | 16++++++++++++++++
Rtest1.txt -> tests/test1.txt | 0
Rtest2.txt -> tests/test2.txt | 0
Mufold.1.in | 138+++++++++++++++++++++++++++++++------------------------------------------------
Mufold.c | 105+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Dufold.do | 10----------
Duninstall.do | 10----------
Mutf8.c | 2+-
Mutf8.h | 2+-
Dversion.do | 17-----------------
измењених датотека: 35, додавања: 432(+), брисања: 765(-)

diff --git a/INSTALL b/INSTALL @@ -1,13 +1,15 @@ Prerequisites ============= -* C compiler - defaults in config.redo +* C compiler - defaults in config.mk -Optional --------- +Testing +------- + +* Perl ≥ 5.010 -* gzip (autodetected, for manpage compression) + $ make test Install @@ -16,33 +18,31 @@ Install $ git clone https://git.sr.ht/~strahinja/ufold $ cd ufold $ su - -If you have djb redo: - - # redo install - -if you don't: - - # ./do install + # make install Persistent build configuration file ----------------------------------- -The file config.redo will be sourced when building targets which also accept -configuration through environment variables. For example, CC and PREFIX -are specified in config.redo. +The file config.mk will be sourced when building targets which also accept +configuration through environment variables. For example, CPPFLAGS and PREFIX +are specified in config.mk. OpenBSD ------- -Replace gcc in CC with cc and add -D_BSD_SOURCE, eg: +Before running make(1), comment the CPPFLAGS definition in config.mk above the +line with the "OpenBSD" comment and uncomment the one below it: - # CC='cc -g -Wall -pedantic -std=c99 -D_POSIX_C_SOURCE=200809L - -D_XOPEN_SOURCE=700 -D_BSD_SOURCE}' \ - ./do install + #CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \ + # -D_XOPEN_SOURCE=700 + # OpenBSD + CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \ + -D_XOPEN_SOURCE=700 -D_BSD_SOURCE -and change MANPREFIX to +Also, do the same for MANPREFIX: - MANPREFIX=$PREFIX/man + #MANPREFIX = $(DESTDIR)$(PREFIX)/share/man + # OpenBSD + MANPREFIX = $(DESTDIR)$(PREFIX)/man diff --git a/Makefile b/Makefile @@ -0,0 +1,66 @@ +.POSIX: + +include config.mk + +.SUFFIXES: +.SUFFIXES: .1.in .1 .h.in .h .c .o + +.1.in.1: + sh lib/replacepat $< $@ + +.h.in.h: + sh lib/replacepat $< $@ + +all: makedate makeversion $(PROG) $(MANPAGE) + if [ -f .rebuild ]; then rm .rebuild; make -e all; else true; fi + +date: + sh lib/makedate + +version: + sh lib/makeversion + +makedate: + sh lib/makedate -l + +makeversion: + sh lib/makeversion -l + +ufold.o: ufold.c defs.h utf8.h version.h + +utf8.o: utf8.c defs.h utf8.h + +$(MANPAGE): $(MANPAGE).in date version + +version.h: version.h.in date version + +$(PROG): $(OBJS) $(SRC) $(HEADERS) + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) + +clean: + rm -f version date version.h *.o *.xz *~ $(PROG) $(MANPAGE) + +install: all + $(INSTALL) -Dm 0644 $(MANPAGE) $(MANPREFIX)/man1/$(MANPAGE) + $(INSTALL) -Dm 0644 README $(DOCDIR)/README + $(INSTALL) -Dm 0644 LICENSE $(DOCDIR)/LICENSE + $(INSTALL) -Dm 0755 $(PROG) $(BINDIR)/$(PROG) + +uninstall: + rm -f $(MANPREFIX)/man1/$(MANPAGE) + rmdir $(MANPREFIX)/man1 2>/dev/null || true + rmdir $(MANPREFIX) 2>/dev/null || true + rm -f $(DOCDIR)/README + rm -f $(DOCDIR)/LICENSE + rmdir $(DOCDIR) 2>/dev/null || true + rm -f $(PROG) $(BINDIR)/$(PROG) + rmdir $(BINDIR) 2>/dev/null || true + +test: all + command -v perl >/dev/null || { echo Test suite requires perl; exit 1; } + perl lib/runtests + +pack: all + sh -x lib/mkpack + +.PHONY: all clean install makedate makeversion pack uninstall diff --git a/all.do b/all.do @@ -1,2 +0,0 @@ -. ./config.redo -redo-ifchange ufold ufold.1"$MANSUFFIX" diff --git a/clean.do b/clean.do @@ -1,2 +0,0 @@ -. ./config.redo -rm -f *~ *.o ufold $MANPAGES *.1 version.h version date diff --git a/config.mk b/config.mk @@ -0,0 +1,22 @@ +#CC = cc +CFLAGS = -Os -Wall -pedantic -std=c99 +CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \ + -D_XOPEN_SOURCE=700 +# OpenBSD +#CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \ +# -D_XOPEN_SOURCE=700 -D_BSD_SOURCE +INSTALL = install +LIBS = +SRC = ufold.c utf8.c +HEADERS = defs.h utf8.h version.h +OBJS = ufold.o utf8.o +PREFIX = /usr/local +PROG = ufold + +# Derived macros +BINDIR = $(DESTDIR)$(PREFIX)/bin +DOCDIR = $(DESTDIR)$(PREFIX)/share/doc/$(PROG) +MANPREFIX = $(DESTDIR)$(PREFIX)/share/man +# OpenBSD +#MANPREFIX = $(DESTDIR)$(PREFIX)/man +MANPAGE = $(PROG).1 diff --git a/config.redo b/config.redo @@ -1,21 +0,0 @@ -PREFIX=${PREFIX:-/usr/local} - -BINDIR=$PREFIX/bin -DOCDIR=$PREFIX/share/doc/ufold -MANPREFIX=$PREFIX/share/man - -# OpenBSD -#MANPREFIX=$PREFIX/man - -PROGS="ufold" -DOCS="README LICENSE" -MANSUFFIX=${MANSUFFIX-$(command -v gzip >/dev/null && printf ".gz")} -MANPAGES="ufold.1${MANSUFFIX}" - -CC=${CC:-gcc -g -Wall -pedantic -std=c99 -D_POSIX_C_SOURCE=200809L \ --D_XOPEN_SOURCE=700} -STRIP=${STRIP:-:} - -# OpenBSD -#CC=${CC:-cc -g -Wall -pedantic -std=c99 -D_POSIX_C_SOURCE=200809L \ -#-D_XOPEN_SOURCE=700 -D_BSD_SOURCE} diff --git a/date.do b/date.do @@ -1,20 +0,0 @@ -FALLBACKDATE=${FALLBACKDATE:-unknown} -LC_ALL=C -export LC_ALL -user=$(unalias ls >/dev/null 2>&1 || true; \ - command ls -ld . | awk '{print $3}') -e_user=$(id -un) -if [ -d .git ] && command -v git >/dev/null 2>&1; then - if [ "$e_user" = "$user" ]; then - git log --format=format:%cd \ - --date=format:"%B %d, %Y" -1 @ >$3 - else - su "${user}" -c 'env LC_ALL=C git log --format=format:%cd \ - --date=format:"%B %d, %Y" -1 @' >$3 - fi - echo >>$3 -else - printf "%s\n" "$FALLBACKDATE" >$3 -fi -redo-always -redo-stamp <$3 diff --git a/default.do b/default.do @@ -1,10 +0,0 @@ -if [ -r $1.in ]; then - redo-ifchange $1.in version date - read VERSION <version - read DATE <date - sed -e "s/%VERSION%/$VERSION/g" \ - -e "s/%DATE%/$DATE/g" <$1.in -else - echo "$0: don't know how to build '$1'" >&2 - exit 99 -fi diff --git a/default.gz.do b/default.gz.do @@ -1,3 +0,0 @@ -redo-ifchange $2 -gzip -cf $2 >$3 - diff --git a/default.o.do b/default.o.do @@ -1,5 +0,0 @@ -. ./config.redo -sed -n 's/^#include "\([^"]*\)"/\1/p' ${2%.o}.c | - xargs redo-ifchange -redo-ifchange $2.c -${CC} -c $2.c -o $3 diff --git a/defs.h b/defs.h @@ -1,5 +1,5 @@ /* This program is licensed under the terms of GNU GPL v3 or (at your option) - * any later version. Copyright (C) 2021-2023 Страхиња Радић. + * any later version. Copyright (C) 2021-2024 Страхиња Радић. * See the file LICENSE for exact copyright and license details. */ #define MAXBUF 1024 diff --git a/do b/do @@ -1,446 +0,0 @@ -#!/usr/bin/env sh -# -# A minimal alternative to djb redo that doesn't support incremental builds. -# For the full version, visit http://github.com/apenwarr/redo -# -# The author disclaims copyright to this source file and hereby places it in -# the public domain. (2010 12 14; updated 2019 02 24) -# -USAGE=" -usage: do [-d] [-x] [-v] [-c] <targets...> - -d print extra debug messages (mostly about dependency checks) - -v run .do files with 'set -v' - -x run .do files with 'set -x' - -c clean up all old targets before starting - - Note: do is an implementation of redo that does *not* check dependencies. - It will never rebuild a target it has already built, unless you use -c. -" - -# CDPATH apparently causes unexpected 'cd' output on some platforms. -unset CDPATH - -# By default, no output coloring. -green="" -bold="" -plain="" - -if [ -n "$TERM" -a "$TERM" != "dumb" ] && tty <&2 >/dev/null 2>&1; then - green="$(printf '\033[32m')" - bold="$(printf '\033[1m')" - plain="$(printf '\033[m')" -fi - -# The 'seq' command is not available on all platforms. -_seq() { - local x=0 max="$1" - while [ "$x" -lt "$max" ]; do - x=$((x + 1)) - echo "$x" - done -} - -# Split $1 into a dir part ($_dirsplit_dir) and base filename ($_dirsplit_base) -_dirsplit() { - _dirsplit_base=${1##*/} - _dirsplit_dir=${1%$_dirsplit_base} -} - -# Like /usr/bin/dirname, but avoids a fork and uses _dirsplit semantics. -qdirname() ( - _dirsplit "$1" - dir=${_dirsplit_dir%/} - echo "${dir:-.}" -) - -_dirsplit "$0" -REDO=$(cd "$(pwd -P)" && - cd "${_dirsplit_dir:-.}" && - echo "$PWD/$_dirsplit_base") -export REDO -_cmd=$_dirsplit_base - -DO_TOP= -if [ -z "$DO_BUILT" ]; then - export _do_opt_debug= - export _do_opt_exec= - export _do_opt_verbose= - export _do_opt_clean= -fi -while getopts 'dxvcj:h?' _opt; do - case $_opt in - d) _do_opt_debug=1 ;; - x) _do_opt_exec=x ;; - v) _do_opt_verbose=v ;; - c) _do_opt_clean=1 ;; - j) ;; # silently ignore, for compat with real redo - \?|h|*) printf "%s" "$USAGE" >&2 - exit 99 - ;; - esac -done -shift "$((OPTIND - 1))" -_debug() { - [ -z "$_do_opt_debug" ] || echo "$@" >&2 -} - -if [ -z "$DO_BUILT" -a "$_cmd" != "redo-whichdo" ]; then - DO_TOP=1 - if [ "$#" -eq 0 ] && [ "$_cmd" = "do" -o "$_cmd" = "redo" ]; then - set all # only toplevel redo has a default target - fi - export DO_STARTDIR="$(pwd -P)" - # If starting /bin/pwd != $PWD, this will fix it. - # That can happen when $PWD contains symlinks that the shell is - # trying helpfully (but unsuccessfully) to hide from the user. - cd "$DO_STARTDIR" || exit 99 - export DO_BUILT="$PWD/.do_built" - if [ -z "$_do_opt_clean" -a -e "$DO_BUILT" ]; then - echo "do: Incremental mode. Use -c for clean rebuild." >&2 - fi - : >>"$DO_BUILT" - sort -u "$DO_BUILT" >"$DO_BUILT.new" - while read f; do - [ -n "$_do_opt_clean" ] && printf "%s\0%s.did\0" "$f" "$f" - printf "%s.did.tmp\0" "$f" - done <"$DO_BUILT.new" | - xargs -0 rm -f 2>/dev/null - mv "$DO_BUILT.new" "$DO_BUILT" - export DO_PATH="$DO_BUILT.dir" - export PATH="$DO_PATH:$PATH" - rm -rf "$DO_PATH" - mkdir "$DO_PATH" - for d in redo redo-ifchange redo-whichdo; do - ln -s "$REDO" "$DO_PATH/$d" - done - for d in redo-ifcreate redo-stamp redo-always redo-ood \ - redo-targets redo-sources; do - echo "#!/bin/sh" >"$DO_PATH/$d" - chmod a+rx "$DO_PATH/$d" - done -fi - - -# Chop the "file" part off a /path/to/file pathname. -# Note that if the filename already ends in a /, we just remove the slash. -_updir() -{ - local v="${1%/*}" - [ "$v" != "$1" ] && echo "$v" - # else "empty" which means we went past the root -} - - -# Returns true if $1 starts with $2. -_startswith() -{ - [ "${1#"$2"}" != "$1" ] -} - - -# Returns true if $1 ends with $2. -_endswith() -{ - [ "${1%"$2"}" != "$1" ] -} - - -# Prints $1 if it's absolute, or $2/$1 if $1 is not absolute. -_abspath() -{ - local here="$2" there="$1" - if _startswith "$1" "/"; then - echo "$1" - else - echo "$2/$1" - fi -} - - -# Prints $1 as a path relative to $PWD (not starting with /). -# If it already doesn't start with a /, doesn't change the string. -_relpath() -{ - local here="$2" there="$1" out= hadslash= - #echo "RP start '$there' hs='$hadslash'" >&2 - _startswith "$there" "/" || { echo "$there" && return; } - [ "$there" != "/" ] && _endswith "$there" "/" && hadslash=/ - here=${here%/}/ - while [ -n "$here" ]; do - #echo "RP out='$out' here='$here' there='$there'" >&2 - [ "${here%/}" = "${there%/}" ] && there= && break; - [ "${there#$here}" != "$there" ] && break - out=../$out - _dirsplit "${here%/}" - here=$_dirsplit_dir - done - there=${there#$here} - if [ -n "$there" ]; then - echo "$out${there%/}$hadslash" - else - echo "${out%/}$hadslash" - fi -} - - -# Prints a "normalized relative" path, with ".." resolved where possible. -# For example, a/b/../c will be reduced to just a/c. -_normpath() -( - local path="$1" relto="$2" out= isabs= - #echo "NP start '$path'" >&2 - if _startswith "$path" "/"; then - isabs=1 - else - path="${relto%/}/$path" - fi - set -f - IFS=/ - for d in ${path%/}; do - #echo "NP out='$out' d='$d'" >&2 - if [ "$d" = ".." ]; then - out=$(_updir "${out%/}")/ - else - out=$out$d/ - fi - done - #echo "NP out='$out' (done)" >&2 - out=${out%/} - if [ -n "$isabs" ]; then - echo "${out:-/}" - else - _relpath "${out:-/}" "$relto" - fi -) - - -# Prints a "real" path, with all symlinks resolved where possible. -_realpath() -{ - local path="$1" relto="$2" isabs= rest= - if _startswith "$path" "/"; then - isabs=1 - else - path="${relto%/}/$path" - fi - ( - for d in $(_seq 100); do - #echo "Trying: $PWD--$path" >&2 - if cd -P "$path" 2>/dev/null; then - # success - pwd=$(pwd -P) - #echo " chdir ok: $pwd--$rest" >&2 - np=$(_normpath "${pwd%/}/$rest" "$relto") - if [ -n "$isabs" ]; then - echo "$np" - else - _relpath "$np" "$relto" - fi - break - fi - _dirsplit "${path%/}" - path=$_dirsplit_dir - rest="$_dirsplit_base/$rest" - done - ) -} - - -# List the possible names for default*.do files in dir $1 matching the target -# pattern in $2. We stop searching when we find the first one that exists. -_find_dofiles_pwd() -{ - local dodir="$1" dofile="$2" - _startswith "$dofile" "default." || dofile=${dofile#*.} - while :; do - dofile=default.${dofile#default.*.} - echo "$dodir$dofile" - [ -e "$dodir$dofile" ] && return 0 - [ "$dofile" = default.do ] && break - done - return 1 -} - - -# List the possible names for default*.do files in $PWD matching the target -# pattern in $1. We stop searching when we find the first name that works. -# If there are no matches in $PWD, we'll search in .., and so on, to the root. -_find_dofiles() -{ - local target="$1" dodir= dofile= newdir= - _debug "find_dofile: '$PWD' '$target'" - dofile="$target.do" - echo "$dofile" - [ -e "$dofile" ] && return 0 - - # Try default.*.do files, walking up the tree - _dirsplit "$dofile" - dodir=$_dirsplit_dir - dofile=$_dirsplit_base - [ -n "$dodir" ] && dodir=${dodir%/}/ - [ -e "$dodir$dofile" ] && return 0 - for i in $(_seq 100); do - [ -n "$dodir" ] && dodir=${dodir%/}/ - #echo "_find_dofiles: '$dodir' '$dofile'" >&2 - _find_dofiles_pwd "$dodir" "$dofile" && return 0 - newdir=$(_realpath "${dodir}.." "$PWD") - [ "$newdir" = "$dodir" ] && break - dodir=$newdir - done - return 1 -} - - -# Print the last .do file returned by _find_dofiles. -# If that file exists, returns 0, else 1. -_find_dofile() -{ - local files="$(_find_dofiles "$1")" - rv=$? - #echo "files='$files'" >&2 - [ "$rv" -ne 0 ] && return $rv - echo "$files" | { - while read -r linex; do line=$linex; done - printf "%s\n" "$line" - } -} - - -# Actually run the given $dofile with the arguments in $@. -# Note: you should always run this in a subshell. -_run_dofile() -{ - export DO_DEPTH="$DO_DEPTH " - export REDO_TARGET="$PWD/$target" - local line1 - set -e - read line1 <"$PWD/$dofile" || true - cmd=${line1#"#!/"} - if [ "$cmd" != "$line1" ]; then - set -$_do_opt_verbose$_do_opt_exec - exec /$cmd "$PWD/$dofile" "$@" - else - set -$_do_opt_verbose$_do_opt_exec - # If $dofile is empty, "." might not change $? at - # all, so we clear it first with ":". - :; . "$PWD/$dofile" - fi -} - - -# Find and run the right .do file, starting in dir $1, for target $2, -# providing a temporary output file as $3. Renames the temp file to $2 when -# done. -_do() -{ - local dir="$1" target="$1$2" tmp="$1$2.redo.tmp" tdir= - local dopath= dodir= dofile= ext= - if [ "$_cmd" = "redo" ] || - ( [ ! -e "$target" -o -d "$target" ] && - [ ! -e "$target.did" ] ); then - printf '%sdo %s%s%s%s\n' \ - "$green" "$DO_DEPTH" "$bold" "$target" "$plain" >&2 - dopath=$(_find_dofile "$target") - if [ ! -e "$dopath" ]; then - echo "do: $target: no .do file ($PWD)" >&2 - return 1 - fi - _dirsplit "$dopath" - dodir=$_dirsplit_dir dofile=$_dirsplit_base - if _startswith "$dofile" "default."; then - ext=${dofile#default} - ext=${ext%.do} - else - ext= - fi - target=$PWD/$target - tmp=$PWD/$tmp - cd "$dodir" || return 99 - target=$(_relpath "$target" "$PWD") || return 98 - tmp=$(_relpath "$tmp" "$PWD") || return 97 - base=${target%$ext} - tdir=$(qdirname "$target") - [ ! -e "$DO_BUILT" ] || [ ! -w "$tdir/." ] || - : >>"$target.did.tmp" - # $qtmp is a temporary file used to capture stdout. - # Since it might be accidentally deleted as a .do file - # does its work, we create it, then open two fds to it, - # then immediately delete the name. We use one fd to - # redirect to stdout, and the other to read from after, - # because there's no way to fseek(fd, 0) in sh. - qtmp=$DO_PATH/do.$$.tmp - ( - rm -f "$qtmp" - ( _run_dofile "$target" "$base" "$tmp" >&3 3>&- 4<&- ) - rv=$? - if [ $rv != 0 ]; then - printf "do: %s%s\n" "$DO_DEPTH" \ - "$target: got exit code $rv" >&2 - rm -f "$tmp.tmp" "$tmp.tmp2" "$target.did" - return $rv - fi - echo "$PWD/$target" >>"$DO_BUILT" - if [ ! -e "$tmp" ]; then - # if $3 wasn't created, copy from stdout file - cat <&4 >$tmp - # if that's zero length too, forget it - [ -s "$tmp" ] || rm -f "$tmp" - fi - ) 3>$qtmp 4<$qtmp # can't use "|| return" here... - # ...because "|| return" would mess up "set -e" inside the () - # on some shells. Running commands in "||" context, even - # deep inside, will stop "set -e" from functioning. - rv=$? - [ "$rv" = 0 ] || return "$rv" - mv "$tmp" "$target" 2>/dev/null - [ -e "$target.did.tmp" ] && - mv "$target.did.tmp" "$target.did" || - : >>"$target.did" - else - _debug "do $DO_DEPTH$target exists." >&2 - fi -} - - -# Implementation of the "redo" command. -_redo() -{ - local i startdir="$PWD" dir base - set +e - for i in "$@"; do - i=$(_abspath "$i" "$startdir") - ( - cd "$DO_STARTDIR" || return 99 - i=$(_realpath "$(_relpath "$i" "$PWD")" "$PWD") - _dirsplit "$i" - dir=$_dirsplit_dir base=$_dirsplit_base - _do "$dir" "$base" - ) - [ "$?" = 0 ] || return 1 - done -} - - -# Implementation of the "redo-whichdo" command. -_whichdo() -{ - _find_dofiles "$1" -} - - -case $_cmd in - do|redo|redo-ifchange) _redo "$@" ;; - redo-whichdo) _whichdo "$1" ;; - do.test) ;; - *) printf "do: '%s': unexpected redo command" "$_cmd" >&2; exit 99 ;; -esac -[ "$?" = 0 ] || exit 1 - -if [ -n "$DO_TOP" ]; then - if [ -n "$_do_opt_clean" ]; then - echo "do: Removing stamp files..." >&2 - [ ! -e "$DO_BUILT" ] || - while read f; do printf "%s.did\0" "$f"; done <"$DO_BUILT" | - xargs -0 rm -f 2>/dev/null - fi -fi diff --git a/do-test b/do-test @@ -1,41 +0,0 @@ -#!/bin/sh - -printf "Test 1 - normal:\t" -fold -w 20 test1.txt > f.out -ufold -w 20 test1.txt > uf.out -if diff f.out uf.out 2>/dev/null >/dev/null; then - echo PASS -else - echo FAIL -fi -rm -f f.out uf.out - -printf "Test 1 - spaces:\t" -fold -w 20 -s test1.txt > f.out -ufold -w 20 -s test1.txt > uf.out -if diff f.out uf.out 2>/dev/null >/dev/null; then - echo PASS -else - echo FAIL -fi -rm -f f.out uf.out - -printf "Test 2 - normal:\t" -fold -w 20 test2.txt > f.out -ufold -w 20 test2.txt > uf.out -if diff f.out uf.out 2>/dev/null >/dev/null; then - echo PASS -else - echo FAIL -fi -rm -f f.out uf.out - -printf "Test 2 - spaces:\t" -fold -w 20 -s test2.txt > f.out -ufold -w 20 -s test2.txt > uf.out -if diff f.out uf.out 2>/dev/null >/dev/null; then - echo PASS -else - echo FAIL -fi -rm -f f.out uf.out diff --git a/install.do b/install.do @@ -1,12 +0,0 @@ -redo-ifchange all -. ./config.redo -install -d "$DOCDIR" "$BINDIR" "${MANPREFIX}/man1" -for docfile in $DOCS; do - install -Dm 0644 "${docfile}" "${DOCDIR}/${docfile}" -done -for binfile in $PROGS; do - install -Dm 0755 "${binfile}" "${BINDIR}/${binfile}" -done -for manfile in $MANPAGES; do - install -Dm 0644 "${manfile}" "${MANPREFIX}/man1/${manfile}" -done diff --git a/lib/makedate b/lib/makedate @@ -0,0 +1,35 @@ +#!/bin/sh -x +# vim: set ft=bash: +FALLBACKDATE=${FALLBACKDATE:-unknown} +LC_ALL=C +export LC_ALL +user=$(unalias ls >/dev/null 2>&1 || true; \ + command ls -ld . | awk '{print $3}') +e_user=$(id -un) +if [ -d .git ] && command -v git >/dev/null 2>&1; then + if [ "$e_user" = "$user" ]; then + git log --format=format:%cd \ + --date=format:"%B %d, %Y" -1 @ >date.new + else + su "${user}" -c 'env LC_ALL=C git log --format=format:%cd \ + --date=format:"%B %d, %Y" -1 @' >date.new + fi + echo >>date.new +else + printf "%s\n" "$FALLBACKDATE" >date.new +fi + +if [ -f date ]; then + if diff date date.new >/dev/null; then + rm date.new + else + mv date.new date + touch .rebuild + fi +else + mv date.new date + touch .rebuild +fi +if [ "$1" = "-l" ]; then + printf "DATE=\"%s\"\n" "$(cat date)" +fi diff --git a/lib/makeversion b/lib/makeversion @@ -0,0 +1,33 @@ +#!/bin/sh -x +# vim: set ft=bash: +FALLBACKVER=${FALLBACKVER:-unknown} +LC_ALL=C +export LC_ALL +user=$(unalias ls >/dev/null 2>&1 || true; \ + command ls -ld . | awk '{print $3}') +e_user=$(id -un) +if [ -d .git ] && command -v git >/dev/null 2>&1; then + if [ "$e_user" = "$user" ]; then + git describe 2>/dev/null | sed 's/^v//' >version.new + else + su "${user}" -c 'git describe' 2>/dev/null | + sed 's/^v//' >version.new + fi +else + printf "%s\n" "$FALLBACKVER" >version.new +fi + +if [ -f version ]; then + if diff version version.new >/dev/null; then + rm version.new + else + mv version.new version + touch .rebuild + fi +else + mv version.new version + touch .rebuild +fi +if [ "$1" = "-l" ]; then + printf "VERSION=\"%s\"\n" "$(cat version)" +fi diff --git a/lib/mkpack b/lib/mkpack @@ -0,0 +1,12 @@ +#!/bin/sh -x +# vim: set ft=bash: +DISTDIR=$(grep ^PROG config.mk | + sed -E 's,.*=[[:space:]]+([[:alpha:]]+),\1,')-$(cat version) +mkdir "$DISTDIR" +for f in $(git ls-files); do + d="$DISTDIR"/"$(dirname "$f")" + mkdir -p "$d" + cp "$f" "$d"/ +done +tar -cf - "$DISTDIR" | xz -9v - > "$DISTDIR".tar.xz +rm -fr "$DISTDIR" diff --git a/lib/replacepat b/lib/replacepat @@ -0,0 +1,5 @@ +#!/bin/sh -x +# vim: set ft=bash: +DATE=$(cat date) +VERSION=$(cat version) +sed -e 's|%VERSION%|'"$VERSION"'|g' -e 's|%DATE%|'"$DATE"'|g' "$1" > "$2" diff --git a/lib/runtests b/lib/runtests @@ -0,0 +1,57 @@ +#!/usr/bin/perl +# This program is licensed under the terms of GNU GPL v3 or (at your option) +# any later version. Copyright (C) 2021-2024 Страхиња Радић. +# See the file LICENSE for exact copyright and license details. + + +use 5.010; +use File::Find; + +my $errors = 0; +my @files; +my $maxlen = 60; +my $testdir = 'tests'; + +#system("sh -c 'command -v expect' >/dev/null") == 0 || +# die "Test suite requires expect"; + +find(sub { + push @files, $File::Find::name if (/\.test$/); +}, $testdir . "/"); + +for (@files) +{ + system $_; + $results{$_} = $?>>8; +} + +# Clear the screen +#print "\033[0m\033[2J\033[2K\033[1;1H"; + +print <<"!"; +TEST RESULTS +============================================================ +! + +for (@files) +{ + printf "%s:%*s%s\t%s\n", $_, $maxlen-5-length($_), " ", + $results{$_} == 0 ? " OK" : "FAIL"; + if ($results{$_}) + { + $errors = 1; + } +} + +if ($errors == 1) +{ + printf "Some tests were unsuccessful - please report\n"; +} +else +{ + printf "All tests passed\n"; +} + +print <<"!"; +============================================================ +! diff --git a/pack.do b/pack.do @@ -1,10 +0,0 @@ -redo all -redo-ifchange version -read VERSION <version -VERSION=$(echo $VERSION | sed 's/^v//') -DISTDIR=ufold-$VERSION -mkdir $DISTDIR -cp *.c *.h *.do README LICENSE $DISTDIR -tar cvf $DISTDIR.tar.xz $DISTDIR >/dev/null -rm -fr $DISTDIR - diff --git a/rebuild.do b/rebuild.do @@ -1,2 +0,0 @@ -redo clean all - diff --git a/test.do b/test.do @@ -1 +0,0 @@ -./do-test >&2 diff --git a/tests/001-generic.test b/tests/001-generic.test @@ -0,0 +1,16 @@ +#!/bin/sh +# vim: set ft=bash: +exec 2>/dev/null +testfile=tests/test1.txt +outfile1=tests/t001.out +outfile2=tests/t001-u.out + +fold -w 20 "$testfile" > "$outfile1" +./ufold -w 20 "$testfile" > "$outfile2" +if diff "$outfile1" "$outfile2" 2>/dev/null >&2; then + result=0 +else + result=1 +fi +rm -f "$outfile1" "$outfile2" +exit "$result" diff --git a/tests/002-spaces.test b/tests/002-spaces.test @@ -0,0 +1,16 @@ +#!/bin/sh +# vim: set ft=bash: +exec 2>/dev/null +testfile=tests/test1.txt +outfile1=tests/t002.out +outfile2=tests/t002-u.out + +fold -w 20 -s "$testfile" > "$outfile1" +./ufold -w 20 -s "$testfile" > "$outfile2" +if diff "$outfile1" "$outfile2" 2>/dev/null >&2; then + result=0 +else + result=1 +fi +rm -f "$outfile1" "$outfile2" +exit "$result" diff --git a/tests/003-generic.test b/tests/003-generic.test @@ -0,0 +1,16 @@ +#!/bin/sh +# vim: set ft=bash: +exec 2>/dev/null +testfile=tests/test2.txt +outfile1=tests/t003.out +outfile2=tests/t003-u.out + +fold -w 20 "$testfile" > "$outfile1" +./ufold -w 20 "$testfile" > "$outfile2" +if diff "$outfile1" "$outfile2" 2>/dev/null >&2; then + result=0 +else + result=1 +fi +rm -f "$outfile1" "$outfile2" +exit "$result" diff --git a/tests/004-spaces.test b/tests/004-spaces.test @@ -0,0 +1,16 @@ +#!/bin/sh +# vim: set ft=bash: +exec 2>/dev/null +testfile=tests/test2.txt +outfile1=tests/t004.out +outfile2=tests/t004-u.out + +fold -w 20 -s "$testfile" > "$outfile1" +./ufold -w 20 -s "$testfile" > "$outfile2" +if diff "$outfile1" "$outfile2" 2>/dev/null >&2; then + result=0 +else + result=1 +fi +rm -f "$outfile1" "$outfile2" +exit "$result" diff --git a/test1.txt b/tests/test1.txt diff --git a/test2.txt b/tests/test2.txt diff --git a/ufold.1.in b/ufold.1.in @@ -1,85 +1,55 @@ -'\" t -.\" Manpage for ufold(1) -.\" vim: set filetype=groff: -. -.mso an-ext.tmac -.de CDS -.EX -.RS \\$1 -.sp 1 -.. -.de CDE -.sp 1 -.RE -.EE -.. -.de RDS -.RS \\$1 -.sp 1 -.. -.de RDE -.sp 1 -.RE -.. -. -.TH UFOLD "1" "%DATE%" "text-tools" "General Commands Manual" -.SH NAME -ufold \- wrap each UTF-8 input line to fit specified width -. -.SH SYNOPSIS -. -.SY ufold -.OP "\-h \fR|\fP \-\-help" -.YS -. -.SY ufold -.OP "\-v \fR|\fP \-\-version" -.YS -. -.SY ufold -.OP "\-s \fR|\fP \-\-spaces" -.OP "\-w \fR|\fP \-\-width=\fIwidth\fP" -.RI [ file ] -.YS -. -.SH COPYRIGHT -. -.LP -ufold Copyright \(co 2021\-2023 Strahinya Radich. -.br -This program is licensed under GNU GPL v3 or later. See the file -.I LICENSE -in the ufold repository for details. -.SH DESCRIPTION -. -.LP -.B ufold -wraps input lines in -.IR file , -writing to standard output. It is a version of a program -.BR fold (1) -from the GNU coreutils which supports input encoded in UTF-8. -. -.LP -If a special filename of \[lq]\-\[rq] is given, -.B ufold -will instead read from the standard input. This also happens when no filename is -given. -. -.SH AUTHOR -. -Strahinya Radich, -.UR https://\:strahinja.org -.UE -. -.SH BUGS -. -.LP +.\" This program is licensed under the terms of GNU GPL v3 or (at your option) +.\" any later version. Copyright (C) 2021-2024 Страхиња Радић. +.\" See the file LICENSE for exact copyright and license details. +.Dd %DATE% +.Dt UFOLD 1 +.Os +.Sh NAME +.Nm ufold +.Nd wrap each UTF-8 input line to fit specified width +.Sh SYNOPSIS +.Nm +.Fl h | Fl \-help | Fl V | Fl \-full\-version | Fl v | Fl \-version +.Nm +.Oo Fl s | Fl \-spaces Oc +.Oo Fl w Ar width | Fl \-width= Ns Ar width Oc +.Op Ar file +.Sh DESCRIPTION +.Nm +wraps input lines in +.Ar file , +writing to standard output. +It emulates the program +.Xr fold 1 +from the GNU coreutils, but adding support for input encoded in UTF-8. +.Pp +If a special +.Ar file +of +.Dq \- +is given, +.Nm +will instead read from the standard input. +This also happens when no filename is given. +.Bl -tag -width Ds +.It Fl h | Fl \-help +Print usage information. +.It Fl s | Fl \-spaces +Don't break words in the middle of a word. +.It Fl V | Fl \-full\-version +Print full version (like +.Fl v Ns +), followed by copyright notice. +.It Fl v | Fl \-version +Print program version and commit date. +.It Fl w Ar width | Fl \-width= Ns Ar width +Set output width in glyphs. +.El +.Sh "SEE ALSO" +.Xr fold 1 +.Sh AUTHORS +.An "Strahinya Radich" Aq contact@strahinja.org , +2021\-2024 +.Sh BUGS Bugs can be reported using the ticket tracker at: -.UR https://\:todo.sr.ht/\:~strahinja/\:text-tools -.UE -. -.SH "SEE ALSO" -. -.BR fold (1) - +.Lk https://\:todo.sr.ht/\:~strahinja/\:text-tools diff --git a/ufold.c b/ufold.c @@ -1,5 +1,5 @@ /* This program is licensed under the terms of GNU GPL v3 or (at your option) - * any later version. Copyright (C) 2021-2023 Страхиња Радић. + * any later version. Copyright (C) 2021-2024 Страхиња Радић. * See the file LICENSE for exact copyright and license details. */ #include <errno.h> @@ -13,13 +13,21 @@ #include "utf8.h" #include "version.h" +#define COPYRIGHT \ + (" This program is licensed under the terms of GNU GPL v3" \ + " or (at your option)\n" \ + " any later version. Copyright (C) 2021-2024 Strahinya Radich.\n" \ + " See the file LICENSE for exact copyright and license " \ + "details.") + char filename[MAXPATH]; int -usage() +usage(void) { - printf("Usage: %s [-h|--help] [-s|--spaces] [-v|--version] " - "[-w|--width=<COLS>] [FILE]\n", PROGRAMNAME); + printf("Usage:\t%s -h | --help | -V | --full-version | -v | --version\n" + "\t%s [-s | --spaces] [-w | --width=<cols>] [file]\n", + PROGRAMNAME, PROGRAMNAME); return 0; } @@ -31,19 +39,21 @@ error(const int code, const char* fmt, ...) va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); - fprintf(stderr, "%s:%s: %s\n", PROGRAMNAME, - *filename ? filename : "(stdin)", buf); + fprintf(stderr, "%s:%s: %s\n", PROGRAMNAME, + *filename ? filename : "(stdin)", buf); return code; } int -version() +version(const int full) { printf("%s %s, built on %s\n", PROGRAMNAME, VERSION, DATE); + if (full) + puts(COPYRIGHT); return 0; } -int +int starts_with(const char* s, const char* with) { while (*s && *s == *with) @@ -63,7 +73,7 @@ fold_line_spaces(const u8* line, const int width) return; /* - * Idea: + * Idea: * 1. store characters in token until line is exhausted * 2. when ' ' or end of line is encountered: * 2,1, if token length > width, see if current column is 0: @@ -77,14 +87,14 @@ fold_line_spaces(const u8* line, const int width) * 3. go to 1. unless line is exhausted */ const u8* pline = line; - u32 uch = 0; + u32 uch = 0; u32 token[MAXINPUTBUF]; u32* ptoken = NULL; u8 u8ch[7]; size_t u8ch_len = 0; - size_t delta = 0; - size_t len = 0; - size_t col = 0; + size_t delta = 0; + size_t len = 0; + size_t col = 0; *token = 0; ptoken = token; @@ -100,7 +110,6 @@ fold_line_spaces(const u8* line, const int width) /* 2. */ if (uch == (u32)L' ' || !uch) { - if (ptoken - token > width) { /* 2.1. */ @@ -113,11 +122,11 @@ fold_line_spaces(const u8* line, const int width) /* 2.1.2. */ *ptoken = 0; - ptoken = token; + ptoken = token; while (*ptoken) { - u8ch_len = u32_char_to_u8(u8ch, - *ptoken); + u8ch_len + = u32_char_to_u8(u8ch, *ptoken); if (!u8ch_len) return; u8ch[u8ch_len] = 0; @@ -155,11 +164,11 @@ fold_line_spaces(const u8* line, const int width) /* 2.2.2. */ *ptoken = 0; - ptoken = token; + ptoken = token; while (*ptoken) { - u8ch_len = u32_char_to_u8(u8ch, - *ptoken); + u8ch_len + = u32_char_to_u8(u8ch, *ptoken); if (!u8ch_len) return; u8ch[u8ch_len] = 0; @@ -190,14 +199,14 @@ fold_line_normal(const u8* line, const int width) if (!line) return; - const u8* pline = line; + const u8* pline = line; const u8* pline_inner = NULL; u32 uch; u8 u8_char[7]; u8* pu8_char = NULL; size_t uch_len; size_t delta = 0; - size_t col = 0; + size_t col = 0; while (*pline) { @@ -207,7 +216,7 @@ fold_line_normal(const u8* line, const int width) return; pline_inner = pline; - pu8_char = u8_char; + pu8_char = u8_char; while (*pline_inner && pline_inner < pline + uch_len) *pu8_char++ = *pline_inner++; *pu8_char = 0; @@ -236,12 +245,12 @@ fold_line_normal(const u8* line, const int width) int main(int argc, char** argv) { - int argn = 1; - char* carg = NULL; - char** earg = NULL; - int spaces = 0; - int in_width = 0; - long width = 80; + int argn = 1; + char* carg = NULL; + char** earg = NULL; + int spaces = 0; + int in_width = 0; + long width = 80; char width_buf[MAXBUF]; *filename = 0; @@ -252,8 +261,10 @@ main(int argc, char** argv) spaces = 1; else if (!strcmp(carg, "-h") || !strcmp(carg, "--help")) return usage(); + else if (!strcmp(carg, "-V") || !strcmp(carg, "--full-version")) + return version(1); else if (!strcmp(carg, "-v") || !strcmp(carg, "--version")) - return version(); + return version(0); else if (!strcmp(carg, "-w")) in_width = 1; else if (starts_with(carg, "--width=")) @@ -261,29 +272,34 @@ main(int argc, char** argv) char* pcarg = carg + strlen("--width="); strncpy(width_buf, pcarg, MAXBUF); errno = 0; - earg = &pcarg; + earg = &pcarg; width = strtol(width_buf, earg, 10); - if (errno == ERANGE || errno == EINVAL || - (**earg && *pcarg)) - return error(EINVAL, "Invalid numerical value " - "for --width: %s", pcarg); + if (errno == ERANGE || errno == EINVAL + || (**earg && *pcarg)) + return error(EINVAL, + "Invalid numerical value " + "for --width: %s", + pcarg); in_width = 0; } else if (in_width) { errno = 0; - earg = &carg; + earg = &carg; width = strtol(carg, earg, 10); - if (errno == ERANGE || errno == EINVAL || - (**earg && *carg)) - return error(EINVAL, "Invalid numerical value " - "for -w: %s", carg); + if (errno == ERANGE || errno == EINVAL + || (**earg && *carg)) + return error(EINVAL, + "Invalid numerical value " + "for -w: %s", + carg); in_width = 0; } else if (starts_with(carg, "--width")) - return error(EINVAL, "Parameter --width requires an " - "argument"); - else if (*carg == '-' && *(carg+1) != 0) + return error(EINVAL, + "Parameter --width requires an " + "argument"); + else if (*carg == '-' && *(carg + 1) != 0) return usage(); else strncpy(filename, carg, MAXPATH); @@ -300,7 +316,7 @@ main(int argc, char** argv) } FILE* input = NULL; - if (!*filename || (*filename == '-' && !*(filename+1))) + if (!*filename || (*filename == '-' && !*(filename + 1))) input = stdin; else input = fopen(filename, "rt"); @@ -330,4 +346,3 @@ main(int argc, char** argv) fclose(input); return 0; } - diff --git a/ufold.do b/ufold.do @@ -1,10 +0,0 @@ -. ./config.redo -for f in *.h.in; do - echo $f | sed -e's/\.in$//g' -done | xargs redo-ifchange -for f in *.h *.c; do - echo $f - echo $f | sed -e's/\.c$/.o/g' -done | xargs redo-ifchange -${CC} -o $3 ufold.o utf8.o -${STRIP} $3 diff --git a/uninstall.do b/uninstall.do @@ -1,10 +0,0 @@ -. ./config.redo -for docfile in $DOCS; do - rm -f "${DOCDIR}/${docfile}" -done -for binfile in $PROGS; do - rm -f "${BINDIR}/${binfile}" -done -for manfile in $MANPAGES; do - rm -f "${MANPREFIX}/man1/${manfile}" -done diff --git a/utf8.c b/utf8.c @@ -1,5 +1,5 @@ /* This program is licensed under the terms of GNU GPL v3 or (at your option) - * any later version. Copyright (C) 2021-2023 Страхиња Радић. + * any later version. Copyright (C) 2021-2024 Страхиња Радић. * See the file LICENSE for exact copyright and license details. */ #include "utf8.h" diff --git a/utf8.h b/utf8.h @@ -1,5 +1,5 @@ /* This program is licensed under the terms of GNU GPL v3 or (at your option) - * any later version. Copyright (C) 2021-2023 Страхиња Радић. + * any later version. Copyright (C) 2021-2024 Страхиња Радић. * See the file LICENSE for exact copyright and license details. */ #include <stdint.h> diff --git a/version.do b/version.do @@ -1,17 +0,0 @@ -FALLBACKVER=${FALLBACKVER:-unknown} -LC_ALL=C -export LC_ALL -user=$(unalias ls >/dev/null 2>&1 || true; \ - command ls -ld . | awk '{print $3}') -e_user=$(id -un) -if [ -d .git ] && command -v git >/dev/null 2>&1; then - if [ "$e_user" = "$user" ]; then - git describe 2>/dev/null | sed 's/^v//' >$3 - else - su "${user}" -c 'git describe' 2>/dev/null | sed 's/^v//' >$3 - fi -else - printf "%s\n" "$FALLBACKVER" >$3 -fi -redo-always -redo-stamp <$3