SummaryRefsLogTreeCommitDiffStats
diff options
context:
space:
mode:
-rwxr-xr-xbuild-aux/announce-gen557
-rwxr-xr-xbuild-aux/do-release-commit-and-tag179
-rwxr-xr-xbuild-aux/gnu-web-doc-update210
-rwxr-xr-xbuild-aux/gnupload440
4 files changed, 1386 insertions, 0 deletions
diff --git a/build-aux/announce-gen b/build-aux/announce-gen
new file mode 100755
index 0000000..eeb9071
--- /dev/null
+++ b/build-aux/announce-gen
@@ -0,0 +1,557 @@
+eval '(exit $?0)' && eval 'exec perl -wS "$0" "$@"'
+ & eval 'exec perl -wS "$0" $argv:q'
+ if 0;
+# Generate a release announcement message.
+
+my $VERSION = '2018-03-07 03:46'; # UTC
+# The definition above must lie within the first 8 lines in order
+# for the Emacs time-stamp write hook (at end) to update it.
+# If you change this file with Emacs, please let the write hook
+# do its job. Otherwise, update this string manually.
+
+# Copyright (C) 2002-2018 Free Software Foundation, Inc.
+
+# 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 3 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, see <https://www.gnu.org/licenses/>.
+
+# Written by Jim Meyering
+
+use strict;
+
+use Getopt::Long;
+use POSIX qw(strftime);
+
+(my $ME = $0) =~ s|.*/||;
+
+my %valid_release_types = map {$_ => 1} qw (alpha beta stable);
+my @archive_suffixes = ('tar.gz', 'tar.bz2', 'tar.lzma', 'tar.xz');
+my %digest_classes =
+ (
+ 'md5' => (eval { require Digest::MD5; } and 'Digest::MD5'),
+ 'sha1' => ((eval { require Digest::SHA; } and 'Digest::SHA')
+ or (eval { require Digest::SHA1; } and 'Digest::SHA1'))
+ );
+my $srcdir = '.';
+
+sub usage ($)
+{
+ my ($exit_code) = @_;
+ my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
+ if ($exit_code != 0)
+ {
+ print $STREAM "Try '$ME --help' for more information.\n";
+ }
+ else
+ {
+ my @types = sort keys %valid_release_types;
+ print $STREAM <<EOF;
+Usage: $ME [OPTIONS]
+Generate an announcement message. Run this from builddir.
+
+OPTIONS:
+
+These options must be specified:
+
+ --release-type=TYPE TYPE must be one of @types
+ --package-name=PACKAGE_NAME
+ --previous-version=VER
+ --current-version=VER
+ --gpg-key-id=ID The GnuPG ID of the key used to sign the tarballs
+ --url-directory=URL_DIR
+
+The following are optional:
+
+ --news=NEWS_FILE include the NEWS section about this release
+ from this NEWS_FILE; accumulates.
+ --srcdir=DIR where to find the NEWS_FILEs (default: $srcdir)
+ --bootstrap-tools=TOOL_LIST a comma-separated list of tools, e.g.,
+ autoconf,automake,bison,gnulib
+ --gnulib-version=VERSION report VERSION as the gnulib version, where
+ VERSION is the result of running git describe
+ in the gnulib source directory.
+ required if gnulib is in TOOL_LIST.
+ --no-print-checksums do not emit MD5 or SHA1 checksums
+ --archive-suffix=SUF add SUF to the list of archive suffixes
+ --mail-headers=HEADERS a space-separated list of mail headers, e.g.,
+ To: x\@example.com Cc: y-announce\@example.com,...
+
+ --help display this help and exit
+ --version output version information and exit
+
+EOF
+ }
+ exit $exit_code;
+}
+
+
+=item C<%size> = C<sizes (@file)>
+
+Compute the sizes of the C<@file> and return them as a hash. Return
+C<undef> if one of the computation failed.
+
+=cut
+
+sub sizes (@)
+{
+ my (@file) = @_;
+
+ my $fail = 0;
+ my %res;
+ foreach my $f (@file)
+ {
+ my $cmd = "du -h $f";
+ my $t = `$cmd`;
+ # FIXME-someday: give a better diagnostic, a la $PROCESS_STATUS
+ $@
+ and (warn "command failed: '$cmd'\n"), $fail = 1;
+ chomp $t;
+ $t =~ s/^\s*([\d.]+[MkK]).*/${1}B/;
+ $res{$f} = $t;
+ }
+ return $fail ? undef : %res;
+}
+
+=item C<print_locations ($title, \@url, \%size, @file)
+
+Print a section C<$title> dedicated to the list of <@file>, which
+sizes are stored in C<%size>, and which are available from the C<@url>.
+
+=cut
+
+sub print_locations ($\@\%@)
+{
+ my ($title, $url, $size, @file) = @_;
+ print "Here are the $title:\n";
+ foreach my $url (@{$url})
+ {
+ for my $file (@file)
+ {
+ print " $url/$file";
+ print " (", $$size{$file}, ")"
+ if exists $$size{$file};
+ print "\n";
+ }
+ }
+ print "\n";
+}
+
+=item C<print_checksums (@file)
+
+Print the MD5 and SHA1 signature section for each C<@file>.
+
+=cut
+
+sub print_checksums (@)
+{
+ my (@file) = @_;
+
+ print "Here are the MD5 and SHA1 checksums:\n";
+ print "\n";
+
+ foreach my $meth (qw (md5 sha1))
+ {
+ my $class = $digest_classes{$meth} or next;
+ foreach my $f (@file)
+ {
+ open IN, '<', $f
+ or die "$ME: $f: cannot open for reading: $!\n";
+ binmode IN;
+ my $dig = $class->new->addfile(*IN)->hexdigest;
+ close IN;
+ print "$dig $f\n";
+ }
+ }
+ print "\n";
+}
+
+=item C<print_news_deltas ($news_file, $prev_version, $curr_version)
+
+Print the section of the NEWS file C<$news_file> addressing changes
+between versions C<$prev_version> and C<$curr_version>.
+
+=cut
+
+sub print_news_deltas ($$$)
+{
+ my ($news_file, $prev_version, $curr_version) = @_;
+
+ my $news_name = $news_file;
+ $news_name =~ s|^\Q$srcdir\E/||;
+
+ print "\n$news_name\n\n";
+
+ # Print all lines from $news_file, starting with the first one
+ # that mentions $curr_version up to but not including
+ # the first occurrence of $prev_version.
+ my $in_items;
+
+ my $re_prefix = qr/(?:\* )?(?:Noteworthy c|Major c|C)(?i:hanges)/;
+
+ my $found_news;
+ open NEWS, '<', $news_file
+ or die "$ME: $news_file: cannot open for reading: $!\n";
+ while (defined (my $line = <NEWS>))
+ {
+ if ( ! $in_items)
+ {
+ # Match lines like these:
+ # * Major changes in release 5.0.1:
+ # * Noteworthy changes in release 6.6 (2006-11-22) [stable]
+ $line =~ /^$re_prefix.*(?:[^\d.]|$)\Q$curr_version\E(?:[^\d.]|$)/o
+ or next;
+ $in_items = 1;
+ print $line;
+ }
+ else
+ {
+ # This regexp must not match version numbers in NEWS items.
+ # For example, they might well say "introduced in 4.5.5",
+ # and we don't want that to match.
+ $line =~ /^$re_prefix.*(?:[^\d.]|$)\Q$prev_version\E(?:[^\d.]|$)/o
+ and last;
+ print $line;
+ $line =~ /\S/
+ and $found_news = 1;
+ }
+ }
+ close NEWS;
+
+ $in_items
+ or die "$ME: $news_file: no matching lines for '$curr_version'\n";
+ $found_news
+ or die "$ME: $news_file: no news item found for '$curr_version'\n";
+}
+
+sub print_changelog_deltas ($$)
+{
+ my ($package_name, $prev_version) = @_;
+
+ # Print new ChangeLog entries.
+
+ # First find all CVS-controlled ChangeLog files.
+ use File::Find;
+ my @changelog;
+ find ({wanted => sub {$_ eq 'ChangeLog' && -d 'CVS'
+ and push @changelog, $File::Find::name}},
+ '.');
+
+ # If there are no ChangeLog files, we're done.
+ @changelog
+ or return;
+ my %changelog = map {$_ => 1} @changelog;
+
+ # Reorder the list of files so that if there are ChangeLog
+ # files in the specified directories, they're listed first,
+ # in this order:
+ my @dir = qw ( . src lib m4 config doc );
+
+ # A typical @changelog array might look like this:
+ # ./ChangeLog
+ # ./po/ChangeLog
+ # ./m4/ChangeLog
+ # ./lib/ChangeLog
+ # ./doc/ChangeLog
+ # ./config/ChangeLog
+ my @reordered;
+ foreach my $d (@dir)
+ {
+ my $dot_slash = $d eq '.' ? $d : "./$d";
+ my $target = "$dot_slash/ChangeLog";
+ delete $changelog{$target}
+ and push @reordered, $target;
+ }
+
+ # Append any remaining ChangeLog files.
+ push @reordered, sort keys %changelog;
+
+ # Remove leading './'.
+ @reordered = map { s!^\./!!; $_ } @reordered;
+
+ print "\nChangeLog entries:\n\n";
+ # print join ("\n", @reordered), "\n";
+
+ $prev_version =~ s/\./_/g;
+ my $prev_cvs_tag = "\U$package_name\E-$prev_version";
+
+ my $cmd = "cvs -n diff -u -r$prev_cvs_tag -rHEAD @reordered";
+ open DIFF, '-|', $cmd
+ or die "$ME: cannot run '$cmd': $!\n";
+ # Print two types of lines, making minor changes:
+ # Lines starting with '+++ ', e.g.,
+ # +++ ChangeLog 22 Feb 2003 16:52:51 -0000 1.247
+ # and those starting with '+'.
+ # Don't print the others.
+ my $prev_printed_line_empty = 1;
+ while (defined (my $line = <DIFF>))
+ {
+ if ($line =~ /^\+\+\+ /)
+ {
+ my $separator = "*"x70 ."\n";
+ $line =~ s///;
+ $line =~ s/\s.*//;
+ $prev_printed_line_empty
+ or print "\n";
+ print $separator, $line, $separator;
+ }
+ elsif ($line =~ /^\+/)
+ {
+ $line =~ s///;
+ print $line;
+ $prev_printed_line_empty = ($line =~ /^$/);
+ }
+ }
+ close DIFF;
+
+ # The exit code should be 1.
+ # Allow in case there are no modified ChangeLog entries.
+ $? == 256 || $? == 128
+ or warn "warning: '$cmd' had unexpected exit code or signal ($?)\n";
+}
+
+sub get_tool_versions ($$)
+{
+ my ($tool_list, $gnulib_version) = @_;
+ @$tool_list
+ or return ();
+
+ my $fail;
+ my @tool_version_pair;
+ foreach my $t (@$tool_list)
+ {
+ if ($t eq 'gnulib')
+ {
+ push @tool_version_pair, ucfirst $t . ' ' . $gnulib_version;
+ next;
+ }
+ # Assume that the last "word" on the first line of
+ # 'tool --version' output is the version string.
+ my ($first_line, undef) = split ("\n", `$t --version`);
+ if ($first_line =~ /.* (\d[\w.-]+)$/)
+ {
+ $t = ucfirst $t;
+ push @tool_version_pair, "$t $1";
+ }
+ else
+ {
+ defined $first_line
+ and $first_line = '';
+ warn "$t: unexpected --version output\n:$first_line";
+ $fail = 1;
+ }
+ }
+
+ $fail
+ and exit 1;
+
+ return @tool_version_pair;
+}
+
+{
+ # Neutralize the locale, so that, for instance, "du" does not
+ # issue "1,2" instead of "1.2", what confuses our regexps.
+ $ENV{LC_ALL} = "C";
+
+ my $mail_headers;
+ my $release_type;
+ my $package_name;
+ my $prev_version;
+ my $curr_version;
+ my $gpg_key_id;
+ my @url_dir_list;
+ my @news_file;
+ my $bootstrap_tools;
+ my $gnulib_version;
+ my $print_checksums_p = 1;
+
+ # Reformat the warnings before displaying them.
+ local $SIG{__WARN__} = sub
+ {
+ my ($msg) = @_;
+ # Warnings from GetOptions.
+ $msg =~ s/Option (\w)/option --$1/;
+ warn "$ME: $msg";
+ };
+
+ GetOptions
+ (
+ 'mail-headers=s' => \$mail_headers,
+ 'release-type=s' => \$release_type,
+ 'package-name=s' => \$package_name,
+ 'previous-version=s' => \$prev_version,
+ 'current-version=s' => \$curr_version,
+ 'gpg-key-id=s' => \$gpg_key_id,
+ 'url-directory=s' => \@url_dir_list,
+ 'news=s' => \@news_file,
+ 'srcdir=s' => \$srcdir,
+ 'bootstrap-tools=s' => \$bootstrap_tools,
+ 'gnulib-version=s' => \$gnulib_version,
+ 'print-checksums!' => \$print_checksums_p,
+ 'archive-suffix=s' => \@archive_suffixes,
+
+ help => sub { usage 0 },
+ version => sub { print "$ME version $VERSION\n"; exit },
+ ) or usage 1;
+
+ my $fail = 0;
+ # Ensure that each required option is specified.
+ $release_type
+ or (warn "release type not specified\n"), $fail = 1;
+ $package_name
+ or (warn "package name not specified\n"), $fail = 1;
+ $prev_version
+ or (warn "previous version string not specified\n"), $fail = 1;
+ $curr_version
+ or (warn "current version string not specified\n"), $fail = 1;
+ $gpg_key_id
+ or (warn "GnuPG key ID not specified\n"), $fail = 1;
+ @url_dir_list
+ or (warn "URL directory name(s) not specified\n"), $fail = 1;
+
+ my @tool_list = split ',', $bootstrap_tools
+ if $bootstrap_tools;
+
+ grep (/^gnulib$/, @tool_list) ^ defined $gnulib_version
+ and (warn "when specifying gnulib as a tool, you must also specify\n"
+ . "--gnulib-version=V, where V is the result of running git describe\n"
+ . "in the gnulib source directory.\n"), $fail = 1;
+
+ !$release_type || exists $valid_release_types{$release_type}
+ or (warn "'$release_type': invalid release type\n"), $fail = 1;
+
+ @ARGV
+ and (warn "too many arguments:\n", join ("\n", @ARGV), "\n"),
+ $fail = 1;
+ $fail
+ and usage 1;
+
+ my $my_distdir = "$package_name-$curr_version";
+
+ my $xd = "$package_name-$prev_version-$curr_version.xdelta";
+
+ my @candidates = map { "$my_distdir.$_" } @archive_suffixes;
+ my @tarballs = grep {-f $_} @candidates;
+
+ @tarballs
+ or die "$ME: none of " . join(', ', @candidates) . " were found\n";
+ my @sizable = @tarballs;
+ -f $xd
+ and push @sizable, $xd;
+ my %size = sizes (@sizable);
+ %size
+ or exit 1;
+
+ my $headers = '';
+ if (defined $mail_headers)
+ {
+ ($headers = $mail_headers) =~ s/\s+(\S+:)/\n$1/g;
+ $headers .= "\n";
+ }
+
+ # The markup is escaped as <\# so that when this script is sent by
+ # mail (or part of a diff), Gnus is not triggered.
+ print <<EOF;
+
+${headers}Subject: $my_distdir released [$release_type]
+
+<\#secure method=pgpmime mode=sign>
+
+FIXME: put comments here
+
+EOF
+
+ if (@url_dir_list == 1 && @tarballs == 1)
+ {
+ # When there's only one tarball and one URL, use a more concise form.
+ my $m = "$url_dir_list[0]/$tarballs[0]";
+ print "Here are the compressed sources and a GPG detached signature[*]:\n"
+ . " $m\n"
+ . " $m.sig\n\n";
+ }
+ else
+ {
+ print_locations ("compressed sources", @url_dir_list, %size, @tarballs);
+ -f $xd
+ and print_locations ("xdelta diffs (useful? if so, "
+ . "please tell bug-gnulib\@gnu.org)",
+ @url_dir_list, %size, $xd);
+ my @sig_files = map { "$_.sig" } @tarballs;
+ print_locations ("GPG detached signatures[*]", @url_dir_list, %size,
+ @sig_files);
+ }
+
+ if ($url_dir_list[0] =~ "gnu\.org")
+ {
+ print "Use a mirror for higher download bandwidth:\n";
+ if (@tarballs == 1 && $url_dir_list[0] =~ m!https://ftp\.gnu\.org/gnu/!)
+ {
+ (my $m = "$url_dir_list[0]/$tarballs[0]")
+ =~ s!https://ftp\.gnu\.org/gnu/!https://ftpmirror\.gnu\.org/!;
+ print " $m\n"
+ . " $m.sig\n\n";
+
+ }
+ else
+ {
+ print " https://www.gnu.org/order/ftp.html\n\n";
+ }
+ }
+
+ $print_checksums_p
+ and print_checksums (@sizable);
+
+ print <<EOF;
+[*] Use a .sig file to verify that the corresponding file (without the
+.sig suffix) is intact. First, be sure to download both the .sig file
+and the corresponding tarball. Then, run a command like this:
+
+ gpg --verify $tarballs[0].sig
+
+If that command fails because you don't have the required public key,
+then run this command to import it:
+
+ gpg --keyserver keys.gnupg.net --recv-keys $gpg_key_id
+
+and rerun the 'gpg --verify' command.
+EOF
+
+ my @tool_versions = get_tool_versions (\@tool_list, $gnulib_version);
+ @tool_versions
+ and print "\nThis release was bootstrapped with the following tools:",
+ join ('', map {"\n $_"} @tool_versions), "\n";
+
+ print_news_deltas ($_, $prev_version, $curr_version)
+ foreach @news_file;
+
+ $release_type eq 'stable'
+ or print_changelog_deltas ($package_name, $prev_version);
+
+ exit 0;
+}
+
+### Setup "GNU" style for perl-mode and cperl-mode.
+## Local Variables:
+## mode: perl
+## perl-indent-level: 2
+## perl-continued-statement-offset: 2
+## perl-continued-brace-offset: 0
+## perl-brace-offset: 0
+## perl-brace-imaginary-offset: 0
+## perl-label-offset: -2
+## perl-extra-newline-before-brace: t
+## perl-merge-trailing-else: nil
+## eval: (add-hook 'before-save-hook 'time-stamp)
+## time-stamp-start: "my $VERSION = '"
+## time-stamp-format: "%:y-%02m-%02d %02H:%02M"
+## time-stamp-time-zone: "UTC0"
+## time-stamp-end: "'; # UTC"
+## End:
diff --git a/build-aux/do-release-commit-and-tag b/build-aux/do-release-commit-and-tag
new file mode 100755
index 0000000..4472ff5
--- /dev/null
+++ b/build-aux/do-release-commit-and-tag
@@ -0,0 +1,179 @@
+#!/bin/sh
+# In a git/autoconf/automake-enabled project with a NEWS file and a version-
+# controlled .prev-version file, automate the procedure by which we record
+# the date, release-type and version string in the NEWS file. That commit
+# will serve to identify the release, so apply a signed tag to it as well.
+VERSION=2018-03-07.03 # UTC
+
+# Note: this is a bash script (could be zsh or dash)
+
+# Copyright (C) 2009-2018 Free Software Foundation, Inc.
+
+# 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 3 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, see <https://www.gnu.org/licenses/>.
+
+# Written by Jim Meyering
+
+ME=$(basename "$0")
+warn() { printf '%s: %s\n' "$ME" "$*" >&2; }
+die() { warn "$*"; exit 1; }
+
+help()
+{
+ cat <<EOF
+Usage: $ME [OPTION...] VERSION RELEASE_TYPE
+
+Run this script from top_srcdir to perform the final pre-release NEWS
+update in which the date, release-type and version string are
+recorded. Commit that result with a log entry marking the release,
+and apply a signed tag. Run it from your project's top-level
+directory.
+
+Requirements:
+- you use git for version-control
+- a version-controlled .prev-version file
+- a NEWS file, with line 3 identical to this:
+$noteworthy_stub
+
+Options:
+ --branch=BRANCH set release branch (default: $branch)
+ -C, --builddir=DIR location of (configured) Makefile (default: $builddir)
+ --help print this help, then exit
+ --version print version number, then exit
+
+EXAMPLE:
+To update NEWS and tag the beta 8.1 release of coreutils, I would run this:
+
+ $ME 8.1 beta
+
+Report bugs and patches to <bug-gnulib@gnu.org>.
+EOF
+ exit
+}
+
+version()
+{
+ year=$(echo "$VERSION" | sed 's/[^0-9].*//')
+ cat <<EOF
+$ME $VERSION
+Copyright (C) $year Free Software Foundation, Inc,
+License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+EOF
+ exit
+}
+
+## ------ ##
+## Main. ##
+## ------ ##
+
+# Constants.
+noteworthy='* Noteworthy changes in release'
+noteworthy_stub="$noteworthy ?.? (????-??-??) [?]"
+
+# Variables.
+branch=$(git branch | sed -ne '/^\* /{s///;p;q;}')
+builddir=.
+
+while test $# != 0
+do
+ # Handle --option=value by splitting apart and putting back on argv.
+ case $1 in
+ --*=*)
+ opt=$(echo "$1" | sed -e 's/=.*//')
+ val=$(echo "$1" | sed -e 's/[^=]*=//')
+ shift
+ set dummy "$opt" "$val" "$@"; shift
+ ;;
+ esac
+
+ case $1 in
+ --help|--version) ${1#--};;
+ --branch) shift; branch=$1; shift ;;
+ -C|--builddir) shift; builddir=$1; shift ;;
+ --*) die "unrecognized option: $1";;
+ *) break;;
+ esac
+done
+
+test $# = 2 \
+ || die "Usage: $ME [OPTION...] VERSION TYPE"
+
+ver=$1
+type=$2
+
+
+## ---------------------- ##
+## First, sanity checks. ##
+## ---------------------- ##
+
+# Verify that $ver looks like a version number, and...
+echo "$ver"|grep -E '^[0-9][0-9.]*[0-9]$' > /dev/null \
+ || die "invalid version: $ver"
+prev_ver=$(cat .prev-version) \
+ || die 'failed to determine previous version number from .prev-version'
+
+# Verify that $ver is sensible (> .prev-version).
+case $(printf "$prev_ver\n$ver\n"|sort -V -u|tr '\n' ':') in
+ "$prev_ver:$ver:") ;;
+ *) die "invalid version: $ver (<= $prev_ver)";;
+esac
+
+case $type in
+ alpha|beta|stable) ;;
+ *) die "invalid release type: $type";;
+esac
+
+# No local modifications allowed.
+case $(git diff-index --name-only HEAD) in
+ '') ;;
+ *) die 'this tree is dirty; commit your changes first';;
+esac
+
+# Ensure the current branch name is correct:
+curr_br=$(git rev-parse --symbolic-full-name HEAD)
+test "$curr_br" = "refs/heads/$branch" || die not on branch $branch
+
+# Extract package name from Makefile.
+Makefile=$builddir/Makefile
+pkg=$(sed -n 's/^PACKAGE = \(.*\)/\1/p' "$Makefile") \
+ || die "failed to determine package name from $Makefile"
+
+# Check that line 3 of NEWS is the stub line about to be replaced.
+test "$(sed -n 3p NEWS)" = "$noteworthy_stub" \
+ || die "line 3 of NEWS must be exactly '$noteworthy_stub'"
+
+## --------------- ##
+## Then, changes. ##
+## --------------- ##
+
+# Update NEWS to have today's date, plus desired version number and $type.
+perl -MPOSIX -ni -e 'my $today = strftime "%F", localtime time;' \
+ -e 'my ($type, $ver) = qw('"$type $ver"');' \
+ -e 'my $pfx = "'"$noteworthy"'";' \
+ -e 'print $.==3 ? "$pfx $ver ($today) [$type]\n" : $_' \
+ NEWS || die 'failed to update NEWS'
+
+printf "version $ver\n\n* NEWS: Record release date.\n" \
+ | git commit -F - -a || die 'git commit failed'
+git tag -s -m "$pkg $ver" v$ver HEAD || die 'git tag failed'
+
+# Local variables:
+# indent-tabs-mode: nil
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "VERSION="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: " # UTC"
+# End:
diff --git a/build-aux/gnu-web-doc-update b/build-aux/gnu-web-doc-update
new file mode 100755
index 0000000..356c561
--- /dev/null
+++ b/build-aux/gnu-web-doc-update
@@ -0,0 +1,210 @@
+#!/bin/sh
+# Run this after each non-alpha release, to update the web documentation at
+# https://www.gnu.org/software/$pkg/manual/
+
+VERSION=2018-03-07.03; # UTC
+
+# Copyright (C) 2009-2018 Free Software Foundation, Inc.
+
+# 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 3 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, see <https://www.gnu.org/licenses/>.
+
+ME=$(basename "$0")
+warn() { printf '%s: %s\n' "$ME" "$*" >&2; }
+die() { warn "$*"; exit 1; }
+
+help()
+{
+ cat <<EOF
+Usage: $ME
+
+Run this script from top_srcdir (no arguments) after each non-alpha
+release, to update the web documentation at
+https://www.gnu.org/software/\$pkg/manual/
+
+This script assumes you're using git for revision control, and
+requires a .prev-version file as well as a Makefile, from which it
+extracts the version number and package name, respectively. Also, it
+assumes all documentation is in the doc/ sub-directory.
+
+Options:
+ -C, --builddir=DIR location of (configured) Makefile (default: .)
+ -n, --dry-run don't actually commit anything
+ -m, --mirror remove out of date files from document server
+ --help print this help, then exit
+ --version print version number, then exit
+
+Report bugs and patches to <bug-gnulib@gnu.org>.
+EOF
+ exit
+}
+
+version()
+{
+ year=$(echo "$VERSION" | sed 's/[^0-9].*//')
+ cat <<EOF
+$ME $VERSION
+Copyright (C) $year Free Software Foundation, Inc,
+License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+EOF
+ exit
+}
+
+# find_tool ENVVAR NAMES...
+# -------------------------
+# Search for a required program. Use the value of ENVVAR, if set,
+# otherwise find the first of the NAMES that can be run (i.e.,
+# supports --version). If found, set ENVVAR to the program name,
+# die otherwise.
+#
+# FIXME: code duplication, see also bootstrap.
+find_tool ()
+{
+ find_tool_envvar=$1
+ shift
+ find_tool_names=$@
+ eval "find_tool_res=\$$find_tool_envvar"
+ if test x"$find_tool_res" = x; then
+ for i
+ do
+ if ($i --version </dev/null) >/dev/null 2>&1; then
+ find_tool_res=$i
+ break
+ fi
+ done
+ else
+ find_tool_error_prefix="\$$find_tool_envvar: "
+ fi
+ test x"$find_tool_res" != x \
+ || die "one of these is required: $find_tool_names"
+ ($find_tool_res --version </dev/null) >/dev/null 2>&1 \
+ || die "${find_tool_error_prefix}cannot run $find_tool_res --version"
+ eval "$find_tool_envvar=\$find_tool_res"
+ eval "export $find_tool_envvar"
+}
+
+## ------ ##
+## Main. ##
+## ------ ##
+
+# Requirements: everything required to bootstrap your package, plus
+# these.
+find_tool CVS cvs
+find_tool GIT git
+find_tool RSYNC rsync
+find_tool XARGS gxargs xargs
+
+builddir=.
+dryrun=
+rm_stale='echo'
+while test $# != 0
+do
+ # Handle --option=value by splitting apart and putting back on argv.
+ case $1 in
+ --*=*)
+ opt=$(echo "$1" | sed -e 's/=.*//')
+ val=$(echo "$1" | sed -e 's/[^=]*=//')
+ shift
+ set dummy "$opt" "$val" "$@"; shift
+ ;;
+ esac
+
+ case $1 in
+ --help|--version) ${1#--};;
+ -C|--builddir) shift; builddir=$1; shift ;;
+ -n|--dry-run) dryrun=echo; shift;;
+ -m|--mirror) rm_stale=''; shift;;
+ --*) die "unrecognized option: $1";;
+ *) break;;
+ esac
+done
+
+test $# = 0 \
+ || die "too many arguments"
+
+prev=.prev-version
+version=$(cat $prev) || die "no $prev file?"
+pkg=$(sed -n 's/^PACKAGE = \(.*\)/\1/p' $builddir/Makefile) \
+ || die "no Makefile?"
+tmp_branch=web-doc-$version-$$
+current_branch=$($GIT branch | sed -ne '/^\* /{s///;p;q;}')
+
+cleanup()
+{
+ __st=$?
+ $dryrun rm -rf "$tmp"
+ $GIT checkout "$current_branch"
+ $GIT submodule update --recursive
+ $GIT branch -d $tmp_branch
+ exit $__st
+}
+trap cleanup 0
+trap 'exit $?' 1 2 13 15
+
+# We must build using sources for which --version reports the
+# just-released version number, not some string like 7.6.18-20761.
+# That version string propagates into all documentation.
+set -e
+$GIT checkout -b $tmp_branch v$version
+$GIT submodule update --recursive
+./bootstrap
+srcdir=$(pwd)
+cd "$builddir"
+builddir=$(pwd)
+ ./config.status --recheck
+ ./config.status
+ make
+ make web-manual
+cd "$srcdir"
+set +e
+
+tmp=$(mktemp -d web-doc-update.XXXXXX) || exit 1
+( cd $tmp \
+ && $CVS -d $USER@cvs.sv.gnu.org:/webcvs/$pkg co $pkg )
+$RSYNC -avP "$builddir"/doc/manual/ $tmp/$pkg/manual
+
+(
+ cd $tmp/$pkg/manual
+
+ # Add all the files. This is simpler than trying to add only the
+ # new ones because of new directories
+ # First add non empty dirs individually
+ find . -name CVS -prune -o -type d \! -empty -print \
+ | $XARGS -n1 --no-run-if-empty -- $dryrun $CVS add -ko
+ # Now add all files
+ find . -name CVS -prune -o -type f -print \
+ | $XARGS --no-run-if-empty -- $dryrun $CVS add -ko
+
+ # Report/Remove stale files
+ # excluding doc server specific files like CVS/* and .symlinks
+ if test -n "$rm_stale"; then
+ echo 'Consider the --mirror option if all of the manual is generated,' >&2
+ echo 'which will run `cvs remove` to remove stale files.' >&2
+ fi
+ { find . \( -name CVS -o -type f -name '.*' \) -prune -o -type f -print
+ (cd "$builddir"/doc/manual/ && find . -type f -print | sed p)
+ } | sort | uniq -u \
+ | $XARGS --no-run-if-empty -- ${rm_stale:-$dryrun} $CVS remove -f
+
+ $dryrun $CVS ci -m $version
+)
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "VERSION="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/build-aux/gnupload b/build-aux/gnupload
new file mode 100755
index 0000000..2a0bfa3
--- /dev/null
+++ b/build-aux/gnupload
@@ -0,0 +1,440 @@
+#!/bin/sh
+# Sign files and upload them.
+
+scriptversion=2018-03-07.03; # UTC
+
+# Copyright (C) 2004-2018 Free Software Foundation, Inc.
+#
+# 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, 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, see <https://www.gnu.org/licenses/>.
+
+# Originally written by Alexandre Duret-Lutz <adl@gnu.org>.
+# The master copy of this file is maintained in the gnulib Git repository.
+# Please send bug reports and feature requests to bug-gnulib@gnu.org.
+
+set -e
+
+GPG='gpg --batch --no-tty'
+conffile=.gnuploadrc
+to=
+dry_run=false
+replace=
+symlink_files=
+delete_files=
+delete_symlinks=
+collect_var=
+dbg=
+nl='
+'
+
+usage="Usage: $0 [OPTION]... [CMD] FILE... [[CMD] FILE...]
+
+Sign all FILES, and process them at the destinations specified with --to.
+If CMD is not given, it defaults to uploading. See examples below.
+
+Commands:
+ --delete delete FILES from destination
+ --symlink create symbolic links
+ --rmsymlink remove symbolic links
+ -- treat the remaining arguments as files to upload
+
+Options:
+ --to DEST specify a destination DEST for FILES
+ (multiple --to options are allowed)
+ --user NAME sign with key NAME
+ --replace allow replacements of existing files
+ --symlink-regex[=EXPR] use sed script EXPR to compute symbolic link names
+ --dry-run do nothing, show what would have been done
+ (including the constructed directive file)
+ --version output version information and exit
+ --help print this help text and exit
+
+If --symlink-regex is given without EXPR, then the link target name
+is created by replacing the version information with '-latest', e.g.:
+ foo-1.3.4.tar.gz -> foo-latest.tar.gz
+
+Recognized destinations are:
+ alpha.gnu.org:DIRECTORY
+ savannah.gnu.org:DIRECTORY
+ savannah.nongnu.org:DIRECTORY
+ ftp.gnu.org:DIRECTORY
+ build directive files and upload files by FTP
+ download.gnu.org.ua:{alpha|ftp}/DIRECTORY
+ build directive files and upload files by SFTP
+ [user@]host:DIRECTORY upload files with scp
+
+Options and commands are applied in order. If the file $conffile exists
+in the current working directory, its contents are prepended to the
+actual command line options. Use this to keep your defaults. Comments
+(#) and empty lines in $conffile are allowed.
+
+<https://www.gnu.org/prep/maintain/html_node/Automated-FTP-Uploads.html>
+gives some further background.
+
+Examples:
+1. Upload foobar-1.0.tar.gz to ftp.gnu.org:
+ gnupload --to ftp.gnu.org:foobar foobar-1.0.tar.gz
+
+2. Upload foobar-1.0.tar.gz and foobar-1.0.tar.xz to ftp.gnu.org:
+ gnupload --to ftp.gnu.org:foobar foobar-1.0.tar.gz foobar-1.0.tar.xz
+
+3. Same as above, and also create symbolic links to foobar-latest.tar.*:
+ gnupload --to ftp.gnu.org:foobar \\
+ --symlink-regex \\
+ foobar-1.0.tar.gz foobar-1.0.tar.xz
+
+4. Upload foobar-0.9.90.tar.gz to two sites:
+ gnupload --to alpha.gnu.org:foobar \\
+ --to sources.redhat.com:~ftp/pub/foobar \\
+ foobar-0.9.90.tar.gz
+
+5. Delete oopsbar-0.9.91.tar.gz and upload foobar-0.9.91.tar.gz
+ (the -- terminates the list of files to delete):
+ gnupload --to alpha.gnu.org:foobar \\
+ --to sources.redhat.com:~ftp/pub/foobar \\
+ --delete oopsbar-0.9.91.tar.gz \\
+ -- foobar-0.9.91.tar.gz
+
+gnupload executes a program ncftpput to do the transfers; if you don't
+happen to have an ncftp package installed, the ncftpput-ftp script in
+the build-aux/ directory of the gnulib package
+(https://savannah.gnu.org/projects/gnulib) may serve as a replacement.
+
+Send patches and bug reports to <bug-gnulib@gnu.org>."
+
+# Read local configuration file
+if test -r "$conffile"; then
+ echo "$0: Reading configuration file $conffile"
+ conf=`sed 's/#.*$//;/^$/d' "$conffile" | tr "\015$nl" ' '`
+ eval set x "$conf \"\$@\""
+ shift
+fi
+
+while test -n "$1"; do
+ case $1 in
+ -*)
+ collect_var=
+ case $1 in
+ --help)
+ echo "$usage"
+ exit $?
+ ;;
+ --to)
+ if test -z "$2"; then
+ echo "$0: Missing argument for --to" 1>&2
+ exit 1
+ elif echo "$2" | grep 'ftp-upload\.gnu\.org' >/dev/null; then
+ echo "$0: Use ftp.gnu.org:PKGNAME or alpha.gnu.org:PKGNAME" >&2
+ echo "$0: for the destination, not ftp-upload.gnu.org (which" >&2
+ echo "$0: is used for direct ftp uploads, not with gnupload)." >&2
+ echo "$0: See --help and its examples if need be." >&2
+ exit 1
+ else
+ to="$to $2"
+ shift
+ fi
+ ;;
+ --user)
+ if test -z "$2"; then
+ echo "$0: Missing argument for --user" 1>&2
+ exit 1
+ else
+ GPG="$GPG --local-user $2"
+ shift
+ fi
+ ;;
+ --delete)
+ collect_var=delete_files
+ ;;
+ --replace)
+ replace="replace: true"
+ ;;
+ --rmsymlink)
+ collect_var=delete_symlinks
+ ;;
+ --symlink-regex=*)
+ symlink_expr=`expr "$1" : '[^=]*=\(.*\)'`
+ ;;
+ --symlink-regex)
+ symlink_expr='s|-[0-9][0-9\.]*\(-[0-9][0-9]*\)\{0,1\}\.|-latest.|'
+ ;;
+ --symlink)
+ collect_var=symlink_files
+ ;;
+ --dry-run|-n)
+ dry_run=:
+ ;;
+ --version)
+ echo "gnupload $scriptversion"
+ exit $?
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ echo "$0: Unknown option '$1', try '$0 --help'" 1>&2
+ exit 1
+ ;;
+ esac
+ ;;
+ *)
+ if test -z "$collect_var"; then
+ break
+ else
+ eval "$collect_var=\"\$$collect_var $1\""
+ fi
+ ;;
+ esac
+ shift
+done
+
+dprint()
+{
+ echo "Running $* ..."
+}
+
+if $dry_run; then
+ dbg=dprint
+fi
+
+if test -z "$to"; then
+ echo "$0: Missing destination sites" >&2
+ exit 1
+fi
+
+if test -n "$symlink_files"; then
+ x=`echo "$symlink_files" | sed 's/[^ ]//g;s/ //g'`
+ if test -n "$x"; then
+ echo "$0: Odd number of symlink arguments" >&2
+ exit 1
+ fi
+fi
+
+if test $# = 0; then
+ if test -z "${symlink_files}${delete_files}${delete_symlinks}"; then
+ echo "$0: No file to upload" 1>&2
+ exit 1
+ fi
+else
+ # Make sure all files exist. We don't want to ask
+ # for the passphrase if the script will fail.
+ for file
+ do
+ if test ! -f $file; then
+ echo "$0: Cannot find '$file'" 1>&2
+ exit 1
+ elif test -n "$symlink_expr"; then
+ linkname=`echo $file | sed "$symlink_expr"`
+ if test -z "$linkname"; then
+ echo "$0: symlink expression produces empty results" >&2
+ exit 1
+ elif test "$linkname" = $file; then
+ echo "$0: symlink expression does not alter file name" >&2
+ exit 1
+ fi
+ fi
+ done
+fi
+
+# Make sure passphrase is not exported in the environment.
+unset passphrase
+unset passphrase_fd_0
+GNUPGHOME=${GNUPGHOME:-$HOME/.gnupg}
+
+# Reset PATH to be sure that echo is a built-in. We will later use
+# 'echo $passphrase' to output the passphrase, so it is important that
+# it is a built-in (third-party programs tend to appear in 'ps'
+# listings with their arguments...).
+# Remember this script runs with 'set -e', so if echo is not built-in
+# it will exit now.
+if $dry_run || grep -q "^use-agent" $GNUPGHOME/gpg.conf; then :; else
+ PATH=/empty echo -n "Enter GPG passphrase: "
+ stty -echo
+ read -r passphrase
+ stty echo
+ echo
+ passphrase_fd_0="--passphrase-fd 0"
+fi
+
+if test $# -ne 0; then
+ for file
+ do
+ echo "Signing $file ..."
+ rm -f $file.sig
+ echo "$passphrase" | $dbg $GPG $passphrase_fd_0 -ba -o $file.sig $file
+ done
+fi
+
+
+# mkdirective DESTDIR BASE FILE STMT
+# Arguments: See upload, below
+mkdirective ()
+{
+ stmt="$4"
+ if test -n "$3"; then
+ stmt="
+filename: $3$stmt"
+ fi
+
+ cat >${2}.directive<<EOF
+version: 1.2
+directory: $1
+comment: gnupload v. $scriptversion$stmt
+EOF
+ if $dry_run; then
+ echo "File ${2}.directive:"
+ cat ${2}.directive
+ echo "File ${2}.directive:" | sed 's/./-/g'
+ fi
+}
+
+mksymlink ()
+{
+ while test $# -ne 0
+ do
+ echo "symlink: $1 $2"
+ shift
+ shift
+ done
+}
+
+# upload DEST DESTDIR BASE FILE STMT FILES
+# Arguments:
+# DEST Destination site;
+# DESTDIR Destination directory;
+# BASE Base name for the directive file;
+# FILE Name of the file to distribute (may be empty);
+# STMT Additional statements for the directive file;
+# FILES List of files to upload.
+upload ()
+{
+ dest=$1
+ destdir=$2
+ base=$3
+ file=$4
+ stmt=$5
+ files=$6
+
+ rm -f $base.directive $base.directive.asc
+ case $dest in
+ alpha.gnu.org:*)
+ mkdirective "$destdir" "$base" "$file" "$stmt"
+ echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive
+ $dbg ncftpput ftp-upload.gnu.org /incoming/alpha $files $base.directive.asc
+ ;;
+ ftp.gnu.org:*)
+ mkdirective "$destdir" "$base" "$file" "$stmt"
+ echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive
+ $dbg ncftpput ftp-upload.gnu.org /incoming/ftp $files $base.directive.asc
+ ;;
+ savannah.gnu.org:*)
+ if test -z "$files"; then
+ echo "$0: warning: standalone directives not applicable for $dest" >&2
+ fi
+ $dbg ncftpput savannah.gnu.org /incoming/savannah/$destdir $files
+ ;;
+ savannah.nongnu.org:*)
+ if test -z "$files"; then
+ echo "$0: warning: standalone directives not applicable for $dest" >&2
+ fi
+ $dbg ncftpput savannah.nongnu.org /incoming/savannah/$destdir $files
+ ;;
+ download.gnu.org.ua:alpha/*|download.gnu.org.ua:ftp/*)
+ destdir_p1=`echo "$destdir" | sed 's,^[^/]*/,,'`
+ destdir_topdir=`echo "$destdir" | sed 's,/.*,,'`
+ mkdirective "$destdir_p1" "$base" "$file" "$stmt"
+ echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive
+ for f in $files $base.directive.asc
+ do
+ echo put $f
+ done | $dbg sftp -b - puszcza.gnu.org.ua:/incoming/$destdir_topdir
+ ;;
+ /*)
+ dest_host=`echo "$dest" | sed 's,:.*,,'`
+ mkdirective "$destdir" "$base" "$file" "$stmt"
+ echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive
+ $dbg cp $files $base.directive.asc $dest_host
+ ;;
+ *)
+ if test -z "$files"; then
+ echo "$0: warning: standalone directives not applicable for $dest" >&2
+ fi
+ $dbg scp $files $dest
+ ;;
+ esac
+ rm -f $base.directive $base.directive.asc
+}
+
+#####
+# Process any standalone directives
+stmt=
+if test -n "$symlink_files"; then
+ stmt="$stmt
+`mksymlink $symlink_files`"
+fi
+
+for file in $delete_files
+do
+ stmt="$stmt
+archive: $file"
+done
+
+for file in $delete_symlinks
+do
+ stmt="$stmt
+rmsymlink: $file"
+done
+
+if test -n "$stmt"; then
+ for dest in $to
+ do
+ destdir=`echo $dest | sed 's/[^:]*://'`
+ upload "$dest" "$destdir" "`hostname`-$$" "" "$stmt"
+ done
+fi
+
+# Process actual uploads
+for dest in $to
+do
+ for file
+ do
+ echo "Uploading $file to $dest ..."
+ stmt=
+ #
+ # allowing file replacement is all or nothing.
+ if test -n "$replace"; then stmt="$stmt
+$replace"
+ fi
+ #
+ files="$file $file.sig"
+ destdir=`echo $dest | sed 's/[^:]*://'`
+ if test -n "$symlink_expr"; then
+ linkname=`echo $file | sed "$symlink_expr"`
+ stmt="$stmt
+symlink: $file $linkname
+symlink: $file.sig $linkname.sig"
+ fi
+ upload "$dest" "$destdir" "$file" "$file" "$stmt" "$files"
+ done
+done
+
+exit 0
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End: