From 3e61f8b43b1c64bf0d27f508b2804f5080358a9b Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 13 Jul 2024 12:24:30 -0400
Subject: [PATCH v1 3/5] Install preprocessor infrastructure for
 tab-complete.c.

Rename tab-complete.c to tab-complete.in.c, create the preprocessor
script gen_tabcomplete.pl, and install Makefile/meson.build rules
to create tab-complete.c from tab-complete.in.c.

Although this is the real preprocessor script, it makes no
meaningful changes to tab-complete.in.c right now, for lack of
any source lines that would trigger changes.  (A boilerplate
header comment is added, but that's about it.)  So the result of
this step still compiles and passes tests.
---
 src/bin/psql/.gitignore                       |   1 +
 src/bin/psql/Makefile                         |   5 +-
 src/bin/psql/gen_tabcomplete.pl               | 179 ++++++++++++++++++
 src/bin/psql/meson.build                      |  15 +-
 .../{tab-complete.c => tab-complete.in.c}     |   5 +-
 5 files changed, 202 insertions(+), 3 deletions(-)
 create mode 100644 src/bin/psql/gen_tabcomplete.pl
 rename src/bin/psql/{tab-complete.c => tab-complete.in.c} (99%)

diff --git a/src/bin/psql/.gitignore b/src/bin/psql/.gitignore
index 10b6dd3a6b..7272f6e35d 100644
--- a/src/bin/psql/.gitignore
+++ b/src/bin/psql/.gitignore
@@ -1,4 +1,5 @@
 /psqlscanslash.c
+/tab-complete.c
 /sql_help.h
 /sql_help.c
 /psql
diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile
index 374c4c3ab8..62636d2663 100644
--- a/src/bin/psql/Makefile
+++ b/src/bin/psql/Makefile
@@ -62,6 +62,9 @@ psqlscanslash.c: FLEXFLAGS = -Cfe -p -p
 psqlscanslash.c: FLEX_NO_BACKUP=yes
 psqlscanslash.c: FLEX_FIX_WARNING=yes
 
+tab-complete.c: gen_tabcomplete.pl tab-complete.in.c
+	$(PERL) $^ --outfile $@
+
 install: all installdirs
 	$(INSTALL_PROGRAM) psql$(X) '$(DESTDIR)$(bindir)/psql$(X)'
 	$(INSTALL_DATA) $(srcdir)/psqlrc.sample '$(DESTDIR)$(datadir)/psqlrc.sample'
@@ -75,7 +78,7 @@ uninstall:
 clean distclean:
 	rm -f psql$(X) $(OBJS) lex.backup
 	rm -rf tmp_check
-	rm -f sql_help.h sql_help.c psqlscanslash.c
+	rm -f sql_help.h sql_help.c psqlscanslash.c tab-complete.c
 
 check:
 	$(prove_check)
diff --git a/src/bin/psql/gen_tabcomplete.pl b/src/bin/psql/gen_tabcomplete.pl
new file mode 100644
index 0000000000..571708a876
--- /dev/null
+++ b/src/bin/psql/gen_tabcomplete.pl
@@ -0,0 +1,179 @@
+#----------------------------------------------------------------------
+#
+# gen_tabcomplete.pl
+#	Perl script that transforms tab-complete.in.c to tab-complete.c.
+#
+# The input is a C file that contains some case labels that are not
+# constants, but look like function calls, for example:
+#	case Matches("A", "B"):
+# The function name can be any one of Matches, HeadMatches, TailMatches,
+# MatchesCS, HeadMatchesCS, or TailMatchesCS.  The argument(s) must be
+# string literals or macros that expand to string literals or NULL.
+# These lines are replaced by "case N:" where N is a unique number
+# for each such case label.  (For convenience, we use the line number
+# of the case label, although other values would work just as well.)
+#
+# In addition, there must be one input line that reads
+#	/* Insert tab-completion pattern data here. */
+# This line is replaced in the output file by macro calls, one for each
+# replaced case label.  The output for the above example would be
+#	TCPAT(N, Match, "A", "B"),
+# where N is the replacement case label, "Match" is the original function
+# name shorn of "es", and the rest are the function arguments.
+# The tab-completion data line must appear before any replaceable
+# case labels, and should appear before any executable code.
+#
+#
+# Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/bin/psql/gen_tabcomplete.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings FATAL => 'all';
+use Getopt::Long;
+
+my $outfile = '';
+
+GetOptions('outfile=s' => \$outfile) or die "$0: wrong arguments";
+
+open my $infh, '<', $ARGV[0]
+  or die "$0: could not open input file '$ARGV[0]': $!\n";
+
+my $outfh;
+if ($outfile)
+{
+	open $outfh, '>', $outfile
+	  or die "$0: could not open output file '$outfile': $!\n";
+}
+else
+{
+	$outfh = *STDOUT;
+}
+
+# Opening boilerplate for output file.
+printf $outfh <<EOM;
+/*-------------------------------------------------------------------------
+ *
+ * tab-complete.c
+ *    Preprocessed tab-completion code.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * NOTES
+ *  ******************************
+ *  *** DO NOT EDIT THIS FILE! ***
+ *  ******************************
+ *
+ *  It has been GENERATED by src/bin/psql/gen_tabcomplete.pl
+ *
+ *-------------------------------------------------------------------------
+ */
+
+EOM
+
+# Scan input file until we find the data-replacement label line.
+# Dump what we scan directly into the output file.
+while (<$infh>)
+{
+	chomp;
+	last if m|^\s*/\* Insert tab-completion pattern data here\. \*/\s*$|;
+	print $outfh "$_\n";
+}
+
+# Remember next input line number for later.
+my $post_data_line_no = $. + 1;
+
+# Scan input file, looking for replaceable case labels.
+# Add the generated table-contents macros to $table_data,
+# and add the replacement labels to $output_code.
+# Lines that aren't replaceable labels go to $output_code verbatim.
+my $table_data = '';
+my $output_code = '';
+# Data extracted from current case label:
+my $in_label = 0;
+my $head;
+my $cs;
+my $rest;
+my $lineno;
+my $nlines;
+
+while (<$infh>)
+{
+	chomp;
+	if ($in_label)
+	{
+		# Append multiple lines to current case label.
+		s/^\s+//;
+		$rest .= ' ' . $_;
+		$nlines++;
+		if ($rest =~ m/\)\s*:\s*$/)
+		{
+			process_case_label($head, $cs, $rest, $lineno, $nlines);
+			$in_label = 0;
+		}
+	}
+	elsif (m/^\s*case\s+(Head|Tail|)Matches(CS|)\((.*)$/)
+	{
+		$head = $1;
+		$cs = $2;
+		$rest = $3;
+		$lineno = $.;
+		$nlines = 1;
+		if ($rest =~ m/\)\s*:\s*$/)
+		{
+			process_case_label($head, $cs, $rest, $lineno, $nlines);
+		}
+		else
+		{
+			$in_label = 1;
+		}
+	}
+	else
+	{
+		$output_code .= $_ . "\n";
+	}
+}
+
+# Dump out the table data.
+print $outfh $table_data;
+
+# Adjust line numbering so that the rest of the file matches the
+# line numbering of the original, to simplify debugging.
+print $outfh "#line ${post_data_line_no} \"tab-complete.in.c\"\n";
+
+# Dump out the modified code, and we're done!
+print $outfh $output_code;
+
+close($infh);
+close($outfh);
+
+
+sub process_case_label
+{
+	my ($head, $cs, $rest, $lineno, $nlines) = @_;
+
+	# We already know $rest ends with "):".
+	$rest =~ s/\)\s*:\s*$//;
+
+	$table_data .= "\tTCPAT(${lineno}, ${head}Match${cs}, ${rest}),\n";
+
+	$output_code .= "\t\tcase $lineno:";
+
+	# Generate the same number of lines as in the input, so that line
+	# numbers stay in sync for debugging purposes.
+	$output_code .= "\n" x $nlines;
+}
+
+sub usage
+{
+	die <<EOM;
+Usage: gen_tabcomplete.pl [--outfile/-o <path>] input_file
+    --outfile       Output file (default is stdout)
+
+gen_tabcomplete.pl transforms tab-complete.in.c to tab-complete.c.
+EOM
+}
diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index f3a6392138..6ee6c6fd52 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -13,7 +13,6 @@ psql_sources = files(
   'prompt.c',
   'startup.c',
   'stringutils.c',
-  'tab-complete.c',
   'variables.c',
 )
 
