]> git.draconx.ca Git - dxcommon.git/commitdiff
Add "join" detection macro.
authorNick Bowler <nbowler@draconx.ca>
Sun, 30 Jan 2022 01:48:17 +0000 (20:48 -0500)
committerNick Bowler <nbowler@draconx.ca>
Sun, 30 Jan 2022 03:46:17 +0000 (22:46 -0500)
It seems that unconditional use of "join" is not portable.  Alpine
Linux, for example, does not include it.  Add a macro which detects
whether join exists and works, with a fallback to an (incomplete)
awk implementation of the tool.

m4/join.m4 [new file with mode: 0644]
m4/linguas.m4
scripts/join.awk [new file with mode: 0755]
tests/macros.at
tests/programs.at

diff --git a/m4/join.m4 b/m4/join.m4
new file mode 100644 (file)
index 0000000..2f25ce8
--- /dev/null
@@ -0,0 +1,77 @@
+dnl Copyright © 2022 Nick Bowler
+dnl
+dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2.
+dnl This is free software: you are free to do what the fuck you want to.
+dnl There is NO WARRANTY, to the extent permitted by law.
+
+dnl DX_PROG_JOIN
+dnl
+dnl Search PATH for standard POSIX "join" utility.  If found, the JOIN
+dnl variable (which is substituted by AC_SUBST) is set to the result and
+dnl the cache variable dx_cv_join_works is set to "yes".  Otherwise,
+dnl dx_cv_join_works is set to "no" and JOIN is set to an incomplete
+dnl awk-based implementation which supports no options.
+
+AC_DEFUN([DX_PROG_JOIN], [AC_PREREQ([2.62])dnl
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_CACHE_CHECK([for join], [ac_cv_path_JOIN],
+[dx_cv_join_found=:
+AC_PATH_PROGS_FEATURE_CHECK([JOIN], [join],
+[_DX_JOIN_DO_TEST([$ac_path_JOIN],
+[DX_BASENAME([join_relcmd], ["$ac_path_JOIN"])
+join_bypath=`{ command -v "$join_relcmd"; } 2>/dev/null` #''
+ac_cv_path_JOIN=$ac_path_JOIN
+test x"$join_bypath" = x"$ac_path_JOIN" && ac_cv_path_JOIN=$join_relcmd
+ac_path_JOIN_found=:
+dx_cv_join_works=yes])], [ac_cv_path_JOIN=no dx_cv_join_found=false])])
+AS_IF([$dx_cv_join_found],
+[JOIN=$ac_cv_path_JOIN
+AC_SUBST([JOIN])dnl
+AC_CACHE_CHECK([whether $JOIN works], [dx_cv_join_works],
+[_DX_JOIN_DO_TEST([$JOIN], [dx_cv_join_works=yes], [dx_cv_join_works=no])])])
+rm -f conftest.a conftest.b conftest.c
+AS_IF([test x"$dx_cv_join_works" != x"yes"],
+[JOIN="$AWK -f m4_do(
+  [m4_pushdef([m4_include], [$][1])],
+  [m4_include(DX_BASEDIR[/scripts/join.awk])],
+  [m4_popdef([m4_include])])"])])
+
+AC_DEFUN([_DX_JOIN_DO_TEST],
+[AS_IF([test ! -f conftest.b],
+[cat >conftest.a <<'EOF'
+1 a
+3 a1 x
+3 a2 x
+5 a
+7 a
+9 a1 x
+9 a2 x
+EOF
+cat >conftest.b <<'EOF'
+2 b
+2 b
+3 b y
+4 b
+6 b
+7 b1 y
+7 b2 y
+8 b
+9 b1 y
+9 b2 y
+EOF])
+AS_IF([$1 conftest.a conftest.b >conftest.c 2>&AS_MESSAGE_LOG_FD],
+[if diff - conftest.c >/dev/null 2>&1 <<'EOF'
+3 a1 x b y
+3 a2 x b y
+7 a b1 y
+7 a b2 y
+9 a1 x b1 y
+9 a1 x b2 y
+9 a2 x b1 y
+9 a2 x b2 y
+EOF
+then :
+$2
+else :
+$3
+fi], [$3])])
index eb6701e2dd92c5696e3fd891b77f44af7ccce2b4..fce4e0c07c919569360b3ca7ebdfd5cbef2c0d7a 100644 (file)
@@ -1,4 +1,4 @@
-dnl Copyright © 2011, 2021 Nick Bowler
+dnl Copyright © 2011, 2021-2022 Nick Bowler
 dnl
 dnl Computes the set of .po and .mo files based on the LINGUAS environment
 dnl variable.  The variable POFILES is set to the complete list of .po files,
@@ -12,6 +12,7 @@ dnl There is NO WARRANTY, to the extent permitted by law.
 
 AC_DEFUN([DX_LINGUAS],
 [AC_REQUIRE([AM_GNU_GETTEXT])dnl
+AC_REQUIRE([DX_PROG_JOIN])dnl
 
 POFILES=
 MOFILES=
@@ -21,7 +22,7 @@ if test -f "$srcdir/po/LINGUAS"; then
     for (i = 1; i <= NF; i++) {
       print $(i)
     }
-  }' "$srcdir/po/LINGUAS" | sort -u >conftest.all
+  }' "$srcdir/po/LINGUAS" | LC_ALL=C sort -u >conftest.all
 
   : "${LINGUAS=all}"
   if test x"$LINGUAS" = x"all"; then
@@ -40,7 +41,7 @@ if test -f "$srcdir/po/LINGUAS"; then
           ;;
         esac
       done
-    } | sort -u >conftest.ena
+    } | LC_ALL=C sort -u >conftest.ena
   fi
 
   exec 3<conftest.all
@@ -49,7 +50,7 @@ if test -f "$srcdir/po/LINGUAS"; then
   done
 
   if test x"$USE_NLS" = x"yes"; then
-    join conftest.ena conftest.all >conftest.out
+    LC_ALL=C $JOIN conftest.ena conftest.all >conftest.out
     exec 3<conftest.out
     while read x <&3; do
       AS_VAR_APPEND([MOFILES], ["${MOFILES:+ }po/$x.mo"])
diff --git a/scripts/join.awk b/scripts/join.awk
new file mode 100755 (executable)
index 0000000..548ca2c
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/awk -f
+#
+# Copyright © 2022 Nick Bowler
+#
+# Partial implementation of POSIX "join" command.  No options are supported.
+#
+# License WTFPL2: Do What The Fuck You Want To Public License, version 2.
+# This is free software: you are free to do what the fuck you want to.
+# There is NO WARRANTY, to the extent permitted by law.
+
+BEGIN {
+  if (ARGC != 3) {
+    exit 1;
+  }
+
+  file2 = ARGV[2];
+  delete ARGV[2];
+
+  advance_rhs();
+}
+
+$1 == lhs_prev {
+  # Rewind RHS as we have duplicate keys in LHS.
+  close(file2);
+  advance_rhs();
+}
+
+$1 < rhs[1] { next }
+{
+  while ($1 > rhs[1]) {
+    if (advance_rhs() == 0)
+      exit(0);
+  }
+}
+
+$1 == rhs[1] {
+  lhs_prev = $1 = $1;
+  do {
+    print_match();
+    advance_rhs();
+  } while ($1 == rhs[1]);
+}
+
+function advance_rhs(raw, rc)
+{
+  rc = getline raw < file2;
+  if (rc < 0)
+    exit(1);
+
+  split(raw, rhs);
+  return rc;
+}
+
+function print_match(i)
+{
+  printf "%s", $0
+  for (i = 2; i in rhs; i++) {
+    printf " %s", rhs[i];
+  }
+  print ""
+}
index f746e54522ae8e849d0fab932ff6b044dc1eddf5..3f39fbde9635612d1b5ebb9033cf20e6364d82e6 100644 (file)
@@ -1,4 +1,4 @@
-dnl Copyright © 2014-2015, 2018-2019, 2021 Nick Bowler
+dnl Copyright © 2014-2015, 2018-2019, 2021-2022 Nick Bowler
 dnl
 dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2.
 dnl This is free software: you are free to do what the fuck you want to.
@@ -236,6 +236,56 @@ MOFILES = @&t@
 
 AT_CLEANUP
 
+AT_SETUP([DX_LINGUAS with broken "join" utility])
+AT_KEYWORDS([DX_LINGUAS macro])
+
+echo : >config.rpath
+chmod +x config.rpath
+
+mkdir bin
+cat >bin/join <<EOF
+#!/bin/sh
+:
+EOF
+chmod +x bin/join
+
+mkdir po
+AT_DATA([po/LINGUAS], [[en ja # a comment ko
+zh en_US
+]])
+
+AT_DATA([test.in], [[POFILES = @POFILES@
+MOFILES = @MOFILES@
+]])
+
+TEST_CONFIGURE_AC(
+[[m4@&t@_traceoff([AM_GNU_GETTEXT])
+AM_GNU_GETTEXT([external])
+DX_LINGUAS
+AC_CONFIG_FILES([test])
+]])
+TEST_AUTORECONF
+
+save_PATH=$PATH
+PATH=`pwd`/bin${PATH:+":$PATH"}
+LINGUAS='en_US'; export LINGUAS; TEST_CONFIGURE
+
+AT_CHECK([cat test], [0],
+[[POFILES = po/en.po po/en_US.po po/ja.po po/zh.po
+MOFILES = po/en.mo po/en_US.mo
+]])
+
+LINGUAS='en_US'; export LINGUAS; TEST_CONFIGURE([JOIN=false])
+
+AT_CHECK([cat test], [0],
+[[POFILES = po/en.po po/en_US.po po/ja.po po/zh.po
+MOFILES = po/en.mo po/en_US.mo
+]])
+
+PATH=$save_PATH
+
+AT_CLEANUP
+
 AT_SETUP([DX_BASENAME])
 AT_KEYWORDS([DX_BASENAME macro])
 
@@ -266,3 +316,26 @@ hello world
 ])
 
 AT_CLEANUP
+
+AT_SETUP([DX_PROG_JOIN distribution])
+AT_KEYWORDS([DX_PROG_JOIN macro])
+
+TEST_CONFIGURE_AC([[AM_INIT_AUTOMAKE([foreign])
+DX_PROG_JOIN
+AC_CONFIG_FILES([Makefile])
+]])
+
+AT_DATA([Makefile.am],
+[[foo: ; printf '%s\n' $(DX_BASEDIR) $(DISTFILES)
+]])
+TEST_AUTORECONF
+
+TEST_CONFIGURE
+AT_CHECK([make -s foo], [0], [stdout])
+AT_CHECK([exec 3<stdout
+read basedir <&3; while read f <&3; do
+  test x"$f" = x"$basedir/scripts/join.awk" && exit
+done
+exit 1])
+
+AT_CLEANUP
index 90262ef458a819a57afb313ed48243efcde2aaa6..b02d9abc7e94c3a965ab4932576842f74dabde6d 100644 (file)
@@ -1,4 +1,4 @@
-dnl Copyright © 2020-2021 Nick Bowler
+dnl Copyright © 2020-2022 Nick Bowler
 dnl
 dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2.
 dnl This is free software: you are free to do what the fuck you want to.
@@ -175,3 +175,58 @@ AT_CHECK([grep dx_cv_md5_works test], [0], [dx_cv_md5_works=no
 ])
 
 AT_CLEANUP
