#! /usr/bin/perl

#################################################################
# add_commit_links.pl -- add commit links to the release notes
#
# Copyright (c) 2024, PostgreSQL Global Development Group
#
# src/tools/add_commit_links.pl
#################################################################

#
# This script adds commit links to the release notes.
#
# Usage: cd to top of source tree and issue
#	src/tools/add_commit_links.pl release_notes_file
#
# The script can add links for release note items that lack them, and update
# those that have them.  The script is sensitive to the release note file being
# in a specific format:
#
#  * File name contains the major version number preceded by a dash
#    and followed by a period
#  * Commit text is generated by src/tools/git_changelog
#  * SGML comments around commit text start in the first column
#  * The commit item title ends with an attribution that ends with
#    a closing parentheses
#  * previously added URL link text is unmodified
#  * a "<para>" follows the commit item title
#
# The major version number is used to select the commit hash for minor
# releases.  An error will be generated if valid commits are found but
# no proper location for the commit links is found.

use strict;
use warnings FATAL => 'all';

sub process_file
{
	my $file = shift;

	my $in_comment = 0;
	my $prev_line_ended_with_paren = 0;
	my $prev_leading_space = '';
	my $lineno = 0;

	my @hashes = ();

	my $tmpfile = $file . '.tmp';

	# Get major version number from the file name.
	$file =~ m/-(\d+)\./;
	my $major_version = $1;

	open(my $fh, '<', $file) || die "could not open file $file: $!\n";
	open(my $tfh, '>', $tmpfile) || die "could not open file $tmpfile: $!\n";

	while (<$fh>)
	{
		$lineno++;

		$in_comment = 1 if (m/^<!--/);

		# skip over commit links because we will add them below
		next
		  if (!$in_comment &&
			m{^\s*<ulink url="&commit_baseurl;[[:xdigit:]]+">&sect;</ulink>\s*$});

		if ($in_comment && m/\[([[:xdigit:]]+)\]/)
		{
			my $hash = $1;

			# major release item
			(!m/^Branch:/) && push(@hashes, $hash);

			# minor release item
			m/^Branch:/ &&
			  defined($major_version) &&
			  m/_${major_version}_/ &&
			  push(@hashes, $hash);
		}

		if (!$in_comment && m{</para>})
		{
			if (@hashes)
			{
				if ($prev_line_ended_with_paren)
				{
					for my $hash (@hashes)
					{
						print $tfh
						  "$prev_leading_space<ulink url=\"&commit_baseurl;$hash\">&sect;</ulink>\n";
					}
					@hashes = ();
				}
				else
				{
					print
					  "hashes found but no matching text found for placement on line $lineno\n";
					exit(1);
				}
			}
		}

		print $tfh $_;

		$prev_line_ended_with_paren = m/\)\s*$/;

		m/^(\s*)/;
		$prev_leading_space = $1;

		$in_comment = 0 if (m/^-->/);
	}

	close($fh);
	close($tfh);

	rename($tmpfile, $file) || die "could not rename %s to %s: $!\n",
	  $tmpfile,
	  $file;

	return;
}

if (@ARGV == 0)
{
	printf(STDERR "Usage: %s release_notes_file [...]\n", $0);
	exit(1);
}

for my $file (@ARGV)
{
	process_file($file);
}