@@ -24,6 +23,20 @@ psqlscanslash = custom_target('psqlscanslash',
 generated_sources += psqlscanslash
 psql_sources += psqlscanslash
 
+tabcomplete = custom_target('tabcomplete',
+  input: 'tab-complete.in.c',
+  output: 'tab-complete.c',
+  command: [
+    perl, files('gen_tabcomplete.pl'), files('tab-complete.in.c'),
+    '--outfile', '@OUTPUT@',
+    '@INPUT@',
+  ],
+  install: true,
+  install_dir: dir_include_server / 'utils',
+)
+generated_sources += tabcomplete
+psql_sources += tabcomplete
+
 sql_help = custom_target('psql_help',
   output: ['sql_help.c', 'sql_help.h'],
   depfile: 'sql_help.dep',
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.in.c
similarity index 99%
rename from src/bin/psql/tab-complete.c
rename to src/bin/psql/tab-complete.in.c
index b628cc3999..c92fc729be 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3,7 +3,10 @@
  *
  * Copyright (c) 2000-2024, PostgreSQL Global Development Group
  *
- * src/bin/psql/tab-complete.c
+ * src/bin/psql/tab-complete.in.c
+ *
+ * NOTE: this file must be passed through gen_tabcomplete.pl
+ * to produce compilable code.
  */
 
 /*----------------------------------------------------------------------
-- 
2.43.5

