aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2022-09-12 21:19:55 +0200
committerPřemysl Eric Janouch <p@janouch.name>2022-09-13 01:01:35 +0200
commit5cda848f9419c77782008dabe6dc7cba9dc34396 (patch)
tree10ab6a83f92b0fb107101ce27edefdccc959b8a4
parenta167ae40b3b0e7a5081167a87cb4621dd15e5dd7 (diff)
downloadnncmpp-5cda848f9419c77782008dabe6dc7cba9dc34396.tar.gz
nncmpp-5cda848f9419c77782008dabe6dc7cba9dc34396.tar.xz
nncmpp-5cda848f9419c77782008dabe6dc7cba9dc34396.zip
Don't depend on a standalone C preprocessor
And get rid of the sed insanity.
-rw-r--r--CMakeLists.txt32
-rw-r--r--nncmpp.actions6
-rw-r--r--nncmpp.actions.awk106
-rw-r--r--nncmpp.c5
4 files changed, 127 insertions, 22 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 451e76b..543e0c6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,6 +10,14 @@ endif ()
# For custom modules
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/liberty/cmake)
+# Collect important build toggles for our simple preprocessor
+# (cpp(1) isn't part of POSIX, otherwise we could reuse config.h)
+set (options)
+macro (add_option variable help value)
+ option (${ARGV})
+ list (APPEND options "${variable}=$<BOOL:${${variable}}>")
+endmacro ()
+
# Dependencies
find_package (Ncursesw REQUIRED)
find_package (PkgConfig REQUIRED)
@@ -19,7 +27,7 @@ pkg_check_modules (curl REQUIRED libcurl)
include (AddThreads)
find_package (Termo QUIET NO_MODULE)
-option (USE_SYSTEM_TERMO
+add_option (USE_SYSTEM_TERMO
"Don't compile our own termo library, use the system one" ${Termo_FOUND})
if (USE_SYSTEM_TERMO)
if (NOT Termo_FOUND)
@@ -41,7 +49,7 @@ else ()
endif ()
pkg_check_modules (fftw fftw3 fftw3f)
-option (WITH_FFTW "Use FFTW to enable spectrum visualisation" ${fftw_FOUND})
+add_option (WITH_FFTW "Use FFTW to enable spectrum visualisation" ${fftw_FOUND})
if (WITH_FFTW)
if (NOT fftw_FOUND)
message (FATAL_ERROR "FFTW not found")
@@ -50,7 +58,8 @@ if (WITH_FFTW)
endif ()
pkg_check_modules (libpulse libpulse)
-option (WITH_PULSE "Enable control of PulseAudio sink volume" ${libpulse_FOUND})
+add_option (WITH_PULSE
+ "Enable PulseAudio sink volume control" ${libpulse_FOUND})
if (WITH_PULSE)
if (NOT libpulse_FOUND)
message (FATAL_ERROR "libpulse not found")
@@ -59,7 +68,7 @@ if (WITH_PULSE)
endif ()
pkg_check_modules (x11 x11 xrender xft fontconfig)
-option (WITH_X11 "Use FFTW to enable spectrum visualisation" ${x11_FOUND})
+add_option (WITH_X11 "Use FFTW to enable spectrum visualisation" ${x11_FOUND})
if (WITH_X11)
if (NOT x11_FOUND)
message (FATAL_ERROR "Some X11 libraries were not found")
@@ -99,20 +108,13 @@ configure_file (${PROJECT_SOURCE_DIR}/config.h.in
${PROJECT_BINARY_DIR}/config.h)
include_directories (${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR})
-# Assuming a Unix-compatible system with a standalone preprocessor
set (actions_list ${PROJECT_SOURCE_DIR}/nncmpp.actions)
+set (actions_awk ${PROJECT_SOURCE_DIR}/nncmpp.actions.awk)
set (actions ${PROJECT_BINARY_DIR}/nncmpp-actions.h)
add_custom_command (OUTPUT ${actions}
- COMMAND cpp -I${PROJECT_BINARY_DIR} -P ${actions_list}
- | grep . | tr [[\n]] ^ | sed -ne [[h; s/,[^^]*/,/g]] -e [[s/$/COUNT/]]
- -e [[s/[^^]*/\tACTION_&/g]] -e [[s/.*/enum action {\n&\n};\n/p]]
- -e [[g; s/,[^^]*//g; y/_/-/]] -e [[s/[^^]\{1,\}/\t"&",/g]]
- -e [[s/.*/static const char *g_action_names[] = {\n&};\n/p]]
- -e [[g; s/[^^]*, *//g;]] -e [[s/[^^]\{1,\}/\t"&",/g]]
- -e [[s/.*/static const char *g_action_descriptions[] = {\n&};/p]]
- | tr ^ [[\n]] > ${actions}
- COMMAND test -s ${actions}
- DEPENDS ${actions_list} ${PROJECT_BINARY_DIR}/config.h VERBATIM)
+ COMMAND env LC_ALL=C ${options}
+ awk -f ${actions_awk} ${actions_list} > ${actions}
+ DEPENDS ${actions_awk} ${actions_list} VERBATIM)
# Build the main executable and link it
add_executable (${PROJECT_NAME} ${PROJECT_NAME}.c ${actions})
diff --git a/nncmpp.actions b/nncmpp.actions
index 38a8f76..7d0662d 100644
--- a/nncmpp.actions
+++ b/nncmpp.actions
@@ -1,5 +1,3 @@
-#include "config.h"
-
NONE, Do nothing
QUIT, Quit
@@ -28,11 +26,11 @@ MPD_CONSUME, Toggle consume
MPD_UPDATE_DB, Update MPD database
MPD_COMMAND, Send raw command to MPD
-#ifdef WITH_PULSE
+.ifdef WITH_PULSE
PULSE_VOLUME_UP, Increase PulseAudio volume
PULSE_VOLUME_DOWN, Decrease PulseAudio volume
PULSE_MUTE, Toggle PulseAudio sink mute
-#endif
+.endif
CHOOSE, Choose item
DELETE, Delete item
diff --git a/nncmpp.actions.awk b/nncmpp.actions.awk
new file mode 100644
index 0000000..b4d7eaf
--- /dev/null
+++ b/nncmpp.actions.awk
@@ -0,0 +1,106 @@
+# nncmpp.actions.awk: produce C code for a list of user actions
+#
+# Copyright (c) 2022, Přemysl Eric Janouch <p@janouch.name>
+# SPDX-License-Identifier: 0BSD
+#
+# Usage: env LC_ALL=C A=0 B=1 awk -f nncmpp.actions.awk \
+# nncmpp.actions > nncmpp-actions.h
+
+# --- Preprocessor -------------------------------------------------------------
+
+function fatal(message) {
+ print "// " FILENAME ":" FNR ": fatal error: " message
+ print FILENAME ":" FNR ": fatal error: " message > "/dev/stderr"
+ exit 1
+}
+
+function condition(pass, passing, a, i) {
+ split(substr($0, RSTART + RLENGTH), a, /[[:space:]]+/)
+ if (!(1 in a))
+ fatal("missing condition")
+
+ passing = 0
+ for (i in a)
+ if (a[i] && !pass == !ENVIRON[a[i]])
+ passing = 1
+
+ while (getline > 0) {
+ if (match($0, /^[[:space:]]*[.]endif[[:space:]]*$/))
+ return 1
+
+ if (match($0, /^[[:space:]]*[.]else[[:space:]]*$/))
+ passing = !passing
+ else if (!directive() && passing)
+ process()
+ }
+
+ fatal("unterminated condition body")
+}
+
+# Multiple arguments mean logical OR, multiple directives mean logical AND.
+# Similar syntax is also used by Exim, BSD make, or various assemblers.
+#
+# Looking at what others have picked for their preprocessor syntax:
+# {OpenGL, FreeBASIC} reuse #ifdef, which would be confusing with C code around,
+# {Mental Ray, RapidQ and UniVerse BASIC} use $ifdef, NSIS has !ifdef,
+# and Verilog went for `ifdef. Not much more can be easily found.
+function directive() {
+ sub(/#.*/, "")
+ if (match($0, /^[[:space:]]*[.]ifdef[[:space:]]+/))
+ return condition(1)
+ if (match($0, /^[[:space:]]*[.]ifndef[[:space:]]+/))
+ return condition(0)
+ if (/^[[:space:]]*[.]/)
+ fatal("unexpected or unsupported directive")
+ return 0
+}
+
+!directive() {
+ process()
+}
+
+# --- Postprocessor ------------------------------------------------------------
+
+function strip(string) {
+ gsub(/^[[:space:]]*|[[:space:]]*$/, "", string)
+ return string
+}
+
+function process( constant, name, description) {
+ if (match($0, /,/)) {
+ constant = name = strip(substr($0, 1, RSTART - 1))
+ description = strip(substr($0, RSTART + RLENGTH))
+ gsub(/_/, "-", name)
+
+ N++
+ Constants[N] = constant
+ Names[N] = tolower(name)
+ Descriptions[N] = description
+ } else if (/[^[:space:]]/) {
+ fatal("invalid action definition syntax")
+ }
+}
+
+function tocstring(string) {
+ gsub(/\\/, "\\\\", string)
+ gsub(/"/, "\\\"", string)
+ return "\"" string "\""
+}
+
+END {
+ print "enum action {"
+ for (i in Constants)
+ print "\t" "ACTION_" Constants[i] ","
+ print "\t" "ACTION_COUNT"
+ print "};"
+ print ""
+ print "static const char *g_action_names[] = {"
+ for (i in Names)
+ print "\t" tocstring(Names[i]) ","
+ print "};"
+ print ""
+ print "static const char *g_action_descriptions[] = {"
+ for (i in Descriptions)
+ print "\t" tocstring(Descriptions[i]) ","
+ print "};"
+}
diff --git a/nncmpp.c b/nncmpp.c
index b96191b..74f0a51 100644
--- a/nncmpp.c
+++ b/nncmpp.c
@@ -4168,9 +4168,8 @@ help_tab_on_action (enum action action)
if (action == ACTION_DESCRIBE)
{
- char *name = xstrdup (g_action_names[a]);
- cstr_transform (name, tolower_ascii);
- app_show_message (xstrdup ("Configuration name: "), name);
+ app_show_message (xstrdup ("Configuration name: "),
+ xstrdup (g_action_names[a]));
return true;
}
if (action != ACTION_CHOOSE || a == ACTION_CHOOSE /* avoid recursion */)