diff --git a/.github/workflows/beep-build.yml b/.github/workflows/beep-build.yml index 8827418..5504e33 100644 --- a/.github/workflows/beep-build.yml +++ b/.github/workflows/beep-build.yml @@ -6,6 +6,8 @@ on: pull_request: branches: [ main, master ] +# Running without pandoc installed. + jobs: build: @@ -15,18 +17,25 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 - - name: create local.mk + - name: apt-get install run: | - echo "prefix = ${PWD}/_root" > local.mk + sudo apt-get update + sudo apt-get install -y automake autoconf + - name: autoreconf + run: autoreconf -vis . + - name: configure + run: ./configure --prefix="${PWD}/_root" - name: make - run: make + run: N="$(nproc||echo 2)"; make -j$N -l$N CFLAGS="-std=gnu11 -pedantic -Wall -Wextra -Werror" - name: 'beep --help' run: | ./beep --help > beep-usage.from--help diff -u beep-usage.txt beep-usage.from--help - name: 'make check' run: make check - - name: 'make install-nobuild' - run: make install-nobuild + - name: 'make install' + run: make install - name: 'list installed files' run: (cd "$PWD/_root" && ls -l $(find . -type f | env LC_ALL=C sort) | nl) + - name: 'make distcheck' + run: make distcheck diff --git a/.gitignore b/.gitignore index ace2672..4faf326 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,21 @@ # Files that should not be tracked by Git +# autoreconf +Makefile.in +/aclocal.m4 +/auto-aux/ +/auto-config.h.in +/autom4te.cache/ +/configure + +# configure +.deps/ +Makefile +/auto-config.h +/config.log +/config.status +/stamp-h1 + # The compiled executables /beep /*.clang @@ -37,6 +53,7 @@ /tarball-git-dist/ /beep-*.tar.gz /beep-*.tar.xz +/tests/run-tests # Test results /tests/*.actual.output.new diff --git a/Doxyfile.in b/Doxyfile.in index 7218778..a7809cc 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -1,4 +1,5 @@ # Doxyfile 1.9.1 +# @configure_input@ # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -864,7 +865,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = . +INPUT = @abs_top_srcdir@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -952,7 +953,6 @@ RECURSIVE = NO # run. EXCLUDE = -EXCLUDE += README.md # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -971,6 +971,12 @@ EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_PATTERNS += *-usage.c +# TODO: Make *.md file links work with both github.com and Doxygen. +EXCLUDE_PATTERNS += INSTALL.md +EXCLUDE_PATTERNS += PACKAGING.md +EXCLUDE_PATTERNS += PERMISSIONS.md +EXCLUDE_PATTERNS += README.md + # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the diff --git a/GNUmakefile b/GNUmakefile deleted file mode 100644 index c27b21b..0000000 --- a/GNUmakefile +++ /dev/null @@ -1,744 +0,0 @@ -######################################################################## -# GNUmakefile for beep -# -# This GNUmakefile has been written to mostly follow the GNU Makefile -# conventions[1], even if there is no "configure" script to accompany -# the GNUmakefile. If you want to keep some definitions across "make" -# invocations instead of providing them on each "make" command line, -# write the definitions into a file "local.mk" make include file, e.g. -# -# CC = clang -# prefix = $(HOME)/foo-prefix -# -# The GNUmakefile also internally follows some conventions known from -# Automake (e.g. bin_PROGRAMS variables), but uses features specific -# to GNU make to implement those Automake like features. -# -# This GNUmakefile makes extensive use of GNU make features and some -# tricks for using GNU make, such as -# * Advanced Auto Dependency Generation[2] -# * Deferred Simple Variable Expansion[3] -# -# [1]: https://www.gnu.org/prep/standards/html_node/Makefile-Conventions.html -# [2]: http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/ -# [3]: http://make.mad-scientist.net/deferred-simple-variable-expansion/ -# -######################################################################## - - -######################################################################## -# Package metadata -######################################################################## - -PACKAGE_TARNAME = beep -PACKAGE_VERSION = 1.4.13 - - -######################################################################## -# Installation directories -######################################################################## - -# We use GNU makefile conventions for directory names. - -prefix = /usr/local -exec_prefix = $(prefix) -bindir = $(exec_prefix)/bin -sbindir = $(exec_prefix)/sbin -datarootdir = $(prefix)/share -mandir = $(datarootdir)/man -man1dir = $(mandir)/man1 -docdir = $(datarootdir)/doc/$(PACKAGE_TARNAME) -contribdir = $(docdir)/contrib -htmldir = $(docdir) - - -# TODO: We might want to autodetect which kind of $(docdir) is used on -# this system. Until then, people will just need to set -# docdir='$(datarootdir)/doc/$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)' -# if they want to use that. Note that what the GNU makefile -# conventions call $(docdir) is called other names by other -# software components, e.g. %{_pkgdocdir} in RPM spec files. - - -######################################################################## -# Tools -######################################################################## - -# This makes it easy to replace any of those tools with specific -# versions, or to disable specific parts of the build. For example, to -# test the portability of the SED commands, you can run with -# SED="busybox sed", or to disable building HTML files from the -# markdown files you can run with PANDOC="false". - -CMP = cmp -DIFF = diff -DOT = dot -DOXYGEN = doxygen -EGREP = $(GREP) -E -FIND = find -GIT = git -GREP = grep -INSTALL = install -MKDIR_P = mkdir -p -PANDOC = pandoc -PYTHON3 = python3 -SED = sed -TAR = tar -TPUT = tput -WC = wc - -DIFF_U = $(DIFF) -u - -# The _DATA and _PROGRAM variants are GNU makefile convention. -# The _DIR variant is our idea. -INSTALL_DIR = $(INSTALL) -m 0755 -d -INSTALL_DATA = $(INSTALL) -m 0644 -p -INSTALL_PROGRAM = $(INSTALL) -m 0755 -p - - -######################################################################## -# This needs to be the first rule -######################################################################## - -.PHONY: all -all: all-local - - -######################################################################## -# Initialize some things for the build system -######################################################################## - - -# Prevent make from using its built-in rules -.SUFFIXES: -COMPILE.c = false COMPILE.c -LINK.c = false LINK.c - - -######################################################################## -# Variables to add to later -######################################################################## - -dist-files = -dist-files += GNUmakefile - -# targets to build for the "all" target -all_TARGETS = - -# targets to build for the "check" target -check_TARGETS = - -bin_PROGRAMS = -check_PROGRAMS = -contrib_DATA = -contrib_SCRIPTS = -sbin_PROGRAMS = -CLEANFILES = -html_DATA = -noinst_html_DATA = -man1_DATA = -doc_DATA = - - -######################################################################## -# Define compiler and linker flags -######################################################################## - -comma := , - -# If supported by $(CC), add given flags to CFLAGS type variable. -# Example usage: -# $(eval $(call check-cflags,common-CFLAGS,-fasynchronous-unwind-tables)) -define check-cflags -$(1) += $$(if $$(shell if $$(CC) $$(patsubst -Wno-%,-W%,$(2)) -x c -o compile-check.o -c - < /dev/null > /dev/null 2>&1; then echo yes; else :; fi; rm -f compile-check.o > /dev/null 2>&1),$(2)) -endef - -# This might be useful or not. -CFLAGS = -CPPFLAGS = -LDFLAGS = -LDADD = - -# Flags common to all executable targets -common_CFLAGS = -common_CPPFLAGS = -common_LDFLAGS = -common_LDADD = - -common_CPPFLAGS += -DPACKAGE_TARNAME='"$(PACKAGE_TARNAME)"' -common_CPPFLAGS += -DPACKAGE_VERSION='"$(PACKAGE_VERSION)"' -common_CFLAGS += -std=gnu99 -common_CFLAGS += $(if $(filter %.o,$@),-Wa$(comma)-adhlns=$(@:.o=.lst)) -common_CFLAGS += -pedantic -$(eval $(call check-cflags,common_CFLAGS,-Werror=unknown-warning-option)) -$(eval $(call check-cflags,common_CFLAGS,-Wall)) -$(eval $(call check-cflags,common_CFLAGS,-Wextra)) -$(eval $(call check-cflags,common_CFLAGS,-Weverything)) -$(eval $(call check-cflags,common_CFLAGS,-Werror)) -$(eval $(call check-cflags,common_CFLAGS,-Wno-padded)) -$(eval $(call check-cflags,common_CFLAGS,-Werror=format-security)) -$(eval $(call check-cflags,common_CFLAGS,-Wno-disabled-macro-expansion)) -$(eval $(call check-cflags,common_CFLAGS,-Wno-format-nonliteral)) -$(eval $(call check-cflags,CPPFLAGS,-D_FORTIFY_SOURCE=2)) -$(eval $(call check-cflags,CPPFLAGS,-D_GLIBCXX_ASSERTIONS)) -# $(eval $(call check-cflags,CFLAGS,-Wp$$(comma)-D_FORTIFY_SOURCE=2)) -# $(eval $(call check-cflags,CFLAGS,-Wp$$(comma)-D_GLIBCXX_ASSERTIONS)) -$(eval $(call check-cflags,CFLAGS,-fasynchronous-unwind-tables)) -$(eval $(call check-cflags,CFLAGS,-fanalyzer)) -$(eval $(call check-cflags,CFLAGS,-fstack-protector-strong)) -$(eval $(call check-cflags,CFLAGS,-fstack-clash-protection)) -$(eval $(call check-cflags,CFLAGS,-fcf-protection)) -$(eval $(call check-cflags,CFLAGS,-fsanitize=undefined)) - - -CFLAGS += -O2 -g -CFLAGS += -save-temps=obj - - -# Create this file to override any of the make variables defined -# above. --include local.mk - - -ifneq (,$(V)) -$(info #=======================================================================) -else -$(info In case of build problems, try running `make' with V=1 to help find the cause.) -endif - - -ifneq (,$(V)) -$(info # common_CFLAGS=$(common_CFLAGS)) -$(info # common_CPPFLAGS=$(common_CPPFLAGS)) -$(info # common_LDADD=$(common_LDADD)) -$(info # common_LDFLAGS=$(common_LDFLAGS)) -$(info # CFLAGS=$(CFLAGS)) -$(info # CPPFLAGS=$(CPPFLAGS)) -$(info # LDADD=$(LDADD)) -$(info # LDFLAGS=$(LDFLAGS)) -endif - - -######################################################################## -# Detect libraries -######################################################################## - - -######################################################################## -# Define executables and their flags -######################################################################## - -check_PROGRAMS += issue-6-benchmark -issue_6_benchmark_SOURCES = issue-6-benchmark.c -issue_6_benchmark_LDADD = -lm - -bin_PROGRAMS += beep -beep_SOURCES = -beep_SOURCES += beep-compiler.h -beep_SOURCES += beep-types.h -beep_SOURCES += beep-log.c -beep_SOURCES += beep-log.h -beep_SOURCES += beep-main.c -beep_SOURCES += beep-library.c -beep_SOURCES += beep-library.h -beep_SOURCES += beep-usage.c -beep_SOURCES += beep-usage.h -beep_SOURCES += beep-drivers.c -beep_SOURCES += beep-drivers.h - -# The drivers here use `__attribute__((constructor))` functions to -# register themselves with `beep_drivers_register()`, so the last one -# listed here will be linked last and have its constructor called at -# the latest time, and thus will have its `driver_detect()` function -# called first. - -beep_SOURCES += beep-driver.h -beep_SOURCES += beep-driver-console.c -beep_SOURCES += beep-driver-evdev.c - -EXTRA_DIST += beep-driver-noop.c -ifneq ($(BEEP_DEBUG_BUILD),) -beep_SOURCES += beep-driver-noop.c -endif - -beep_LDADD = - -beep-log.o : override common_CPPFLAGS += -D_GNU_SOURCE -# beep-log.clang-o : override CFLAGS_clang += -Wno-format-nonliteral - -# sbin_PROGRAMS += beep-foo -# beep_foo_SOURCES = -# beep_foo_SOURCES += beep-log.c -# beep_foo_SOURCES += beep.c -# beep_foo_LDADD = -# beep_foo_LDADD += -lm - - -######################################################################## -# Built sources -######################################################################## - -EXTRA_DIST += beep-usage.txt.in -CLEANFILES += beep-usage.txt - -BUILT_SOURCES += beep-usage.c -CLEANFILES += beep-usage.c -beep-usage.c: beep-usage.txt - $(inhibit-build-command) - @$(call print-rule-description,CONVERT,$@,$<) - printf '%s\n' '/* Auto-generated from `$<`. Modify that file instead. */' > $@ - printf '%s\n' '#include "beep-usage.h"' >> $@ - printf '%s\n' 'char beep_usage[] =' >> $@ - set -e; IFS=""; while read line; do \ - printf ' "%s\\n"\n' "$${line}" >> $@; \ - done < $< - printf '%s\n' ' ;' >> $@ - - -######################################################################## -# Compile and Link rules including automatic dependency generation -# -# For details on the automatic dependency generation, see -# http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/ -######################################################################## - - -# CALL: define-link-rule -# Defines the per-executable rules. -define define-link-rule -CLEANFILES += $(1).map -dist-files += $$($(2)_SOURCES) -$(2)_OBJS := $$(foreach src,$$($(2)_SOURCES),$$(if $$(filter %.c,$$(src)),$$(src:%.c=%.o),$$(if $$(filter %.h,$$(src)),,$$(error Unhandled source type in $(2)_SOURCES: $$(src))))) - -$(1): $$($(2)_OBJS) - $$(inhibit-build-command) - @$$(call print-rule-description,LINK,$$@) - $$(CC) -Wl,-Map=$(1).map,--cref $$(common_CFLAGS) $$(CFLAGS) $$(common_LDFLAGS) $$($(2)_LDFLAGS) $$(LDFLAGS) -o $$@ $$^ $$(common_LDADD) $$($(2)_LDADD) $$(LDADD) - -$$(patsubst %.o,.deps/%.o.dep,$$($(2)_OBJS))): - --include $$(wildcard $$(patsubst %.o,.deps/%.o.dep,$$($(2)_OBJS))) -endef - - -$(foreach exec,$(bin_PROGRAMS), $(eval $(call define-link-rule,$(exec),$(subst -,_,$(exec)),bin))) -$(foreach exec,$(check_PROGRAMS),$(eval $(call define-link-rule,$(exec),$(subst -,_,$(exec)),check))) -$(foreach exec,$(sbin_PROGRAMS), $(eval $(call define-link-rule,$(exec),$(subst -,_,$(exec)),sbin))) - -%.o: %.c | .deps - $(inhibit-build-command) - @$(call print-rule-description,COMPILE,$@,$<) - $(CC) -MT $@ -MMD -MP -MF .deps/$*.o.dep $(common_CPPFLAGS) $(CPPFLAGS) $(common_CFLAGS) $(CFLAGS) -o $@ -c $< - -.deps: - $(inhibit-build-command) - @$(MKDIR_P) $@ - - -######################################################################## -# Generate doc and similar files -######################################################################## - -EXTRA_DIST += gen-freq-table - -EXTRA_DIST += beep.1.in -man1_DATA += beep.1 -CLEANFILES += beep.1 - -CLEANFILES += CREDITS.html -CLEANFILES += NEWS.html -CLEANFILES += PERMISSIONS.html -CLEANFILES += README.html - -CLEANFILES += DEVELOPMENT.html -CLEANFILES += INSTALL.html -CLEANFILES += PACKAGING.html - -EXTRA_DIST += pandoc.css - -ifeq (yes,$(shell if $(PANDOC) --version > /dev/null 2>&1; then echo yes; else echo no; fi)) - -html_DATA += pandoc.css - -html_DATA += CREDITS.html -html_DATA += NEWS.html -html_DATA += PERMISSIONS.html -html_DATA += README.html - -noinst_html_DATA += DEVELOPMENT.html -noinst_html_DATA += INSTALL.html -noinst_html_DATA += PACKAGING.html - -%.html: %.md - $(inhibit-build-command) - @$(call print-rule-description,PANDOC,$@,$<) - $(PANDOC) --from gfm --to html --standalone -M pagetitle="$$($(SED) -n 1p $<)" -M title="" -c pandoc.css $< -o $@ -endif - -.PHONY: html -html: $(html_DATA) $(noinst_html_DATA) -all-local: $(html_DATA) $(noinst_html_DATA) - -DEFAULT_FREQ = 440 -DEFAULT_LENGTH = 200 -DEFAULT_DELAY = 100 - -REPLACEMENTS = -REPLACEMENTS += -e 's|@PACKAGE_TARNAME@|$(PACKAGE_TARNAME)|g' -REPLACEMENTS += -e 's|@PACKAGE_VERSION@|$(PACKAGE_VERSION)|g' - -REPLACEMENTS += -e 's|@DEFAULT_FREQ@|$(DEFAULT_FREQ)|g' -REPLACEMENTS += -e 's|@DEFAULT_LENGTH@|$(DEFAULT_LENGTH)|g' -REPLACEMENTS += -e 's|@DEFAULT_DELAY@|$(DEFAULT_DELAY)|g' - -REPLACEMENTS += -e 's|[@]docdir@|$(docdir)|g' - -EXTRA_DIST += beep-config.h.in -CLEANFILES += beep-config.h -BUILT_SOURCES += beep-config.h -beep-main.o : beep-config.h - -EXTRA_DIST += Doxyfile.in -CLEANFILES += Doxyfile -CLEANFILES += Doxyfile.new - -%: %.in GNUmakefile - $(inhibit-build-command) - @$(call print-rule-description,SUBSTITUTE,$@,$<) - $(SED) $(REPLACEMENTS) < $< > $@.new - @if $(EGREP) '@([A-Za-z][A-Za-z0-9_]*)@' $@.new; then \ - echo "Error: GNUmakefile fails to substitute some of the variables in \`$<'."; \ - exit 1; \ - fi - mv -f $@.new $@ - -CLEANFILES += doxygen.stamp -.PHONY: doxygen.stamp -doxygen.stamp: Doxyfile $(wildcard *.c) $(wildcard *.h) - $(inhibit-build-command) - @$(call print-rule-description,DOXYGEN,html dox) - cat $< $(if $(V),,| $(SED) -e 's!^QUIET *=.*!QUIET = YES!') | $(DOXYGEN) - - echo > $@ - -dox: doxygen.stamp - -.PHONY: serve-dox -serve-dox: dox - @$(call print-rule-description,SERVING,doxygen html files,dox/html) - $(PYTHON3) -m http.server --directory dox/html - -EXTRA_DIST += COPYING -doc_DATA += COPYING - -EXTRA_DIST += CREDITS.md -doc_DATA += CREDITS.md - -EXTRA_DIST += NEWS.md -doc_DATA += NEWS.md - -EXTRA_DIST += README.md -doc_DATA += README.md - -EXTRA_DIST += PERMISSIONS.md -doc_DATA += PERMISSIONS.md - -EXTRA_DIST += DEVELOPMENT.md -EXTRA_DIST += INSTALL.md -EXTRA_DIST += PACKAGING.md - -EXTRA_DIST += contrib/failure-beeps -contrib_SCRIPTS += contrib/failure-beeps -EXTRA_DIST += contrib/success-beeps -contrib_SCRIPTS += contrib/success-beeps -EXTRA_DIST += contrib/morse/morse2beep.pl -contrib_SCRIPTS += contrib/morse/morse2beep.pl -EXTRA_DIST += contrib/morse/morse2beep.sed -contrib_SCRIPTS += contrib/morse/morse2beep.sed - - -######################################################################## -# Generic targets -######################################################################## - -all_TARGETS += $(bin_PROGRAMS) -all_TARGETS += $(sbin_PROGRAMS) -all_TARGETS += $(man1_DATA) - -.PHONY: all-local -all-local: $(all_TARGETS) - -check_TARGETS += $(all_TARGETS) -check_TARGETS += $(check_PROGRAMS) - -.PHONY: check-targets -check-targets: $(check_TARGETS) - -.PHONY: check -check: tests/run-tests beep $(check_TARGETS) - $(inhibit-build-command) - @$(call print-rule-description,CHECK,$(PWD)/beep) - env PACKAGE_VERSION="${PACKAGE_VERSION}" \ - /bin/bash $< $( __tmp/$(distdir)/local.mk - (cd __tmp/$(distdir) && $(FIND) . -type f) | env LC_ALL=C sort > __tmp/before-build.filelist - $(MAKE) -C __tmp/$(distdir) - $(MAKE) -C __tmp/$(distdir) check - $(MAKE) -C __tmp/$(distdir) install-nobuild - (cd __tmp/_prefix && $(FIND) . -type f) | env LC_ALL=C sort > __tmp/after-install.filelist - @n="$$($(WC) -l < __tmp/after-install.filelist)"; \ - if test "$$n" -eq 0; then \ - echo "Error: Found no installed files"; \ - exit 1; \ - elif test "$$n" -gt 10; then \ - echo "Found $${n} installed files: good."; \ - else \ - echo "Error: Found only $${n} installed files"; \ - exit 1; \ - fi - $(MAKE) -C __tmp/$(distdir) uninstall - (cd __tmp/_prefix && $(FIND) . -type f) | env LC_ALL=C sort > __tmp/after-uninstall.filelist - @n="$$($(WC) -l < __tmp/after-uninstall.filelist)"; \ - if test "$$n" -gt 0; then \ - echo "Error: Found $${n} left over installed files after uninstall"; \ - exit 1; \ - else \ - echo "Found no installed files after uninstall"; \ - fi - $(MAKE) -C __tmp/$(distdir) clean - (cd __tmp/$(distdir) && $(FIND) . -type f) | env LC_ALL=C sort > __tmp/after-clean.filelist - cd __tmp && $(DIFF_U) before-build.filelist after-clean.filelist - - -######################################################################## -# Development helpers -######################################################################## - -# Only have make deal with those variables and rules if this is a git -# repo, and if the git executable has been found. -GIT_INFO_EXCLUDE = $(firstword $(wildcard .git/info/exclude)) -ifneq ($(strip $(GIT_INFO_EXCLUDE)),) -ifdef GIT - -# List all references to documentation in the git repo -.PHONY: refs -refs: - $(GIT) grep -n -E '((http|https)://[a-zA-Z0-9\._/-]+|([A-Z]+\.md)|([a-zA-Z][a-zA-Z0-9_-]+\([0-9]+\)))' - -# List all TODOs and FIXMEs in the git repo -.PHONY: todo fixme -todo fixme: - $(GIT) grep -n -E '(TODO:|FIXME:|\\todo\s|@todo\s)' - -# Generate a kind of dist tarball to help with preparing for release -PACKAGE_TARBASE = $(eval PACKAGE_TARBASE := $$(PACKAGE_TARNAME)-$$(shell $$(GIT) describe --tags | $$(SED) 's/^v\([0-9]\)/\1/'))$(PACKAGE_TARBASE) -.PHONY: git-dist -git-dist: - @$(call print-rule-description,GIT ARCHIVE,$(PACKAGE_TARBASE).tar.gz) - $(GIT) archive --format=tar.gz --prefix=$(PACKAGE_TARBASE)/ --output=$(PACKAGE_TARBASE).tar.gz HEAD - -# Check that the lists of files inside the "git archive" and the "tar" -# dist tarballs are the same. -.PHONY: compare-tarballs -distcheck: compare-tarballs -compare-tarballs: dist git-dist - @$(call print-rule-description,COMPARE,contents of git archive and dist tarball) - rm -rf tarball-dist tarball-git-dist - mkdir tarball-dist && cd tarball-dist && $(TAR) xf ../$(distdir).tar.gz - mkdir tarball-git-dist && cd tarball-git-dist && $(TAR) xf ../$(PACKAGE_TARBASE).tar.gz - diff -ruN tarball-git-dist/$(PACKAGE_TARBASE) tarball-dist/$(distdir) - rm -rf tarball-dist tarball-git-dist - -endif -endif - - -######################################################################## -# Print rule descriptions and silent rules -# -# This is a bit more complex than mad scientist's simple silent -# rules[1]: -# -# * In silent mode, we print aligned descriptions for each target -# being built. -# -# * In non-silent mode, we print a description of the recipe before -# each recipe is actually run, but highlighted a bit to stand out -# from the endless sequence of recipe command text: The description -# line always starts with a "#", and on a TTY, it will be printed -# in bold. -# -# [1]: http://make.mad-scientist.net/managing-recipe-echoing/ -######################################################################## - -# Set V to empty to disable silent rules, non-empty to enable them. -V=1 -V= - -# Note that we cannot use "test -t 1" inside a $(shell ) command, as -# the $(shell) will capture stdout into a variable, so the "test -t 1" -# will always test negative. - -ifeq (,$(V)) -.SILENT: -print-rule-description = $(or\ -$(if $(3),printf "%12s %-22s FROM %s\n" "$(1)" "$(2)" "$(3)"),\ -$(if $(2),printf "%12s %s\n" "$(1)" "$(2)"),\ -$(error $(0) requires at least two parameters)) -else -set-color-vars = if test -t 1; then cb="$$($(TPUT) bold)"; cn="$$($(TPUT) sgr0)"; fi -print-rule-description = $(set-color-vars); $(or\ -$(if $(3),printf "%s# %s %s FROM %s%s\n" "$$cb" "$(1)" "$(2)" "$(3)" "$$cn"),\ -$(if $(2),printf "%s# %s %s%s\n" "$$cb" "$(1)" "$(2)" "$$cn"),\ -$(error $(0) requires at least two parameters)) -endif - - -######################################################################## -# End of GNUmakefile -######################################################################## - -ifneq (,$(V)) -$(info #=======================================================================) -endif diff --git a/INSTALL.md b/INSTALL.md index c4dbae4..b5eec0f 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -12,10 +12,13 @@ notable exception should be the step adding users to the `beep` group. Build requirements ================== - * GNU make + * autoconf, automake, make * clang or gcc * Linux kernel headers +Running the tests with "make check" has more requirement like `bash` +or `tput` which are not tested for (at this time). + Compile and Install =================== @@ -23,63 +26,38 @@ Compile and Install The basic way to build and install beep is ``` +[user@host beep]$ autoreconf -vif +[user@host beep]$ ./configure [user@host beep]$ make -[user@host beep]$ make install-nobuild +[user@host beep]$ make install ``` -`beep` does not have a `configure` script, so to make some -configuration options last over different make invocations, do -generate a `local.mk` file containing the appropriate definitions: - The buildsystem will use whatever value `CC` is as a compiler, and use a few tools like `PANDOC` for building html from markdown. You can set these to your desired values, including `PANDOC=false` to disable building and installing html files. -The installation location will be written into some files during -`make`, and will then be used during `make install`. The default -installation location is `prefix=/usr/local`, with `bindir`, `docdir`, -`htmldir`, `mandir`, etc. being used for more specialized installation -directories. For installing into a chroot environment, the `install` -target(s) support `DESTDIR=/path/to/chroot`. - -You can override any of these variables from the `make` command line -like e.g. - -``` -[user@host beep]$ make prefix=$HOME/.local -[user@host beep]$ make prefix=$HOME/.local install -``` +For installing into a chroot environment, the use +`DESTDIR=/path/to/chroot` with the `install` target. -or you can put that definition (and others) into a `local.mk` file: - -``` -[user@host beep]$ cat>local.mk< $@ + echo '#include "beep-usage.h"' >> $@ + echo 'char beep_usage[] =' >> $@ + set -e; IFS=""; while read line; do \ + printf ' "%s\\n"\n' "$${line}" >> $@; \ + done < $< + echo ' ;' >> $@ + +EXTRA_DIST += beep.1.in +CLEANFILES += beep.1 +man1_MANS += beep.1 +beep.1: beep.1.in Makefile + $(SED) $(REPLACEMENTS) < $< > $@ + +EXTRA_DIST += beep-usage.txt.in +CLEANFILES += beep-usage.txt +beep-usage.txt: beep-usage.txt.in Makefile + $(SED) $(REPLACEMENTS) < $< > $@ + +BUILT_SOURCES += beep-config.h +EXTRA_DIST += beep-config.h.in +CLEANFILES += beep-config.h +beep-config.h: beep-config.h.in Makefile + $(SED) $(REPLACEMENTS) < $< > $@ + +EXTRA_DIST += Doxyfile.in +CLEANFILES += Doxyfile +Doxyfile: Doxyfile.in Makefile + $(SED) $(REPLACEMENTS) < $< > $@ + +if HAVE_TOOL_DOXYGEN +CLEANFILES += doxygen.stamp +doxygen.stamp: Doxyfile $(BUILT_SOURCES) $(beep_SOURCES) + $(DOXYGEN) $< + echo timestamp > $@ + +dox: doxygen.stamp + +if HAVE_TOOL_PYTHON3 +.PHONY: serve-dox +serve-dox: dox + $(PYTHON3) -m http.server --directory dox/html +endif +endif + +include contrib/Makefile-files +include contrib/morse/Makefile-files +include m4/Makefile-files +include tests/Makefile-files + +# TODO: make tests run with "make check" and possibly "make installcheck" +# installcheck-local: diff --git a/NEWS.md b/NEWS.md index 7d3ea3b..cc360bb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,10 @@ The big and user visible changes. 1.4.xx ------ * Silence the speaker on SIGHUP (like we do on SIGINT, SIGTERM) + * Changed buildsystem to Automake from proprietary GNUmakefile. + This is primarily to reduce the effort for maintenance and + packaging. Building beep with both clang *and* gcc is still + available for developers through the `testbuild-all` script. 1.4.12 ------ diff --git a/PACKAGING.md b/PACKAGING.md index 7c7e39e..499e3a2 100644 --- a/PACKAGING.md +++ b/PACKAGING.md @@ -12,16 +12,15 @@ also be interesting. Building and installing ----------------------- -The building and installing during a package build can be achieved with +The building and installing during a package build can be achieved as +for every other automake based package, i.e. with ```sh +./configure --prefix=... make -make install-nobuild DESTDIR=/path/to/package-root +make install DESTDIR=/path/to/package-root ``` -While not strictly necessary, using `make install-nobuild` makes sure -that no compilation happens during the installation phase. - You can also add to the default compiler flags by setting or adding to `CFLAGS`, `CPPFLAGS`, `LDFLAGS`, and `LDADD`, by running `make` like e.g. @@ -30,13 +29,13 @@ e.g. make CFLAGS="-O -g -flto=auto -ffat-lto-objects" CPPFLAGS="-I/opt/include" LDFLAGS="-f" LDADD="-L/opt/lib -lmoo" ``` -or putting those definitions into the `local.mk` file. - The buildsystem will use whatever compiler `CC` is set to. `clang` and `gcc` are known to work, other toolchains might. ```sh -make CC=clang +./configure CC=clang +make +make install ``` If your package build has `pandoc` available and working, HTML files @@ -44,39 +43,14 @@ will be built from the markdown files and installed later. If you do not want HTML files built and installed, you can set `PANDOC=false`. If you need to set any of the `*dir` variables like `prefix` or -`docdir` on the `make` command line, you need to set them for both the -build step (`make`) and for the install step (`make install`). You -might either give the very same command line for every invocation of -`make`, or you can write those definitions into a make include file -called `local.mk` once. For example, you might want to achieve the -effect of +`docdir`, set them with `configure --prefix=...` or `configure +--docdir=...` so that the same values are available both for the build +(`make`) and for the install (`make install`) step. -```sh -make prefix='/usr' docdir='$(docdir)/$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)' -make prefix='/usr' docdir='$(docdir)/$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)' DESTDIR=/path/to/package-root install -``` - -by creating a `local.mk` which might look as follows in e.g. an RPM -spec file: +Every distribution should contain examples of how to build automake +based packages by invoking `configure`, `make`, and `make install` +with the appropriate parameters and environment variables. -```sh -cat>local.mk< local.mk - echo 'prefix = /usr' >> local.mk - echo 'CFLAGS = $(shell dpkg-buildflags --get CFLAGS)' >> local.mk -``` Files to install for beep ------------------------- diff --git a/auto-m4/.gitignore b/auto-m4/.gitignore new file mode 100644 index 0000000..fe9c2dc --- /dev/null +++ b/auto-m4/.gitignore @@ -0,0 +1 @@ +# dummy file diff --git a/beep-config.h.in b/beep-config.h.in index 760d036..71765df 100644 --- a/beep-config.h.in +++ b/beep-config.h.in @@ -5,6 +5,8 @@ * \author Copyright (C) 2010-2013 Gerfried Fuchs * \author Copyright (C) 2013-2018 Hans Ulrich Niedermann * + * @configure_input@ + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/beep-event-loop.c b/beep-event-loop.c new file mode 100644 index 0000000..02b9ffb --- /dev/null +++ b/beep-event-loop.c @@ -0,0 +1,461 @@ +/** \file beep-event-loop.c + * \brief main event loop for beep + * \author Copyright (C) 2022 Hans Ulrich Niedermann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * + * \defgroup beep_event_loop The main event loop + * + * @{ + * + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "auto-config.h" + +#include "beep-drivers.h" +#include "beep-event-loop.h" +#include "beep-library.h" +#include "beep-log.h" + + +#define LOG_MODULE "evloop" + + +/** state machine states */ +typedef enum { + BEEPING, + SILENCE, + PAUSED_BEEPING, + PAUSED_SILENCE, + THE_END, +} state_T; + + +/** state machine event types */ +typedef enum { + EV_TIMEOUT, + EV_TERMINATE, + EV_PAUSE, + EV_CONTINUE, +} event_T; + + +/** main event loop context */ +typedef struct { + beep_driver *driver; + beep_parms_T *parms; + int timerfd; +} context_T; + + +/** main event loop: helper function to set the timer */ +static +void mel_set_timeout(const context_T *const context, + const unsigned int duration_ms) + __attribute__(( nonnull(1) )); + +static +void mel_set_timeout(const context_T *const context, + const unsigned int duration_ms) +{ + LOG_VERBOSE("mel_set_timeout(%u ms)", duration_ms); + const unsigned int part_sec = (duration_ms / 1000UL); + const unsigned int part_nsec = (duration_ms % 1000UL) * 1000000UL; + struct itimerspec new_times = { + .it_interval = { .tv_sec=0, + .tv_nsec=0}, + .it_value = { .tv_sec=part_sec, + .tv_nsec=part_nsec}, + }; + timerfd_settime(context->timerfd, 0, &new_times, NULL); +} + + +/** main event loop: FSM startup code */ +static +state_T mel_fsm_startup(context_T *const context) + __attribute__(( nonnull(1) )) + __attribute__(( warn_unused_result )) + ; + +static +state_T mel_fsm_startup(context_T *const context) +{ + beep_drivers_begin_tone(context->driver, + context->parms->freq & 0xffff); + mel_set_timeout(context, context->parms->length); + return BEEPING; +} + + +/** main event loop: FSM process one event + * + * @dot + * digraph main_event_loop { + * ranksep = 1.5; + * + * node [shape=box]; + * + * //{ rank=same; + * begin [label="begin", shape=Mdiamond]; + * end [label="end", shape=Msquare]; + * // begin [shape=circle,label="",width=0.15,peripheries=1,color=black,style=filled]; + * // end [shape=circle,label="",width=0.15,peripheries=2,color=black,style=filled]; + * //} + * + * { rank=same; + * beeping [label="beeping"]; + * silence [label="silence"]; + * } + * { rank=same; + * paused_beeping [label="paused_beeping"]; + * paused_silence [label="paused_silence"]; + * } + * + * begin -> beeping [label="start beep\nset timeout"]; + * beeping -> silence [label="EV_TIMEOUT\nelse\nset timeout (repeat or to next beep)"]; + * beeping -> paused_beeping [label="EV_PAUSE\nstop beep"]; + * silence -> paused_silence [label="EV_PAUSE\n"]; + * paused_beeping -> beeping [label="EV_CONT\nstart beep\ncontinue wait"]; + * paused_silence -> silence [label="EV_CONT\ncontinue wait"]; + * silence -> beeping [label="EV_TIMEOUT\nif next tone parms\nstart beep\nset timeout"]; + * silence -> beeping [label="EV_DATA\nrepeat this tone parms\nstart beep\nset timeout"]; + * beeping -> end [label="EV_TIMEOUT\nif tone seq done\nstop beep"]; + * + * silence -> end [label="EV_TIMEOUT\nelse"]; + * + * beeping -> end [label="EV_TERM\nstop beep"]; + * silence -> end [label="EV_TERM"]; + * paused_beeping -> end [label="EV_TERM"]; + * paused_silence -> end [label="EV_TERM"]; + * } + * @enddot + */ +static +state_T mel_fsm_evhandler(context_T *const context, + const state_T state, const event_T event) + __attribute__(( nonnull(1) )) + __attribute__(( warn_unused_result )) + ; + +static +state_T mel_fsm_evhandler(context_T *const context, + const state_T state, const event_T event) +{ + switch (state) { + case THE_END: + /* This should never happen, but humour the compiler warnings */ + return THE_END; + case BEEPING: + switch (event) { + case EV_TERMINATE: + beep_drivers_end_tone(context->driver); + return THE_END; + case EV_PAUSE: + beep_drivers_end_tone(context->driver); + /* FIXME: Make sure we later continue at the same place */ + return PAUSED_BEEPING; + case EV_CONTINUE: + return state; + case EV_TIMEOUT: + beep_drivers_end_tone(context->driver); + context->parms->reps--; + if (context->parms->reps == 0) { + switch (context->parms->end_delay) { + case END_DELAY_NO: + /* AHEM. REALLY? */ + mel_set_timeout(context, 100); + break; + case END_DELAY_YES: + mel_set_timeout(context, context->parms->delay); + break; + } + context->parms = context->parms->next; + return SILENCE; + } else { + if (context->parms->next) { + mel_set_timeout(context, context->parms->delay); + return SILENCE; + } else { + return THE_END; + } + } + } + break; + case PAUSED_BEEPING: + switch (event) { + case EV_TERMINATE: + return THE_END; + case EV_TIMEOUT: + /* FIXME */ + return state; + case EV_PAUSE: + /* FIXME */ + return state; + case EV_CONTINUE: + beep_drivers_begin_tone(context->driver, + context->parms->freq & 0xffff); + /* FIXME: Make sure we continue the wait at the same place */ + return BEEPING; + } + break; + case SILENCE: + switch (event) { + case EV_TERMINATE: + return THE_END; + case EV_CONTINUE: + return state; + case EV_PAUSE: + /* FIXME: Make sure we later continue the waite the same place */ + return PAUSED_SILENCE; + case EV_TIMEOUT: + if (context->parms) { + beep_drivers_begin_tone(context->driver, + context->parms->freq & 0xffff); + mel_set_timeout(context, context->parms->length); + return BEEPING; + } else { + beep_drivers_end_tone(context->driver); + return THE_END; + } + } + break; + case PAUSED_SILENCE: + switch (event) { + case EV_TERMINATE: + return THE_END; + case EV_CONTINUE: + /* FIXME: Make sure we continue the wait */ + return SILENCE; + case EV_TIMEOUT: + /* FIXME */ + return PAUSED_SILENCE; + case EV_PAUSE: + /* FIXME */ + return PAUSED_SILENCE; + } + } + beep_drivers_end_tone(context->driver); + LOG_ERROR("internal logic error"); + _exit(2); +} + + +/** Max number of events the main event loop will process at once */ +#define MAX_EVENTS 16 + + +/* documented in header file */ +int main_event_loop(beep_driver *driver, beep_parms_T *parms) +{ + /* As we use signalfd() to have the main loop react to received + * signals in sequence, we do not need to deal with signal + * handlers being asynchronous, volatile flags of atomic types, or + * memory barriers. + */ + + /* main event loop + * + * We have 0 or 1 file descriptors to watch for either characters + * or whatever units data arrives in, and some signals (SIGHUP, + * SIGINT, SIGTERM terminate beep, SIGTSTP pauses beep, SIGCONT + * continues beep). + * + * This can be handled by either poll() or epoll(). + * + * states: BEEPING SILENCE + */ + + /* listen to a set of signals via signalfd()... */ + sigset_t fd_signal_mask; + sigemptyset(&fd_signal_mask); + sigaddset(&fd_signal_mask, SIGHUP); + sigaddset(&fd_signal_mask, SIGINT); + sigaddset(&fd_signal_mask, SIGTERM); + sigaddset(&fd_signal_mask, SIGTSTP); + sigaddset(&fd_signal_mask, SIGSTOP); + sigaddset(&fd_signal_mask, SIGCONT); + const int sigfd = signalfd(-1, &fd_signal_mask, SFD_CLOEXEC); + if (sigfd == -1) { + safe_errno_exit("signalfd()"); + } + + /* ...and block these signals' handlers */ + if (sigprocmask(SIG_BLOCK, &fd_signal_mask, NULL) == -1) { + safe_errno_exit("sigprocmask(SIG_BLOCK, ...)"); + } + + /* To be emulated by a state machine: */ + /* */ + /* for (unsigned int i = 0; (!global_flag_abort) && (i < parms.reps); i++) { */ + /* beep_drivers_begin_tone(driver, parms.freq & 0xffff); */ + /* sleep_ms(driver, parms.length); */ + /* beep_drivers_end_tone(driver); */ + /* if ((parms.end_delay == END_DELAY_YES) || ((i+1) < parms.reps)) { */ + /* sleep_ms(driver, parms.delay); */ + /* } */ + /* } */ + + const int epollfd = epoll_create1(0); + if (epollfd == -1) { + safe_errno_exit("epoll_create1()"); + } + + if (true) { + struct epoll_event ev; + ev.data.fd = sigfd; + ev.events = EPOLLIN; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1) { + safe_errno_exit("epoll_ctl() EPOLL_CTL_ADD sigfd"); + } + } + + const int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + if (timerfd == -1) { + safe_errno_exit("timerfd_create()"); + } + + if (true) { + struct epoll_event ev; + ev.data.fd = timerfd; + ev.events = EPOLLIN; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1) { + safe_errno_exit("epoll_ctl() EPOLL_CTL_ADD timerfd"); + } + } + + context_T context = { + .driver = driver, + .parms = parms, + .timerfd = timerfd, + }; + state_T state = mel_fsm_startup(&context); + + while (true) { + LOG_VERBOSE("event loop body begin"); + + struct epoll_event events[MAX_EVENTS]; + + LOG_VERBOSE("doing epoll_wait(...)"); + const int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); + if (nfds == -1) { + if (errno == EINTR) { + LOG_VERBOSE("epoll_wait() EINTR"); + } else { + safe_errno_exit("epoll_wait() error"); + } + } else if (nfds == 0) { + LOG_VERBOSE("epoll_wait() timeout"); + } else if (nfds > 0) { + LOG_VERBOSE("epoll_wait() ready to read (nfds==%d)", nfds); + for (int n = 0; n < nfds; ++n) { + if (events[n].data.fd == sigfd) { + LOG_VERBOSE("reading event from sigfd"); + struct signalfd_siginfo siginfo[MAX_EVENTS]; + const ssize_t ssz = read(sigfd, siginfo, sizeof(siginfo)); + if (ssz < 0) { + safe_errno_exit("reading signalfd event"); + } else { + const size_t usz = ssz; + LOG_VERBOSE("read %zu bytes from sigfd (%zu)", + usz, sizeof(struct signalfd_siginfo)); + if (usz < sizeof(struct signalfd_siginfo)) { + LOG_ERROR("only read %zu bytes from sigfd", usz); + _exit(EXIT_FAILURE); + } + } + const size_t max_m = ((size_t) ssz) / sizeof(struct signalfd_siginfo); + for (size_t m=0; m < max_m; m++) { + switch (siginfo[m].ssi_signo) { + case SIGHUP: + LOG_VERBOSE("SIGHUP"); + state = mel_fsm_evhandler(&context, state, EV_TERMINATE); + break; + case SIGINT: + LOG_VERBOSE("SIGINT"); + state = mel_fsm_evhandler(&context, state, EV_TERMINATE); + break; + case SIGTERM: + LOG_VERBOSE("SIGTERM"); + state = mel_fsm_evhandler(&context, state, EV_TERMINATE); + break; + case SIGCONT: + LOG_VERBOSE("SIGCONT"); + state = mel_fsm_evhandler(&context, state, EV_CONTINUE); + break; + case SIGSTOP: + // TODO: We cannot catch this, right? + LOG_VERBOSE("SIGSTOP"); + state = mel_fsm_evhandler(&context, state, EV_PAUSE); + break; + case SIGTSTP: + LOG_VERBOSE("SIGTSTP"); + state = mel_fsm_evhandler(&context, state, EV_PAUSE); + break; + default: + LOG_ERROR("unknown signal: %d", siginfo[m].ssi_signo); + state = mel_fsm_evhandler(&context, state, EV_TERMINATE); + break; + } + } + } else if (events[n].data.fd == timerfd) { + LOG_VERBOSE("reading event from timerfd"); + uint64_t value; + const ssize_t ssz = read(timerfd, &value, sizeof(value)); + if (ssz < 0) { + safe_errno_exit("reading timerfd event"); + } else if (ssz == sizeof(value)) { + /* ok */ + } else { + LOG_ERROR("error reading from timerfd (%zd bytes)", ssz); + _exit(EXIT_FAILURE); + } + state = mel_fsm_evhandler(&context, state, EV_TIMEOUT); + } else { /* input file */ + /* FIXME: Handle input data */ + safe_errno_exit("to be implemented: read from input file"); + } + } + } + LOG_VERBOSE("event loop body end"); + if (state == THE_END) { + break; + } + } + + beep_drivers_fini(driver); + return EXIT_SUCCESS; +} + + +/** @} */ + + +/* + * Local Variables: + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/beep-event-loop.h b/beep-event-loop.h new file mode 100644 index 0000000..2e70e69 --- /dev/null +++ b/beep-event-loop.h @@ -0,0 +1,59 @@ +/** \file beep-event-loop.h + * \brief main event loop for beep + * \author Copyright (C) 2022 Hans Ulrich Niedermann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * + * \addtogroup beep_event_loop + * + * @{ + * + */ + + +#ifndef BEEP_EVENT_LOOP_H +#define BEEP_EVENT_LOOP_H + + +#include "beep-driver.h" +#include "beep-types.h" + + +/** + * main event loop + * + * Condense the raw events into events for the main event loop state + * machine. + */ + +int main_event_loop(beep_driver *driver, beep_parms_T *parms) + __attribute__(( nonnull(1,2) )) + __attribute__(( warn_unused_result )) + ; + + +#endif /* !defined(BEEP_EVENT_LOOP_H) */ + + +/** @} */ + + +/* + * Local Variables: + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/beep-library.c b/beep-library.c index 27ab3f4..3207dcb 100644 --- a/beep-library.c +++ b/beep-library.c @@ -90,6 +90,8 @@ void safe_errno_exit(const char *const msg) { const int saved_errno = errno; char strerr_buf[128]; +#if (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L) && !defined(_GNU_SOURCE)) + /* XSI compliant int strerror_r(int errnum, char *buf, size_t buflen); */ const int ret = strerror_r(saved_errno, strerr_buf, sizeof(strerr_buf)); if (ret != 0) { if (write(STDERR_FILENO, "strerror_r error\n", @@ -98,6 +100,12 @@ void safe_errno_exit(const char *const msg) } _exit(EXIT_FAILURE); } + const char *const error_string = strerr_buf; +#else + /* GNU-specific char *strerror_r(int errnum, char *buf, size_t buflen); */ + const char *const error_string = + strerror_r(saved_errno, strerr_buf, sizeof(strerr_buf)); +#endif const size_t msglen = strlen(msg); if (write(STDERR_FILENO, msg, msglen)) { /* ignore all write errors */ @@ -105,8 +113,8 @@ void safe_errno_exit(const char *const msg) if (write(STDERR_FILENO, ": ", 2)) { /* ignore all write errors */ } - const size_t errlen = strlen(strerr_buf); - if (write(STDERR_FILENO, strerr_buf, errlen)) { + const size_t errlen = strlen(error_string); + if (write(STDERR_FILENO, error_string, errlen)) { /* ignore all write errors */ } if (write(STDERR_FILENO, "\n", 1)) { diff --git a/beep-log.c b/beep-log.c index c75e45b..e869356 100644 --- a/beep-log.c +++ b/beep-log.c @@ -64,7 +64,16 @@ void log_internal_vf(const char *const module, const char *levelstr, } else { fprintf(stdout, "%s: %s: ", progname, levelstr); } +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#else /* to clang or not to clang */ +#endif vfprintf(stdout, format, copied_args); +#ifdef __clang__ +#pragma clang diagnostic pop +#else /* to clang or not to clang */ +#endif fputc('\n', stdout); va_end(copied_args); diff --git a/beep-main.c b/beep-main.c index a7b2e5b..63a7ebb 100644 --- a/beep-main.c +++ b/beep-main.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -52,10 +51,12 @@ #include #include +#include "auto-config.h" #include "beep-config.h" #include "beep-compiler.h" #include "beep-drivers.h" +#include "beep-event-loop.h" #include "beep-library.h" #include "beep-log.h" #include "beep-types.h" @@ -77,85 +78,6 @@ const char version_message[] = "For information: http://www.gnu.org/copyleft/.\n"; -/** - * Per note parameter set. - */ -typedef struct beep_parms_T beep_parms_T; - - -/* The default values are defined in beep-config.h */ - - -/** - * Per note parameter set (including heritage information and linked list pointer). - */ -struct beep_parms_T -{ - unsigned int freq; /**< tone frequency (Hz) */ - unsigned int length; /**< tone length (ms) */ - unsigned int reps; /**< number of repetitions */ - unsigned int delay; /**< delay between reps (ms) */ - end_delay_E end_delay; /**< do we delay after last rep? */ - - /** Are we using stdin triggers? - * - * We have three options: - * - just beep and terminate (default) - * - beep after a line of input - * - beep after a character of input - * In the latter two cases, pass the text back out again, - * so that beep can be tucked appropriately into a text- - * processing pipe. - */ - stdin_beep_E stdin_beep; - - beep_parms_T *next; /**< in case -n/--new is used. */ -}; - - -/** - * Global flag for signalling program abort due to signal handlers. - * - * Initialized as false. Written only by the signal handlers (set to - * true), read only by the main thread. - */ -static -volatile sig_atomic_t global_abort = false; - - -/** - * Signal handler for signals like SIGHUP, SIGINT and SIGTERM. - * - * If we get terminated in any way, it would be nice to not leave the - * speaker beeping in perpetuity. - * - * Everything called from this signal handler must be thread-safe, - * signal-safe, reentrant including all API functions. Otherwise, we - * get another CVE-2018-0492. - * - * So we make certain we keep to using the following API calls: - * - * * close(2): safe - * * _exit(2): safe (which exit(3) is NOT) - * * bzero(3): MT-safe - * * memset(3): MT-safe - * * write(2): safe - * * strerror_r(3): MT-safe - * * strlen(3): MT-safe - * - * Just setting a global flag of type sig_atomic_t is MT-safe. - * - * @param unused_signum The signal number being handled. Unused. - */ - -void handle_signal_global_abort(int unused_signum UNUSED_PARAM); - -void handle_signal_global_abort(int unused_signum UNUSED_PARAM) -{ - global_abort = true; -} - - /** * Print usage message, but leave exit code up to the caller. */ @@ -356,68 +278,6 @@ void parse_command_line(const int argc, char *const argv[], beep_parms_T *result } -/** - * Sleep for a number of milliseconds. - * - * @param driver The driver to close in case of #global_abort. - * @param milliseconds The number of milliseconds to sleep. - * - * @return The nanosleep(2) result (0 if successful, -1 on signal handler or error) - */ - -static -int sleep_ms(beep_driver *driver, unsigned int milliseconds) - __attribute__(( nonnull(1) )); - -static -int sleep_ms(beep_driver *driver, unsigned int milliseconds) -{ - const time_t seconds = milliseconds / 1000U; - const long nanoseconds = (milliseconds % 1000UL) * 1000UL * 1000UL; - const struct timespec request = - { seconds, - nanoseconds }; - const int retcode = nanosleep(&request, NULL); - if (global_abort) { - beep_drivers_end_tone(driver); - beep_drivers_fini(driver); - exit(EXIT_FAILURE); - } - return retcode; -} - - -/** - * Play one (possibly repeated) note. - * - * @param driver The driver to run the note on. - * @param parms The note parameters. - */ - -static -void play_beep(beep_driver *driver, beep_parms_T parms) - __attribute__(( nonnull(1) )); - -static -void play_beep(beep_driver *driver, beep_parms_T parms) -{ - LOG_VERBOSE("%d times %d ms beeps (%d ms delay between, " - "%d ms delay after) @ %d Hz", - parms.reps, parms.length, parms.delay, parms.end_delay, - parms.freq); - - /* repeat the beep */ - for (unsigned int i = 0; (!global_abort) && (i < parms.reps); i++) { - beep_drivers_begin_tone(driver, parms.freq & 0xffff); - sleep_ms(driver, parms.length); - beep_drivers_end_tone(driver); - if ((parms.end_delay == END_DELAY_YES) || ((i+1) < parms.reps)) { - sleep_ms(driver, parms.delay); - } - } -} - - /** * If stdout is a TTY, print a bell character to stdout as a fallback. */ @@ -535,76 +395,7 @@ int main(const int argc, char *const argv[]) * not have to fall back onto printing '\a' any more. */ - /* Memory barrier. All globals have been set up, so we make sure the - * values are actually written to memory. Only then do we install - * the signal handlers. - * - * TBD: Use C11 atomic_signal_fence() instead of "__asm__ volatile"? - */ - __asm__ volatile ("" : : : "memory"); - - /* After all the initialization has happened and the global - * variables used to communicate with the signal handlers have - * actually been set up properly, we can finally install the - * signal handlers. As we do not start making any noises until - * later, there is no need to install the signal handlers any - * earlier. - */ - signal(SIGHUP, handle_signal_global_abort); - signal(SIGINT, handle_signal_global_abort); - signal(SIGTERM, handle_signal_global_abort); - - /* This outermost while loop handles the possibility that -n/--new - * has been used, i.e. that we have a sequence of multiple beeps - * specified. Each loop iteration will play, then free() one parms - * instance. - */ - while ((!global_abort) && parms) { - beep_parms_T *next = parms->next; - - if (parms->stdin_beep != STDIN_BEEP_NONE) { - /* In this case, beep is probably part of a pipe, in which - case POSIX says stdin and out should be fully buffered. - This however means very laggy performance with beep - just twiddling it's thumbs until a buffer fills. Thus, - kill the buffering. In some situations, this too won't - be enough, namely if we're in the middle of a long - pipe, and the processes feeding us stdin are buffered, - we'll have to wait for them, not much to be done about - that. */ - setvbuf(stdin, NULL, _IONBF, 0); - setvbuf(stdout, NULL, _IONBF, 0); - - char sin[4096]; - while ((!global_abort) && (fgets(sin, 4096, stdin))) { - if (parms->stdin_beep == STDIN_BEEP_CHAR) { - for (char *ptr=sin; (!global_abort) && (*ptr); ptr++) { - putchar(*ptr); - fflush(stdout); - play_beep(driver, *parms); - } - } else { /* STDIN_BEEP_LINE */ - fputs(sin, stdout); - play_beep(driver, *parms); - } - } - } else { - play_beep(driver, *parms); - } - - /* Junk each parms struct after playing it */ - free(parms); - parms = next; - } - - beep_drivers_end_tone(driver); - beep_drivers_fini(driver); - - if (global_abort) { - return EXIT_FAILURE; - } else { - return EXIT_SUCCESS; - } + return main_event_loop(driver, parms); } diff --git a/beep-types.h b/beep-types.h index 2d118ac..36bd47d 100644 --- a/beep-types.h +++ b/beep-types.h @@ -44,6 +44,42 @@ typedef enum { } stdin_beep_E; +/** + * Per note parameter set. + */ +typedef struct beep_parms_T beep_parms_T; + + +/* The default values are defined in beep-config.h */ + + +/** + * Per note parameter set (including heritage information and linked list pointer). + */ +struct beep_parms_T +{ + unsigned int freq; /**< tone frequency (Hz) */ + unsigned int length; /**< tone length (ms) */ + unsigned int reps; /**< number of repetitions */ + unsigned int delay; /**< delay between reps (ms) */ + end_delay_E end_delay; /**< do we delay after last rep? */ + + /** Are we using stdin triggers? + * + * We have three options: + * - just beep and terminate (default) + * - beep after a line of input + * - beep after a character of input + * In the latter two cases, pass the text back out again, + * so that beep can be tucked appropriately into a text- + * processing pipe. + */ + stdin_beep_E stdin_beep; + + beep_parms_T *next; /**< in case -n/--new is used. */ +}; + + #endif /* !defined(BEEP_TYPES_H) */ diff --git a/beep.1.in b/beep.1.in index 49577c5..311ccca 100644 --- a/beep.1.in +++ b/beep.1.in @@ -1,4 +1,5 @@ .\" The beep(1) man page +.\" @configure_input@ .\" .\" This man page has been rewritten adhering to the following .\" documentation: man(7), man-pages(7), tbl(1) diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..dd03084 --- /dev/null +++ b/configure.ac @@ -0,0 +1,106 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.69]) +AC_INIT([beep], + [1.4.13], + [https://github.com/spkr-beep/beep/issues], + [beep], + [https://github.com/spkr-beep/beep]) +AC_CONFIG_AUX_DIR([auto-aux]) +AC_CONFIG_MACRO_DIR([auto-m4]) +AC_CONFIG_SRCDIR([beep-log.h]) +AC_CONFIG_HEADERS([auto-config.h]) + +AM_INIT_AUTOMAKE([ + 1.16 + foreign + serial-tests + subdir-objects + -Wall + -Werror +]) + +# Checks for programs. +AC_PROG_AWK +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_MAKE_SET +AC_PROG_MKDIR_P +AC_PROG_SED + +dnl Catch our project specific (*s*pkr-*b*beep) macros +m4_pattern_forbid([^SB_])dnl + +SB_PROG([BASH], [bash]) +SB_PROG([DIFF], [diff]) +SB_PROG([DOXYGEN], [doxygen]) +SB_PROG([PANDOC], [pandoc]) +SB_PROG([PYTHON3], [python3]) +SB_PROG([TPUT], [tput]) + +# TODO: Use the proper C standard for compiling. +# TODO: Produce as many warnings and errors as possible when compiling. + +# Checks for libraries. +saved_LIBS="$LIBS" +LIBS="" +AC_CHECK_LIB([m], [lrint]) +AC_SUBST([M_LIBS], ["$LIBS"]) +LIBS="$saved_LIBS" + +# Checks for header files. +AC_CHECK_HEADERS(m4_flatten([ + fcntl.h + limits.h + stddef.h + stdint.h + stdlib.h + string.h + sys/epoll.h + sys/ioctl.h + sys/signalfd.h + sys/stat.h + sys/time.h + sys/timerfd.h + unistd.h +])) + +# Checks for typedefs, structures, and compiler characteristics. +AC_CHECK_HEADER_STDBOOL +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_TYPE_UINT16_T + +# Checks for library functions. +AC_FUNC_FORK +AC_FUNC_MALLOC +AC_FUNC_STRERROR_R +AC_CHECK_FUNCS(m4_flatten([ + clock_gettime + memset + strdup + strerror + strrchr + strtol + strtoul +])) + +AC_SUBST([DEFAULT_FREQ], [440]) +AC_SUBST([DEFAULT_LENGTH], [200]) +AC_SUBST([DEFAULT_DELAY], [100]) + +dnl TODO: While we are still using the old run-tests script, +dnl check for tput and /bin/bash + +SB_SUMMARY_VAR([DEFAULT_FREQ]) +SB_SUMMARY_VAR([DEFAULT_LENGTH]) +SB_SUMMARY_VAR([DEFAULT_DELAY]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT +SB_SUMMARY + +dnl Local Variables: +dnl indent-tabs-mode: nil +dnl End: diff --git a/contrib/Makefile-files b/contrib/Makefile-files new file mode 100644 index 0000000..ce7b90c --- /dev/null +++ b/contrib/Makefile-files @@ -0,0 +1,7 @@ +# -*- makefile-automake -*- + +EXTRA_DIST += %reldir%/failure-beeps +contrib_SCRIPTS += %reldir%/failure-beeps + +EXTRA_DIST += %reldir%/success-beeps +contrib_SCRIPTS += %reldir%/success-beeps diff --git a/contrib/morse/Makefile-files b/contrib/morse/Makefile-files new file mode 100644 index 0000000..fb2ff8d --- /dev/null +++ b/contrib/morse/Makefile-files @@ -0,0 +1,7 @@ +# -*- makefile-automake -*- + +EXTRA_DIST += %reldir%/morse2beep.pl +contrib_SCRIPTS += %reldir%/morse2beep.pl + +EXTRA_DIST += %reldir%/morse2beep.sed +contrib_SCRIPTS += %reldir%/morse2beep.sed diff --git a/m4/Makefile-files b/m4/Makefile-files new file mode 100644 index 0000000..38f3f37 --- /dev/null +++ b/m4/Makefile-files @@ -0,0 +1,4 @@ +# -*- makefile-automake -*- + +EXTRA_DIST += %reldir%/sb-prog.m4 +EXTRA_DIST += %reldir%/sb-summary.m4 diff --git a/m4/sb-prog.m4 b/m4/sb-prog.m4 new file mode 100644 index 0000000..b405574 --- /dev/null +++ b/m4/sb-prog.m4 @@ -0,0 +1,18 @@ +# sb-prog.m4 - check for program, but let user override it -*- Autoconf -*- +# serial 2 +dnl +dnl +dnl +AC_DEFUN([SB_PROG], [dnl +AC_ARG_VAR([$1], [$2 program]) +AS_IF([test x = "x$][$1]["], [dnl +AC_PATH_PROG([$1], [$2], [false]) +]) +AM_CONDITIONAL([HAVE_TOOL_][$1], [test "x$][$1][" != xfalse]) +])dnl +dnl +dnl +dnl +dnl Local Variables: +dnl indent-tabs-mode: nil +dnl End: diff --git a/m4/sb-summary.m4 b/m4/sb-summary.m4 new file mode 100644 index 0000000..93588fd --- /dev/null +++ b/m4/sb-summary.m4 @@ -0,0 +1,40 @@ +# sb-summary.m4 - print summary at the end of configure -*- Autoconf -*- +# serial 3 +dnl +dnl +m4_pattern_forbid([SB_SUMMARY_ITEM_WIDTH])dnl +AC_DEFUN([SB_SUMMARY_ITEM_WIDTH], [17])dnl +dnl +dnl +m4_pattern_forbid([SB_SUMMARY_FILE])dnl +AC_DEFUN([SB_SUMMARY_FILE], [sb_summary.txt])dnl +dnl +dnl +AC_DEFUN_ONCE([SB_SUMMARY_INIT], [dnl +printf '%s for %s-%s:\n' 'Build Summary' "$PACKAGE_TARNAME" "$PACKAGE_VERSION" > SB_SUMMARY_FILE +])dnl +dnl +dnl +AC_DEFUN([SB_SUMMARY_ITEM], [dnl +AC_REQUIRE([SB_SUMMARY_INIT])dnl +printf ' %-]SB_SUMMARY_ITEM_WIDTH[s %s\n' m4_default(["AS_ESCAPE($1)"],[""]) m4_default($2,[""]) >> SB_SUMMARY_FILE +])dnl +dnl +dnl +AC_DEFUN([SB_SUMMARY_VAR], [dnl +AC_REQUIRE([SB_SUMMARY_INIT])dnl +SB_SUMMARY_ITEM([$1], ["$][$1]["])dnl +])dnl +dnl +dnl +AC_DEFUN([SB_SUMMARY], [dnl +AC_REQUIRE([SB_SUMMARY_INIT])dnl +cat "SB_SUMMARY_FILE" +rm -f "SB_SUMMARY_FILE" +])dnl +dnl +dnl +dnl +dnl Local Variables: +dnl indent-tabs-mode: nil +dnl End: diff --git a/testbuild-all b/testbuild-all index 70bc12b..0122bd5 100755 --- a/testbuild-all +++ b/testbuild-all @@ -4,11 +4,15 @@ cd "$(dirname "$0")" export LC_ALL=C +MAKE="${MAKE-make}" + set -ex -# We need to remove the built files from the $(srcdir) in order to -# make the VPATH search work properly for the test builds. -make clean +if test -f Makefile; then + make distclean +fi + +NPROC="$(nproc || echo e)" while read id cc cflags do @@ -19,24 +23,16 @@ do ( mkdir "$builddir" cd "$builddir" - srcdir="${abs_top_srcdir}" - cat>local.mk< after-installation.txt nl after-installation.txt if test -s after-installation.txt; then @@ -55,9 +51,6 @@ EOF fi ) done < $@ + chmod +x $@ +endif +endif + +clean-local: + rm -f %reldir%/*.actual + rm -f %reldir%/*.actual.new + +EXTRA_DIST += %reldir%/00-nothing.expected.0 +EXTRA_DIST += %reldir%/00-nothing.sh +EXTRA_DIST += %reldir%/01-nothing.sh +EXTRA_DIST += %reldir%/02-second-device-name.expected.0 +EXTRA_DIST += %reldir%/02-second-device-name.sh +EXTRA_DIST += %reldir%/03-cmdline-unhandled-option.expected.0 +EXTRA_DIST += %reldir%/03-cmdline-unhandled-option.sh +EXTRA_DIST += %reldir%/04-cmdline-unhandled-non-option.sh +EXTRA_DIST += %reldir%/05-cmdline-help.bash +EXTRA_DIST += %reldir%/05-cmdline-help.expected.0 +EXTRA_DIST += %reldir%/05-cmdline-version.bash +EXTRA_DIST += %reldir%/05-cmdline-version.expected.0 +EXTRA_DIST += %reldir%/10-beep-default.sh +EXTRA_DIST += %reldir%/11-beep-default-logs.expected.0 +EXTRA_DIST += %reldir%/11-beep-default-logs.expected.1 +EXTRA_DIST += %reldir%/11-beep-default-logs.sh +EXTRA_DIST += %reldir%/11-beep-f-880Hz.sh +EXTRA_DIST += %reldir%/20-beep-default.sh +EXTRA_DIST += %reldir%/20-beep-device-dev-evdev-pcspkr.sh +EXTRA_DIST += %reldir%/21-beep-device-dev-console.expected.0 +EXTRA_DIST += %reldir%/21-beep-device-dev-console.expected.1 +EXTRA_DIST += %reldir%/21-beep-device-dev-console.sh +EXTRA_DIST += %reldir%/30-beep-device-dev-tty0.expected.0 +EXTRA_DIST += %reldir%/30-beep-device-dev-tty0.expected.1 +EXTRA_DIST += %reldir%/30-beep-device-dev-tty0.sh +EXTRA_DIST += %reldir%/31-beep-device-dev-tty4.expected.0 +EXTRA_DIST += %reldir%/31-beep-device-dev-tty4.expected.1 +EXTRA_DIST += %reldir%/31-beep-device-dev-tty4.expected.2 +EXTRA_DIST += %reldir%/31-beep-device-dev-tty4.expected.3 +EXTRA_DIST += %reldir%/31-beep-device-dev-tty4.sh +EXTRA_DIST += %reldir%/33-beep-device-dev-tty63.expected.0 +EXTRA_DIST += %reldir%/33-beep-device-dev-tty63.expected.1 +EXTRA_DIST += %reldir%/33-beep-device-dev-tty63.sh +EXTRA_DIST += %reldir%/34-beep-device-dev-ttyXX.expected.0 +EXTRA_DIST += %reldir%/34-beep-device-dev-ttyXX.sh +EXTRA_DIST += %reldir%/35-beep-device-dev-tty99.expected.0 +EXTRA_DIST += %reldir%/35-beep-device-dev-tty99.sh +EXTRA_DIST += %reldir%/36-beep-device-dev-ttyX.expected.0 +EXTRA_DIST += %reldir%/36-beep-device-dev-ttyX.sh +EXTRA_DIST += %reldir%/37-beep-device-dev-ttyS1.expected.0 +EXTRA_DIST += %reldir%/37-beep-device-dev-ttyS1.expected.1 +EXTRA_DIST += %reldir%/37-beep-device-dev-ttyS1.sh +EXTRA_DIST += %reldir%/38-beep-device-dev-tty999.expected.0 +EXTRA_DIST += %reldir%/38-beep-device-dev-tty999.sh +EXTRA_DIST += %reldir%/40-beep-device-dev-vc-0.expected.0 +EXTRA_DIST += %reldir%/40-beep-device-dev-vc-0.sh +EXTRA_DIST += %reldir%/50-long-len-and-signal-SIGINT.expected.0 +EXTRA_DIST += %reldir%/50-long-len-and-signal-SIGINT.sh +EXTRA_DIST += %reldir%/50-long-len-and-signal-SIGTERM.expected.0 +EXTRA_DIST += %reldir%/50-long-len-and-signal-SIGTERM.sh +EXTRA_DIST += %reldir%/51-long-delay-and-signal-SIGINT.expected.0 +EXTRA_DIST += %reldir%/51-long-delay-and-signal-SIGINT.sh +EXTRA_DIST += %reldir%/60-beep-from-stdin.expected.0 +EXTRA_DIST += %reldir%/60-beep-from-stdin.sh +EXTRA_DIST += %reldir%/61-beep-from-stdin.expected.0 +EXTRA_DIST += %reldir%/61-beep-from-stdin.sh +EXTRA_DIST += %reldir%/62-beep-from-stdin.expected.0 +EXTRA_DIST += %reldir%/62-beep-from-stdin.sh +EXTRA_DIST += %reldir%/63-beep-from-stdin.expected.0 +EXTRA_DIST += %reldir%/63-beep-from-stdin.sh diff --git a/tests/run-tests b/tests/run-tests.in similarity index 82% rename from tests/run-tests rename to tests/run-tests.in index 4c30e01..ed4509e 100644 --- a/tests/run-tests +++ b/tests/run-tests.in @@ -1,7 +1,9 @@ -#!/bin/bash +#!@BASH@ +# @configure_input@ # # run-tests - run a given series of test cases for beep # Copyright (C) 2018-2019 Hans Ulrich Niedermann +# Copyright (C) 2022 Hans Ulrich Niedermann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -17,10 +19,10 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -CMP=cmp -DIFF=diff -SED=sed -SHELL=sh +CMP="${CMP-cmp}" +DIFF="${DIFF-@DIFF@}" +SED="${SED-sed}" +TPUT="${TPUT-@TPUT@}" # TODO: If we have more tone generating tests, find a well-known # melody without repeating notes and play that melody so that a @@ -30,22 +32,23 @@ FREQS=(440 494 554 587 659 740 831 880 988 1109 1175 1319 1480 1661 1760) : set -e : set -x -test_dir="$1" -shift -BEEP="$1" -shift +BEEP="${BEEP-@BEEP@}" export BEEP +PACKAGE_VERSION="${PACKAGE_VERSION-@PACKAGE_VERSION@}" +test_src_dir="${srcdir-@srcdir@}/tests" +test_dst_dir="${builddir-@builddir@}/tests" + skip=0 pass=0 fail=0 if test -t 1; then - hilite="$(tput setaf 3)" - normal="$(tput sgr0)" - green="$(tput setaf 2)" - blue="$(tput setaf 4)" - red="$(tput setaf 1)$(tput bold)" + hilite="$(${TPUT} setaf 3)" + normal="$(${TPUT} sgr0)" + green="$(${TPUT} setaf 2)" + blue="$(${TPUT} setaf 4)" + red="$(${TPUT} setaf 1)$(${TPUT} bold)" else hilite="" normal="" @@ -89,11 +92,10 @@ fi if true; then freq_idx=0 - for test in $(ls -1 "${test_dir}/"*.{bash,sh}); do + for test in $(ls -1 "${test_src_dir}/"*.{bash,sh}); do export FREQ="${FREQS[${freq_idx}]}" base="$(basename $(basename "$test" .bash) .sh)" - dir="$(dirname "$test")" - actual="${dir}/${base}.actual" + actual="${test_dst_dir}/${base}.actual" printf "${hilite}%-32s${normal} " "${base}" if "$have_no_hardware" && grep "^: REQUIRES_HARDWARE" "$test" > /dev/null; then skip="$(expr "${skip}" + 1)" @@ -101,7 +103,7 @@ if true; then elif "${test}" > "${actual}.new" 2>&1; then canonicalize_output < "${actual}.new" > "${actual}" expects_output=false - for file in "${dir}/${base}.expected".[0-9]; do + for file in "${test_src_dir}/${base}.expected".[0-9]; do if test -f "$file"; then expects_output=true break @@ -109,7 +111,7 @@ if true; then done if "${expects_output}"; then expect_fail="true" - for expected in "${dir}/${base}.expected".[0-9]; do + for expected in "${test_src_dir}/${base}.expected".[0-9]; do if ${CMP} --quiet "${expected}" "${actual}"; then pass="$(expr "${pass}" + 1)" echo "${passmsg}${expected/#*expected}" @@ -120,7 +122,7 @@ if true; then if "$expect_fail"; then fail="$(expr "${fail}" + 1)" echo "${failmsg}" - for expected in "${dir}/${base}.expected".[0-9]; do + for expected in "${test_src_dir}/${base}.expected".[0-9]; do ${DIFF} -u "${expected}" "${actual}" done fi