+
+AT_SETUP([join probes])
+
+AT_DATA([test.in],
+[[JOIN=@JOIN@
+dx_cv_join_works=@dx_cv_join_works@
+]])
+
+TEST_CONFIGURE_AC([[DX_PROG_JOIN
+AC_SUBST([dx_cv_join_works])
+
+set x conftest*; shift
+if test -f $[]1; then
+  AC_MSG_ERROR([$[]1 left behind by [D@@&t@&t@X_PROG_JOIN]])
+fi
+
+AC_CONFIG_FILES([test])
+]])
+TEST_AUTORECONF
+
+TEST_CONFIGURE([JOIN=true AWK="$AWK"])
+AT_CHECK_UNQUOTED([$AWK '{ print $1 }' test], [0],
+[JOIN=$AWK
+dx_cv_join_works=no
+])
+
+save_IFS=$IFS
+IFS='='; read x JOIN <test
+IFS=$save_IFS
+
+AT_DATA([a],
+[[a
+b
+c
+d
+]])
+
+AT_DATA([b],
+[[b
+d
+d
+]])
+
+AT_CHECK([$JOIN a b], [0], [[b
+d
+d
+]])
+
+TEST_CONFIGURE([JOIN="$JOIN"])
+AT_CHECK_UNQUOTED([cat test], [0],
+[JOIN=$JOIN
+dx_cv_join_works=yes
+])
+
+AT_CLEANUP