diff options
Diffstat (limited to 'src/tools')
149 files changed, 21361 insertions, 0 deletions
diff --git a/src/tools/PerfectHash.pm b/src/tools/PerfectHash.pm new file mode 100644 index 0000000..e54905a --- /dev/null +++ b/src/tools/PerfectHash.pm @@ -0,0 +1,380 @@ +#---------------------------------------------------------------------- +# +# PerfectHash.pm +# Perl module that constructs minimal perfect hash functions +# +# This code constructs a minimal perfect hash function for the given +# set of keys, using an algorithm described in +# "An optimal algorithm for generating minimal perfect hash functions" +# by Czech, Havas and Majewski in Information Processing Letters, +# 43(5):256-264, October 1992. +# This implementation is loosely based on NetBSD's "nbperf", +# which was written by Joerg Sonnenberger. +# +# The resulting hash function is perfect in the sense that if the presented +# key is one of the original set, it will return the key's index in the set +# (in range 0..N-1). However, the caller must still verify the match, +# as false positives are possible. Also, the hash function may return +# values that are out of range (negative or >= N), due to summing unrelated +# hashtable entries. This indicates that the presented key is definitely +# not in the set. +# +# +# Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/tools/PerfectHash.pm +# +#---------------------------------------------------------------------- + +package PerfectHash; + +use strict; +use warnings; + + +# At runtime, we'll compute two simple hash functions of the input key, +# and use them to index into a mapping table. The hash functions are just +# multiply-and-add in uint32 arithmetic, with different multipliers and +# initial seeds. All the complexity in this module is concerned with +# selecting hash parameters that will work and building the mapping table. + +# We support making case-insensitive hash functions, though this only +# works for a strict-ASCII interpretation of case insensitivity, +# ie, A-Z maps onto a-z and nothing else. +my $case_fold = 0; + + +# +# Construct a C function implementing a perfect hash for the given keys. +# The C function definition is returned as a string. +# +# The keys should be passed as an array reference. They can be any set +# of Perl strings; it is caller's responsibility that there not be any +# duplicates. (Note that the "strings" can be binary data, but hashing +# e.g. OIDs has endianness hazards that callers must overcome.) +# +# The name to use for the function is specified as the second argument. +# It will be a global function by default, but the caller may prepend +# "static " to the result string if it wants a static function. +# +# Additional options can be specified as keyword-style arguments: +# +# case_fold => bool +# If specified as true, the hash function is case-insensitive, for the +# limited idea of case-insensitivity explained above. +# +# fixed_key_length => N +# If specified, all keys are assumed to have length N bytes, and the +# hash function signature will be just "int f(const void *key)" +# rather than "int f(const void *key, size_t keylen)". +# +sub generate_hash_function +{ + my ($keys_ref, $funcname, %options) = @_; + + # It's not worth passing this around as a parameter; just use a global. + $case_fold = $options{case_fold} || 0; + + # Try different hash function parameters until we find a set that works + # for these keys. The multipliers are chosen to be primes that are cheap + # to calculate via shift-and-add, so don't change them without care. + # (Commonly, random seeds are tried, but we want reproducible results + # from this program so we don't do that.) + my $hash_mult1 = 257; + my $hash_mult2; + my $hash_seed1; + my $hash_seed2; + my @subresult; + FIND_PARAMS: + for ($hash_seed1 = 0; $hash_seed1 < 10; $hash_seed1++) + { + + for ($hash_seed2 = 0; $hash_seed2 < 10; $hash_seed2++) + { + foreach (17, 31, 127, 8191) + { + $hash_mult2 = $_; # "foreach $hash_mult2" doesn't work + @subresult = _construct_hash_table( + $keys_ref, $hash_mult1, $hash_mult2, + $hash_seed1, $hash_seed2); + last FIND_PARAMS if @subresult; + } + } + } + + # Choke if we couldn't find a workable set of parameters. + die "failed to generate perfect hash" if !@subresult; + + # Extract info from _construct_hash_table's result array. + my $elemtype = $subresult[0]; + my @hashtab = @{ $subresult[1] }; + my $nhash = scalar(@hashtab); + + # OK, construct the hash function definition including the hash table. + my $f = ''; + $f .= sprintf "int\n"; + if (defined $options{fixed_key_length}) + { + $f .= sprintf "%s(const void *key)\n{\n", $funcname; + } + else + { + $f .= sprintf "%s(const void *key, size_t keylen)\n{\n", $funcname; + } + $f .= sprintf "\tstatic const %s h[%d] = {\n\t\t", $elemtype, $nhash; + for (my $i = 0; $i < $nhash; $i++) + { + # Hash element. + $f .= sprintf "%d", $hashtab[$i]; + next if ($i == $nhash - 1); + + # Optional indentation and newline, with eight items per line. + $f .= sprintf ",%s", + ($i % 8 == 7 ? "\n\t\t" : ' ' x (6 - length($hashtab[$i]))); + } + $f .= sprintf "\n" if ($nhash % 8 != 0); + $f .= sprintf "\t};\n\n"; + $f .= sprintf "\tconst unsigned char *k = (const unsigned char *) key;\n"; + $f .= sprintf "\tsize_t\t\tkeylen = %d;\n", $options{fixed_key_length} + if (defined $options{fixed_key_length}); + $f .= sprintf "\tuint32\t\ta = %d;\n", $hash_seed1; + $f .= sprintf "\tuint32\t\tb = %d;\n\n", $hash_seed2; + $f .= sprintf "\twhile (keylen--)\n\t{\n"; + $f .= sprintf "\t\tunsigned char c = *k++"; + $f .= sprintf " | 0x20" if $case_fold; # see comment below + $f .= sprintf ";\n\n"; + $f .= sprintf "\t\ta = a * %d + c;\n", $hash_mult1; + $f .= sprintf "\t\tb = b * %d + c;\n", $hash_mult2; + $f .= sprintf "\t}\n"; + $f .= sprintf "\treturn h[a %% %d] + h[b %% %d];\n", $nhash, $nhash; + $f .= sprintf "}\n"; + + return $f; +} + + +# Calculate a hash function as the run-time code will do. +# +# If we are making a case-insensitive hash function, we implement that +# by OR'ing 0x20 into each byte of the key. This correctly transforms +# upper-case ASCII into lower-case ASCII, while not changing digits or +# dollar signs. (It does change '_', as well as other characters not +# likely to appear in keywords; this has little effect on the hash's +# ability to discriminate keywords.) +sub _calc_hash +{ + my ($key, $mult, $seed) = @_; + + my $result = $seed; + for my $c (split //, $key) + { + my $cn = ord($c); + $cn |= 0x20 if $case_fold; + $result = ($result * $mult + $cn) % 4294967296; + } + return $result; +} + + +# Attempt to construct a mapping table for a minimal perfect hash function +# for the given keys, using the specified hash parameters. +# +# Returns an array containing the mapping table element type name as the +# first element, and a ref to an array of the table values as the second. +# +# Returns an empty array on failure; then caller should choose different +# hash parameter(s) and try again. +sub _construct_hash_table +{ + my ($keys_ref, $hash_mult1, $hash_mult2, $hash_seed1, $hash_seed2) = @_; + my @keys = @{$keys_ref}; + + # This algorithm is based on a graph whose edges correspond to the + # keys and whose vertices correspond to entries of the mapping table. + # A key's edge links the two vertices whose indexes are the outputs of + # the two hash functions for that key. For K keys, the mapping + # table must have at least 2*K+1 entries, guaranteeing that there's at + # least one unused entry. (In principle, larger mapping tables make it + # easier to find a workable hash and increase the number of inputs that + # can be rejected due to touching unused hashtable entries. In practice, + # neither effect seems strong enough to justify using a larger table.) + my $nedges = scalar @keys; # number of edges + my $nverts = 2 * $nedges + 1; # number of vertices + + # However, it would be very bad if $nverts were exactly equal to either + # $hash_mult1 or $hash_mult2: effectively, that hash function would be + # sensitive to only the last byte of each key. Cases where $nverts is a + # multiple of either multiplier likewise lose information. (But $nverts + # can't actually divide them, if they've been intelligently chosen as + # primes.) We can avoid such problems by adjusting the table size. + while ($nverts % $hash_mult1 == 0 + || $nverts % $hash_mult2 == 0) + { + $nverts++; + } + + # Initialize the array of edges. + my @E = (); + foreach my $kw (@keys) + { + # Calculate hashes for this key. + # The hashes are immediately reduced modulo the mapping table size. + my $hash1 = _calc_hash($kw, $hash_mult1, $hash_seed1) % $nverts; + my $hash2 = _calc_hash($kw, $hash_mult2, $hash_seed2) % $nverts; + + # If the two hashes are the same for any key, we have to fail + # since this edge would itself form a cycle in the graph. + return () if $hash1 == $hash2; + + # Add the edge for this key. + push @E, { left => $hash1, right => $hash2 }; + } + + # Initialize the array of vertices, giving them all empty lists + # of associated edges. (The lists will be hashes of edge numbers.) + my @V = (); + for (my $v = 0; $v < $nverts; $v++) + { + push @V, { edges => {} }; + } + + # Insert each edge in the lists of edges connected to its vertices. + for (my $e = 0; $e < $nedges; $e++) + { + my $v = $E[$e]{left}; + $V[$v]{edges}->{$e} = 1; + + $v = $E[$e]{right}; + $V[$v]{edges}->{$e} = 1; + } + + # Now we attempt to prove the graph acyclic. + # A cycle-free graph is either empty or has some vertex of degree 1. + # Removing the edge attached to that vertex doesn't change this property, + # so doing that repeatedly will reduce the size of the graph. + # If the graph is empty at the end of the process, it was acyclic. + # We track the order of edge removal so that the next phase can process + # them in reverse order of removal. + my @output_order = (); + + # Consider each vertex as a possible starting point for edge-removal. + for (my $startv = 0; $startv < $nverts; $startv++) + { + my $v = $startv; + + # If vertex v is of degree 1 (i.e. exactly 1 edge connects to it), + # remove that edge, and then consider the edge's other vertex to see + # if it is now of degree 1. The inner loop repeats until reaching a + # vertex not of degree 1. + while (scalar(keys(%{ $V[$v]{edges} })) == 1) + { + # Unlink its only edge. + my $e = (keys(%{ $V[$v]{edges} }))[0]; + delete($V[$v]{edges}->{$e}); + + # Unlink the edge from its other vertex, too. + my $v2 = $E[$e]{left}; + $v2 = $E[$e]{right} if ($v2 == $v); + delete($V[$v2]{edges}->{$e}); + + # Push e onto the front of the output-order list. + unshift @output_order, $e; + + # Consider v2 on next iteration of inner loop. + $v = $v2; + } + } + + # We succeeded only if all edges were removed from the graph. + return () if (scalar(@output_order) != $nedges); + + # OK, build the hash table of size $nverts. + my @hashtab = (0) x $nverts; + # We need a "visited" flag array in this step, too. + my @visited = (0) x $nverts; + + # The goal is that for any key, the sum of the hash table entries for + # its first and second hash values is the desired output (i.e., the key + # number). By assigning hash table values in the selected edge order, + # we can guarantee that that's true. This works because the edge first + # removed from the graph (and hence last to be visited here) must have + # at least one vertex it shared with no other edge; hence it will have at + # least one vertex (hashtable entry) still unvisited when we reach it here, + # and we can assign that unvisited entry a value that makes the sum come + # out as we wish. By induction, the same holds for all the other edges. + foreach my $e (@output_order) + { + my $l = $E[$e]{left}; + my $r = $E[$e]{right}; + if (!$visited[$l]) + { + # $hashtab[$r] might be zero, or some previously assigned value. + $hashtab[$l] = $e - $hashtab[$r]; + } + else + { + die "oops, doubly used hashtab entry" if $visited[$r]; + # $hashtab[$l] might be zero, or some previously assigned value. + $hashtab[$r] = $e - $hashtab[$l]; + } + # Now freeze both of these hashtab entries. + $visited[$l] = 1; + $visited[$r] = 1; + } + + # Detect range of values needed in hash table. + my $hmin = $nedges; + my $hmax = 0; + for (my $v = 0; $v < $nverts; $v++) + { + $hmin = $hashtab[$v] if $hashtab[$v] < $hmin; + $hmax = $hashtab[$v] if $hashtab[$v] > $hmax; + } + + # Choose width of hashtable entries. In addition to the actual values, + # we need to be able to store a flag for unused entries, and we wish to + # have the property that adding any other entry value to the flag gives + # an out-of-range result (>= $nedges). + my $elemtype; + my $unused_flag; + + if ( $hmin >= -0x7F + && $hmax <= 0x7F + && $hmin + 0x7F >= $nedges) + { + # int8 will work + $elemtype = 'int8'; + $unused_flag = 0x7F; + } + elsif ($hmin >= -0x7FFF + && $hmax <= 0x7FFF + && $hmin + 0x7FFF >= $nedges) + { + # int16 will work + $elemtype = 'int16'; + $unused_flag = 0x7FFF; + } + elsif ($hmin >= -0x7FFFFFFF + && $hmax <= 0x7FFFFFFF + && $hmin + 0x3FFFFFFF >= $nedges) + { + # int32 will work + $elemtype = 'int32'; + $unused_flag = 0x3FFFFFFF; + } + else + { + die "hash table values too wide"; + } + + # Set any unvisited hashtable entries to $unused_flag. + for (my $v = 0; $v < $nverts; $v++) + { + $hashtab[$v] = $unused_flag if !$visited[$v]; + } + + return ($elemtype, \@hashtab); +} + +1; diff --git a/src/tools/RELEASE_CHANGES b/src/tools/RELEASE_CHANGES new file mode 100644 index 0000000..73b02fa --- /dev/null +++ b/src/tools/RELEASE_CHANGES @@ -0,0 +1,252 @@ +For All Releases (major, minor, beta, RC) +================ + +* Release version number changes + o run src/tools/version_stamp.pl, then run autoconf + (by packager) (beta) + +* Release notes + o run git log and, if useful, src/tools/git_changelog + o update doc/src/sgml/release-NN.sgml in relevant branches + o run spellchecker on result + o add SGML markup + +* Update timezone data to match latest IANA timezone database and new + Windows releases, if any (see src/timezone/README) + +* Translation updates + 1. Check out the messages repository (of the right branch) from + <http://git.postgresql.org/git/pgtranslation/messages.git>. + 2. Check out the admin repository from + <http://git.postgresql.org/git/pgtranslation/admin.git>. + 3. From babel.postgresql.org, download the "qualified list" + for the respective branch. + 4. Run ".../admin/cp-po -L qualified-list-xxx.txt -g .../messages .../postgresql". + This creates a commit in the postgresql repository. + 5. Push everything. + + +For Major Releases +================== +(in addition to the above) + +Note that once the release branch has been forked off in git, +release-note editing happens in that branch, not in master. +Updates to the rest of the documentation usually need to happen +in both master and the branch. + +* Release notes + o use src/tools/git_changelog + o retain the relevant commits + o new features and options + o major performance improvements + o bug fixes for serious or common bugs + o incompatibilities and significant user-visible changes + o major source code changes + o update TODO list, http://wiki.postgresql.org/wiki/Todo + o verify items marked as completed are completed + o mark additional items as completed + o remove completed items + o group items into categories + o select incompatibilities + o add documentation links for items + o select major features + +* Documentation + o document all new features + o update help output from inside the programs + o doc/src/sgml/ref manual pages + +* Ports + o update ports list in doc/src/sgml/installation.sgml + + +Pre-Beta Tasks +============== + +These things should be done at least once per development cycle. +Typically we do them between feature freeze and start of beta test, +but there may be reasons to do them at other times as well. + +* Run mechanical code beautification tools: + pgindent, pgperltidy, and "make reformat-dat-files" + (complete steps from src/tools/pgindent/README) + +* Renumber any manually-assigned OIDs between 8000 and 9999 + to lower numbers, using renumber_oids.pl (see notes in bki.sgml) + +* Update config.guess and config.sub + (from https://savannah.gnu.org/projects/config) + +* Update Unicode data: Edit UNICODE_VERSION and CLDR_VERSION in + src/Makefile.global.in, run make update-unicode, and commit. + + +Starting a New Development Cycle +================================ + +* Typically, we do pgindent and perltidy runs just before branching, + as well as before beta (complete steps from src/tools/pgindent/README) + +* Create a branch in git for maintenance of the previous release + o on master branch, do: + git pull # be sure you have the latest "master" + git branch "new-branch-name" + git push -u origin "new-branch-name" + for example, + git branch REL_11_STABLE + git push -u origin REL_11_STABLE + +* Add new branch's name to list in src/tools/git_changelog + +* Increment the major version number in src/tools/version_stamp.pl + +* Run "src/tools/version_stamp.pl devel", then run autoconf + +* Create a new doc/src/sgml/release-NN.sgml file (initially just a + placeholder), "git rm" the previous one, and update release.sgml and + filelist.sgml to match. + +* In the newly-made branch, change src/backend/nodes/gen_node_support.pl + to enforce ABI stability of the NodeTag list (see "ARM ABI STABILITY + CHECK HERE" therein). + +* Notify the private committers email list, to ensure all committers + are aware of the new branch even if they're not paying close attention + to pgsql-hackers. + +* Get the buildfarm's 'branches_of_interest.txt' file updated with the new + branch. Once the buildfarm server is accepting reports, notify the + buildfarm owners' email list, for the benefit of owners who use manual + branch scheduling. + + +Creating Back-Branch Release Notes +================================== + +* Run src/tools/git_changelog to generate a list of relevant commits. + You can also run 'git log' in each branch. Be sure to use the --since + branch tag and not the release date, as commits could have been done + between branch stamping and the release date. + +* Remember to include any older branch commits not in the newest branch. + This can be accomplished by diff'ing the newest and older branch commit + logs and looking for lines that only appear in the older branch, e.g.: + + diff commit-N.N.log commit-O.O.log | grep '^>' + +* On the most recent release branch (*not* in master), edit and create SGML + markup for relevant changes in that branch's release-NN.sgml file. + Minor release notes should include more small change details because + testing is limited. + +* Copy this text into older branches' release-NN.sgml files, then remove + items that do not apply based on commit logs for that branch. + +* The minor release notes for the oldest active branch should always + include a warning about its impending EOL. Use the same boilerplate + text used in previous branches. + + +Retiring a Branch +================= + +* Notify the private committers email list, to ensure all committers + are aware of the branch being dead. + +* Get the buildfarm's 'branches_of_interest.txt' file updated to remove + the retired branch. Then notify the buildfarm owners' email list, + for the benefit of owners who use manual branch scheduling. + + + +--------------------------------------------------------------------------- + + Library Version Changes + ======================= + +Major Version +============= + +The major version number should be updated whenever the source of the +library changes to make it binary incompatible. Such changes include, +but are not limited to: + +1. Removing a public function or structure (or typedef, enum, ...) + +2. Modifying a public functions arguments. + +3. Removing a field from a public structure. + +4. Adding a field to a public structure, unless steps have been +previously taken to shield users from such a change, for example by +such structures only ever being allocated/instantiated by a library +function which would give the new field a suitable default value. + +Adding a new function should NOT force an increase in the major version +number. (Packagers will see the standard minor number update and install +the new library.) When the major version is increased all applications +which link to the library MUST be recompiled - this is not desirable. + +Minor Version +============= + +The minor version number should be updated whenever the functionality of +the library has changed, typically a change in source code between releases +would mean an increase in the minor version number so long as it does not +require a major version increase. + +Given that we make at least some changes to our libraries in every major +PostgreSQL version, we always bump all minor library version numbers in +each development cycle as a matter of policy. This is currently mechanized +by referencing the MAJORVERSION make macro in the value of SO_MINOR_VERSION +for each shared library. As of v10, SO_MINOR_VERSION is simply equal to +MAJORVERSION in all cases. If we ever make an incompatible break in a +library's API, forcing a major version bump, we could continue to increase +SO_MINOR_VERSION (thus, perhaps, going from libpq.so.5.12 to libpq.so.6.13), +or we could reset SO_MINOR_VERSION to zero, using makefile code along the +lines of + SO_MINOR_VERSION= $(shell expr $(MAJORVERSION) - 13) +so that the number continues to increase automatically in later branches. +For now, that complication is not necessary. + +Minimizing Changes +================== + +When modifying public functions arguments, steps should be taken to +maintain binary compatibility across minor PostgreSQL releases (e.g. the +7.2 series, the 7.3 series, the 7.4/8.0 series). Consider the following +function: + + void print_stuff(int arg1, int arg2) + { + printf("stuff: %d %d\n", arg1, arg2); + } + +If we wanted to add a third argument: + + void print_stuff(int arg1, int arg2, int arg3) + { + printf("stuff: %d %d %d\n", arg1, arg2, arg3); + } + +Then doing it like this: + + void print_stuff2(int arg1, int arg2, int arg3) + { + printf("stuff: %d %d %d\n", arg1, arg2, arg3); + } + + void print_stuff(int arg1, int arg2) + { + print_stuff2(arg1, arg2, 0); + } + +would maintain binary compatibility. Obviously this would add a fair +bit of cruft if used extensively, but considering the changes between +minor versions would probably be worthwhile to avoid bumping library +major version. Naturally in the next major version print_stuff() would +assume the functionality and arguments of print_stuff2(). + + +Lee Kindness diff --git a/src/tools/ccsym b/src/tools/ccsym new file mode 100755 index 0000000..972c843 --- /dev/null +++ b/src/tools/ccsym @@ -0,0 +1,4 @@ +#!/bin/sh + +# display gcc predefined preprocessor macros +gcc -dM -E - < /dev/null diff --git a/src/tools/check_bison_recursion.pl b/src/tools/check_bison_recursion.pl new file mode 100755 index 0000000..18f14ad --- /dev/null +++ b/src/tools/check_bison_recursion.pl @@ -0,0 +1,90 @@ +#! /usr/bin/perl + +################################################################# +# +# check_bison_recursion.pl -- check for right recursion in Bison grammars +# +# The standard way to parse list constructs in Bison grammars is via left +# recursion, wherein a nonterminal symbol has itself as the first symbol +# in one of its expansion rules. It is also possible to parse a list via +# right recursion, wherein a nonterminal symbol has itself as the last +# symbol of an expansion; but that's a bad way to write it because a long +# enough list will result in parser stack overflow. Since Bison doesn't +# have any built-in way to warn about use of right recursion, we use this +# script when we want to check for the problem. +# +# To use: run bison with the -v switch, then feed the produced y.output +# file to this script. +# +# Copyright (c) 2011-2023, PostgreSQL Global Development Group +# +# src/tools/check_bison_recursion.pl +################################################################# + +use strict; +use warnings; + +my $debug = 0; + +# must retain this across input lines +my $cur_nonterminal; + +# We parse the input and emit warnings on the fly. +my $in_grammar = 0; + +while (<>) +{ + my $rule_number; + my $rhs; + + # We only care about the "Grammar" part of the input. + if (m/^Grammar$/) + { + $in_grammar = 1; + } + elsif (m/^Terminal/) + { + $in_grammar = 0; + } + elsif ($in_grammar) + { + if (m/^\s*(\d+)\s+(\S+):\s+(.*)$/) + { + + # first rule for nonterminal + $rule_number = $1; + $cur_nonterminal = $2; + $rhs = $3; + } + elsif (m/^\s*(\d+)\s+\|\s+(.*)$/) + { + + # additional rule for nonterminal + $rule_number = $1; + $rhs = $2; + } + } + + # Process rule if we found one + if (defined $rule_number) + { + + # deconstruct the RHS + $rhs =~ s|^/\* empty \*/$||; + my @rhs = split '\s', $rhs; + print "Rule $rule_number: $cur_nonterminal := @rhs\n" if $debug; + + # We complain if the nonterminal appears as the last RHS element + # but not elsewhere, since "expr := expr + expr" is reasonable + my $lastrhs = pop @rhs; + if ( defined $lastrhs + && $cur_nonterminal eq $lastrhs + && !grep { $cur_nonterminal eq $_ } @rhs) + { + print + "Right recursion in rule $rule_number: $cur_nonterminal := $rhs\n"; + } + } +} + +exit 0; diff --git a/src/tools/ci/README b/src/tools/ci/README new file mode 100644 index 0000000..30ddd20 --- /dev/null +++ b/src/tools/ci/README @@ -0,0 +1,84 @@ +Postgres Continuous Integration (CI) +==================================== + +Postgres has two forms of CI: + +1) All supported branches in the main postgres repository are continuously + tested via the buildfarm. As this covers only the main repository, it + cannot be used during development of features. + + For details see https://buildfarm.postgresql.org/ + +2) For not yet merged development work, CI can be enabled for some git hosting + providers. This allows developers to test patches on a number of platforms + before they are merged (or even submitted). + + +Configuring CI on personal repositories +======================================= + +Currently postgres contains CI support utilizing cirrus-ci. cirrus-ci +currently is only available for github. + + +Enabling cirrus-ci in a github repository +========================================= + +To enable cirrus-ci on a repository, go to +https://github.com/marketplace/cirrus-ci and select "Public +Repositories". Then "Install it for free" and "Complete order". The next page +allows to configure which repositories cirrus-ci has access to. Choose the +relevant repository and "Install". + +See also https://cirrus-ci.org/guide/quick-start/ + +Once enabled on a repository, future commits and pull-requests in that +repository will automatically trigger CI builds. These are visible from the +commit history / PRs, and can also be viewed in the cirrus-ci UI at +https://cirrus-ci.com/github/<username>/<reponame>/ + +Hint: all build log files are uploaded to cirrus-ci and can be downloaded +from the "Artifacts" section from the cirrus-ci UI after clicking into a +specific task on a build's summary page. + + +Images used for CI +================== + +To keep CI times tolerable, most platforms use pre-generated images. Some +platforms use containers, others use full VMs. Images for both are generated +separately from CI runs, otherwise each git repository that is being tested +would need to build its own set of containers, which would be wasteful (both +in space and time. + +These images are built, on a daily basis, from the specifications in +github.com/anarazel/pg-vm-images/ + + +Controlling CI via commit messages +================================== + +The behavior of CI can be controlled by special content in commit +messages. Currently the following controls are available: + +- ci-os-only: {(freebsd|linux|macos|windows|mingw)} + + Only runs CI on operating systems specified. This can be useful when + addressing portability issues affecting only a subset of platforms. + + +Using custom compute resources for CI +===================================== + +When running a lot of tests in a repository, cirrus-ci's free credits do not +suffice. In those cases a repository can be configured to use other +infrastructure for running tests. To do so, the REPO_CI_CONFIG_GIT_URL +variable can be configured for the repository in the cirrus-ci web interface, +at https://cirrus-ci.com/github/<user or organization>. The file referenced +(see https://cirrus-ci.org/guide/programming-tasks/#fs) by the variable can +overwrite the default execution method for different operating systems, +defined in .cirrus.yml, by redefining the relevant yaml anchors. + +Custom compute resources can be provided using +- https://cirrus-ci.org/guide/supported-computing-services/ +- https://cirrus-ci.org/guide/persistent-workers/ diff --git a/src/tools/ci/ci_macports_packages.sh b/src/tools/ci/ci_macports_packages.sh new file mode 100755 index 0000000..4bc594a --- /dev/null +++ b/src/tools/ci/ci_macports_packages.sh @@ -0,0 +1,97 @@ +#!/bin/sh + +# Installs the passed in packages via macports. To make it fast enough +# for CI, cache the installation as a .dmg file. To avoid +# unnecessarily updating the cache, the cached image is only modified +# when packages are installed or removed. Any package this script is +# not instructed to install, will be removed again. +# +# This currently expects to be run in a macos cirrus-ci environment. + +set -e +# set -x + +packages="$@" + +macports_url="https://github.com/macports/macports-base/releases/download/v2.8.1/MacPorts-2.8.1-13-Ventura.pkg" +cache_dmg="macports.hfs.dmg" + +if [ "$CIRRUS_CI" != "true" ]; then + echo "expect to be called within cirrus-ci" 1>2 + exit 1 +fi + +sudo mkdir -p /opt/local +mkdir -p ${MACPORTS_CACHE}/ + +# If we are starting from clean cache, perform a fresh macports +# install. Otherwise decompress the .dmg we created previously. +# +# After this we have a working macports installation, with an unknown set of +# packages installed. +new_install=0 +update_cached_image=0 +if [ -e ${MACPORTS_CACHE}/${cache_dmg}.zstd ]; then + time zstd -T0 -d ${MACPORTS_CACHE}/${cache_dmg}.zstd -o ${cache_dmg} + time sudo hdiutil attach -kernel ${cache_dmg} -owners on -shadow ${cache_dmg}.shadow -mountpoint /opt/local +else + new_install=1 + curl -fsSL -o macports.pkg "$macports_url" + time sudo installer -pkg macports.pkg -target / + # this is a throwaway environment, and it'd be a few lines to gin + # up a correct user / group when using the cache. + echo macportsuser root | sudo tee -a /opt/local/etc/macports/macports.conf +fi +export PATH=/opt/local/sbin/:/opt/local/bin/:$PATH + +# mark all installed packages unrequested, that allows us to detect +# packages that aren't needed anymore +if [ -n "$(port -q installed installed)" ] ; then + sudo port unsetrequested installed +fi + +# if setting all the required packages as requested fails, we need +# to install at least one of them +if ! sudo port setrequested $packages > /dev/null 2>&1 ; then + echo not all required packages installed, doing so now + update_cached_image=1 + # to keep the image small, we deleted the ports tree from the image... + sudo port selfupdate + # XXX likely we'll need some other way to force an upgrade at some + # point... + sudo port upgrade outdated + sudo port install -N $packages + sudo port setrequested $packages +fi + +# check if any ports should be uninstalled +if [ -n "$(port -q installed rleaves)" ] ; then + echo superflous packages installed + update_cached_image=1 + sudo port uninstall --follow-dependencies rleaves + + # remove prior cache contents, don't want to increase size + rm -f ${MACPORTS_CACHE}/* +fi + +# Shrink installation if we created / modified it +if [ "$new_install" -eq 1 -o "$update_cached_image" -eq 1 ]; then + sudo /opt/local/bin/port clean --all installed + sudo rm -rf /opt/local/var/macports/{software,sources}/* +fi + +# If we're starting from a clean cache, start a new image. If we have +# an image, but the contents changed, update the image in the cache +# location. +if [ "$new_install" -eq 1 ]; then + # use a generous size, so additional software can be installed later + time sudo hdiutil create -fs HFS+ -format UDRO -size 10g -layout NONE -srcfolder /opt/local/ ${cache_dmg} + time zstd -T -10 -z ${cache_dmg} -o ${MACPORTS_CACHE}/${cache_dmg}.zstd +elif [ "$update_cached_image" -eq 1 ]; then + sudo hdiutil detach /opt/local/ + time hdiutil convert -format UDRO ${cache_dmg} -shadow ${cache_dmg}.shadow -o updated.hfs.dmg + rm ${cache_dmg}.shadow + mv updated.hfs.dmg ${cache_dmg} + time zstd --force -T -10 -z ${cache_dmg} -o ${MACPORTS_CACHE}/${cache_dmg}.zstd + time sudo hdiutil attach -kernel ${cache_dmg} -owners on -shadow ${cache_dmg}.shadow -mountpoint /opt/local +fi diff --git a/src/tools/ci/cores_backtrace.sh b/src/tools/ci/cores_backtrace.sh new file mode 100755 index 0000000..28d3cec --- /dev/null +++ b/src/tools/ci/cores_backtrace.sh @@ -0,0 +1,50 @@ +#! /bin/sh + +if [ $# -ne 2 ]; then + echo "cores_backtrace.sh <os> <directory>" + exit 1 +fi + +os=$1 +directory=$2 + +case $os in + freebsd|linux|macos) + ;; + *) + echo "unsupported operating system ${os}" + exit 1 + ;; +esac + +first=1 +for corefile in $(find "$directory" -type f) ; do + if [ "$first" -eq 1 ]; then + first=0 + else + # to make it easier to separate the different crash reports + echo -e '\n\n' + fi + + if [ "$os" = 'macos' ]; then + lldb -c $corefile --batch -o 'thread backtrace all' -o 'quit' + else + auxv=$(gdb --quiet --core ${corefile} --batch -ex 'info auxv' 2>/dev/null) + if [ $? -ne 0 ]; then + echo "could not process ${corefile}" + continue + fi + + if [ "$os" = 'freebsd' ]; then + binary=$(echo "$auxv" | grep AT_EXECPATH | perl -pe "s/^.*\"(.*)\"\$/\$1/g") + elif [ "$os" = 'linux' ]; then + binary=$(echo "$auxv" | grep AT_EXECFN | perl -pe "s/^.*\"(.*)\"\$/\$1/g") + else + echo 'should not get here' + exit 1 + fi + + echo "dumping ${corefile} for ${binary}" + gdb --batch --quiet -ex "thread apply all bt full" -ex "quit" "$binary" "$corefile" 2>/dev/null + fi +done diff --git a/src/tools/ci/gcp_freebsd_repartition.sh b/src/tools/ci/gcp_freebsd_repartition.sh new file mode 100755 index 0000000..2d5e173 --- /dev/null +++ b/src/tools/ci/gcp_freebsd_repartition.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +set -e +set -x + +# The default filesystem on freebsd gcp images is very slow to run tests on, +# due to its 32KB block size +# +# XXX: It'd probably better to fix this in the image, using something like +# https://people.freebsd.org/~lidl/blog/re-root.html + +# fix backup partition table after resize +gpart recover da0 +gpart show da0 +# kill swap, so we can delete a partition +swapoff -a || true +# (apparently we can only have 4!?) +gpart delete -i 3 da0 +gpart add -t freebsd-ufs -l data8k -a 4096 da0 +gpart show da0 +newfs -U -b 8192 /dev/da0p3 + +# Migrate working directory +du -hs $CIRRUS_WORKING_DIR +mv $CIRRUS_WORKING_DIR $CIRRUS_WORKING_DIR.orig +mkdir $CIRRUS_WORKING_DIR +mount -o noatime /dev/da0p3 $CIRRUS_WORKING_DIR +cp -r $CIRRUS_WORKING_DIR.orig/* $CIRRUS_WORKING_DIR/ diff --git a/src/tools/ci/pg_ci_base.conf b/src/tools/ci/pg_ci_base.conf new file mode 100644 index 0000000..d8faa9c --- /dev/null +++ b/src/tools/ci/pg_ci_base.conf @@ -0,0 +1,14 @@ +# Tends to produce too many core files, taking a long time +restart_after_crash = false + +# So that tests using the "manually" started postgres on windows can use +# prepared statements +max_prepared_transactions = 10 + +# Settings that make logs more useful +log_autovacuum_min_duration = 0 +log_checkpoints = true +log_connections = true +log_disconnections = true +log_line_prefix = '%m [%p][%b] %q[%a][%v:%x] ' +log_lock_waits = true diff --git a/src/tools/ci/windows_build_config.pl b/src/tools/ci/windows_build_config.pl new file mode 100644 index 0000000..b0d4360 --- /dev/null +++ b/src/tools/ci/windows_build_config.pl @@ -0,0 +1,13 @@ +use strict; +use warnings; + +our $config; + +$config->{"tap_tests"} = 1; +$config->{"asserts"} = 1; + +$config->{"openssl"} = "c:/openssl/1.1/"; +$config->{"perl"} = "c:/strawberry/$ENV{DEFAULT_PERL_VERSION}/perl/"; +$config->{"python"} = "c:/python/"; + +1; diff --git a/src/tools/codelines b/src/tools/codelines new file mode 100755 index 0000000..11e86ac --- /dev/null +++ b/src/tools/codelines @@ -0,0 +1,7 @@ +#!/bin/sh + +# src/tools/codelines + +# This script is used to compute the total number of "C" lines in the release +# This should be run from the top of the Git tree after a 'make distclean' +find . -name '*.[chyl]' | xargs cat| wc -l diff --git a/src/tools/copyright.pl b/src/tools/copyright.pl new file mode 100755 index 0000000..30c38c7 --- /dev/null +++ b/src/tools/copyright.pl @@ -0,0 +1,75 @@ +#!/usr/bin/perl +################################################################# +# copyright.pl -- update copyright notices throughout the source tree, idempotently. +# +# Copyright (c) 2011-2023, PostgreSQL Global Development Group +# +# src/tools/copyright.pl +# +# FYI, Tie adds a trailing newline on the last line if missing. +################################################################# + +use strict; +use warnings; + +use File::Find; +use File::Basename; +use Tie::File; + +my $pgdg = 'PostgreSQL Global Development Group'; +my $cc = 'Copyright \(c\)'; +my $ccliteral = 'Copyright (c)'; + +# year-1900 is what localtime(time) puts in element 5 +my $year = 1900 + ${ [ localtime(time) ] }[5]; + +print "Using current year: $year\n"; + +find({ wanted => \&wanted, no_chdir => 1 }, '.'); + +sub wanted +{ + + # prevent corruption of git indexes by ignoring any .git/ + if (basename($_) eq '.git') + { + $File::Find::prune = 1; + return; + } + + return if !-f $File::Find::name || -l $File::Find::name; + + # skip file names with binary extensions + # How are these updated? bjm 2012-01-02 + return if ($_ =~ m/\.(ico|bin|po|key)$/); + + my @lines; + tie @lines, "Tie::File", $File::Find::name; + + # We process all lines because some files have copyright + # strings embedded in them, e.g. src/bin/psql/help.c + foreach my $line (@lines) + { + + # We only care about lines with a copyright notice. + next unless $line =~ m/$cc.*$pgdg/i; + + # Skip line if it already matches the current year; if not + # we get $year-$year, e.g. 2012-2012. + next if $line =~ m/$cc $year, $pgdg/i; + + # Skip already-updated lines too, to avoid unnecessary + # file updates. + next if $line =~ m/$cc \d{4}-$year, $pgdg/i; + + # Apply the update, relying on Tie::File to write the file. + $line =~ s/$cc (\d{4})-\d{4}, $pgdg/$ccliteral $1-$year, $pgdg/i; + $line =~ s/$cc (\d{4}), $pgdg/$ccliteral $1-$year, $pgdg/i; + } + untie @lines; + return; +} + +print "Manually update:\n"; +print " ./doc/src/sgml/legal.sgml in head and back branches\n"; +print " ./COPYRIGHT in back branches\n"; diff --git a/src/tools/darwin_sysroot b/src/tools/darwin_sysroot new file mode 100755 index 0000000..1cdf812 --- /dev/null +++ b/src/tools/darwin_sysroot @@ -0,0 +1,41 @@ +#!/bin/sh +# +# Select where system include files should be sought. If the user specified a +# sysroot, validate it. +# +# A separate script so it can be shared between autoconf and meson. + +PG_SYSROOT=$1 + +if test x"$PG_SYSROOT" = x"" ; then + # This is far more complicated than it ought to be. We first ask + # "xcrun --show-sdk-path", which seems to match the default -isysroot + # setting of Apple's compilers. + PG_SYSROOT=`xcrun --show-sdk-path 2>/dev/null` + # That may fail, or produce a result that is not version-specific (i.e., + # just ".../SDKs/MacOSX.sdk"). Using a version-specific sysroot seems + # desirable, so if the path is a non-version-specific symlink, expand it. + if test -L "$PG_SYSROOT"; then + if expr x"$PG_SYSROOT" : '.*[0-9]\.[0-9][^/]*$' >/dev/null ; then : okay + else + PG_SYSROOT=`expr "$PG_SYSROOT" : '\(.*\)/'`/`readlink "$PG_SYSROOT"` + fi + fi + # If there are still not digits in the directory name, try + # "xcrun --sdk macosx --show-sdk-path"; and if that still doesn't work, + # fall back to asking xcodebuild, which is often a good deal slower. + if expr x"$PG_SYSROOT" : '.*[0-9]\.[0-9][^/]*$' >/dev/null ; then : okay + else + PG_SYSROOT=`xcrun --sdk macosx --show-sdk-path 2>/dev/null` + if expr x"$PG_SYSROOT" : '.*[0-9]\.[0-9][^/]*$' >/dev/null ; then : okay + else + PG_SYSROOT=`xcodebuild -version -sdk macosx Path 2>/dev/null` + fi + fi +fi +# Validate the result: if it doesn't point at a directory, ignore it. +if test x"$PG_SYSROOT" != x"" ; then + if test -d "$PG_SYSROOT" ; then + echo $PG_SYSROOT + fi +fi diff --git a/src/tools/editors/emacs.samples b/src/tools/editors/emacs.samples new file mode 100644 index 0000000..529c98a --- /dev/null +++ b/src/tools/editors/emacs.samples @@ -0,0 +1,91 @@ +;; -*- mode: emacs-lisp -*- + +;; This file contains code to set up Emacs to edit PostgreSQL source +;; code. Copy these snippets into your .emacs file or equivalent, or +;; use load-file to load this file directly. +;; +;; Note also that there is a .dir-locals.el file at the top of the +;; PostgreSQL source tree, which contains many of the settings shown +;; here (but not all, mainly because not all settings are allowed as +;; local variables). So for light editing, you might not need any +;; additional Emacs configuration. + + +;;; C files + +;; Style that matches the formatting used by +;; src/tools/pgindent/pgindent. Many extension projects also use this +;; style. +(c-add-style "postgresql" + '("bsd" + (c-auto-align-backslashes . nil) + (c-basic-offset . 4) + (c-offsets-alist . ((case-label . +) + (label . -) + (statement-case-open . +))) + (fill-column . 78) + (indent-tabs-mode . t) + (tab-width . 4))) + +(add-hook 'c-mode-hook + (defun postgresql-c-mode-hook () + (when (string-match "/postgres\\(ql\\)?/" buffer-file-name) + (c-set-style "postgresql") + ;; Don't override the style we just set with the style in + ;; `dir-locals-file'. Emacs 23.4.1 needs this; it is obsolete, + ;; albeit harmless, by Emacs 24.3.1. + (set (make-local-variable 'ignored-local-variables) + (append '(c-file-style) ignored-local-variables))))) + + +;;; Perl files + +;; Style that matches the formatting used by +;; src/tools/pgindent/perltidyrc. +(defun pgsql-perl-style () + "Perl style adjusted for PostgreSQL project" + (interactive) + (setq perl-brace-imaginary-offset 0) + (setq perl-brace-offset 0) + (setq perl-continued-statement-offset 2) + (setq perl-continued-brace-offset (- perl-continued-statement-offset)) + (setq perl-indent-level 4) + (setq perl-label-offset -2) + ;; Next two aren't marked safe-local-variable, so .dir-locals.el omits them. + (setq perl-indent-continued-arguments 4) + (setq perl-indent-parens-as-block t) + (setq indent-tabs-mode t) + (setq tab-width 4)) + +(add-hook 'perl-mode-hook + (defun postgresql-perl-mode-hook () + (when (string-match "/postgres\\(ql\\)?/" buffer-file-name) + (pgsql-perl-style)))) + + +;;; documentation files + +;; *.sgml files are actually XML +(add-to-list 'auto-mode-alist '("/postgres\\(ql\\)?/.*\\.sgml\\'" . nxml-mode)) + +(add-hook 'nxml-mode-hook + (defun postgresql-xml-mode-hook () + (when (string-match "/postgres\\(ql\\)?/" buffer-file-name) + (setq fill-column 78) + (setq indent-tabs-mode nil)))) + +;; The *.xsl files use 2-space indent, which is consistent with +;; docbook-xsl sources and also the nxml-mode default. But the *.sgml +;; files use 1-space indent, mostly for historical reasons at this +;; point. +(add-hook 'nxml-mode-hook + (defun postgresql-xml-src-mode-hook () + (when (string-match "/postgres\\(ql\\)?/.*\\.sgml\\'" buffer-file-name) + (setq nxml-child-indent 1)))) + + +;;; Makefiles + +;; use GNU make mode instead of plain make mode +(add-to-list 'auto-mode-alist '("/postgres\\(ql\\)?/.*Makefile.*" . makefile-gmake-mode)) +(add-to-list 'auto-mode-alist '("/postgres\\(ql\\)?/.*\\.mk\\'" . makefile-gmake-mode)) diff --git a/src/tools/editors/vim.samples b/src/tools/editors/vim.samples new file mode 100644 index 0000000..ccbc93f --- /dev/null +++ b/src/tools/editors/vim.samples @@ -0,0 +1,17 @@ +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" These settings are appropriate for editing PostgreSQL code with vim +" +" You would copy this into your .vimrc or equivalent +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +:if match(getcwd(), "/pgsql") >=0 || match(getcwd(), "/postgresql") >= 0 + +: set cinoptions=(0 +: set tabstop=4 +: set shiftwidth=4 + +:endif + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" diff --git a/src/tools/find_badmacros b/src/tools/find_badmacros new file mode 100755 index 0000000..58f4308 --- /dev/null +++ b/src/tools/find_badmacros @@ -0,0 +1,22 @@ +#!/bin/sh + +# This script attempts to find bad ifdef's, i.e. ifdef's that use braces +# but not the do { ... } while (0) syntax +# +# src/tools/find_badmacros +# +# This is useful for running before pgindent + +for FILE +do + awk ' BEGIN {was_define = "N"} + { if (was_define == "Y" && + $0 ~ /^{/) + printf "%s %d\n", FILENAME, NR + if ($0 ~ /^#define/) + was_define = "Y" + else + was_define = "N" + }' "$FILE" + grep -on '^#define.*{' "$FILE" | grep -v 'do[ ]*{' +done diff --git a/src/tools/find_meson b/src/tools/find_meson new file mode 100755 index 0000000..50e501a --- /dev/null +++ b/src/tools/find_meson @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# +# Returns the path to the meson binary, for cases where we need to call it as +# part of the build, e.g. to install into tmp_install/ for the tests. + +import os +import shlex +import sys + +to_print = [] + +if 'MUON_PATH' in os.environ: + to_print += ['muon', os.environ['MUON_PATH']] +else: + mesonintrospect = os.environ['MESONINTROSPECT'] + components = shlex.split(mesonintrospect) + + if len(components) < 2: + print('expected more than two components, got: %s' % components) + sys.exit(1) + + if components[-1] != 'introspect': + print('expected introspection at the end') + sys.exit(1) + + to_print += ['meson'] + components[:-1] + +print('\n'.join(to_print), end='') + +sys.exit(0) diff --git a/src/tools/find_static b/src/tools/find_static new file mode 100755 index 0000000..b759302 --- /dev/null +++ b/src/tools/find_static @@ -0,0 +1,50 @@ +#!/bin/sh + +# src/tools/find_static + +trap "ret=$?; rm -rf /tmp/$$; exit $ret" 0 1 2 3 15 + +# This script finds functions that are either never called, or +# should be static. +# Some functions, like library functions and debug_print functions, +# should remain unchanged. + +# Run on a compiled source tree, from the top of the source tree + +# My nm utility has 9 characters of address which I strip, then a 'type' +# character, with T as a text function, and U as an undefined function +# symbol, then the function name. + +find . -name '[a-z]*.o' -type f -print | while read FILE +do nm $FILE | cut -c17-100 |awk '{printf "%s\t%s\t%s\n", "'"$FILE"'",$1,$2}' +done >/tmp/$$ +dropdb debug +createdb debug +echo " + create table debug (file text, scope char, func text); + + copy debug from '/tmp/"$$"'; + + select * + into table debug2 + from debug; + + create index idebug on debug(scope,func); + create index idebug2 on debug2(func,scope); + vacuum debug; + vacuum debug2; + + update debug2 + set scope = '_' + from debug + where debug2.func = debug.func and + debug2.scope = 'T' and debug.scope = 'U'; + + delete from debug2 + where scope = '_'; + + select * + from debug2 + where scope = 'T' and func != 'main' + order by file, func; +" |psql -X debug diff --git a/src/tools/find_typedef b/src/tools/find_typedef new file mode 100755 index 0000000..24e9b76 --- /dev/null +++ b/src/tools/find_typedef @@ -0,0 +1,53 @@ +#!/bin/sh + +# src/tools/find_typedef + +# This script attempts to find all typedef's in the postgres binaries +# by using 'objdump' or local equivalent to print typedef debugging symbols. +# We need this because pgindent needs a list of typedef names. +# +# For this program to work, you must have compiled all code with +# debugging symbols. +# +# We intentionally examine all files in the targeted directories so as to +# find both .o files and executables. Therefore, ignore error messages about +# unsuitable files being fed to objdump. +# +# This is known to work on Linux and on some BSDen, including macOS. +# +# Caution: on the platforms we use, this only prints typedefs that are used +# to declare at least one variable or struct field. If you have say +# "typedef struct foo { ... } foo;", and then the structure is only ever +# referenced as "struct foo", "foo" will not be reported as a typedef, +# causing pgindent to indent the typedef definition oddly. This is not a +# huge problem, since by definition there's just the one misindented line. +# +# We get typedefs by reading "STABS": +# http://www.informatik.uni-frankfurt.de/doc/texi/stabs_toc.html + + +if [ "$#" -eq 0 -o ! -d "$1" ] +then echo "Usage: $0 postgres_binary_directory [...]" 1>&2 + exit 1 +fi + +for DIR +do # if objdump -W is recognized, only one line of error should appear + if [ `objdump -W 2>&1 | wc -l` -eq 1 ] + then # Linux + objdump -W "$DIR"/* | + egrep -A3 '\(DW_TAG_typedef\)' | + awk ' $2 == "DW_AT_name" {print $NF}' + elif [ `readelf -w 2>&1 | wc -l` -gt 1 ] + then # FreeBSD, similar output to Linux + readelf -w "$DIR"/* | + egrep -A3 '\(DW_TAG_typedef\)' | + awk ' $1 == "DW_AT_name" {print $NF}' + fi +done | +grep -v ' ' | # some typedefs have spaces, remove them +sort | +uniq | +# these are used both for typedefs and variable names +# so do not include them +egrep -v '^(date|interval|timestamp|ANY)$' diff --git a/src/tools/fix-old-flex-code.pl b/src/tools/fix-old-flex-code.pl new file mode 100644 index 0000000..d88e68b --- /dev/null +++ b/src/tools/fix-old-flex-code.pl @@ -0,0 +1,66 @@ +#!/usr/bin/perl +#---------------------------------------------------------------------- +# +# fix-old-flex-code.pl +# +# flex versions before 2.5.36, with certain option combinations, produce +# code that causes an "unused variable" warning. That's annoying, so +# let's suppress it by inserting a dummy reference to the variable. +# (That's exactly what 2.5.36 and later do ...) +# +# Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/tools/fix-old-flex-code.pl +# +#---------------------------------------------------------------------- + +use strict; +use warnings; + +# Get command line argument. +usage() if $#ARGV != 0; +my $filename = shift; + +# Suck in the whole file. +local $/ = undef; +my $cfile; +open($cfile, '<', $filename) || die "opening $filename for reading: $!"; +my $ccode = <$cfile>; +close($cfile); + +# No need to do anything if it's not flex 2.5.x for x < 36. +exit 0 if $ccode !~ m/^#define YY_FLEX_MAJOR_VERSION 2$/m; +exit 0 if $ccode !~ m/^#define YY_FLEX_MINOR_VERSION 5$/m; +exit 0 if $ccode !~ m/^#define YY_FLEX_SUBMINOR_VERSION (\d+)$/m; +exit 0 if $1 >= 36; + +# Apply the desired patch. +$ccode =~ + s|(struct yyguts_t \* yyg = \(struct yyguts_t\*\)yyscanner; /\* This var may be unused depending upon options. \*/ +.*?) + return yy_is_jam \? 0 : yy_current_state; +|$1 + (void) yyg; + return yy_is_jam ? 0 : yy_current_state; +|s; + +# Write the modified file back out. +open($cfile, '>', $filename) || die "opening $filename for writing: $!"; +print $cfile $ccode; +close($cfile); + +exit 0; + + +sub usage +{ + die <<EOM; +Usage: fix-old-flex-code.pl c-file-name + +fix-old-flex-code.pl modifies a flex output file to suppress +an unused-variable warning that occurs with older flex versions. + +Report bugs to <pgsql-bugs\@lists.postgresql.org>. +EOM +} diff --git a/src/tools/gen_export.pl b/src/tools/gen_export.pl new file mode 100644 index 0000000..ed60abe --- /dev/null +++ b/src/tools/gen_export.pl @@ -0,0 +1,84 @@ +use strict; +use warnings; +use Getopt::Long; + +my $format; +my $libname; +my $input; +my $output; + +GetOptions( + 'format:s' => \$format, + 'libname:s' => \$libname, + 'input:s' => \$input, + 'output:s' => \$output) or die "wrong arguments"; + +if (not( $format eq 'aix' + or $format eq 'darwin' + or $format eq 'gnu' + or $format eq 'win')) +{ + die "$0: $format is not yet handled (only aix, darwin, gnu, win are)\n"; +} + +open(my $input_handle, '<', $input) + or die "$0: could not open input file '$input': $!\n"; + +open(my $output_handle, '>', $output) + or die "$0: could not open output file '$output': $!\n"; + + +if ($format eq 'gnu') +{ + print $output_handle "{ + global: +"; +} +elsif ($format eq 'win') +{ + # XXX: Looks like specifying LIBRARY $libname is optional, which makes it + # easier to build a generic command for generating export files... + if ($libname) + { + print $output_handle "LIBRARY $libname\n"; + } + print $output_handle "EXPORTS\n"; +} + +while (<$input_handle>) +{ + if (/^#/) + { + # don't do anything with a comment + } + elsif (/^(\S+)\s+(\S+)/) + { + if ($format eq 'aix') + { + print $output_handle "$1\n"; + } + elsif ($format eq 'darwin') + { + print $output_handle "_$1\n"; + } + elsif ($format eq 'gnu') + { + print $output_handle " $1;\n"; + } + elsif ($format eq 'win') + { + print $output_handle "$1 @ $2\n"; + } + } + else + { + die "$0: unexpected line $_\n"; + } +} + +if ($format eq 'gnu') +{ + print $output_handle " local: *; +}; +"; +} diff --git a/src/tools/gen_keywordlist.pl b/src/tools/gen_keywordlist.pl new file mode 100644 index 0000000..97a9ff1 --- /dev/null +++ b/src/tools/gen_keywordlist.pl @@ -0,0 +1,197 @@ +#---------------------------------------------------------------------- +# +# gen_keywordlist.pl +# Perl script that transforms a list of keywords into a ScanKeywordList +# data structure that can be passed to ScanKeywordLookup(). +# +# The input is a C header file containing a series of macro calls +# PG_KEYWORD("keyword", ...) +# Lines not starting with PG_KEYWORD are ignored. The keywords are +# implicitly numbered 0..N-1 in order of appearance in the header file. +# Currently, the keywords are required to appear in ASCII order. +# +# The output is a C header file that defines a "const ScanKeywordList" +# variable named according to the -v switch ("ScanKeywords" by default). +# The variable is marked "static" unless the -e switch is given. +# +# ScanKeywordList uses hash-based lookup, so this script also selects +# a minimal perfect hash function for the keyword set, and emits a +# static hash function that is referenced in the ScanKeywordList struct. +# The hash function is case-insensitive unless --no-case-fold is specified. +# Note that case folding works correctly only for all-ASCII keywords! +# +# +# Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/tools/gen_keywordlist.pl +# +#---------------------------------------------------------------------- + +use strict; +use warnings; +use Getopt::Long; + +use FindBin; +use lib $FindBin::RealBin; + +use PerfectHash; + +my $output_path = ''; +my $extern = 0; +my $case_fold = 1; +my $varname = 'ScanKeywords'; + +GetOptions( + 'output:s' => \$output_path, + 'extern' => \$extern, + 'case-fold!' => \$case_fold, + 'varname:s' => \$varname) || usage(); + +my $kw_input_file = shift @ARGV || die "No input file.\n"; + +# Make sure output_path ends in a slash if needed. +if ($output_path ne '' && substr($output_path, -1) ne '/') +{ + $output_path .= '/'; +} + +$kw_input_file =~ /(\w+)\.h$/ + || die "Input file must be named something.h.\n"; +my $base_filename = $1 . '_d'; +my $kw_def_file = $output_path . $base_filename . '.h'; + +open(my $kif, '<', $kw_input_file) || die "$kw_input_file: $!\n"; +open(my $kwdef, '>', $kw_def_file) || die "$kw_def_file: $!\n"; + +# Opening boilerplate for keyword definition header. +printf $kwdef <<EOM, $base_filename, uc $base_filename, uc $base_filename; +/*------------------------------------------------------------------------- + * + * %s.h + * List of keywords represented as a ScanKeywordList. + * + * Portions Copyright (c) 1996-2023, 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/tools/gen_keywordlist.pl + * + *------------------------------------------------------------------------- + */ + +#ifndef %s_H +#define %s_H + +#include "common/kwlookup.h" + +EOM + +# Parse input file for keyword names. +my @keywords; +while (<$kif>) +{ + if (/^PG_KEYWORD\("(\w+)"/) + { + push @keywords, $1; + } +} + +# When being case-insensitive, insist that the input be all-lower-case. +if ($case_fold) +{ + foreach my $kw (@keywords) + { + die qq|The keyword "$kw" is not lower-case in $kw_input_file\n| + if ($kw ne lc $kw); + } +} + +# Error out if the keyword names are not in ASCII order. +# +# While this isn't really necessary with hash-based lookup, it's still +# helpful because it provides a cheap way to reject duplicate keywords. +# Also, insisting on sorted order ensures that code that scans the keyword +# table linearly will see the keywords in a canonical order. +for my $i (0 .. $#keywords - 1) +{ + die + qq|The keyword "$keywords[$i + 1]" is out of order in $kw_input_file\n| + if ($keywords[$i] cmp $keywords[ $i + 1 ]) >= 0; +} + +# Emit the string containing all the keywords. + +printf $kwdef qq|static const char %s_kw_string[] =\n\t"|, $varname; +print $kwdef join qq|\\0"\n\t"|, @keywords; +print $kwdef qq|";\n\n|; + +# Emit an array of numerical offsets which will be used to index into the +# keyword string. Also determine max keyword length. + +printf $kwdef "static const uint16 %s_kw_offsets[] = {\n", $varname; + +my $offset = 0; +my $max_len = 0; +foreach my $name (@keywords) +{ + my $this_length = length($name); + + print $kwdef "\t$offset,\n"; + + # Calculate the cumulative offset of the next keyword, + # taking into account the null terminator. + $offset += $this_length + 1; + + # Update max keyword length. + $max_len = $this_length if $max_len < $this_length; +} + +print $kwdef "};\n\n"; + +# Emit a macro defining the number of keywords. +# (In some places it's useful to have access to that as a constant.) + +printf $kwdef "#define %s_NUM_KEYWORDS %d\n\n", uc $varname, scalar @keywords; + +# Emit the definition of the hash function. + +my $funcname = $varname . "_hash_func"; + +my $f = PerfectHash::generate_hash_function(\@keywords, $funcname, + case_fold => $case_fold); + +printf $kwdef qq|static %s\n|, $f; + +# Emit the struct that wraps all this lookup info into one variable. + +printf $kwdef "static " if !$extern; +printf $kwdef "const ScanKeywordList %s = {\n", $varname; +printf $kwdef qq|\t%s_kw_string,\n|, $varname; +printf $kwdef qq|\t%s_kw_offsets,\n|, $varname; +printf $kwdef qq|\t%s,\n|, $funcname; +printf $kwdef qq|\t%s_NUM_KEYWORDS,\n|, uc $varname; +printf $kwdef qq|\t%d\n|, $max_len; +printf $kwdef "};\n\n"; + +printf $kwdef "#endif\t\t\t\t\t\t\t/* %s_H */\n", uc $base_filename; + + +sub usage +{ + die <<EOM; +Usage: gen_keywordlist.pl [--output/-o <path>] [--varname/-v <varname>] [--extern/-e] [--[no-]case-fold] input_file + --output Output directory (default '.') + --varname Name for ScanKeywordList variable (default 'ScanKeywords') + --extern Allow the ScanKeywordList variable to be globally visible + --no-case-fold Keyword matching is to be case-sensitive + +gen_keywordlist.pl transforms a list of keywords into a ScanKeywordList. +The output filename is derived from the input file by inserting _d, +for example kwlist_d.h is produced from kwlist.h. +EOM +} diff --git a/src/tools/git-external-diff b/src/tools/git-external-diff new file mode 100644 index 0000000..39ddd01 --- /dev/null +++ b/src/tools/git-external-diff @@ -0,0 +1,59 @@ +#!/bin/bash + +# This script is used to produce git context diffs + +# Supplied parameters: +# $1 $2 $3 $4 $5 $6 $7 +# path old-file old-hash old-mode new-file new-hash new-mode +# 'path' is the git-tree-relative path of the file being diff'ed + +=comment + +This info is copied from the old wiki page on Working with git: + +Context diffs with Git + +Copy git-external-diff into libexec/git-core/ of your git installation +and configure git to use that wrapper with: + + git config [--global] diff.external git-external-diff + +--global makes the configuration global for your user - otherwise it is +just configured for the current repository. + +For every command which displays diffs in some way you can use the +parameter "--[no-]-ext-diff" to enable respectively disable using the +external diff command. + +For the git diff command --ext-diff is enabled by default - for any +other command like git log -p or git format-patch it is not! + +This method should work on all platforms supported by git. + +If you do not want to configure the external wrapper permanently or you +want to overwrite it you can also invoke git like: + + export GIT_EXTERNAL_DIFF=git-external-diff + git diff --[no-]ext-diff + +Alternatively, configure a git alias in ~/.gitconfig or .git/config: + + [alias] + cdiff = !GIT_EXTERNAL_DIFF=git-context-diff git diff +=cut + +old_hash="$3" +new_hash=$(git hash-object "$5") + +# no change? +[ "$old_hash" = "$new_hash" ] && exit 0 + +[ "$DIFF_OPTS" = "" ] && DIFF_OPTS='-pcd' + +echo "diff --git a/$1 b/$1" +echo "new file mode $7" +echo "index ${old_hash:0:7}..${new_hash:0:7}" + +diff --label a/"$1" --label b/"$1" $DIFF_OPTS "$2" "$5" + +exit 0 diff --git a/src/tools/git_changelog b/src/tools/git_changelog new file mode 100755 index 0000000..14b9472 --- /dev/null +++ b/src/tools/git_changelog @@ -0,0 +1,419 @@ +#!/usr/bin/perl + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +# +# src/tools/git_changelog +# +# Display all commits on active branches, merging together commits from +# different branches that occur close together in time and with identical +# log messages. +# +# By default, commits are annotated with branch and release info thus: +# Branch: REL8_3_STABLE Release: REL8_3_2 [92c3a8004] 2008-03-29 00:15:37 +0000 +# This shows that the commit on REL8_3_STABLE was released in 8.3.2. +# Commits on master will usually instead have notes like +# Branch: master Release: REL8_4_BR [6fc9d4272] 2008-03-29 00:15:28 +0000 +# showing that this commit is ancestral to release branches 8.4 and later. +# If no Release: marker appears, the commit hasn't yet made it into any +# release. +# +# The --brief option shortens that to a format like: +# YYYY-MM-DD [hash] abbreviated commit subject line +# Since the branch isn't shown, this is mainly useful in conjunction +# with --master-only. +# +# Most of the time, matchable commits occur in the same order on all branches, +# and we print them out in that order. However, if commit A occurs before +# commit B on branch X and commit B occurs before commit A on branch Y, then +# there's no ordering which is consistent with both branches. In such cases +# we sort a merged commit according to its timestamp on the newest branch +# it appears in. +# +# The default output of this script is meant for generating minor release +# notes, where we need to know which branches a merged commit affects. +# +# To generate major release notes, use: +# git_changelog --master-only --brief --oldest-first --since='start-date' +# To find the appropriate start date, use: +# git show --summary $(git merge-base REL_12_STABLE master) +# where the branch to mention is the previously forked-off branch. This +# shows the last commit before that branch was made. +# +# Note that --master-only is an imperfect filter, since it will not detect +# cases where a master patch was back-patched awhile later or with a slightly +# different commit message. To find such cases, it's a good idea to look +# through the output of +# git_changelog --non-master-only --oldest-first --since='start-date' +# and then remove anything from the --master-only output that would be +# duplicative. + + +use strict; +use warnings; +require Time::Local; +require Getopt::Long; +require IPC::Open2; + +# Adjust this list when the set of interesting branches changes. +# (We could get this from "git branches", but not worth the trouble.) +# NB: master must be first! +my @BRANCHES = qw(master + REL_15_STABLE REL_14_STABLE REL_13_STABLE + REL_12_STABLE REL_11_STABLE REL_10_STABLE REL9_6_STABLE REL9_5_STABLE + REL9_4_STABLE REL9_3_STABLE REL9_2_STABLE REL9_1_STABLE REL9_0_STABLE + REL8_4_STABLE REL8_3_STABLE REL8_2_STABLE REL8_1_STABLE REL8_0_STABLE + REL7_4_STABLE REL7_3_STABLE REL7_2_STABLE REL7_1_STABLE REL7_0_PATCHES + REL6_5_PATCHES REL6_4); + +# Might want to make this parameter user-settable. +my $timestamp_slop = 24 * 60 * 60; + +my $brief = 0; +my $details_after = 0; +my $post_date = 0; +my $master_only = 0; +my $non_master_only = 0; +my $oldest_first = 0; +my $since; +my @output_buffer; +my $output_line = ''; + +Getopt::Long::GetOptions( + 'brief' => \$brief, + 'details-after' => \$details_after, + 'master-only' => \$master_only, + 'non-master-only' => \$non_master_only, + 'post-date' => \$post_date, + 'oldest-first' => \$oldest_first, + 'since=s' => \$since) || usage(); +usage() if @ARGV; + +my @git = qw(git log --format=fuller --date=iso); +push @git, '--since=' . $since if defined $since; + +# Collect the release tag data +my %rel_tags; + +{ + my $cmd = "git for-each-ref refs/tags"; + my $pid = IPC::Open2::open2(my $git_out, my $git_in, $cmd) + || die "can't run $cmd: $!"; + while (my $line = <$git_out>) + { + if ($line =~ m|^([a-f0-9]+)\s+commit\s+refs/tags/(\S+)|) + { + my $commit = $1; + my $tag = $2; + if ( $tag =~ /^REL_\d+_\d+$/ + || $tag =~ /^REL\d+_\d+$/ + || $tag =~ /^REL\d+_\d+_\d+$/) + { + $rel_tags{$commit} = $tag; + } + } + } + waitpid($pid, 0); + my $child_exit_status = $? >> 8; + die "$cmd failed" if $child_exit_status != 0; +} + +# Collect the commit data +my %all_commits; +my %all_commits_by_branch; + +# This remembers where each branch sprouted from master. Note the values +# will be wrong if --since terminates the log listing before the branch +# sprouts; but in that case it doesn't matter since we also won't reach +# the part of master where it would matter. +my %sprout_tags; + +for my $branch (@BRANCHES) +{ + my @cmd = @git; + if ($branch eq "master") + { + push @cmd, "origin/$branch"; + } + else + { + push @cmd, "--parents"; + push @cmd, "master..origin/$branch"; + } + my $pid = IPC::Open2::open2(my $git_out, my $git_in, @cmd) + || die "can't run @cmd: $!"; + my $last_tag = undef; + my $last_parent; + my %commit; + while (my $line = <$git_out>) + { + if ($line =~ /^commit\s+(\S+)/) + { + push_commit(\%commit) if %commit; + $last_tag = $rel_tags{$1} if defined $rel_tags{$1}; + %commit = ( + 'branch' => $branch, + 'commit' => $1, + 'last_tag' => $last_tag, + 'message' => '',); + if ($line =~ /^commit\s+\S+\s+(\S+)/) + { + $last_parent = $1; + } + else + { + $last_parent = undef; + } + } + elsif ($line =~ /^Author:\s+(.*)/) + { + $commit{'author'} = $1; + } + elsif ($line =~ /^CommitDate:\s+(.*)/) + { + $commit{'date'} = $1; + } + elsif ($line =~ /^\s\s/) + { + $commit{'message'} .= $line; + } + } + push_commit(\%commit) if %commit; + $sprout_tags{$last_parent} = $branch if defined $last_parent; + waitpid($pid, 0); + my $child_exit_status = $? >> 8; + die "@cmd failed" if $child_exit_status != 0; +} + +# Run through the master branch and apply tags. We already tagged the other +# branches, but master needs a separate pass after we've acquired the +# sprout_tags data. Also, in post-date mode we need to add phony entries +# for branches that sprouted after a particular master commit was made. +{ + my $last_tag = undef; + my %sprouted_branches; + for my $cc (@{ $all_commits_by_branch{'master'} }) + { + my $commit = $cc->{'commit'}; + my $c = $cc->{'commits'}->[0]; + $last_tag = $rel_tags{$commit} if defined $rel_tags{$commit}; + if (defined $sprout_tags{$commit}) + { + $last_tag = $sprout_tags{$commit}; + + # normalize branch names for making sprout tags + $last_tag =~ s/^(REL_\d+).*/$1_BR/; + $last_tag =~ s/^(REL\d+_\d+).*/$1_BR/; + } + $c->{'last_tag'} = $last_tag; + if ($post_date) + { + if (defined $sprout_tags{$commit}) + { + $sprouted_branches{ $sprout_tags{$commit} } = 1; + } + + # insert new commits between master and any other commits + my @new_commits = (shift @{ $cc->{'commits'} }); + for my $branch (reverse sort keys %sprouted_branches) + { + my $ccopy = { %{$c} }; + $ccopy->{'branch'} = $branch; + push @new_commits, $ccopy; + } + $cc->{'commits'} = [ @new_commits, @{ $cc->{'commits'} } ]; + } + } +} + +my %position; +for my $branch (@BRANCHES) +{ + $position{$branch} = 0; +} + +while (1) +{ + my $best_branch; + my $best_timestamp; + for my $branch (@BRANCHES) + { + my $leader = $all_commits_by_branch{$branch}->[ $position{$branch} ]; + next if !defined $leader; + if (!defined $best_branch + || $leader->{'timestamp'} > $best_timestamp) + { + $best_branch = $branch; + $best_timestamp = $leader->{'timestamp'}; + } + } + last if !defined $best_branch; + my $winner = + $all_commits_by_branch{$best_branch}->[ $position{$best_branch} ]; + + my $print_it = 1; + if ($master_only) + { + $print_it = (@{ $winner->{'commits'} } == 1) + && ($winner->{'commits'}[0]->{'branch'} eq 'master'); + } + elsif ($non_master_only) + { + foreach my $c (@{ $winner->{'commits'} }) + { + $print_it = 0 if ($c->{'branch'} eq 'master'); + } + } + + if ($print_it) + { + output_details($winner) if (!$details_after); + output_str("%s", $winner->{'message'} . "\n"); + output_details($winner) if ($details_after); + unshift(@output_buffer, $output_line) if ($oldest_first); + $output_line = ''; + } + + $winner->{'done'} = 1; + for my $branch (@BRANCHES) + { + my $leader = $all_commits_by_branch{$branch}->[ $position{$branch} ]; + if (defined $leader && $leader->{'done'}) + { + ++$position{$branch}; + redo; + } + } +} + +print @output_buffer if ($oldest_first); + +sub push_commit +{ + my ($c) = @_; + my $ht = hash_commit($c); + my $ts = parse_datetime($c->{'date'}); + my $cc; + + # Note that this code will never merge two commits on the same branch, + # even if they have the same hash (author/message) and nearby + # timestamps. This means that there could be multiple potential + # matches when we come to add a commit from another branch. Prefer + # the closest-in-time one. + for my $candidate (@{ $all_commits{$ht} }) + { + my $diff = abs($ts - $candidate->{'timestamp'}); + if ($diff < $timestamp_slop + && !exists $candidate->{'branch_position'}{ $c->{'branch'} }) + { + if (!defined $cc + || $diff < abs($ts - $cc->{'timestamp'})) + { + $cc = $candidate; + } + } + } + if (!defined $cc) + { + $cc = { + 'author' => $c->{'author'}, + 'message' => $c->{'message'}, + 'commit' => $c->{'commit'}, + 'commits' => [], + 'timestamp' => $ts + }; + push @{ $all_commits{$ht} }, $cc; + } + + # stash only the fields we'll need later + my $smallc = { + 'branch' => $c->{'branch'}, + 'commit' => $c->{'commit'}, + 'date' => $c->{'date'}, + 'last_tag' => $c->{'last_tag'} + }; + push @{ $cc->{'commits'} }, $smallc; + push @{ $all_commits_by_branch{ $c->{'branch'} } }, $cc; + $cc->{'branch_position'}{ $c->{'branch'} } = + -1 + @{ $all_commits_by_branch{ $c->{'branch'} } }; + return; +} + +sub hash_commit +{ + my ($c) = @_; + return $c->{'author'} . "\0" . $c->{'message'}; +} + +sub parse_datetime +{ + my ($dt) = @_; + $dt =~ + /^(\d\d\d\d)-(\d\d)-(\d\d)\s+(\d\d):(\d\d):(\d\d)\s+([-+])(\d\d)(\d\d)$/; + my $gm = Time::Local::timegm($6, $5, $4, $3, $2 - 1, $1); + my $tzoffset = ($8 * 60 + $9) * 60; + $tzoffset = -$tzoffset if $7 eq '-'; + return $gm - $tzoffset; +} + +sub output_str +{ + ($oldest_first) ? ($output_line .= sprintf(shift, @_)) : printf(@_); + return; +} + +sub output_details +{ + my $item = shift; + + if ($details_after) + { + $item->{'author'} =~ m{^(.*?)\s*<[^>]*>$}; + + # output only author name, not email address + output_str("(%s)\n", $1); + } + else + { + output_str("Author: %s\n", $item->{'author'}); + } + foreach my $c (@{ $item->{'commits'} }) + { + if ($brief) + { + $item->{'message'} =~ m/^\s*(.*)/; + + output_str( + "%s [%s] %s\n", + substr($c->{'date'}, 0, 10), + substr($c->{'commit'}, 0, 9), + substr($1, 0, 56)); + } + else + { + output_str("Branch: %s ", $c->{'branch'}) + if (!$master_only); + output_str("Release: %s ", $c->{'last_tag'}) + if (defined $c->{'last_tag'}); + output_str("[%s] %s\n", substr($c->{'commit'}, 0, 9), + $c->{'date'}); + } + } + output_str("\n"); + return; +} + +sub usage +{ + print STDERR <<EOM; +Usage: git_changelog [--brief/-b] [--details-after/-d] [--master-only/-m] [--non-master-only/-n] [--oldest-first/-o] [--post-date/-p] [--since=SINCE] + --brief Shorten commit descriptions, omitting branch identification + --details-after Show branch and author info after the commit description + --master-only Show only commits made just in the master branch + --non-master-only Show only commits made just in back branches + --oldest-first Show oldest commits first + --post-date Show branches made after a commit occurred + --since Show only commits dated since SINCE +EOM + exit 1; +} diff --git a/src/tools/ifaddrs/.gitignore b/src/tools/ifaddrs/.gitignore new file mode 100644 index 0000000..2e90032 --- /dev/null +++ b/src/tools/ifaddrs/.gitignore @@ -0,0 +1 @@ +/test_ifaddrs diff --git a/src/tools/ifaddrs/Makefile b/src/tools/ifaddrs/Makefile new file mode 100644 index 0000000..e74d754 --- /dev/null +++ b/src/tools/ifaddrs/Makefile @@ -0,0 +1,28 @@ +#------------------------------------------------------------------------- +# +# Makefile for src/tools/ifaddrs +# +# Copyright (c) 2003-2023, PostgreSQL Global Development Group +# +# src/tools/ifaddrs/Makefile +# +#------------------------------------------------------------------------- + +subdir = src/tools/ifaddrs +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +libpq_backend_dir = $(top_builddir)/src/backend/libpq + +override CPPFLAGS := -I$(libpq_backend_dir) $(CPPFLAGS) + +OBJS = \ + test_ifaddrs.o + +all: test_ifaddrs + +test_ifaddrs: test_ifaddrs.o $(libpq_backend_dir)/ifaddr.o + $(CC) $(CFLAGS) test_ifaddrs.o $(libpq_backend_dir)/ifaddr.o $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) + +clean distclean maintainer-clean: + rm -f test_ifaddrs$(X) $(OBJS) diff --git a/src/tools/ifaddrs/README b/src/tools/ifaddrs/README new file mode 100644 index 0000000..9f92803 --- /dev/null +++ b/src/tools/ifaddrs/README @@ -0,0 +1,12 @@ +src/tools/ifaddrs/README + +test_ifaddrs +============ + +This program prints the addresses and netmasks of all the IPv4 and IPv6 +interfaces on the local machine. It is useful for testing that this +functionality works on various platforms. If "samehost" and "samenet" +in pg_hba.conf don't seem to work right, run this program to see what +is happening. + +Usage: test_ifaddrs diff --git a/src/tools/ifaddrs/test_ifaddrs.c b/src/tools/ifaddrs/test_ifaddrs.c new file mode 100644 index 0000000..b9a1b7b --- /dev/null +++ b/src/tools/ifaddrs/test_ifaddrs.c @@ -0,0 +1,71 @@ +/* + * src/tools/ifaddrs/test_ifaddrs.c + * + * + * test_ifaddrs.c + * test pg_foreach_ifaddr() + */ + +#include "postgres.h" + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "libpq/ifaddr.h" + + +static void +print_addr(struct sockaddr *addr) +{ + char buffer[256]; + int ret, + len; + + switch (addr->sa_family) + { + case AF_INET: + len = sizeof(struct sockaddr_in); + break; + case AF_INET6: + len = sizeof(struct sockaddr_in6); + break; + default: + len = sizeof(struct sockaddr_storage); + break; + } + + ret = getnameinfo(addr, len, buffer, sizeof(buffer), NULL, 0, + NI_NUMERICHOST); + if (ret != 0) + printf("[unknown: family %d]", addr->sa_family); + else + printf("%s", buffer); +} + +static void +callback(struct sockaddr *addr, struct sockaddr *mask, void *unused) +{ + printf("addr: "); + print_addr(addr); + printf(" mask: "); + print_addr(mask); + printf("\n"); +} + +int +main(int argc, char *argv[]) +{ +#ifdef WIN32 + WSADATA wsaData; + + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) + { + fprintf(stderr, "WSAStartup failed\n"); + return 1; + } +#endif + + if (pg_foreach_ifaddr(callback, NULL) < 0) + fprintf(stderr, "pg_foreach_ifaddr failed: %s\n", strerror(errno)); + return 0; +} diff --git a/src/tools/install_files b/src/tools/install_files new file mode 100644 index 0000000..08436c7 --- /dev/null +++ b/src/tools/install_files @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 + +# Helper to install files that are not part of the default meson install +# target. +# +# This includes files that should only get installed into the temporary +# installation for tests and documentation. + +import argparse +import os +import shutil +import sys +from pathlib import PurePath + +parser = argparse.ArgumentParser() + +parser.add_argument('--destdir', type=str, + default=os.environ.get('DESTDIR', None)) +parser.add_argument('--prefix', type=str) +parser.add_argument('--install', type=str, nargs='+', + action='append', default=[]) +parser.add_argument('--install-dirs', type=str, nargs='+', + action='append', default=[]) +parser.add_argument('--install-dir-contents', type=str, nargs='+', + action='append', default=[]) + +args = parser.parse_args() + + +def error_exit(msg: str): + print(msg, file=sys.stderr) + exit(1) + + +def create_target_dir(prefix: str, destdir: str, targetdir: str): + if not os.path.isabs(targetdir): + targetdir = os.path.join(prefix, targetdir) + + if destdir is not None: + # copy of meson's logic for joining destdir and install paths + targetdir = str(PurePath(destdir, *PurePath(targetdir).parts[1:])) + + os.makedirs(targetdir, exist_ok=True) + + return targetdir + + +def copy_files(targetdir: str, src_list: list): + for src in src_list: + shutil.copy2(src, targetdir) + + +def copy_dirs(targetdir: str, src_list: list, contents: bool): + for src in src_list: + if not os.path.isdir(src): + error_exit('{0} is not a directory'.format(src)) + + if contents: + target = targetdir + else: + target = os.path.join(targetdir, os.path.split(src)[1]) + shutil.copytree(src, target, dirs_exist_ok=True) + + +for installs in args.install: + targetdir = create_target_dir(args.prefix, args.destdir, installs[0]) + copy_files(targetdir, installs[1:]) + +for installs in args.install_dirs: + targetdir = create_target_dir(args.prefix, args.destdir, installs[0]) + copy_dirs(targetdir, installs[1:], contents=False) + +for installs in args.install_dir_contents: + targetdir = create_target_dir(args.prefix, args.destdir, installs[0]) + copy_dirs(targetdir, installs[1:], contents=True) diff --git a/src/tools/make_ctags b/src/tools/make_ctags new file mode 100755 index 0000000..ad027c7 --- /dev/null +++ b/src/tools/make_ctags @@ -0,0 +1,105 @@ +#!/bin/sh + +# src/tools/make_ctags [-e] [-n] +# If -e is specified, generate tags files for emacs. +# If -n is specified, don't create symbolic links of tags file. +usage="Usage: $0 [-e][-n]" +if [ $# -gt 2 ] +then echo $usage + exit 1 +fi + +EMACS_MODE= +NO_SYMLINK= +IS_EXUBERANT= +PROG="ctags" +TAGS_OPT="-a -f" +TAGS_FILE="tags" +FLAGS= +IGNORE_IDENTIFIES= + +while [ $# -gt 0 ] +do + if [ $1 = "-e" ] + then EMACS_MODE="Y" + elif [ $1 = "-n" ] + then NO_SYMLINK="Y" + else + echo $usage + exit 1 + fi + shift +done + +if [ ! "$EMACS_MODE" ] +then (command -v ctags >/dev/null) || \ + { echo "'ctags' program not found" 1>&2; exit 1; } +fi + +ctags --version 2>&1 | grep Exuberant && IS_EXUBERANT="Y" + +if [ "$EMACS_MODE" ] +then TAGS_FILE="TAGS" + if [ "$IS_EXUBERANT" ] + then PROG="ctags -e" + else (command -v etags >/dev/null) || \ + { echo "neither 'etags' nor exuberant 'ctags' program not found" 1>&2; exit 1; } + PROG="etags" + TAGS_OPT="-a -o" + fi +fi + +# List of kinds supported by Exuberant Ctags 5.8 +# generated by ctags --list-kinds +# --c-kinds was called --c-types before 2003 +# c classes +# d macro definitions +# e enumerators (values inside an enumeration) +# f function definitions +# g enumeration names +# l local variables [off] +# m class, struct, and union members +# n namespaces +# p function prototypes [off] +# s structure names +# t typedefs +# u union names +# v variable definitions +# x external and forward variable declarations [off] + +if [ "$IS_EXUBERANT" ] +then FLAGS="--c-kinds=+dfmstuv" +elif [ ! "$EMACS_MODE" ] +then FLAGS="-dt" +fi + +# Use -I option to ignore a macro +if [ "$IS_EXUBERANT" ] +then IGNORE_IDENTIFIES="-I pg_node_attr+" +fi + +trap "ret=$?; rm -rf /tmp/$$; exit $ret" 0 1 2 3 15 +rm -f ./$TAGS_FILE + +# this is outputting the tags into the file 'tags', and appending +find `pwd`/ \( -name tmp_install -prune -o -name tmp_check -prune \) \ + -o \( -name "*.[chly]" -o -iname "*makefile*" -o -name "*.mk" -o -name "*.in" \ + -o -name "*.sql" -o -name "*.p[lm]" \) -type f -print | + xargs $PROG $TAGS_OPT $TAGS_FILE $FLAGS $IGNORE_IDENTIFIES + +# Sorting non-Exuberant ctags file allows for fast searching of the tags file. +# Since etags file has a header that we cannot sort in with the other entries +# we skip the sort step. +if [ ! "$IS_EXUBERANT" -a ! "$EMACS_MODE" ] +then LC_ALL=C + export LC_ALL + sort $TAGS_FILE >/tmp/$$ && mv /tmp/$$ $TAGS_FILE +fi + +# create symbolic links +if [ ! "$NO_SYMLINK" ] +then find . \( -name 'CVS' -prune \) -o \( -name .git -prune \) -o -type d -print | + while read DIR + do [ "$DIR" != "." ] && ln -f -s `echo "$DIR" | sed 's;/[^/]*;/..;g'`/$TAGS_FILE "$DIR"/$TAGS_FILE + done +fi diff --git a/src/tools/make_etags b/src/tools/make_etags new file mode 100755 index 0000000..7e51bb4 --- /dev/null +++ b/src/tools/make_etags @@ -0,0 +1,12 @@ +#!/bin/sh + +# src/tools/make_etags [-n] + +if [ $# -gt 1 ] || ( [ $# -eq 1 ] && [ $1 != "-n" ] ) +then echo "Usage: $0 [-n]" + exit 1 +fi + +cdir=`dirname $0` +dir=`(cd $cdir && pwd)` +exec $dir/make_ctags -e $* diff --git a/src/tools/make_mkid b/src/tools/make_mkid new file mode 100755 index 0000000..6f16061 --- /dev/null +++ b/src/tools/make_mkid @@ -0,0 +1,11 @@ +#!/bin/sh + +# src/tools/make_mkid + +mkid `find \`pwd\`/ \( -name _deadcode -a -prune \) -o \ + -type f -name '*.[chyl]' -print|sed 's;//;/;g'` + +find . \( -name .git -a -prune \) -o -type d -print |while read DIR +do + [ "$DIR" != "." ] && ln -f -s `echo "$DIR" | sed 's;/[^/]*;/..;g'`/ID $DIR/ID +done diff --git a/src/tools/mark_pgdllimport.pl b/src/tools/mark_pgdllimport.pl new file mode 100755 index 0000000..45b4e73 --- /dev/null +++ b/src/tools/mark_pgdllimport.pl @@ -0,0 +1,78 @@ +#!/usr/bin/perl + +#---------------------------------------------------------------------- +# +# mark_pgdllimport.pl +# Perl script that tries to add PGDLLIMPORT markings to PostgreSQL +# header files. +# +# This relies on a few idiosyncrasies of the PostgreSQL coding style, +# such as the fact that we always use "extern" in function +# declarations, and that we don't use // comments. It's not very +# smart and may not catch all cases. +# +# It's probably a good idea to run pgindent on any files that this +# script modifies before committing. This script uses as arguments +# a list of the header files to scan for the markings. +# +# Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/tools/mark_pgdllimport.pl +# +#---------------------------------------------------------------------- + +use strict; +use warnings; + +for my $include_file (@ARGV) +{ + open(my $rfh, '<', $include_file) || die "$include_file: $!"; + my $buffer = ''; + my $num_pgdllimport_added = 0; + + while (my $raw_line = <$rfh>) + { + my $needs_pgdllimport = 1; + + # By convention we declare global variables explicitly extern. We're + # looking for those not already marked with PGDLLIMPORT. + $needs_pgdllimport = 0 + if $raw_line !~ /^extern\s+/ + || $raw_line =~ /PGDLLIMPORT/; + + # Make a copy of the line and perform a simple-minded comment strip. + # Also strip trailing whitespace. + my $stripped_line = $raw_line; + $stripped_line =~ s/\/\*.*\*\///g; + $stripped_line =~ s/\s+$//; + + # Variable declarations should end in a semicolon. If we see an + # opening parenthesis, it's probably a function declaration. + $needs_pgdllimport = 0 + if $stripped_line !~ /;$/ + || $stripped_line =~ /\(/; + + # Add PGDLLIMPORT marker, if required. + if ($needs_pgdllimport) + { + $raw_line =~ s/^extern/extern PGDLLIMPORT/; + ++$num_pgdllimport_added; + } + + # Add line to buffer. + $buffer .= $raw_line; + } + + close($rfh); + + # If we added any PGDLLIMPORT markers, rewrite the file. + if ($num_pgdllimport_added > 0) + { + printf "%s: adding %d PGDLLIMPORT markers\n", + $include_file, $num_pgdllimport_added; + open(my $wfh, '>', $include_file) || die "$include_file: $!"; + print $wfh $buffer; + close($wfh); + } +} diff --git a/src/tools/msvc/.gitignore b/src/tools/msvc/.gitignore new file mode 100644 index 0000000..2470e78 --- /dev/null +++ b/src/tools/msvc/.gitignore @@ -0,0 +1,3 @@ +# Custom configuration files for MSVC build +/config.pl +/buildenv.pl diff --git a/src/tools/msvc/Install.pm b/src/tools/msvc/Install.pm new file mode 100644 index 0000000..05548d7 --- /dev/null +++ b/src/tools/msvc/Install.pm @@ -0,0 +1,746 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +package Install; + +# +# Package that provides 'make install' functionality for msvc builds +# +# src/tools/msvc/Install.pm +# +use strict; +use warnings; +use Carp; +use File::Basename; +use File::Copy; +use File::Find (); + +use Exporter; +our (@ISA, @EXPORT_OK); +@ISA = qw(Exporter); +@EXPORT_OK = qw(Install); + +my $insttype; +my @client_contribs = ('oid2name', 'pgbench', 'vacuumlo'); +my @client_program_files = ( + 'clusterdb', 'createdb', 'createuser', 'dropdb', + 'dropuser', 'ecpg', 'libecpg', 'libecpg_compat', + 'libpgtypes', 'libpq', 'pg_amcheck', 'pg_basebackup', + 'pg_config', 'pg_dump', 'pg_dumpall', 'pg_isready', + 'pg_receivewal', 'pg_recvlogical', 'pg_restore', 'psql', + 'reindexdb', 'vacuumdb', @client_contribs); + +sub lcopy +{ + my $src = shift; + my $target = shift; + + if (-f $target) + { + unlink $target || confess "Could not delete $target\n"; + } + + (my $retval = copy($src, $target)) + || confess "Could not copy $src to $target\n"; + + return $retval; +} + +sub Install +{ + $| = 1; + + my $target = shift; + $insttype = shift; + $insttype = "all" unless ($insttype); + + # if called from vcregress, the config will be passed to us + # so no need to re-include these + our $config = shift; + unless ($config) + { + + # suppress warning about harmless redeclaration of $config + no warnings 'misc'; + do "./config_default.pl"; + do "./config.pl" if (-f "config.pl"); + } + + # Move to the root path depending on the current location. + if (-f "../../../configure") + { + chdir("../../.."); + } + elsif (-f "../../../../configure") + { + chdir("../../../.."); + } + + my $conf = ""; + if (-d "debug") + { + $conf = "debug"; + } + if (-d "release") + { + $conf = "release"; + } + die "Could not find debug or release binaries" if ($conf eq ""); + my $majorver = DetermineMajorVersion(); + print "Installing version $majorver for $conf in $target\n"; + + my @client_dirs = ('bin', 'lib', 'share', 'symbols'); + my @all_dirs = ( + @client_dirs, 'doc', 'doc/contrib', 'doc/extension', 'share/contrib', + 'share/extension', 'share/timezonesets', 'share/tsearch_data'); + if ($insttype eq "client") + { + EnsureDirectories($target, @client_dirs); + } + else + { + EnsureDirectories($target, @all_dirs); + } + + CopySolutionOutput($conf, $target); + my $sample_files = []; + my @top_dir = ("src"); + @top_dir = ("src\\bin", "src\\interfaces") if ($insttype eq "client"); + File::Find::find( + { + wanted => sub { + /^.*\.sample\z/s + && push(@$sample_files, $File::Find::name); + + # Don't find files of in-tree temporary installations. + $_ eq 'share' and $File::Find::prune = 1; + } + }, + @top_dir); + CopySetOfFiles('config files', $sample_files, $target . '/share/'); + CopyFiles( + 'Import libraries', + $target . '/lib/', + "$conf\\", "postgres\\postgres.lib", "libpgcommon\\libpgcommon.lib", + "libpgport\\libpgport.lib"); + CopyContribFiles($config, $target); + CopyIncludeFiles($target); + + if ($insttype ne "client") + { + CopySetOfFiles( + 'timezone names', + [ glob('src\timezone\tznames\*.txt') ], + $target . '/share/timezonesets/'); + CopyFiles( + 'timezone sets', + $target . '/share/timezonesets/', + 'src/timezone/tznames/', 'Default', 'Australia', 'India'); + CopySetOfFiles( + 'BKI files', + [ glob("src\\backend\\catalog\\postgres.*") ], + $target . '/share/'); + CopySetOfFiles( + 'SQL files', + [ glob("src\\backend\\catalog\\*.sql") ], + $target . '/share/'); + CopyFiles( + 'Information schema data', $target . '/share/', + 'src/backend/catalog/', 'sql_features.txt'); + CopyFiles( + 'Error code data', $target . '/share/', + 'src/backend/utils/', 'errcodes.txt'); + GenerateTimezoneFiles($target, $conf); + GenerateTsearchFiles($target); + CopySetOfFiles( + 'Stopword files', + [ glob("src\\backend\\snowball\\stopwords\\*.stop") ], + $target . '/share/tsearch_data/'); + CopySetOfFiles( + 'Dictionaries sample files', + [ glob("src\\backend\\tsearch\\dicts\\*_sample*") ], + $target . '/share/tsearch_data/'); + + my $pl_extension_files = []; + my @pldirs = ('src/pl/plpgsql/src'); + push @pldirs, "src/pl/plperl" if $config->{perl}; + push @pldirs, "src/pl/plpython" if $config->{python}; + push @pldirs, "src/pl/tcl" if $config->{tcl}; + File::Find::find( + { + wanted => sub { + /^(.*--.*\.sql|.*\.control)\z/s + && push(@$pl_extension_files, $File::Find::name); + + # Don't find files of in-tree temporary installations. + $_ eq 'share' and $File::Find::prune = 1; + } + }, + @pldirs); + CopySetOfFiles('PL Extension files', + $pl_extension_files, $target . '/share/extension/'); + } + + GenerateNLSFiles($target, $config->{nls}, $majorver) if ($config->{nls}); + + print "Installation complete.\n"; + return; +} + +sub EnsureDirectories +{ + my $target = shift; + mkdir $target unless -d ($target); + while (my $d = shift) + { + mkdir $target . '/' . $d unless -d ($target . '/' . $d); + } + return; +} + +sub CopyFiles +{ + my $what = shift; + my $target = shift; + my $basedir = shift; + + print "Copying $what"; + while (my $f = shift) + { + print "."; + $f = $basedir . $f; + die "No file $f\n" if (!-f $f); + lcopy($f, $target . basename($f)) || croak "Could not copy $f: $!\n"; + } + print "\n"; + return; +} + +sub CopySetOfFiles +{ + my $what = shift; + my $flist = shift; + my $target = shift; + print "Copying $what" if $what; + foreach (@$flist) + { + my $tgt = $target . basename($_); + print "."; + lcopy($_, $tgt) || croak "Could not copy $_: $!\n"; + } + print "\n"; + return; +} + +sub CopySolutionOutput +{ + my $conf = shift; + my $target = shift; + my $rem = + qr{Project\("\{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942\}"\) = "([^"]+)"}; + + my $sln = read_file("pgsql.sln") || croak "Could not open pgsql.sln\n"; + + my $vcproj = 'vcproj'; + if ($sln =~ + /Microsoft Visual Studio Solution File, Format Version (\d+)\.\d+/ + && $1 >= 11) + { + $vcproj = 'vcxproj'; + } + + print "Copying build output files..."; + while ($sln =~ $rem) + { + my $pf = $1; + + # Hash-of-arrays listing where to install things. For each + # subdirectory there's a hash key, and the value is an array + # of file extensions to install in that subdirectory. Example: + # { 'bin' => [ 'dll', 'lib' ], + # 'lib' => [ 'lib' ] } + my %install_list; + my $is_sharedlib = 0; + + $sln =~ s/$rem//; + + next + if ($insttype eq "client" && !grep { $_ eq $pf } + @client_program_files); + + my $proj = read_file("$pf.$vcproj") + || croak "Could not open $pf.$vcproj\n"; + + # Check if this project uses a shared library by looking if + # SO_MAJOR_VERSION is defined in its Makefile, whose path + # can be found using the resource file of this project. + if (( $vcproj eq 'vcxproj' + && $proj =~ qr{ResourceCompile\s*Include="([^"]+)"}) + || ( $vcproj eq 'vcproj' + && $proj =~ qr{File\s*RelativePath="([^\"]+)\.rc"})) + { + my $projpath = dirname($1); + my $mfname = + -e "$projpath/GNUmakefile" + ? "$projpath/GNUmakefile" + : "$projpath/Makefile"; + my $mf = read_file($mfname) || croak "Could not open $mfname\n"; + + $is_sharedlib = 1 if ($mf =~ /^SO_MAJOR_VERSION\s*=\s*(.*)$/mg); + } + + if ($vcproj eq 'vcproj' && $proj =~ qr{ConfigurationType="([^"]+)"}) + { + if ($1 == 1) + { + push(@{ $install_list{'bin'} }, "exe"); + } + elsif ($1 == 2) + { + push(@{ $install_list{'lib'} }, "dll"); + if ($is_sharedlib) + { + push(@{ $install_list{'bin'} }, "dll"); + push(@{ $install_list{'lib'} }, "lib"); + } + } + else + { + + # Static libraries, such as libpgport, only used internally + # during build, don't install. + next; + } + } + elsif ($vcproj eq 'vcxproj' + && $proj =~ qr{<ConfigurationType>(\w+)</ConfigurationType>}) + { + if ($1 eq 'Application') + { + push(@{ $install_list{'bin'} }, "exe"); + } + elsif ($1 eq 'DynamicLibrary') + { + push(@{ $install_list{'lib'} }, "dll"); + if ($is_sharedlib) + { + push(@{ $install_list{'bin'} }, "dll"); + push(@{ $install_list{'lib'} }, "lib"); + } + } + else # 'StaticLibrary' + { + + # Static lib, such as libpgport, only used internally + # during build, don't install. + next; + } + } + else + { + croak "Could not parse $pf.$vcproj\n"; + } + + # Install each element + foreach my $dir (keys %install_list) + { + foreach my $ext (@{ $install_list{$dir} }) + { + lcopy("$conf\\$pf\\$pf.$ext", "$target\\$dir\\$pf.$ext") + || croak "Could not copy $pf.$ext\n"; + } + } + lcopy("$conf\\$pf\\$pf.pdb", "$target\\symbols\\$pf.pdb") + || croak "Could not copy $pf.pdb\n"; + print "."; + } + print "\n"; + return; +} + +sub GenerateTimezoneFiles +{ + my $target = shift; + my $conf = shift; + my $mf = read_file("src/timezone/Makefile"); + $mf =~ s{\\\r?\n}{}g; + + $mf =~ /^TZDATAFILES\s*:?=\s*(.*)$/m + || die "Could not find TZDATAFILES line in timezone makefile\n"; + my @tzfiles = split /\s+/, $1; + + print "Generating timezone files..."; + + my @args = ("$conf/zic/zic", '-d', "$target/share/timezone"); + foreach (@tzfiles) + { + my $tzfile = $_; + $tzfile =~ s|\$\(srcdir\)|src/timezone|; + push(@args, $tzfile); + } + + system(@args); + print "\n"; + return; +} + +sub GenerateTsearchFiles +{ + my $target = shift; + + print "Generating tsearch script..."; + system( + 'perl', 'src/backend/snowball/snowball_create.pl', + '--input', 'src/backend/snowball/', + '--outdir', "$target/share/"); + print "\n"; + return; +} + +sub CopyContribFiles +{ + my $config = shift; + my $target = shift; + + print "Copying contrib data files..."; + foreach my $subdir ('contrib', 'src/test/modules') + { + my $D; + opendir($D, $subdir) || croak "Could not opendir on $subdir!\n"; + while (my $d = readdir($D)) + { + # These configuration-based exclusions must match vcregress.pl + next if ($d eq "uuid-ossp" && !defined($config->{uuid})); + next if ($d eq "sslinfo" && !defined($config->{openssl})); + next if ($d eq "pgcrypto" && !defined($config->{openssl})); + next if ($d eq "xml2" && !defined($config->{xml})); + next if ($d =~ /_plperl$/ && !defined($config->{perl})); + next if ($d =~ /_plpython$/ && !defined($config->{python})); + next if ($d eq "sepgsql"); + + CopySubdirFiles($subdir, $d, $config, $target); + } + } + print "\n"; + return; +} + +sub CopySubdirFiles +{ + my $subdir = shift; + my $module = shift; + my $config = shift; + my $target = shift; + + return if ($module =~ /^\./); + return unless (-f "$subdir/$module/Makefile"); + return + if ($insttype eq "client" && !grep { $_ eq $module } @client_contribs); + + my $mf = read_file("$subdir/$module/Makefile"); + $mf =~ s{\\\r?\n}{}g; + + # Note: we currently don't support setting MODULEDIR in the makefile + my $moduledir = 'contrib'; + + my $flist = ''; + if ($mf =~ /^EXTENSION\s*=\s*(.*)$/m) { $flist .= $1 } + if ($flist ne '') + { + $moduledir = 'extension'; + $flist = ParseAndCleanRule($flist, $mf); + + foreach my $f (split /\s+/, $flist) + { + lcopy("$subdir/$module/$f.control", + "$target/share/extension/$f.control") + || croak("Could not copy file $f.control in contrib $module"); + print '.'; + } + } + + $flist = ''; + if ($mf =~ /^DATA_built\s*=\s*(.*)$/m) { $flist .= $1 } + if ($mf =~ /^DATA\s*=\s*(.*)$/m) { $flist .= " $1" } + $flist =~ s/^\s*//; # Remove leading spaces if we had only DATA_built + + if ($flist ne '') + { + $flist = ParseAndCleanRule($flist, $mf); + + foreach my $f (split /\s+/, $flist) + { + lcopy("$subdir/$module/$f", + "$target/share/$moduledir/" . basename($f)) + || croak("Could not copy file $f in contrib $module"); + print '.'; + } + } + + $flist = ''; + if ($mf =~ /^DATA_TSEARCH\s*=\s*(.*)$/m) { $flist .= $1 } + if ($flist ne '') + { + $flist = ParseAndCleanRule($flist, $mf); + + foreach my $f (split /\s+/, $flist) + { + lcopy("$subdir/$module/$f", + "$target/share/tsearch_data/" . basename($f)) + || croak("Could not copy file $f in $subdir $module"); + print '.'; + } + } + + { + $flist = ''; + if ($mf =~ /^HEADERS\s*=\s*(.*)$/m) { $flist .= $1 } + my @modlist = (); + my %fmodlist = (); + while ($mf =~ /^HEADERS_([^\s=]+)\s*=\s*(.*)$/mg) + { + $fmodlist{$1} .= $2; + } + + if ($mf =~ /^MODULE_big\s*=\s*(.*)$/m) + { + push @modlist, $1; + if ($flist ne '') + { + $fmodlist{$1} = $flist; + $flist = ''; + } + } + elsif ($mf =~ /^MODULES\s*=\s*(.*)$/m) + { + push @modlist, split /\s+/, $1; + } + + croak "HEADERS requires MODULE_big in $subdir $module" + if $flist ne ''; + + foreach my $mod (keys %fmodlist) + { + croak "HEADERS_$mod for unknown module in $subdir $module" + unless grep { $_ eq $mod } @modlist; + $flist = ParseAndCleanRule($fmodlist{$mod}, $mf); + EnsureDirectories($target, "include", "include/server", + "include/server/$moduledir", + "include/server/$moduledir/$mod"); + foreach my $f (split /\s+/, $flist) + { + lcopy("$subdir/$module/$f", + "$target/include/server/$moduledir/$mod/" . basename($f)) + || croak("Could not copy file $f in $subdir $module"); + print '.'; + } + } + } + + $flist = ''; + if ($mf =~ /^DOCS\s*=\s*(.*)$/mg) { $flist .= $1 } + if ($flist ne '') + { + $flist = ParseAndCleanRule($flist, $mf); + + # Special case for contrib/spi + $flist = + "autoinc.example insert_username.example moddatetime.example refint.example" + if ($module eq 'spi'); + foreach my $f (split /\s+/, $flist) + { + lcopy("$subdir/$module/$f", "$target/doc/$moduledir/$f") + || croak("Could not copy file $f in contrib $module"); + print '.'; + } + } + return; +} + +sub ParseAndCleanRule +{ + my $flist = shift; + my $mf = shift; + + # Strip out $(addsuffix) rules + if (index($flist, '$(addsuffix ') >= 0) + { + my $pcount = 0; + my $i; + for ( + $i = index($flist, '$(addsuffix ') + 12; + $i < length($flist); + $i++) + { + $pcount++ if (substr($flist, $i, 1) eq '('); + $pcount-- if (substr($flist, $i, 1) eq ')'); + last if ($pcount < 0); + } + $flist = + substr($flist, 0, index($flist, '$(addsuffix ')) + . substr($flist, $i + 1); + } + return $flist; +} + +sub CopyIncludeFiles +{ + my $target = shift; + + EnsureDirectories($target, 'include', 'include/libpq', 'include/internal', + 'include/internal/libpq', 'include/server', 'include/server/parser'); + + CopyFiles( + 'Public headers', $target . '/include/', + 'src/include/', 'postgres_ext.h', + 'pg_config.h', 'pg_config_ext.h', + 'pg_config_os.h', 'pg_config_manual.h'); + lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/') + || croak 'Could not copy libpq-fs.h'; + + CopyFiles( + 'Libpq headers', + $target . '/include/', + 'src/interfaces/libpq/', 'libpq-fe.h', 'libpq-events.h'); + CopyFiles( + 'Libpq internal headers', + $target . '/include/internal/', + 'src/interfaces/libpq/', 'libpq-int.h', 'fe-auth-sasl.h', + 'pqexpbuffer.h'); + + CopyFiles( + 'Internal headers', + $target . '/include/internal/', + 'src/include/', 'c.h', 'port.h', 'postgres_fe.h'); + lcopy('src/include/libpq/pqcomm.h', $target . '/include/internal/libpq/') + || croak 'Could not copy pqcomm.h'; + + CopyFiles( + 'Server headers', + $target . '/include/server/', + 'src/include/', 'pg_config.h', 'pg_config_ext.h', 'pg_config_os.h'); + CopySetOfFiles( + '', + [ glob("src\\include\\*.h") ], + $target . '/include/server/'); + my $D; + opendir($D, 'src/include') || croak "Could not opendir on src/include!\n"; + + CopyFiles( + 'PL/pgSQL header', + $target . '/include/server/', + 'src/pl/plpgsql/src/', 'plpgsql.h'); + + # some xcopy progs don't like mixed slash style paths + (my $ctarget = $target) =~ s!/!\\!g; + while (my $d = readdir($D)) + { + next if ($d =~ /^\./); + next if ($d eq '.git'); + next if ($d eq 'CVS'); + next unless (-d "src/include/$d"); + + EnsureDirectories("$target/include/server/$d"); + my @args = ( + 'xcopy', '/s', '/i', '/q', '/r', '/y', "src\\include\\$d\\*.h", + "$ctarget\\include\\server\\$d\\"); + system(@args) && croak("Failed to copy include directory $d\n"); + } + closedir($D); + + my $mf = read_file('src/interfaces/ecpg/include/Makefile'); + $mf =~ s{\\\r?\n}{}g; + $mf =~ /^ecpg_headers\s*=\s*(.*)$/m + || croak "Could not find ecpg_headers line\n"; + CopyFiles( + 'ECPG headers', + $target . '/include/', + 'src/interfaces/ecpg/include/', + 'ecpg_config.h', split /\s+/, $1); + $mf =~ /^informix_headers\s*=\s*(.*)$/m + || croak "Could not find informix_headers line\n"; + EnsureDirectories($target . '/include', 'informix', 'informix/esql'); + CopyFiles( + 'ECPG informix headers', + $target . '/include/informix/esql/', + 'src/interfaces/ecpg/include/', + split /\s+/, $1); + return; +} + +sub GenerateNLSFiles +{ + my $target = shift; + my $nlspath = shift; + my $majorver = shift; + + print "Installing NLS files..."; + EnsureDirectories($target, "share/locale"); + my @flist; + File::Find::find( + { + wanted => sub { + /^nls\.mk\z/s + && !push(@flist, $File::Find::name); + } + }, + "src"); + foreach (@flist) + { + my $prgm = DetermineCatalogName($_); + s/nls.mk/po/; + my $dir = $_; + next unless ($dir =~ /([^\/]+)\/po$/); + foreach (glob("$dir/*.po")) + { + my $lang; + next unless /([^\/]+)\.po/; + $lang = $1; + + EnsureDirectories($target, "share/locale/$lang", + "share/locale/$lang/LC_MESSAGES"); + my @args = ( + "$nlspath\\bin\\msgfmt", + '-o', + "$target\\share\\locale\\$lang\\LC_MESSAGES\\$prgm-$majorver.mo", + $_); + system(@args) && croak("Could not run msgfmt on $dir\\$_"); + print "."; + } + } + print "\n"; + return; +} + +sub DetermineMajorVersion +{ + my $f = read_file('src/include/pg_config.h') + || croak 'Could not open pg_config.h'; + $f =~ /^#define\s+PG_MAJORVERSION\s+"([^"]+)"/m + || croak 'Could not determine major version'; + return $1; +} + +sub DetermineCatalogName +{ + my $filename = shift; + + my $f = read_file($filename) || croak "Could not open $filename"; + $f =~ /CATALOG_NAME\s*\:?=\s*(\S+)/m + || croak "Could not determine catalog name in $filename"; + return $1; +} + +sub read_file +{ + my $filename = shift; + my $F; + local $/ = undef; + open($F, '<', $filename) || die "Could not open file $filename\n"; + my $txt = <$F>; + close($F); + + return $txt; +} + +1; diff --git a/src/tools/msvc/MSBuildProject.pm b/src/tools/msvc/MSBuildProject.pm new file mode 100644 index 0000000..62fec1f --- /dev/null +++ b/src/tools/msvc/MSBuildProject.pm @@ -0,0 +1,508 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +package MSBuildProject; + +# +# Package that encapsulates a MSBuild project file (Visual C++ 2015 or greater) +# +# src/tools/msvc/MSBuildProject.pm +# + +use Carp; +use strict; +use warnings; +use base qw(Project); + +no warnings qw(redefine); ## no critic + +sub _new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{filenameExtension} = '.vcxproj'; + $self->{ToolsVersion} = '4.0'; + + return $self; +} + +sub WriteHeader +{ + my ($self, $f) = @_; + + print $f <<EOF; +<?xml version="1.0" encoding="Windows-1252"?> +<Project DefaultTargets="Build" ToolsVersion="$self->{ToolsVersion}" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> +EOF + $self->WriteConfigurationHeader($f, 'Debug'); + $self->WriteConfigurationHeader($f, 'Release'); + print $f <<EOF; + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>$self->{guid}</ProjectGuid> +EOF + # Check whether WindowsSDKVersion env variable is present. + # Add WindowsTargetPlatformVersion node if so. + my $sdkVersion = $ENV{'WindowsSDKVersion'}; + if (defined($sdkVersion)) + { + # remove trailing backslash if necessary. + $sdkVersion =~ s/\\$//; + print $f <<EOF; + <WindowsTargetPlatformVersion>$sdkVersion</WindowsTargetPlatformVersion> +EOF + } + print $f <<EOF; + </PropertyGroup> + <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.Default.props" /> +EOF + $self->WriteConfigurationPropertyGroup($f, 'Release', + { wholeopt => 'false' }); + $self->WriteConfigurationPropertyGroup($f, 'Debug', + { wholeopt => 'false' }); + print $f <<EOF; + <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> +EOF + $self->WritePropertySheetsPropertyGroup($f, 'Release'); + $self->WritePropertySheetsPropertyGroup($f, 'Debug'); + print $f <<EOF; + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> +EOF + $self->WriteAdditionalProperties($f, 'Debug'); + $self->WriteAdditionalProperties($f, 'Release'); + print $f <<EOF; + </PropertyGroup> +EOF + + $self->WriteItemDefinitionGroup( + $f, 'Debug', + { + defs => "_DEBUG;DEBUG=1", + opt => 'Disabled', + strpool => 'false', + runtime => 'MultiThreadedDebugDLL' + }); + $self->WriteItemDefinitionGroup( + $f, + 'Release', + { + defs => "", + opt => 'Full', + strpool => 'true', + runtime => 'MultiThreadedDLL' + }); + return; +} + +sub AddDefine +{ + my ($self, $def) = @_; + + $self->{defines} .= $def . ';'; + return; +} + +sub WriteReferences +{ + my ($self, $f) = @_; + + my @references = @{ $self->{references} }; + + if (scalar(@references)) + { + print $f <<EOF; + <ItemGroup> +EOF + foreach my $ref (@references) + { + print $f <<EOF; + <ProjectReference Include="$ref->{name}$ref->{filenameExtension}"> + <Project>$ref->{guid}</Project> + </ProjectReference> +EOF + } + print $f <<EOF; + </ItemGroup> +EOF + } + return; +} + +sub WriteFiles +{ + my ($self, $f) = @_; + print $f <<EOF; + <ItemGroup> +EOF + my @grammarFiles = (); + my @resourceFiles = (); + my %uniquefiles; + foreach my $fileNameWithPath (sort keys %{ $self->{files} }) + { + confess "Bad format filename '$fileNameWithPath'\n" + unless ($fileNameWithPath =~ m!^(.*)/([^/]+)\.(c|cpp|y|l|rc)$!); + my $dir = $1; + my $fileName = $2; + if ($fileNameWithPath =~ /\.y$/ or $fileNameWithPath =~ /\.l$/) + { + push @grammarFiles, $fileNameWithPath; + } + elsif ($fileNameWithPath =~ /\.rc$/) + { + push @resourceFiles, $fileNameWithPath; + } + elsif (defined($uniquefiles{$fileName})) + { + + # File already exists, so fake a new name + my $obj = $dir; + $obj =~ s!/!_!g; + + print $f <<EOF; + <ClCompile Include="$fileNameWithPath"> + <ObjectFileName Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">.\\debug\\$self->{name}\\${obj}_$fileName.obj</ObjectFileName> + <ObjectFileName Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">.\\release\\$self->{name}\\${obj}_$fileName.obj</ObjectFileName> + </ClCompile> +EOF + } + else + { + $uniquefiles{$fileName} = 1; + print $f <<EOF; + <ClCompile Include="$fileNameWithPath" /> +EOF + } + + } + print $f <<EOF; + </ItemGroup> +EOF + if (scalar(@grammarFiles)) + { + print $f <<EOF; + <ItemGroup> +EOF + foreach my $grammarFile (@grammarFiles) + { + (my $outputFile = $grammarFile) =~ s/\.(y|l)$/.c/; + if ($grammarFile =~ /\.y$/) + { + $outputFile =~ + s{^src\\pl\\plpgsql\\src\\gram.c$}{src\\pl\\plpgsql\\src\\pl_gram.c}; + print $f <<EOF; + <CustomBuild Include="$grammarFile"> + <Message Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">Running bison on $grammarFile</Message> + <Command Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">perl "src\\tools\\msvc\\pgbison.pl" "$grammarFile"</Command> + <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs> + <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">$outputFile;%(Outputs)</Outputs> + <Message Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">Running bison on $grammarFile</Message> + <Command Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">perl "src\\tools\\msvc\\pgbison.pl" "$grammarFile"</Command> + <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs> + <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">$outputFile;%(Outputs)</Outputs> + </CustomBuild> +EOF + } + else #if ($grammarFile =~ /\.l$/) + { + print $f <<EOF; + <CustomBuild Include="$grammarFile"> + <Message Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">Running flex on $grammarFile</Message> + <Command Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">perl "src\\tools\\msvc\\pgflex.pl" "$grammarFile"</Command> + <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs> + <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Debug|$self->{platform}'">$outputFile;%(Outputs)</Outputs> + <Message Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">Running flex on $grammarFile</Message> + <Command Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">perl "src\\tools\\msvc\\pgflex.pl" "$grammarFile"</Command> + <AdditionalInputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">%(AdditionalInputs)</AdditionalInputs> + <Outputs Condition="'\$(Configuration)|\$(Platform)'=='Release|$self->{platform}'">$outputFile;%(Outputs)</Outputs> + </CustomBuild> +EOF + } + } + print $f <<EOF; + </ItemGroup> +EOF + } + if (scalar(@resourceFiles)) + { + print $f <<EOF; + <ItemGroup> +EOF + foreach my $rcFile (@resourceFiles) + { + print $f <<EOF; + <ResourceCompile Include="$rcFile" /> +EOF + } + print $f <<EOF; + </ItemGroup> +EOF + } + return; +} + +sub WriteConfigurationHeader +{ + my ($self, $f, $cfgname) = @_; + print $f <<EOF; + <ProjectConfiguration Include="$cfgname|$self->{platform}"> + <Configuration>$cfgname</Configuration> + <Platform>$self->{platform}</Platform> + </ProjectConfiguration> +EOF + return; +} + +sub WriteConfigurationPropertyGroup +{ + my ($self, $f, $cfgname, $p) = @_; + my $cfgtype = + ($self->{type} eq "exe") + ? 'Application' + : ($self->{type} eq "dll" ? 'DynamicLibrary' : 'StaticLibrary'); + + print $f <<EOF; + <PropertyGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'" Label="Configuration"> + <ConfigurationType>$cfgtype</ConfigurationType> + <UseOfMfc>false</UseOfMfc> + <CharacterSet>MultiByte</CharacterSet> + <WholeProgramOptimization>$p->{wholeopt}</WholeProgramOptimization> + <PlatformToolset>$self->{PlatformToolset}</PlatformToolset> + </PropertyGroup> +EOF + return; +} + +sub WritePropertySheetsPropertyGroup +{ + my ($self, $f, $cfgname) = @_; + print $f <<EOF; + <ImportGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'" Label="PropertySheets"> + <Import Project="\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props" Condition="exists('\$(UserRootDir)\\Microsoft.Cpp.\$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> +EOF + return; +} + +sub WriteAdditionalProperties +{ + my ($self, $f, $cfgname) = @_; + print $f <<EOF; + <OutDir Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">.\\$cfgname\\$self->{name}\\</OutDir> + <IntDir Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">.\\$cfgname\\$self->{name}\\</IntDir> + <LinkIncremental Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'">false</LinkIncremental> +EOF + return; +} + +sub WriteItemDefinitionGroup +{ + my ($self, $f, $cfgname, $p) = @_; + my $cfgtype = + ($self->{type} eq "exe") + ? 'Application' + : ($self->{type} eq "dll" ? 'DynamicLibrary' : 'StaticLibrary'); + my $libs = $self->GetAdditionalLinkerDependencies($cfgname, ';'); + + my $targetmachine = + $self->{platform} eq 'Win32' ? 'MachineX86' : 'MachineX64'; + my $arch = $self->{platform} eq 'Win32' ? 'x86' : 'x86_64'; + + my $includes = join ';', @{ $self->{includes} }, ""; + + print $f <<EOF; + <ItemDefinitionGroup Condition="'\$(Configuration)|\$(Platform)'=='$cfgname|$self->{platform}'"> + <ClCompile> + <Optimization>$p->{opt}</Optimization> + <AdditionalIncludeDirectories>$self->{prefixincludes}src/include;src/include/port/win32;src/include/port/win32_msvc;$includes\%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_WINDOWS;__WINDOWS__;__WIN32__;WIN32_STACK_RLIMIT=4194304;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE$self->{defines}$p->{defs}\%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>$p->{strpool}</StringPooling> + <RuntimeLibrary>$p->{runtime}</RuntimeLibrary> + <DisableSpecificWarnings>$self->{disablewarnings};\%(DisableSpecificWarnings)</DisableSpecificWarnings> + <AdditionalOptions>/MP \%(AdditionalOptions)</AdditionalOptions> + <AssemblerOutput> + </AssemblerOutput> + <AssemblerListingLocation>.\\$cfgname\\$self->{name}\\</AssemblerListingLocation> + <ObjectFileName>.\\$cfgname\\$self->{name}\\</ObjectFileName> + <ProgramDataBaseFileName>.\\$cfgname\\$self->{name}\\</ProgramDataBaseFileName> + <BrowseInformation>false</BrowseInformation> + <WarningLevel>Level3</WarningLevel> + <SuppressStartupBanner>true</SuppressStartupBanner> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <CompileAs>Default</CompileAs> + </ClCompile> + <Link> + <OutputFile>.\\$cfgname\\$self->{name}\\$self->{name}.$self->{type}</OutputFile> + <AdditionalDependencies>$libs;\%(AdditionalDependencies)</AdditionalDependencies> + <SuppressStartupBanner>true</SuppressStartupBanner> + <AdditionalLibraryDirectories>\%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <IgnoreSpecificDefaultLibraries>libc;\%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries> + <StackReserveSize>4194304</StackReserveSize> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>.\\$cfgname\\$self->{name}\\$self->{name}.pdb</ProgramDatabaseFile> + <GenerateMapFile>false</GenerateMapFile> + <MapFileName>.\\$cfgname\\$self->{name}\\$self->{name}.map</MapFileName> + <!-- Permit links to MinGW-built, 32-bit DLLs (default before VS2012). --> + <ImageHasSafeExceptionHandlers/> + <SubSystem>Console</SubSystem> + <TargetMachine>$targetmachine</TargetMachine> +EOF + if ($self->{disablelinkerwarnings}) + { + print $f + " <AdditionalOptions>/ignore:$self->{disablelinkerwarnings} \%(AdditionalOptions)</AdditionalOptions>\n"; + } + if ($self->{implib}) + { + my $l = $self->{implib}; + $l =~ s/__CFGNAME__/$cfgname/g; + print $f " <ImportLibrary>$l</ImportLibrary>\n"; + } + if ($self->{def}) + { + my $d = $self->{def}; + $d =~ s/__CFGNAME__/$cfgname/g; + print $f " <ModuleDefinitionFile>$d</ModuleDefinitionFile>\n"; + } + print $f <<EOF; + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>src\\include;\%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ResourceCompile> +EOF + if ($self->{builddef}) + { + print $f <<EOF; + <PreLinkEvent> + <Message>Generate DEF file</Message> + <Command>perl src\\tools\\msvc\\gendef.pl --arch $arch --deffile $cfgname\\$self->{name}\\$self->{name}.def --tempdir $cfgname\\$self->{name} $cfgname\\$self->{name}</Command> + </PreLinkEvent> +EOF + } + print $f <<EOF; + </ItemDefinitionGroup> +EOF + return; +} + +sub Footer +{ + my ($self, $f) = @_; + $self->WriteReferences($f); + + print $f <<EOF; + <Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> +EOF + return; +} + +package VC2015Project; + +# +# Package that encapsulates a Visual C++ 2015 project file +# + +use strict; +use warnings; +use base qw(MSBuildProject); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{vcver} = '14.00'; + $self->{PlatformToolset} = 'v140'; + $self->{ToolsVersion} = '14.0'; + + return $self; +} + +package VC2017Project; + +# +# Package that encapsulates a Visual C++ 2017 project file +# + +use strict; +use warnings; +use base qw(MSBuildProject); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{vcver} = '15.00'; + $self->{PlatformToolset} = 'v141'; + $self->{ToolsVersion} = '15.0'; + + return $self; +} + +package VC2019Project; + +# +# Package that encapsulates a Visual C++ 2019 project file +# + +use strict; +use warnings; +use base qw(MSBuildProject); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{vcver} = '16.00'; + $self->{PlatformToolset} = 'v142'; + $self->{ToolsVersion} = '16.0'; + + return $self; +} + +package VC2022Project; + +# +# Package that encapsulates a Visual C++ 2022 project file +# + +use strict; +use warnings; +use base qw(MSBuildProject); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{vcver} = '17.00'; + $self->{PlatformToolset} = 'v143'; + $self->{ToolsVersion} = '17.0'; + + return $self; +} + +1; diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm new file mode 100644 index 0000000..9e05eb9 --- /dev/null +++ b/src/tools/msvc/Mkvcbuild.pm @@ -0,0 +1,1216 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +package Mkvcbuild; + +# +# Package that generates build files for msvc build +# +# src/tools/msvc/Mkvcbuild.pm +# +use strict; +use warnings; + +use Carp; +use if ($^O eq "MSWin32"), 'Win32'; +use Project; +use Solution; +use Cwd; +use File::Copy; +use Config; +use VSObjectFactory; +use List::Util qw(first); + +use Exporter; +our (@ISA, @EXPORT_OK); +@ISA = qw(Exporter); +@EXPORT_OK = qw(Mkvcbuild); + +my $solution; +my $libpgport; +my $libpgcommon; +my $libpgfeutils; +my $postgres; +my $libpq; +my @unlink_on_exit; + +# Set of variables for modules in contrib/ and src/test/modules/ +my $contrib_defines = {}; +my @contrib_uselibpq = (); +my @contrib_uselibpgport = (); +my @contrib_uselibpgcommon = (); +my $contrib_extralibs = { 'libpq_pipeline' => ['ws2_32.lib'] }; +my $contrib_extraincludes = {}; +my $contrib_extrasource = {}; +my @contrib_excludes = ( + 'bool_plperl', 'commit_ts', + 'hstore_plperl', 'hstore_plpython', + 'intagg', 'jsonb_plperl', + 'jsonb_plpython', 'ltree_plpython', + 'sepgsql', 'brin', + 'test_extensions', 'test_misc', + 'test_pg_dump', 'snapshot_too_old', + 'unsafe_tests'); + +# Set of variables for frontend modules +my $frontend_defines = { 'pgbench' => 'FD_SETSIZE=1024' }; +my @frontend_uselibpq = + ('pg_amcheck', 'pg_ctl', 'pg_upgrade', 'pgbench', 'psql', 'initdb'); +my @frontend_uselibpgport = ( + 'pg_amcheck', 'pg_archivecleanup', + 'pg_test_fsync', 'pg_test_timing', + 'pg_upgrade', 'pg_waldump', + 'pgbench'); +my @frontend_uselibpgcommon = ( + 'pg_amcheck', 'pg_archivecleanup', + 'pg_test_fsync', 'pg_test_timing', + 'pg_upgrade', 'pg_waldump', + 'pgbench'); +my $frontend_extralibs = { + 'initdb' => ['ws2_32.lib'], + 'pg_amcheck' => ['ws2_32.lib'], + 'pg_restore' => ['ws2_32.lib'], + 'pgbench' => ['ws2_32.lib'], + 'psql' => ['ws2_32.lib'] +}; +my $frontend_extraincludes = { + 'initdb' => ['src/timezone'], + 'psql' => ['src/backend'] +}; +my $frontend_extrasource = { + 'psql' => ['src/bin/psql/psqlscanslash.l'], + 'pgbench' => + [ 'src/bin/pgbench/exprscan.l', 'src/bin/pgbench/exprparse.y' ] +}; +my @frontend_excludes = ( + 'pgevent', 'pg_basebackup', 'pg_rewind', 'pg_dump', + 'pg_waldump', 'scripts'); + +sub mkvcbuild +{ + our $config = shift; + + chdir('../../..') if (-d '../msvc' && -d '../../../src'); + die 'Must run from root or msvc directory' + unless (-d 'src/tools/msvc' && -d 'src'); + + my $vsVersion = DetermineVisualStudioVersion(); + + $solution = CreateSolution($vsVersion, $config); + + our @pgportfiles = qw( + chklocale.c explicit_bzero.c + getpeereid.c inet_aton.c + inet_net_ntop.c kill.c open.c + snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c + dirent.c getopt.c getopt_long.c + preadv.c pwritev.c pg_bitutils.c + pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c + pqsignal.c mkdtemp.c qsort.c qsort_arg.c bsearch_arg.c quotes.c system.c + strerror.c tar.c + win32common.c + win32dlopen.c + win32env.c win32error.c + win32fdatasync.c + win32fseek.c + win32getrusage.c + win32gettimeofday.c + win32link.c + win32pread.c + win32pwrite.c + win32ntdll.c + win32security.c win32setlocale.c win32stat.c); + + push(@pgportfiles, 'strtof.c') if ($vsVersion < '14.00'); + + if ($vsVersion >= '9.00') + { + push(@pgportfiles, 'pg_crc32c_sse42_choose.c'); + push(@pgportfiles, 'pg_crc32c_sse42.c'); + push(@pgportfiles, 'pg_crc32c_sb8.c'); + } + else + { + push(@pgportfiles, 'pg_crc32c_sb8.c'); + } + + our @pgcommonallfiles = qw( + archive.c base64.c checksum_helper.c compression.c + config_info.c controldata_utils.c d2s.c encnames.c exec.c + f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.c + keywords.c kwlookup.c link-canary.c md5_common.c percentrepl.c + pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c + rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c + username.c wait_error.c wchar.c); + + if ($solution->{options}->{openssl}) + { + push(@pgcommonallfiles, 'cryptohash_openssl.c'); + push(@pgcommonallfiles, 'hmac_openssl.c'); + push(@pgcommonallfiles, 'protocol_openssl.c'); + } + else + { + push(@pgcommonallfiles, 'cryptohash.c'); + push(@pgcommonallfiles, 'hmac.c'); + push(@pgcommonallfiles, 'md5.c'); + push(@pgcommonallfiles, 'sha1.c'); + push(@pgcommonallfiles, 'sha2.c'); + } + + our @pgcommonfrontendfiles = ( + @pgcommonallfiles, qw(fe_memutils.c + logging.c restricted_token.c sprompt.c)); + + our @pgcommonbkndfiles = @pgcommonallfiles; + + our @pgfeutilsfiles = qw( + archive.c cancel.c conditional.c connect_utils.c mbprint.c option_utils.c + parallel_slot.c print.c psqlscan.l psqlscan.c query_utils.c simple_list.c + string_utils.c recovery_gen.c); + + $libpgport = $solution->AddProject('libpgport', 'lib', 'misc'); + $libpgport->AddDefine('FRONTEND'); + $libpgport->AddFiles('src/port', @pgportfiles); + + $libpgcommon = $solution->AddProject('libpgcommon', 'lib', 'misc'); + $libpgcommon->AddDefine('FRONTEND'); + $libpgcommon->AddFiles('src/common', @pgcommonfrontendfiles); + + $libpgfeutils = $solution->AddProject('libpgfeutils', 'lib', 'misc'); + $libpgfeutils->AddDefine('FRONTEND'); + $libpgfeutils->AddDefine('FD_SETSIZE=1024'); + $libpgfeutils->AddIncludeDir('src/interfaces/libpq'); + $libpgfeutils->AddFiles('src/fe_utils', @pgfeutilsfiles); + + $postgres = $solution->AddProject('postgres', 'exe', '', 'src/backend'); + $postgres->AddIncludeDir('src/backend'); + $postgres->AddDir('src/backend/port/win32'); + $postgres->AddFile('src/backend/utils/fmgrtab.c'); + $postgres->ReplaceFile('src/backend/port/pg_sema.c', + 'src/backend/port/win32_sema.c'); + $postgres->ReplaceFile('src/backend/port/pg_shmem.c', + 'src/backend/port/win32_shmem.c'); + $postgres->AddFiles('src/port', @pgportfiles); + $postgres->AddFiles('src/common', @pgcommonbkndfiles); + $postgres->AddDir('src/timezone'); + + # We need source files from src/timezone, but that directory's resource + # file pertains to "zic", not to the backend. + $postgres->RemoveFile('src/timezone/win32ver.rc'); + $postgres->AddFiles('src/backend/parser', 'scan.l', 'gram.y'); + $postgres->AddFiles('src/backend/bootstrap', 'bootscanner.l', + 'bootparse.y'); + $postgres->AddFiles('src/backend/utils/misc', 'guc-file.l'); + $postgres->AddFiles( + 'src/backend/replication', 'repl_scanner.l', + 'repl_gram.y', 'syncrep_scanner.l', + 'syncrep_gram.y'); + $postgres->AddFiles('src/backend/utils/adt', 'jsonpath_scan.l', + 'jsonpath_gram.y'); + $postgres->AddDefine('BUILDING_DLL'); + $postgres->AddLibrary('secur32.lib'); + $postgres->AddLibrary('ws2_32.lib'); + $postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap}); + $postgres->FullExportDLL('postgres.lib'); + + # The OBJS scraper doesn't know about ifdefs, so remove appropriate files + # if building without OpenSSL. + if (!$solution->{options}->{openssl}) + { + $postgres->RemoveFile('src/backend/libpq/be-secure-common.c'); + $postgres->RemoveFile('src/backend/libpq/be-secure-openssl.c'); + } + if (!$solution->{options}->{gss}) + { + $postgres->RemoveFile('src/backend/libpq/be-gssapi-common.c'); + $postgres->RemoveFile('src/backend/libpq/be-secure-gssapi.c'); + } + + my $snowball = $solution->AddProject('dict_snowball', 'dll', '', + 'src/backend/snowball'); + + # This Makefile uses VPATH to find most source files in a subdirectory. + $snowball->RelocateFiles( + 'src/backend/snowball/libstemmer', + sub { + return shift !~ /(dict_snowball.c|win32ver.rc)$/; + }); + $snowball->AddIncludeDir('src/include/snowball'); + $snowball->AddReference($postgres); + + my $plpgsql = + $solution->AddProject('plpgsql', 'dll', 'PLs', 'src/pl/plpgsql/src'); + $plpgsql->AddFiles('src/pl/plpgsql/src', 'pl_gram.y'); + $plpgsql->AddReference($postgres); + + if ($solution->{options}->{tcl}) + { + my $found = 0; + my $pltcl = + $solution->AddProject('pltcl', 'dll', 'PLs', 'src/pl/tcl'); + $pltcl->AddIncludeDir($solution->{options}->{tcl} . '/include'); + $pltcl->AddReference($postgres); + + for my $tclver (qw(86t 86 85 84)) + { + my $tcllib = $solution->{options}->{tcl} . "/lib/tcl$tclver.lib"; + if (-e $tcllib) + { + $pltcl->AddLibrary($tcllib); + $found = 1; + last; + } + } + die "Unable to find $solution->{options}->{tcl}/lib/tcl<version>.lib" + unless $found; + } + + $libpq = $solution->AddProject('libpq', 'dll', 'interfaces', + 'src/interfaces/libpq'); + $libpq->AddIncludeDir('src/port'); + $libpq->AddLibrary('secur32.lib'); + $libpq->AddLibrary('ws2_32.lib'); + $libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap}); + $libpq->UseDef('src/interfaces/libpq/libpqdll.def'); + $libpq->AddReference($libpgcommon, $libpgport); + + # The OBJS scraper doesn't know about ifdefs, so remove appropriate files + # if building without OpenSSL. + if (!$solution->{options}->{openssl}) + { + $libpq->RemoveFile('src/interfaces/libpq/fe-secure-common.c'); + $libpq->RemoveFile('src/interfaces/libpq/fe-secure-openssl.c'); + } + if (!$solution->{options}->{gss}) + { + $libpq->RemoveFile('src/interfaces/libpq/fe-gssapi-common.c'); + $libpq->RemoveFile('src/interfaces/libpq/fe-secure-gssapi.c'); + } + + my $libpqwalreceiver = + $solution->AddProject('libpqwalreceiver', 'dll', '', + 'src/backend/replication/libpqwalreceiver'); + $libpqwalreceiver->AddIncludeDir('src/interfaces/libpq'); + $libpqwalreceiver->AddReference($postgres, $libpq); + + my $libpq_testclient = + $solution->AddProject('libpq_testclient', 'exe', 'misc', + 'src/interfaces/libpq/test'); + $libpq_testclient->AddFile( + 'src/interfaces/libpq/test/libpq_testclient.c'); + $libpq_testclient->AddIncludeDir('src/interfaces/libpq'); + $libpq_testclient->AddReference($libpgport, $libpq); + $libpq_testclient->AddLibrary('ws2_32.lib'); + + my $libpq_uri_regress = + $solution->AddProject('libpq_uri_regress', 'exe', 'misc', + 'src/interfaces/libpq/test'); + $libpq_uri_regress->AddFile( + 'src/interfaces/libpq/test/libpq_uri_regress.c'); + $libpq_uri_regress->AddIncludeDir('src/interfaces/libpq'); + $libpq_uri_regress->AddReference($libpgport, $libpq); + $libpq_uri_regress->AddLibrary('ws2_32.lib'); + + my $pgoutput = $solution->AddProject('pgoutput', 'dll', '', + 'src/backend/replication/pgoutput'); + $pgoutput->AddReference($postgres); + + my $pgtypes = $solution->AddProject( + 'libpgtypes', 'dll', + 'interfaces', 'src/interfaces/ecpg/pgtypeslib'); + $pgtypes->AddReference($libpgcommon, $libpgport); + $pgtypes->UseDef('src/interfaces/ecpg/pgtypeslib/pgtypeslib.def'); + $pgtypes->AddIncludeDir('src/interfaces/ecpg/include'); + + my $libecpg = $solution->AddProject('libecpg', 'dll', 'interfaces', + 'src/interfaces/ecpg/ecpglib'); + $libecpg->AddIncludeDir('src/interfaces/ecpg/include'); + $libecpg->AddIncludeDir('src/interfaces/libpq'); + $libecpg->AddIncludeDir('src/port'); + $libecpg->UseDef('src/interfaces/ecpg/ecpglib/ecpglib.def'); + $libecpg->AddLibrary('ws2_32.lib'); + $libecpg->AddReference($libpq, $pgtypes, $libpgport); + + my $libecpgcompat = $solution->AddProject( + 'libecpg_compat', 'dll', + 'interfaces', 'src/interfaces/ecpg/compatlib'); + $libecpgcompat->AddIncludeDir('src/interfaces/ecpg/include'); + $libecpgcompat->AddIncludeDir('src/interfaces/libpq'); + $libecpgcompat->UseDef('src/interfaces/ecpg/compatlib/compatlib.def'); + $libecpgcompat->AddReference($pgtypes, $libecpg, $libpgport, + $libpgcommon); + + my $ecpg = $solution->AddProject('ecpg', 'exe', 'interfaces', + 'src/interfaces/ecpg/preproc'); + $ecpg->AddIncludeDir('src/interfaces/ecpg/include'); + $ecpg->AddIncludeDir('src/interfaces/ecpg/ecpglib'); + $ecpg->AddIncludeDir('src/interfaces/libpq'); + $ecpg->AddPrefixInclude('src/interfaces/ecpg/preproc'); + $ecpg->AddFiles('src/interfaces/ecpg/preproc', 'pgc.l', 'preproc.y'); + $ecpg->AddReference($libpgcommon, $libpgport); + + my $pgregress_ecpg = + $solution->AddProject('pg_regress_ecpg', 'exe', 'misc'); + $pgregress_ecpg->AddFile('src/interfaces/ecpg/test/pg_regress_ecpg.c'); + $pgregress_ecpg->AddFile('src/test/regress/pg_regress.c'); + $pgregress_ecpg->AddIncludeDir('src/port'); + $pgregress_ecpg->AddIncludeDir('src/test/regress'); + $pgregress_ecpg->AddDefine('HOST_TUPLE="i686-pc-win32vc"'); + $pgregress_ecpg->AddLibrary('ws2_32.lib'); + $pgregress_ecpg->AddDirResourceFile('src/interfaces/ecpg/test'); + $pgregress_ecpg->AddReference($libpgcommon, $libpgport); + + my $isolation_tester = + $solution->AddProject('isolationtester', 'exe', 'misc'); + $isolation_tester->AddFile('src/test/isolation/isolationtester.c'); + $isolation_tester->AddFile('src/test/isolation/specparse.y'); + $isolation_tester->AddFile('src/test/isolation/specscanner.l'); + $isolation_tester->AddFile('src/test/isolation/specparse.c'); + $isolation_tester->AddFile('src/test/isolation/specscanner.c'); + $isolation_tester->AddIncludeDir('src/test/isolation'); + $isolation_tester->AddIncludeDir('src/port'); + $isolation_tester->AddIncludeDir('src/test/regress'); + $isolation_tester->AddIncludeDir('src/interfaces/libpq'); + $isolation_tester->AddDefine('HOST_TUPLE="i686-pc-win32vc"'); + $isolation_tester->AddLibrary('ws2_32.lib'); + $isolation_tester->AddDirResourceFile('src/test/isolation'); + $isolation_tester->AddReference($libpq, $libpgcommon, $libpgport); + + my $pgregress_isolation = + $solution->AddProject('pg_isolation_regress', 'exe', 'misc'); + $pgregress_isolation->AddFile('src/test/isolation/isolation_main.c'); + $pgregress_isolation->AddFile('src/test/regress/pg_regress.c'); + $pgregress_isolation->AddIncludeDir('src/port'); + $pgregress_isolation->AddIncludeDir('src/test/regress'); + $pgregress_isolation->AddDefine('HOST_TUPLE="i686-pc-win32vc"'); + $pgregress_isolation->AddLibrary('ws2_32.lib'); + $pgregress_isolation->AddDirResourceFile('src/test/isolation'); + $pgregress_isolation->AddReference($libpgcommon, $libpgport); + + # src/bin + my $D; + opendir($D, 'src/bin') || croak "Could not opendir on src/bin!\n"; + while (my $d = readdir($D)) + { + next if ($d =~ /^\./); + next unless (-f "src/bin/$d/Makefile"); + next if (grep { /^$d$/ } @frontend_excludes); + AddSimpleFrontend($d); + } + + my $pgbasebackup = AddSimpleFrontend('pg_basebackup', 1); + # This list of files has to match BBOBJS in pg_basebackup's Makefile. + $pgbasebackup->AddFile('src/bin/pg_basebackup/pg_basebackup.c'); + $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_file.c'); + $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_gzip.c'); + $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_inject.c'); + $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_lz4.c'); + $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_tar.c'); + $pgbasebackup->AddFile('src/bin/pg_basebackup/bbstreamer_zstd.c'); + $pgbasebackup->AddLibrary('ws2_32.lib'); + + my $pgreceivewal = AddSimpleFrontend('pg_basebackup', 1); + $pgreceivewal->{name} = 'pg_receivewal'; + $pgreceivewal->AddFile('src/bin/pg_basebackup/pg_receivewal.c'); + $pgreceivewal->AddLibrary('ws2_32.lib'); + + my $pgrecvlogical = AddSimpleFrontend('pg_basebackup', 1); + $pgrecvlogical->{name} = 'pg_recvlogical'; + $pgrecvlogical->AddFile('src/bin/pg_basebackup/pg_recvlogical.c'); + $pgrecvlogical->AddLibrary('ws2_32.lib'); + + my $pgrewind = AddSimpleFrontend('pg_rewind', 1); + $pgrewind->{name} = 'pg_rewind'; + $pgrewind->AddFile('src/backend/access/transam/xlogreader.c'); + $pgrewind->AddLibrary('ws2_32.lib'); + $pgrewind->AddDefine('FRONTEND'); + + my $pgevent = $solution->AddProject('pgevent', 'dll', 'bin'); + $pgevent->AddFiles('src/bin/pgevent', 'pgevent.c', 'pgmsgevent.rc'); + $pgevent->AddResourceFile('src/bin/pgevent', 'Eventlog message formatter', + 'win32'); + $pgevent->UseDef('src/bin/pgevent/pgevent.def'); + $pgevent->DisableLinkerWarnings('4104'); + + my $pgdump = AddSimpleFrontend('pg_dump', 1); + $pgdump->AddIncludeDir('src/backend'); + $pgdump->AddFile('src/bin/pg_dump/pg_dump.c'); + $pgdump->AddFile('src/bin/pg_dump/common.c'); + $pgdump->AddFile('src/bin/pg_dump/pg_dump_sort.c'); + $pgdump->AddLibrary('ws2_32.lib'); + + my $pgdumpall = AddSimpleFrontend('pg_dump', 1); + + # pg_dumpall doesn't use the files in the Makefile's $(OBJS), unlike + # pg_dump and pg_restore. + # So remove their sources from the object, keeping the other setup that + # AddSimpleFrontend() has done. + my @nodumpall = grep { m!src/bin/pg_dump/.*\.c$! } + keys %{ $pgdumpall->{files} }; + delete @{ $pgdumpall->{files} }{@nodumpall}; + $pgdumpall->{name} = 'pg_dumpall'; + $pgdumpall->AddIncludeDir('src/backend'); + $pgdumpall->AddFile('src/bin/pg_dump/pg_dumpall.c'); + $pgdumpall->AddFile('src/bin/pg_dump/dumputils.c'); + $pgdumpall->AddLibrary('ws2_32.lib'); + + my $pgrestore = AddSimpleFrontend('pg_dump', 1); + $pgrestore->{name} = 'pg_restore'; + $pgrestore->AddIncludeDir('src/backend'); + $pgrestore->AddFile('src/bin/pg_dump/pg_restore.c'); + $pgrestore->AddLibrary('ws2_32.lib'); + + my $zic = $solution->AddProject('zic', 'exe', 'utils'); + $zic->AddFiles('src/timezone', 'zic.c'); + $zic->AddDirResourceFile('src/timezone'); + $zic->AddReference($libpgcommon, $libpgport); + + if (!$solution->{options}->{xml}) + { + push @contrib_excludes, 'xml2'; + } + + if (!$solution->{options}->{openssl}) + { + push @contrib_excludes, 'sslinfo', 'ssl_passphrase_callback', + 'pgcrypto'; + } + + if (!$solution->{options}->{ldap}) + { + push @contrib_excludes, 'ldap_password_func'; + } + + if (!$solution->{options}->{uuid}) + { + push @contrib_excludes, 'uuid-ossp'; + } + + foreach my $subdir ('contrib', 'src/test/modules') + { + opendir($D, $subdir) || croak "Could not opendir on $subdir!\n"; + while (my $d = readdir($D)) + { + next if ($d =~ /^\./); + next unless (-f "$subdir/$d/Makefile"); + next if (grep { /^$d$/ } @contrib_excludes); + AddContrib($subdir, $d); + } + closedir($D); + } + + # Build Perl and Python modules after contrib/ modules to satisfy some + # dependencies with transform contrib modules, like hstore_plpython + # ltree_plpython and hstore_plperl. + if ($solution->{options}->{python}) + { + + # Attempt to get python version and location. + # Assume python.exe in specified dir. + my $pythonprog = "import sys;print(sys.prefix);" + . "print(str(sys.version_info[0])+str(sys.version_info[1]))"; + my $prefixcmd = + qq("$solution->{options}->{python}\\python" -c "$pythonprog"); + my $pyout = `$prefixcmd`; + die "Could not query for python version!\n" if $?; + my ($pyprefix, $pyver) = split(/\r?\n/, $pyout); + + # Sometimes (always?) if python is not present, the execution + # appears to work, but gives no data... + die "Failed to query python for version information\n" + if (!(defined($pyprefix) && defined($pyver))); + + my $pymajorver = substr($pyver, 0, 1); + + die + "Python version $pyver is too old (version 3 or later is required)" + if int($pymajorver) < 3; + + my $plpython = $solution->AddProject('plpython' . $pymajorver, + 'dll', 'PLs', 'src/pl/plpython'); + $plpython->AddIncludeDir($pyprefix . '/include'); + $plpython->AddLibrary($pyprefix . "/Libs/python$pyver.lib"); + $plpython->AddReference($postgres); + + # Add transform modules dependent on plpython + my $hstore_plpython = AddTransformModule( + 'hstore_plpython' . $pymajorver, 'contrib/hstore_plpython', + 'plpython' . $pymajorver, 'src/pl/plpython', + 'hstore', 'contrib'); + $hstore_plpython->AddDefine( + 'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"'); + my $jsonb_plpython = AddTransformModule( + 'jsonb_plpython' . $pymajorver, 'contrib/jsonb_plpython', + 'plpython' . $pymajorver, 'src/pl/plpython'); + $jsonb_plpython->AddDefine( + 'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"'); + my $ltree_plpython = AddTransformModule( + 'ltree_plpython' . $pymajorver, 'contrib/ltree_plpython', + 'plpython' . $pymajorver, 'src/pl/plpython', + 'ltree', 'contrib'); + $ltree_plpython->AddDefine( + 'PLPYTHON_LIBNAME="plpython' . $pymajorver . '"'); + } + + if ($solution->{options}->{perl}) + { + my $plperlsrc = "src/pl/plperl/"; + my $plperl = + $solution->AddProject('plperl', 'dll', 'PLs', 'src/pl/plperl'); + $plperl->AddIncludeDir($solution->{options}->{perl} . '/lib/CORE'); + $plperl->AddReference($postgres); + + my $perl_path = $solution->{options}->{perl} . '\lib\CORE\*perl*'; + + # ActivePerl 5.16 provided perl516.lib; 5.18 provided libperl518.a + # Starting with ActivePerl 5.24, both perlnn.lib and libperlnn.a are provided. + # In this case, prefer .lib. + my @perl_libs = + reverse sort grep { /perl\d+\.lib$|libperl\d+\.a$/ } + glob($perl_path); + if (@perl_libs > 0) + { + $plperl->AddLibrary($perl_libs[0]); + } + else + { + die + "could not identify perl library version matching pattern $perl_path\n"; + } + + # Add defines from Perl's ccflags; see PGAC_CHECK_PERL_EMBED_CCFLAGS + my @perl_embed_ccflags; + foreach my $f (split(" ", $Config{ccflags})) + { + if ($f =~ /^-D[^_]/) + { + $f =~ s/\-D//; + push(@perl_embed_ccflags, $f); + } + } + + # hack to prevent duplicate definitions of uid_t/gid_t + push(@perl_embed_ccflags, 'PLPERL_HAVE_UID_GID'); + # prevent binary mismatch between MSVC built plperl and + # Strawberry or msys ucrt perl libraries + push(@perl_embed_ccflags, 'NO_THREAD_SAFE_LOCALE'); + + # Windows offers several 32-bit ABIs. Perl is sensitive to + # sizeof(time_t), one of the ABI dimensions. To get 32-bit time_t, + # use "cl -D_USE_32BIT_TIME_T" or plain "gcc". For 64-bit time_t, use + # "gcc -D__MINGW_USE_VC2005_COMPAT" or plain "cl". Before MSVC 2005, + # plain "cl" chose 32-bit time_t. PostgreSQL doesn't support building + # with pre-MSVC-2005 compilers, but it does support linking to Perl + # built with such a compiler. MSVC-built Perl 5.13.4 and later report + # -D_USE_32BIT_TIME_T in $Config{ccflags} if applicable, but + # MinGW-built Perl never reports -D_USE_32BIT_TIME_T despite typically + # needing it. Ignore the $Config{ccflags} opinion about + # -D_USE_32BIT_TIME_T, and use a runtime test to deduce the ABI Perl + # expects. Specifically, test use of PL_modglobal, which maps to a + # PerlInterpreter field whose position depends on sizeof(time_t). + if ($solution->{platform} eq 'Win32') + { + my $source_file = 'conftest.c'; + my $obj = 'conftest.obj'; + my $exe = 'conftest.exe'; + my @conftest = ($source_file, $obj, $exe); + push @unlink_on_exit, @conftest; + unlink $source_file; + open my $o, '>', $source_file + || croak "Could not write to $source_file"; + print $o ' + /* compare to plperl.h */ + #define __inline__ __inline + #define PERL_NO_GET_CONTEXT + #include <EXTERN.h> + #include <perl.h> + + int + main(int argc, char **argv) + { + int dummy_argc = 1; + char *dummy_argv[1] = {""}; + char *dummy_env[1] = {NULL}; + static PerlInterpreter *interp; + + PERL_SYS_INIT3(&dummy_argc, (char ***) &dummy_argv, + (char ***) &dummy_env); + interp = perl_alloc(); + perl_construct(interp); + { + dTHX; + const char key[] = "dummy"; + + PL_exit_flags |= PERL_EXIT_DESTRUCT_END; + hv_store(PL_modglobal, key, sizeof(key) - 1, newSViv(1), 0); + return hv_fetch(PL_modglobal, key, sizeof(key) - 1, 0) == NULL; + } + } +'; + close $o; + + # Build $source_file with a given #define, and return a true value + # if a run of the resulting binary exits successfully. + my $try_define = sub { + my $define = shift; + + unlink $obj, $exe; + my @cmd = ( + 'cl', + '-I' . $solution->{options}->{perl} . '/lib/CORE', + (map { "-D$_" } @perl_embed_ccflags, $define || ()), + $source_file, + '/link', + $perl_libs[0]); + my $compile_output = `@cmd 2>&1`; + -f $exe || die "Failed to build Perl test:\n$compile_output"; + + { + + # Some builds exhibit runtime failure through Perl warning + # 'Can't spawn "conftest.exe"'; suppress that. + no warnings; + + no strict 'subs'; ## no critic (ProhibitNoStrict) + + # Disable error dialog boxes like we do in the postmaster. + # Here, we run code that triggers relevant errors. + use + if ($^O eq "MSWin32"), 'Win32API::File', + qw(SetErrorMode :SEM_); + my $oldmode = SetErrorMode( + SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + system(".\\$exe"); + SetErrorMode($oldmode); + } + + return !($? >> 8); + }; + + my $define_32bit_time = '_USE_32BIT_TIME_T'; + my $ok_now = $try_define->(undef); + my $ok_32bit = $try_define->($define_32bit_time); + unlink @conftest; + if (!$ok_now && !$ok_32bit) + { + + # Unsupported configuration. Since we used %Config from the + # Perl running the build scripts, this is expected if + # attempting to link with some other Perl. + die "Perl test fails with or without -D$define_32bit_time"; + } + elsif ($ok_now && $ok_32bit) + { + + # Resulting build may work, but it's especially important to + # verify with "vcregress plcheck". A refined test may avoid + # this outcome. + warn "Perl test passes with or without -D$define_32bit_time"; + } + elsif ($ok_32bit) + { + push(@perl_embed_ccflags, $define_32bit_time); + } # else $ok_now, hence no flag required + } + + print "CFLAGS recommended by Perl: $Config{ccflags}\n"; + print "CFLAGS to compile embedded Perl: ", + (join ' ', map { "-D$_" } @perl_embed_ccflags), "\n"; + foreach my $f (@perl_embed_ccflags) + { + $plperl->AddDefine($f); + } + + foreach my $xs ('SPI.xs', 'Util.xs') + { + (my $xsc = $xs) =~ s/\.xs/.c/; + if (Solution::IsNewer("$plperlsrc$xsc", "$plperlsrc$xs")) + { + my $xsubppdir = first { -e "$_/ExtUtils/xsubpp" } (@INC); + print "Building $plperlsrc$xsc...\n"; + system( $solution->{options}->{perl} + . '/bin/perl ' + . "$xsubppdir/ExtUtils/xsubpp -typemap " + . $solution->{options}->{perl} + . '/lib/ExtUtils/typemap ' + . "$plperlsrc$xs " + . ">$plperlsrc$xsc"); + if ((!(-f "$plperlsrc$xsc")) || -z "$plperlsrc$xsc") + { + unlink("$plperlsrc$xsc"); # if zero size + die "Failed to create $xsc.\n"; + } + } + } + if (Solution::IsNewer( + 'src/pl/plperl/perlchunks.h', + 'src/pl/plperl/plc_perlboot.pl') + || Solution::IsNewer( + 'src/pl/plperl/perlchunks.h', + 'src/pl/plperl/plc_trusted.pl')) + { + print 'Building src/pl/plperl/perlchunks.h ...' . "\n"; + my $basedir = getcwd; + chdir 'src/pl/plperl'; + system( $solution->{options}->{perl} + . '/bin/perl ' + . 'text2macro.pl ' + . '--strip="^(\#.*|\s*)$$" ' + . 'plc_perlboot.pl plc_trusted.pl ' + . '>perlchunks.h'); + chdir $basedir; + if ((!(-f 'src/pl/plperl/perlchunks.h')) + || -z 'src/pl/plperl/perlchunks.h') + { + unlink('src/pl/plperl/perlchunks.h'); # if zero size + die 'Failed to create perlchunks.h' . "\n"; + } + } + if (Solution::IsNewer( + 'src/pl/plperl/plperl_opmask.h', + 'src/pl/plperl/plperl_opmask.pl')) + { + print 'Building src/pl/plperl/plperl_opmask.h ...' . "\n"; + my $basedir = getcwd; + chdir 'src/pl/plperl'; + system( $solution->{options}->{perl} + . '/bin/perl ' + . 'plperl_opmask.pl ' + . 'plperl_opmask.h'); + chdir $basedir; + if ((!(-f 'src/pl/plperl/plperl_opmask.h')) + || -z 'src/pl/plperl/plperl_opmask.h') + { + unlink('src/pl/plperl/plperl_opmask.h'); # if zero size + die 'Failed to create plperl_opmask.h' . "\n"; + } + } + + # Add transform modules dependent on plperl + my $bool_plperl = AddTransformModule( + 'bool_plperl', 'contrib/bool_plperl', + 'plperl', 'src/pl/plperl'); + my $hstore_plperl = AddTransformModule( + 'hstore_plperl', 'contrib/hstore_plperl', + 'plperl', 'src/pl/plperl', + 'hstore', 'contrib'); + my $jsonb_plperl = AddTransformModule( + 'jsonb_plperl', 'contrib/jsonb_plperl', + 'plperl', 'src/pl/plperl'); + + foreach my $f (@perl_embed_ccflags) + { + $bool_plperl->AddDefine($f); + $hstore_plperl->AddDefine($f); + $jsonb_plperl->AddDefine($f); + } + } + + my $mf = + Project::read_file('src/backend/utils/mb/conversion_procs/Makefile'); + $mf =~ s{\\\r?\n}{}g; + $mf =~ m{SUBDIRS\s*=\s*(.*)$}m + || die 'Could not match in conversion makefile' . "\n"; + foreach my $sub (split /\s+/, $1) + { + my $dir = 'src/backend/utils/mb/conversion_procs/' . $sub; + my $p = $solution->AddProject($sub, 'dll', 'conversion procs', $dir); + $p->AddFile("$dir/$sub.c"); # implicit source file + $p->AddReference($postgres); + } + + $mf = Project::read_file('src/bin/scripts/Makefile'); + $mf =~ s{\\\r?\n}{}g; + $mf =~ m{PROGRAMS\s*=\s*(.*)$}m + || die 'Could not match in bin/scripts/Makefile' . "\n"; + foreach my $prg (split /\s+/, $1) + { + my $proj = $solution->AddProject($prg, 'exe', 'bin'); + $mf =~ m{$prg\s*:\s*(.*)$}m + || die 'Could not find script define for $prg' . "\n"; + my @files = split /\s+/, $1; + foreach my $f (@files) + { + $f =~ s/\.o$/\.c/; + if ($f =~ /\.c$/) + { + $proj->AddFile('src/bin/scripts/' . $f); + } + } + $proj->AddIncludeDir('src/interfaces/libpq'); + $proj->AddReference($libpq, $libpgfeutils, $libpgcommon, $libpgport); + $proj->AddDirResourceFile('src/bin/scripts'); + $proj->AddLibrary('ws2_32.lib'); + } + + # Regression DLL and EXE + my $regress = $solution->AddProject('regress', 'dll', 'misc'); + $regress->AddFile('src/test/regress/regress.c'); + $regress->AddDirResourceFile('src/test/regress'); + $regress->AddReference($postgres); + + my $pgregress = $solution->AddProject('pg_regress', 'exe', 'misc'); + $pgregress->AddFile('src/test/regress/pg_regress.c'); + $pgregress->AddFile('src/test/regress/pg_regress_main.c'); + $pgregress->AddIncludeDir('src/port'); + $pgregress->AddDefine('HOST_TUPLE="i686-pc-win32vc"'); + $pgregress->AddLibrary('ws2_32.lib'); + $pgregress->AddDirResourceFile('src/test/regress'); + $pgregress->AddReference($libpgcommon, $libpgport); + + # fix up pg_waldump once it's been set up + # files symlinked on Unix are copied on windows + my $pg_waldump = AddSimpleFrontend('pg_waldump'); + $pg_waldump->AddDefine('FRONTEND'); + foreach my $xf (glob('src/backend/access/rmgrdesc/*desc*.c')) + { + $pg_waldump->AddFile($xf); + } + $pg_waldump->AddFile('src/backend/access/transam/xlogreader.c'); + + $solution->Save(); + return $solution->{vcver}; +} + +##################### +# Utility functions # +##################### + +# Add a simple frontend project (exe) +sub AddSimpleFrontend +{ + my $n = shift; + my $uselibpq = shift; + + my $p = $solution->AddProject($n, 'exe', 'bin'); + $p->AddDir('src/bin/' . $n); + $p->AddReference($libpgfeutils, $libpgcommon, $libpgport); + if ($uselibpq) + { + $p->AddIncludeDir('src/interfaces/libpq'); + $p->AddReference($libpq); + } + + # Adjust module definition using frontend variables + AdjustFrontendProj($p); + + return $p; +} + +# Add a simple transform module +sub AddTransformModule +{ + my $n = shift; + my $n_src = shift; + my $pl_proj_name = shift; + my $pl_src = shift; + my $type_name = shift; + my $type_src = shift; + + my $type_proj = undef; + if ($type_name) + { + foreach my $proj (@{ $solution->{projects}->{'contrib'} }) + { + if ($proj->{name} eq $type_name) + { + $type_proj = $proj; + last; + } + } + die "could not find base module $type_name for transform module $n" + if (!defined($type_proj)); + } + + my $pl_proj = undef; + foreach my $proj (@{ $solution->{projects}->{'PLs'} }) + { + if ($proj->{name} eq $pl_proj_name) + { + $pl_proj = $proj; + last; + } + } + die "could not find PL $pl_proj_name for transform module $n" + if (!defined($pl_proj)); + + my $p = $solution->AddProject($n, 'dll', 'contrib', $n_src); + for my $file (glob("$n_src/*.c")) + { + $p->AddFile($file); + } + $p->AddReference($postgres); + + # Add PL dependencies + $p->AddIncludeDir($pl_src); + $p->AddReference($pl_proj); + $p->AddIncludeDir($_) for @{ $pl_proj->{includes} }; + foreach my $pl_lib (@{ $pl_proj->{libraries} }) + { + $p->AddLibrary($pl_lib); + } + + # Add base module dependencies + if ($type_proj) + { + $p->AddIncludeDir($type_src); + $p->AddIncludeDir($_) for @{ $type_proj->{includes} }; + foreach my $type_lib (@{ $type_proj->{libraries} }) + { + $p->AddLibrary($type_lib); + } + $p->AddReference($type_proj); + } + + return $p; +} + +# Add a simple contrib project +sub AddContrib +{ + my $subdir = shift; + my $n = shift; + my $mf = Project::read_file("$subdir/$n/Makefile"); + my @projects = (); + + if ($mf =~ /^MODULE_big\s*=\s*(.*)$/mg) + { + my $dn = $1; + my $proj = $solution->AddProject($dn, 'dll', 'contrib', "$subdir/$n"); + $proj->AddReference($postgres); + AdjustContribProj($proj); + push @projects, $proj; + } + elsif ($mf =~ /^MODULES\s*=\s*(.*)$/mg) + { + foreach my $mod (split /\s+/, $1) + { + my $proj = + $solution->AddProject($mod, 'dll', 'contrib', "$subdir/$n"); + my $filename = $mod . '.c'; + $proj->AddFile("$subdir/$n/$filename"); + $proj->AddReference($postgres); + AdjustContribProj($proj); + push @projects, $proj; + } + } + elsif ($mf =~ /^PROGRAM\s*=\s*(.*)$/mg) + { + my $proj = $solution->AddProject($1, 'exe', 'contrib', "$subdir/$n"); + AdjustContribProj($proj); + push @projects, $proj; + } + else + { + croak "Could not determine contrib module type for $n\n"; + } + + # Process custom compiler flags + if ( $mf =~ /^PG_CPPFLAGS\s*=\s*(.*)$/mg + || $mf =~ /^override\s*CPPFLAGS\s*[+:]?=\s*(.*)$/mg) + { + foreach my $flag (split /\s+/, $1) + { + if ($flag =~ /^-D(.*)$/) + { + foreach my $proj (@projects) + { + $proj->AddDefine($1); + } + } + elsif ($flag =~ /^-I(.*)$/) + { + if ($1 eq '$(libpq_srcdir)') + { + foreach my $proj (@projects) + { + $proj->AddIncludeDir('src/interfaces/libpq'); + $proj->AddReference($libpq); + } + } + } + } + } + + if ($mf =~ /^SHLIB_LINK_INTERNAL\s*[+:]?=\s*(.*)$/mg) + { + foreach my $lib (split /\s+/, $1) + { + if ($lib eq '$(libpq)') + { + foreach my $proj (@projects) + { + $proj->AddIncludeDir('src/interfaces/libpq'); + $proj->AddReference($libpq); + } + } + } + } + + if ($mf =~ /^PG_LIBS_INTERNAL\s*[+:]?=\s*(.*)$/mg) + { + foreach my $lib (split /\s+/, $1) + { + if ($lib eq '$(libpq_pgport)') + { + foreach my $proj (@projects) + { + $proj->AddReference($libpgport); + $proj->AddReference($libpgcommon); + } + } + } + } + + foreach my $line (split /\n/, $mf) + { + if ($line =~ /^[A-Za-z0-9_]*\.o:\s(.*)/) + { + foreach my $file (split /\s+/, $1) + { + foreach my $proj (@projects) + { + $proj->AddDependantFiles("$subdir/$n/$file"); + } + } + } + } + + # Are there any output data files to build? + GenerateContribSqlFiles($n, $mf); + return; +} + +sub GenerateContribSqlFiles +{ + my $n = shift; + my $mf = shift; + $mf =~ s{\\\r?\n}{}g; + if ($mf =~ /^DATA_built\s*=\s*(.*)$/mg) + { + my $l = $1; + + # Strip out $(addsuffix) rules + if (index($l, '$(addsuffix ') >= 0) + { + my $pcount = 0; + my $i; + for ($i = index($l, '$(addsuffix ') + 12; $i < length($l); $i++) + { + $pcount++ if (substr($l, $i, 1) eq '('); + $pcount-- if (substr($l, $i, 1) eq ')'); + last if ($pcount < 0); + } + $l = + substr($l, 0, index($l, '$(addsuffix ')) . substr($l, $i + 1); + } + + foreach my $d (split /\s+/, $l) + { + my $in = "$d.in"; + my $out = "$d"; + + if (Solution::IsNewer("contrib/$n/$out", "contrib/$n/$in")) + { + print "Building $out from $in (contrib/$n)...\n"; + my $cont = Project::read_file("contrib/$n/$in"); + my $dn = $out; + $dn =~ s/\.sql$//; + $cont =~ s/MODULE_PATHNAME/\$libdir\/$dn/g; + my $o; + open($o, '>', "contrib/$n/$out") + || croak "Could not write to contrib/$n/$d"; + print $o $cont; + close($o); + } + } + } + return; +} + +sub AdjustContribProj +{ + my $proj = shift; + AdjustModule( + $proj, $contrib_defines, + \@contrib_uselibpq, \@contrib_uselibpgport, + \@contrib_uselibpgcommon, $contrib_extralibs, + $contrib_extrasource, $contrib_extraincludes); + return; +} + +sub AdjustFrontendProj +{ + my $proj = shift; + AdjustModule( + $proj, $frontend_defines, + \@frontend_uselibpq, \@frontend_uselibpgport, + \@frontend_uselibpgcommon, $frontend_extralibs, + $frontend_extrasource, $frontend_extraincludes); + return; +} + +sub AdjustModule +{ + my $proj = shift; + my $module_defines = shift; + my $module_uselibpq = shift; + my $module_uselibpgport = shift; + my $module_uselibpgcommon = shift; + my $module_extralibs = shift; + my $module_extrasource = shift; + my $module_extraincludes = shift; + my $n = $proj->{name}; + + if ($module_defines->{$n}) + { + foreach my $d ($module_defines->{$n}) + { + $proj->AddDefine($d); + } + } + if (grep { /^$n$/ } @{$module_uselibpq}) + { + $proj->AddIncludeDir('src\interfaces\libpq'); + $proj->AddReference($libpq); + } + if (grep { /^$n$/ } @{$module_uselibpgport}) + { + $proj->AddReference($libpgport); + } + if (grep { /^$n$/ } @{$module_uselibpgcommon}) + { + $proj->AddReference($libpgcommon); + } + if ($module_extralibs->{$n}) + { + foreach my $l (@{ $module_extralibs->{$n} }) + { + $proj->AddLibrary($l); + } + } + if ($module_extraincludes->{$n}) + { + foreach my $i (@{ $module_extraincludes->{$n} }) + { + $proj->AddIncludeDir($i); + } + } + if ($module_extrasource->{$n}) + { + foreach my $i (@{ $module_extrasource->{$n} }) + { + print "Files $i\n"; + $proj->AddFile($i); + } + } + return; +} + +END +{ + unlink @unlink_on_exit; +} + +1; diff --git a/src/tools/msvc/Project.pm b/src/tools/msvc/Project.pm new file mode 100644 index 0000000..0507ad0 --- /dev/null +++ b/src/tools/msvc/Project.pm @@ -0,0 +1,482 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +package Project; + +# +# Package that encapsulates a Visual C++ project file generation +# +# src/tools/msvc/Project.pm +# +use Carp; +use strict; +use warnings; +use File::Basename; + +sub _new +{ + my ($classname, $name, $type, $solution) = @_; + my $good_types = { + lib => 1, + exe => 1, + dll => 1, + }; + confess("Bad project type: $type\n") unless exists $good_types->{$type}; + my $self = { + name => $name, + type => $type, + guid => $^O eq "MSWin32" ? Win32::GuidGen() : 'FAKE', + files => {}, + references => [], + libraries => [], + suffixlib => [], + includes => [], + prefixincludes => '', + defines => ';', + solution => $solution, + disablewarnings => '4018;4244;4273;4101;4102;4090;4267', + disablelinkerwarnings => '', + platform => $solution->{platform}, + }; + + bless($self, $classname); + return $self; +} + +sub AddFile +{ + my ($self, $filename) = @_; + + $self->FindAndAddAdditionalFiles($filename); + $self->{files}->{$filename} = 1; + return; +} + +sub AddDependantFiles +{ + my ($self, $filename) = @_; + + $self->FindAndAddAdditionalFiles($filename); + return; +} + +sub AddFiles +{ + my $self = shift; + my $dir = shift; + + while (my $f = shift) + { + $self->AddFile($dir . "/" . $f, 1); + } + return; +} + +# Handle Makefile rules by searching for other files which exist with the same +# name but a different file extension and add those files too. +sub FindAndAddAdditionalFiles +{ + my $self = shift; + my $fname = shift; + $fname =~ /(.*)(\.[^.]+)$/; + my $filenoext = $1; + my $fileext = $2; + + # For .c files, check if either a .l or .y file of the same name + # exists and add that too. + if ($fileext eq ".c") + { + my $file = $filenoext . ".l"; + if (-e $file) + { + $self->AddFile($file); + } + + $file = $filenoext . ".y"; + if (-e $file) + { + $self->AddFile($file); + } + } +} + +sub ReplaceFile +{ + my ($self, $filename, $newname) = @_; + my $re = "\\/$filename\$"; + + foreach my $file (keys %{ $self->{files} }) + { + + # Match complete filename + if ($filename =~ m!/!) + { + if ($file eq $filename) + { + delete $self->{files}{$file}; + $self->AddFile($newname); + return; + } + } + elsif ($file =~ m/($re)/) + { + delete $self->{files}{$file}; + $self->AddFile("$newname/$filename"); + return; + } + } + confess("Could not find file $filename to replace\n"); +} + +sub RemoveFile +{ + my ($self, $filename) = @_; + my $orig = scalar keys %{ $self->{files} }; + delete $self->{files}->{$filename}; + if ($orig > scalar keys %{ $self->{files} }) + { + return; + } + confess("Could not find file $filename to remove\n"); +} + +sub RelocateFiles +{ + my ($self, $targetdir, $proc) = @_; + foreach my $f (keys %{ $self->{files} }) + { + my $r = &$proc($f); + if ($r) + { + $self->RemoveFile($f); + $self->AddFile($targetdir . '/' . basename($f)); + } + } + return; +} + +sub AddReference +{ + my $self = shift; + + while (my $ref = shift) + { + if (!grep { $_ eq $ref } @{ $self->{references} }) + { + push @{ $self->{references} }, $ref; + } + $self->AddLibrary( + "__CFGNAME__/" . $ref->{name} . "/" . $ref->{name} . ".lib"); + } + return; +} + +sub AddLibrary +{ + my ($self, $lib, $dbgsuffix) = @_; + + # quote lib name if it has spaces and isn't already quoted + if ($lib =~ m/\s/ && $lib !~ m/^[&]quot;/) + { + $lib = '"' . $lib . """; + } + + if (!grep { $_ eq $lib } @{ $self->{libraries} }) + { + push @{ $self->{libraries} }, $lib; + } + + if ($dbgsuffix) + { + push @{ $self->{suffixlib} }, $lib; + } + return; +} + +sub AddIncludeDir +{ + my ($self, $incstr) = @_; + + foreach my $inc (split(/;/, $incstr)) + { + if (!grep { $_ eq $inc } @{ $self->{includes} }) + { + push @{ $self->{includes} }, $inc; + } + } + return; +} + +sub AddPrefixInclude +{ + my ($self, $inc) = @_; + + $self->{prefixincludes} = $inc . ';' . $self->{prefixincludes}; + return; +} + +sub AddDefine +{ + my ($self, $def) = @_; + + $def =~ s/"/""/g; + $self->{defines} .= $def . ';'; + return; +} + +sub FullExportDLL +{ + my ($self, $libname) = @_; + + $self->{builddef} = 1; + $self->{def} = "./__CFGNAME__/$self->{name}/$self->{name}.def"; + $self->{implib} = "__CFGNAME__/$self->{name}/$libname"; + return; +} + +sub UseDef +{ + my ($self, $def) = @_; + + $self->{def} = $def; + return; +} + +sub AddDir +{ + my ($self, $reldir) = @_; + my $mf = read_makefile($reldir); + + $mf =~ s{\\\r?\n}{}g; + if ($mf =~ m{^(?:SUB)?DIRS[^=]*=\s*(.*)$}mg) + { + foreach my $subdir (split /\s+/, $1) + { + next + if $subdir eq "\$(top_builddir)/src/timezone" + ; #special case for non-standard include + next + if $reldir . "/" . $subdir eq "src/backend/port/darwin"; + + $self->AddDir($reldir . "/" . $subdir); + } + } + while ($mf =~ m{^(?:EXTRA_)?OBJS[^=]*=\s*(.*)$}m) + { + my $s = $1; + my $filter_re = qr{\$\(filter ([^,]+),\s+\$\(([^\)]+)\)\)}; + while ($s =~ /$filter_re/) + { + + # Process $(filter a b c, $(VAR)) expressions + my $list = $1; + my $filter = $2; + $list =~ s/\.o/\.c/g; + my @pieces = split /\s+/, $list; + my $matches = ""; + foreach my $p (@pieces) + { + + if ($filter eq "LIBOBJS") + { + no warnings qw(once); + if (grep(/$p/, @main::pgportfiles) == 1) + { + $p =~ s/\.c/\.o/; + $matches .= $p . " "; + } + } + else + { + confess "Unknown filter $filter\n"; + } + } + $s =~ s/$filter_re/$matches/; + } + foreach my $f (split /\s+/, $s) + { + next if $f =~ /^\s*$/; + next if $f eq "\\"; + next if $f =~ /\/SUBSYS.o$/; + $f =~ s/,$// + ; # Remove trailing comma that can show up from filter stuff + next unless $f =~ /.*\.o$/; + $f =~ s/\.o$/\.c/; + if ($f =~ /^\$\(top_builddir\)\/(.*)/) + { + $f = $1; + $self->AddFile($f); + } + else + { + $self->AddFile("$reldir/$f"); + } + } + $mf =~ s{OBJS[^=]*=\s*(.*)$}{}m; + } + + # Match rules that pull in source files from different directories, eg + # pgstrcasecmp.c rint.c snprintf.c: % : $(top_srcdir)/src/port/% + my $replace_re = + qr{^([^:\n\$]+\.c)\s*:\s*(?:%\s*: )?\$(\([^\)]+\))\/(.*)\/[^\/]+\n}m; + while ($mf =~ m{$replace_re}m) + { + my $match = $1; + my $top = $2; + my $target = $3; + my @pieces = split /\s+/, $match; + foreach my $fn (@pieces) + { + if ($top eq "(top_srcdir)") + { + eval { $self->ReplaceFile($fn, $target) }; + } + elsif ($top eq "(backend_src)") + { + eval { $self->ReplaceFile($fn, "src/backend/$target") }; + } + else + { + confess "Bad replacement top: $top, on line $_\n"; + } + } + $mf =~ s{$replace_re}{}m; + } + + $self->AddDirResourceFile($reldir); + return; +} + +# If the directory's Makefile bears a description string, add a resource file. +sub AddDirResourceFile +{ + my ($self, $reldir) = @_; + my $mf = read_makefile($reldir); + + if ($mf =~ /^PGFILEDESC\s*=\s*\"([^\"]+)\"/m) + { + my $desc = $1; + my $ico; + if ($mf =~ /^PGAPPICON\s*=\s*(.*)$/m) { $ico = $1; } + $self->AddResourceFile($reldir, $desc, $ico); + } + return; +} + +sub AddResourceFile +{ + my ($self, $dir, $desc, $ico) = @_; + + if (Solution::IsNewer("$dir/win32ver.rc", 'src/port/win32ver.rc')) + { + print "Generating win32ver.rc for $dir\n"; + open(my $i, '<', 'src/port/win32ver.rc') + || confess "Could not open win32ver.rc"; + open(my $o, '>', "$dir/win32ver.rc") + || confess "Could not write win32ver.rc"; + my $icostr = $ico ? "IDI_ICON ICON \"src/port/$ico.ico\"" : ""; + while (<$i>) + { + s/FILEDESC/"$desc"/gm; + s/_ICO_/$icostr/gm; + if ($self->{type} eq "dll") + { + s/VFT_APP/VFT_DLL/gm; + my $name = $self->{name}; + s/_INTERNAL_NAME_/"$name"/; + s/_ORIGINAL_NAME_/"$name.dll"/; + } + else + { + /_INTERNAL_NAME_/ && next; + /_ORIGINAL_NAME_/ && next; + } + print $o $_; + } + close($o); + close($i); + } + $self->AddFile("$dir/win32ver.rc"); + return; +} + +sub DisableLinkerWarnings +{ + my ($self, $warnings) = @_; + + $self->{disablelinkerwarnings} .= ',' + unless ($self->{disablelinkerwarnings} eq ''); + $self->{disablelinkerwarnings} .= $warnings; + return; +} + +sub Save +{ + my ($self) = @_; + + # Warning 4197 is about double exporting, disable this per + # http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=99193 + $self->DisableLinkerWarnings('4197') if ($self->{platform} eq 'x64'); + + # Dump the project + open(my $f, '>', "$self->{name}$self->{filenameExtension}") + || croak( + "Could not write to $self->{name}$self->{filenameExtension}\n"); + $self->WriteHeader($f); + $self->WriteFiles($f); + $self->Footer($f); + close($f); + return; +} + +sub GetAdditionalLinkerDependencies +{ + my ($self, $cfgname, $separator) = @_; + my $libcfg = (uc $cfgname eq "RELEASE") ? "MD" : "MDd"; + my $libs = ''; + foreach my $lib (@{ $self->{libraries} }) + { + my $xlib = $lib; + foreach my $slib (@{ $self->{suffixlib} }) + { + if ($slib eq $lib) + { + $xlib =~ s/\.lib$/$libcfg.lib/; + last; + } + } + $libs .= $xlib . $separator; + } + $libs =~ s/.$//; + $libs =~ s/__CFGNAME__/$cfgname/g; + return $libs; +} + +# Utility function that loads a complete file +sub read_file +{ + my $filename = shift; + my $F; + local $/ = undef; + open($F, '<', $filename) || croak "Could not open file $filename\n"; + my $txt = <$F>; + close($F); + + return $txt; +} + +sub read_makefile +{ + my $reldir = shift; + my $F; + local $/ = undef; + open($F, '<', "$reldir/GNUmakefile") + || open($F, '<', "$reldir/Makefile") + || confess "Could not open $reldir/Makefile\n"; + my $txt = <$F>; + close($F); + + return $txt; +} + +1; diff --git a/src/tools/msvc/README b/src/tools/msvc/README new file mode 100644 index 0000000..1c36925 --- /dev/null +++ b/src/tools/msvc/README @@ -0,0 +1,99 @@ +src/tools/msvc/README + +MSVC build +========== + +This directory contains the tools required to build PostgreSQL using +Microsoft Visual Studio 2015 - 2022. This builds the whole backend, not just +the libpq frontend library. For more information, see the documentation +chapter "Installation on Windows" and the description below. + + +Notes about Visual Studio Express +--------------------------------- +To build PostgreSQL using Visual Studio Express, the Microsoft Windows SDK +has to be installed. Since this is not included in the product +originally, extra steps are needed to make it work. + +First, download and install a supported version of the Microsoft Windows SDK +from www.microsoft.com (v8.1a or greater). + +Locate the files vcprojectengine.dll.express.config and +vcprojectengine.dll.config in the vc\vcpackages directory of +the Visual C++ Express installation. In these files, add the paths +to the Platform SDK to the Include, Library and Path tags. Be sure +to add them to the beginning of the list. + +This should work for both GUI and commandline builds, but a restart +may be necessary. + +If you are using a recent version of the Microsoft Windows SDK that includes +the compilers and build tools you probably don't even need Visual Studio +Express to build PostgreSQL. + + +Structure of the build tools +---------------------------- +The tools for building PostgreSQL using Microsoft Visual Studio currently +consist of the following files: + +- Configuration files - +config_default.pl default configuration arguments + +A typical build environment has two more files, buildenv.pl and config.pl +that contain the user's build environment settings and configuration +arguments. + + +- User tools - +build.pl tool to build the binaries +clean.bat batch file for cleaning up generated files +install.pl tool to install the generated files +mkvcbuild.pl tool to generate the Visual Studio build files +vcregress.pl tool to run the regression tests + + +- Internal tools - +gendef.pl internal tool to generate .DEF files +pgbison.pl internal tool to process .y files using bison +pgflex.pl internal tool to process .l files using flex + +Many of those .pl files also have a corresponding .bat-wrapper that doesn't +contain any additional logic. + + +- Internal modules - +Install.pm module containing the install logic +Mkvcbuild.pm module containing the code to generate the Visual + Studio build (project/solution) files +MSBuildProject.pm module containing the code to generate MSBuild based + project files (Visual Studio 2015 or greater) +Project.pm module containing the common code to generate the + Visual Studio project files. Also provides the + common interface of all project file generators +Solution.pm module containing the code to generate the Visual + Studio solution files. +VSObjectFactory.pm factory module providing the code to create the + appropriate project/solution files for the current + environment + + +Description of the internals of the Visual Studio build process +--------------------------------------------------------------- +By typing 'build' the user starts the build.bat wrapper which simply passes +its arguments to build.pl. +In build.pl the user's buildenv.pl is used to set up the build environment +(i. e. path to bison and flex). In addition his config.pl file is merged into +config_default.pl to create the configuration arguments. +These configuration arguments are passed over to Mkvcbuild::mkvcbuild +(Mkvcbuild.pm) which creates the Visual Studio project and solution files. +It does this by using VSObjectFactory::CreateSolution to create an object +implementing the Solution interface (this could be either +VS2015Solution, VS2017Solution, VS2019Solution or VS2022Solution, all in +Solution.pm, depending on the user's build environment) and adding objects +implementing the corresponding Project interface ( +VC2015Project, VC2017Project, VC2019Project or VC2022Project from +MSBuildProject.pm) to it. When Solution::Save is called, the implementations +of Solution and Project save their content in the appropriate format. +The final step of starting the appropriate build program (msbuild) is +performed in build.pl again. diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm new file mode 100644 index 0000000..711fae8 --- /dev/null +++ b/src/tools/msvc/Solution.pm @@ -0,0 +1,1345 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +package Solution; + +# +# Package that encapsulates a Visual C++ solution file generation +# +# src/tools/msvc/Solution.pm +# +use Carp; +use strict; +use warnings; +use VSObjectFactory; + +no warnings qw(redefine); ## no critic + +sub _new +{ + my $classname = shift; + my $options = shift; + my $self = { + projects => {}, + options => $options, + VisualStudioVersion => undef, + MinimumVisualStudioVersion => undef, + vcver => undef, + platform => undef, + }; + bless($self, $classname); + + $self->DeterminePlatform(); + + if ($options->{xslt} && !$options->{xml}) + { + die "XSLT requires XML\n"; + } + $options->{blocksize} = 8 + unless $options->{blocksize}; # undef or 0 means default + die "Bad blocksize $options->{blocksize}" + unless grep { $_ == $options->{blocksize} } (1, 2, 4, 8, 16, 32); + $options->{segsize} = 1 + unless $options->{segsize}; # undef or 0 means default + # only allow segsize 1 for now, as we can't do large files yet in windows + die "Bad segsize $options->{segsize}" + unless $options->{segsize} == 1; + $options->{wal_blocksize} = 8 + unless $options->{wal_blocksize}; # undef or 0 means default + die "Bad wal_blocksize $options->{wal_blocksize}" + unless grep { $_ == $options->{wal_blocksize} } + (1, 2, 4, 8, 16, 32, 64); + + return $self; +} + +sub GetAdditionalHeaders +{ + return ''; +} + +sub DeterminePlatform +{ + my $self = shift; + + if ($^O eq "MSWin32") + { + # Examine CL help output to determine if we are in 32 or 64-bit mode. + my $output = `cl /help 2>&1`; + $? >> 8 == 0 or die "cl command not found"; + $self->{platform} = + ($output =~ /^\/favor:<.+AMD64/m) ? 'x64' : 'Win32'; + } + else + { + $self->{platform} = 'FAKE'; + } + print "Detected hardware platform: $self->{platform}\n"; + return; +} + +# Return 1 if $oldfile is newer than $newfile, or if $newfile doesn't exist. +# Special case - if config.pl has changed, always return 1 +sub IsNewer +{ + my ($newfile, $oldfile) = @_; + -e $oldfile or warn "source file \"$oldfile\" does not exist"; + if ( $oldfile ne 'src/tools/msvc/config.pl' + && $oldfile ne 'src/tools/msvc/config_default.pl') + { + return 1 + if (-f 'src/tools/msvc/config.pl') + && IsNewer($newfile, 'src/tools/msvc/config.pl'); + return 1 + if (-f 'src/tools/msvc/config_default.pl') + && IsNewer($newfile, 'src/tools/msvc/config_default.pl'); + } + return 1 if (!(-e $newfile)); + my @nstat = stat($newfile); + my @ostat = stat($oldfile); + return 1 if ($nstat[9] < $ostat[9]); + return 0; +} + +# Copy a file, *not* preserving date. Only works for text files. +sub copyFile +{ + my ($src, $dest) = @_; + open(my $i, '<', $src) || croak "Could not open $src"; + open(my $o, '>', $dest) || croak "Could not open $dest"; + while (<$i>) + { + print $o $_; + } + close($i); + close($o); + return; +} + +# Fetch version of OpenSSL based on a parsing of the command shipped with +# the installer this build is linking to. This returns as result an array +# made of the three first digits of the OpenSSL version, which is enough +# to decide which options to apply depending on the version of OpenSSL +# linking with. +sub GetOpenSSLVersion +{ + my $self = shift; + + # Attempt to get OpenSSL version and location. This assumes that + # openssl.exe is in the specified directory. + # Quote the .exe name in case it has spaces + my $opensslcmd = + qq("$self->{options}->{openssl}\\bin\\openssl.exe" version 2>&1); + my $sslout = `$opensslcmd`; + + $? >> 8 == 0 + or croak + "Unable to determine OpenSSL version: The openssl.exe command wasn't found."; + + if ($sslout =~ /(\d+)\.(\d+)\.(\d+)(\D)/m) + { + return ($1, $2, $3); + } + + croak + "Unable to determine OpenSSL version: The openssl.exe version could not be determined."; +} + +sub GenerateFiles +{ + my $self = shift; + my $bits = $self->{platform} eq 'Win32' ? 32 : 64; + my $ac_init_found = 0; + my $package_name; + my $package_version; + my $package_bugreport; + my $package_url; + my ($majorver, $minorver); + my $ac_define_openssl_api_compat_found = 0; + my $openssl_api_compat; + + # Parse configure.ac to get version numbers + open(my $c, '<', "configure.ac") + || confess("Could not open configure.ac for reading\n"); + while (<$c>) + { + if (/^AC_INIT\(\[([^\]]+)\], \[([^\]]+)\], \[([^\]]+)\], \[([^\]]*)\], \[([^\]]+)\]/ + ) + { + $ac_init_found = 1; + + $package_name = $1; + $package_version = $2; + $package_bugreport = $3; + #$package_tarname = $4; + $package_url = $5; + + if ($package_version !~ /^(\d+)(?:\.(\d+))?/) + { + confess "Bad format of version: $package_version\n"; + } + $majorver = sprintf("%d", $1); + $minorver = sprintf("%d", $2 ? $2 : 0); + } + elsif (/\bAC_DEFINE\(OPENSSL_API_COMPAT, \[([0-9xL]+)\]/) + { + $ac_define_openssl_api_compat_found = 1; + $openssl_api_compat = $1; + } + } + close($c); + confess "Unable to parse configure.ac for all variables!" + unless $ac_init_found && $ac_define_openssl_api_compat_found; + + if (IsNewer("src/include/pg_config_os.h", "src/include/port/win32.h")) + { + print "Copying pg_config_os.h...\n"; + copyFile("src/include/port/win32.h", "src/include/pg_config_os.h"); + } + + print "Generating configuration headers...\n"; + my $extraver = $self->{options}->{extraver}; + $extraver = '' unless defined $extraver; + my $port = $self->{options}->{"--with-pgport"} || 5432; + + # Every symbol in pg_config.h.in must be accounted for here. Set + # to undef if the symbol should not be defined. + my %define = ( + ALIGNOF_DOUBLE => 8, + ALIGNOF_INT => 4, + ALIGNOF_LONG => 4, + ALIGNOF_LONG_LONG_INT => 8, + ALIGNOF_PG_INT128_TYPE => undef, + ALIGNOF_SHORT => 2, + AC_APPLE_UNIVERSAL_BUILD => undef, + BLCKSZ => 1024 * $self->{options}->{blocksize}, + CONFIGURE_ARGS => '"' . $self->GetFakeConfigure() . '"', + DEF_PGPORT => $port, + DEF_PGPORT_STR => qq{"$port"}, + DLSUFFIX => '".dll"', + ENABLE_GSS => $self->{options}->{gss} ? 1 : undef, + ENABLE_NLS => $self->{options}->{nls} ? 1 : undef, + ENABLE_THREAD_SAFETY => 1, + HAVE_APPEND_HISTORY => undef, + HAVE_ASN1_STRING_GET0_DATA => undef, + HAVE_ATOMICS => 1, + HAVE_ATOMIC_H => undef, + HAVE_BACKTRACE_SYMBOLS => undef, + HAVE_BIO_METH_NEW => undef, + HAVE_COMPUTED_GOTO => undef, + HAVE_COPYFILE => undef, + HAVE_COPYFILE_H => undef, + HAVE_CRTDEFS_H => undef, + HAVE_CRYPTO_LOCK => undef, + HAVE_DECL_FDATASYNC => 0, + HAVE_DECL_F_FULLFSYNC => 0, + HAVE_DECL_LLVMCREATEGDBREGISTRATIONLISTENER => 0, + HAVE_DECL_LLVMCREATEPERFJITEVENTLISTENER => 0, + HAVE_DECL_LLVMGETHOSTCPUNAME => 0, + HAVE_DECL_LLVMGETHOSTCPUFEATURES => 0, + HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN => 0, + HAVE_DECL_POSIX_FADVISE => 0, + HAVE_DECL_PREADV => 0, + HAVE_DECL_PWRITEV => 0, + HAVE_DECL_STRLCAT => 0, + HAVE_DECL_STRLCPY => 0, + HAVE_DECL_STRNLEN => 1, + HAVE_EDITLINE_HISTORY_H => undef, + HAVE_EDITLINE_READLINE_H => undef, + HAVE_EXECINFO_H => undef, + HAVE_EXPLICIT_BZERO => undef, + HAVE_FSEEKO => 1, + HAVE_GCC__ATOMIC_INT32_CAS => undef, + HAVE_GCC__ATOMIC_INT64_CAS => undef, + HAVE_GCC__SYNC_CHAR_TAS => undef, + HAVE_GCC__SYNC_INT32_CAS => undef, + HAVE_GCC__SYNC_INT32_TAS => undef, + HAVE_GCC__SYNC_INT64_CAS => undef, + HAVE_GETIFADDRS => undef, + HAVE_GETOPT => undef, + HAVE_GETOPT_H => undef, + HAVE_GETOPT_LONG => undef, + HAVE_GETPEEREID => undef, + HAVE_GETPEERUCRED => undef, + HAVE_GSSAPI_EXT_H => undef, + HAVE_GSSAPI_GSSAPI_EXT_H => undef, + HAVE_GSSAPI_GSSAPI_H => undef, + HAVE_GSSAPI_H => undef, + HAVE_HMAC_CTX_FREE => undef, + HAVE_HMAC_CTX_NEW => undef, + HAVE_HISTORY_H => undef, + HAVE_HISTORY_TRUNCATE_FILE => undef, + HAVE_IFADDRS_H => undef, + HAVE_INET_ATON => undef, + HAVE_INET_PTON => 1, + HAVE_INT_TIMEZONE => 1, + HAVE_INT64 => undef, + HAVE_INT8 => undef, + HAVE_INTTYPES_H => undef, + HAVE_INT_OPTERR => undef, + HAVE_INT_OPTRESET => undef, + HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P => undef, + HAVE_KQUEUE => undef, + HAVE_LANGINFO_H => undef, + HAVE_LDAP_INITIALIZE => undef, + HAVE_LIBCRYPTO => undef, + HAVE_LIBLDAP => undef, + HAVE_LIBLZ4 => undef, + HAVE_LIBM => undef, + HAVE_LIBPAM => undef, + HAVE_LIBREADLINE => undef, + HAVE_LIBSELINUX => undef, + HAVE_LIBSSL => undef, + HAVE_LIBWLDAP32 => undef, + HAVE_LIBXML2 => undef, + HAVE_LIBXSLT => undef, + HAVE_LIBZ => $self->{options}->{zlib} ? 1 : undef, + HAVE_LIBZSTD => undef, + HAVE_LOCALE_T => 1, + HAVE_LONG_INT_64 => undef, + HAVE_LONG_LONG_INT_64 => 1, + HAVE_MBARRIER_H => undef, + HAVE_MBSTOWCS_L => 1, + HAVE_MEMORY_H => 1, + HAVE_MEMSET_S => undef, + HAVE_MKDTEMP => undef, + HAVE_OPENSSL_INIT_SSL => undef, + HAVE_OSSP_UUID_H => undef, + HAVE_PAM_PAM_APPL_H => undef, + HAVE_POSIX_FADVISE => undef, + HAVE_POSIX_FALLOCATE => undef, + HAVE_PPOLL => undef, + HAVE_PTHREAD => undef, + HAVE_PTHREAD_BARRIER_WAIT => undef, + HAVE_PTHREAD_IS_THREADED_NP => undef, + HAVE_PTHREAD_PRIO_INHERIT => undef, + HAVE_READLINE_H => undef, + HAVE_READLINE_HISTORY_H => undef, + HAVE_READLINE_READLINE_H => undef, + HAVE_RL_COMPLETION_MATCHES => undef, + HAVE_RL_COMPLETION_SUPPRESS_QUOTE => undef, + HAVE_RL_FILENAME_COMPLETION_FUNCTION => undef, + HAVE_RL_FILENAME_QUOTE_CHARACTERS => undef, + HAVE_RL_FILENAME_QUOTING_FUNCTION => undef, + HAVE_RL_RESET_SCREEN_SIZE => undef, + HAVE_RL_VARIABLE_BIND => undef, + HAVE_SECURITY_PAM_APPL_H => undef, + HAVE_SETPROCTITLE => undef, + HAVE_SETPROCTITLE_FAST => undef, + HAVE_SOCKLEN_T => 1, + HAVE_SPINLOCKS => 1, + HAVE_SSL_CTX_SET_CERT_CB => undef, + HAVE_STDBOOL_H => 1, + HAVE_STDINT_H => 1, + HAVE_STDLIB_H => 1, + HAVE_STRCHRNUL => undef, + HAVE_STRERROR_R => undef, + HAVE_STRINGS_H => undef, + HAVE_STRING_H => 1, + HAVE_STRLCAT => undef, + HAVE_STRLCPY => undef, + HAVE_STRNLEN => 1, + HAVE_STRSIGNAL => undef, + HAVE_STRUCT_OPTION => undef, + HAVE_STRUCT_SOCKADDR_SA_LEN => undef, + HAVE_STRUCT_TM_TM_ZONE => undef, + HAVE_SYNC_FILE_RANGE => undef, + HAVE_SYNCFS => undef, + HAVE_SYSLOG => undef, + HAVE_SYS_EPOLL_H => undef, + HAVE_SYS_EVENT_H => undef, + HAVE_SYS_PERSONALITY_H => undef, + HAVE_SYS_PRCTL_H => undef, + HAVE_SYS_PROCCTL_H => undef, + HAVE_SYS_SIGNALFD_H => undef, + HAVE_SYS_STAT_H => 1, + HAVE_SYS_TYPES_H => 1, + HAVE_SYS_UCRED_H => undef, + HAVE_TERMIOS_H => undef, + HAVE_TYPEOF => undef, + HAVE_UCRED_H => undef, + HAVE_UINT64 => undef, + HAVE_UINT8 => undef, + HAVE_UNION_SEMUN => undef, + HAVE_UNISTD_H => 1, + HAVE_USELOCALE => undef, + HAVE_UUID_BSD => undef, + HAVE_UUID_E2FS => undef, + HAVE_UUID_OSSP => undef, + HAVE_UUID_H => undef, + HAVE_UUID_UUID_H => undef, + HAVE_WCSTOMBS_L => 1, + HAVE_VISIBILITY_ATTRIBUTE => undef, + HAVE_X509_GET_SIGNATURE_NID => 1, + HAVE_X509_GET_SIGNATURE_INFO => undef, + HAVE_X86_64_POPCNTQ => undef, + HAVE__BOOL => undef, + HAVE__BUILTIN_BSWAP16 => undef, + HAVE__BUILTIN_BSWAP32 => undef, + HAVE__BUILTIN_BSWAP64 => undef, + HAVE__BUILTIN_CLZ => undef, + HAVE__BUILTIN_CONSTANT_P => undef, + HAVE__BUILTIN_CTZ => undef, + HAVE__BUILTIN_FRAME_ADDRESS => undef, + HAVE__BUILTIN_OP_OVERFLOW => undef, + HAVE__BUILTIN_POPCOUNT => undef, + HAVE__BUILTIN_TYPES_COMPATIBLE_P => undef, + HAVE__BUILTIN_UNREACHABLE => undef, + HAVE__CONFIGTHREADLOCALE => 1, + HAVE__CPUID => 1, + HAVE__GET_CPUID => undef, + HAVE__STATIC_ASSERT => undef, + INT64_MODIFIER => qq{"ll"}, + LOCALE_T_IN_XLOCALE => undef, + MAXIMUM_ALIGNOF => 8, + MEMSET_LOOP_LIMIT => 1024, + OPENSSL_API_COMPAT => $openssl_api_compat, + PACKAGE_BUGREPORT => qq{"$package_bugreport"}, + PACKAGE_NAME => qq{"$package_name"}, + PACKAGE_STRING => qq{"$package_name $package_version"}, + PACKAGE_TARNAME => lc qq{"$package_name"}, + PACKAGE_URL => qq{"$package_url"}, + PACKAGE_VERSION => qq{"$package_version"}, + PG_INT128_TYPE => undef, + PG_INT64_TYPE => 'long long int', + PG_KRB_SRVNAM => qq{"postgres"}, + PG_MAJORVERSION => qq{"$majorver"}, + PG_MAJORVERSION_NUM => $majorver, + PG_MINORVERSION_NUM => $minorver, + PG_PRINTF_ATTRIBUTE => undef, + PG_USE_STDBOOL => 1, + PG_VERSION => qq{"$package_version$extraver"}, + PG_VERSION_NUM => sprintf("%d%04d", $majorver, $minorver), + PG_VERSION_STR => + qq{"PostgreSQL $package_version$extraver, compiled by Visual C++ build " CppAsString2(_MSC_VER) ", $bits-bit"}, + PROFILE_PID_DIR => undef, + PTHREAD_CREATE_JOINABLE => undef, + RELSEG_SIZE => (1024 / $self->{options}->{blocksize}) * + $self->{options}->{segsize} * 1024, + SIZEOF_BOOL => 1, + SIZEOF_LONG => 4, + SIZEOF_OFF_T => undef, + SIZEOF_SIZE_T => $bits / 8, + SIZEOF_VOID_P => $bits / 8, + STDC_HEADERS => 1, + STRERROR_R_INT => undef, + USE_ARMV8_CRC32C => undef, + USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK => undef, + USE_ASSERT_CHECKING => $self->{options}->{asserts} ? 1 : undef, + USE_BONJOUR => undef, + USE_BSD_AUTH => undef, + USE_ICU => $self->{options}->{icu} ? 1 : undef, + USE_LIBXML => undef, + USE_LIBXSLT => undef, + USE_LZ4 => undef, + USE_LDAP => $self->{options}->{ldap} ? 1 : undef, + USE_LLVM => undef, + USE_NAMED_POSIX_SEMAPHORES => undef, + USE_OPENSSL => undef, + USE_PAM => undef, + USE_SLICING_BY_8_CRC32C => undef, + USE_SSE42_CRC32C => undef, + USE_SSE42_CRC32C_WITH_RUNTIME_CHECK => 1, + USE_SYSTEMD => undef, + USE_SYSV_SEMAPHORES => undef, + USE_SYSV_SHARED_MEMORY => undef, + USE_UNNAMED_POSIX_SEMAPHORES => undef, + USE_WIN32_SEMAPHORES => 1, + USE_WIN32_SHARED_MEMORY => 1, + USE_ZSTD => undef, + WCSTOMBS_L_IN_XLOCALE => undef, + WORDS_BIGENDIAN => undef, + XLOG_BLCKSZ => 1024 * $self->{options}->{wal_blocksize}, + _FILE_OFFSET_BITS => undef, + _LARGEFILE_SOURCE => undef, + _LARGE_FILES => undef, + inline => '__inline', + pg_restrict => '__restrict', + # not defined, because it'd conflict with __declspec(restrict) + restrict => undef, + typeof => undef,); + + if ($self->{options}->{uuid}) + { + $define{HAVE_UUID_OSSP} = 1; + $define{HAVE_UUID_H} = 1; + } + if ($self->{options}->{xml}) + { + $define{HAVE_LIBXML2} = 1; + $define{USE_LIBXML} = 1; + } + if ($self->{options}->{xslt}) + { + $define{HAVE_LIBXSLT} = 1; + $define{USE_LIBXSLT} = 1; + } + if ($self->{options}->{lz4}) + { + $define{HAVE_LIBLZ4} = 1; + $define{USE_LZ4} = 1; + } + if ($self->{options}->{zstd}) + { + $define{HAVE_LIBZSTD} = 1; + $define{USE_ZSTD} = 1; + } + if ($self->{options}->{openssl}) + { + $define{USE_OPENSSL} = 1; + + my ($digit1, $digit2, $digit3) = $self->GetOpenSSLVersion(); + + # Symbols needed with OpenSSL 1.1.1 and above. + if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0') + || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '1')) + { + $define{HAVE_X509_GET_SIGNATURE_INFO} = 1; + } + + # Symbols needed with OpenSSL 1.1.0 and above. + if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0') + || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '0')) + { + $define{HAVE_ASN1_STRING_GET0_DATA} = 1; + $define{HAVE_BIO_METH_NEW} = 1; + $define{HAVE_HMAC_CTX_FREE} = 1; + $define{HAVE_HMAC_CTX_NEW} = 1; + $define{HAVE_OPENSSL_INIT_SSL} = 1; + } + + # Symbols needed with OpenSSL 1.0.2 and above. + if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0') + || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '0') + || ($digit1 >= '1' && $digit2 >= '0' && $digit3 >= '2')) + { + $define{HAVE_SSL_CTX_SET_CERT_CB} = 1; + } + } + + $self->GenerateConfigHeader('src/include/pg_config.h', \%define, 1); + $self->GenerateConfigHeader('src/include/pg_config_ext.h', \%define, 0); + $self->GenerateConfigHeader('src/interfaces/ecpg/include/ecpg_config.h', + \%define, 0); + + $self->GenerateDefFile( + "src/interfaces/libpq/libpqdll.def", + "src/interfaces/libpq/exports.txt", + "LIBPQ"); + $self->GenerateDefFile( + "src/interfaces/ecpg/ecpglib/ecpglib.def", + "src/interfaces/ecpg/ecpglib/exports.txt", + "LIBECPG"); + $self->GenerateDefFile( + "src/interfaces/ecpg/compatlib/compatlib.def", + "src/interfaces/ecpg/compatlib/exports.txt", + "LIBECPG_COMPAT"); + $self->GenerateDefFile( + "src/interfaces/ecpg/pgtypeslib/pgtypeslib.def", + "src/interfaces/ecpg/pgtypeslib/exports.txt", + "LIBPGTYPES"); + + chdir('src/backend/utils'); + my $pg_proc_dat = '../../../src/include/catalog/pg_proc.dat'; + if ( IsNewer('fmgr-stamp', 'Gen_fmgrtab.pl') + || IsNewer('fmgr-stamp', '../catalog/Catalog.pm') + || IsNewer('fmgr-stamp', $pg_proc_dat) + || IsNewer('fmgr-stamp', '../../../src/include/access/transam.h')) + { + system( + "perl -I ../catalog Gen_fmgrtab.pl --include-path ../../../src/include/ $pg_proc_dat" + ); + open(my $f, '>', 'fmgr-stamp') + || confess "Could not touch fmgr-stamp"; + close($f); + } + chdir('../../..'); + + if (IsNewer( + 'src/include/utils/fmgroids.h', + 'src/backend/utils/fmgroids.h')) + { + copyFile('src/backend/utils/fmgroids.h', + 'src/include/utils/fmgroids.h'); + } + + if (IsNewer( + 'src/include/utils/fmgrprotos.h', + 'src/backend/utils/fmgrprotos.h')) + { + copyFile( + 'src/backend/utils/fmgrprotos.h', + 'src/include/utils/fmgrprotos.h'); + } + + if (IsNewer( + 'src/include/storage/lwlocknames.h', + 'src/backend/storage/lmgr/lwlocknames.txt')) + { + print "Generating lwlocknames.c and lwlocknames.h...\n"; + my $lmgr = 'src/backend/storage/lmgr'; + system( + "perl $lmgr/generate-lwlocknames.pl --outdir $lmgr $lmgr/lwlocknames.txt" + ); + } + if (IsNewer( + 'src/include/storage/lwlocknames.h', + 'src/backend/storage/lmgr/lwlocknames.h')) + { + copyFile( + 'src/backend/storage/lmgr/lwlocknames.h', + 'src/include/storage/lwlocknames.h'); + } + + if (IsNewer('src/include/utils/probes.h', 'src/backend/utils/probes.d')) + { + print "Generating probes.h...\n"; + system( + 'perl src/backend/utils/Gen_dummy_probes.pl src/backend/utils/probes.d > src/include/utils/probes.h' + ); + } + + if ($self->{options}->{python} + && IsNewer( + 'src/pl/plpython/spiexceptions.h', + 'src/backend/utils/errcodes.txt')) + { + print "Generating spiexceptions.h...\n"; + system( + 'perl src/pl/plpython/generate-spiexceptions.pl src/backend/utils/errcodes.txt > src/pl/plpython/spiexceptions.h' + ); + } + + if (IsNewer( + 'src/include/utils/errcodes.h', + 'src/backend/utils/errcodes.txt')) + { + print "Generating errcodes.h...\n"; + system( + 'perl src/backend/utils/generate-errcodes.pl --outfile src/backend/utils/errcodes.h src/backend/utils/errcodes.txt' + ); + copyFile('src/backend/utils/errcodes.h', + 'src/include/utils/errcodes.h'); + } + + if (IsNewer( + 'src/pl/plpgsql/src/plerrcodes.h', + 'src/backend/utils/errcodes.txt')) + { + print "Generating plerrcodes.h...\n"; + system( + 'perl src/pl/plpgsql/src/generate-plerrcodes.pl src/backend/utils/errcodes.txt > src/pl/plpgsql/src/plerrcodes.h' + ); + } + + if ($self->{options}->{tcl} + && IsNewer( + 'src/pl/tcl/pltclerrcodes.h', 'src/backend/utils/errcodes.txt')) + { + print "Generating pltclerrcodes.h...\n"; + system( + 'perl src/pl/tcl/generate-pltclerrcodes.pl src/backend/utils/errcodes.txt > src/pl/tcl/pltclerrcodes.h' + ); + } + + if (IsNewer( + 'contrib/fuzzystrmatch/daitch_mokotoff.h', + 'contrib/fuzzystrmatch/daitch_mokotoff_header.pl')) + { + print "Generating daitch_mokotoff.h...\n"; + system( 'perl contrib/fuzzystrmatch/daitch_mokotoff_header.pl ' + . 'contrib/fuzzystrmatch/daitch_mokotoff.h'); + } + + if (IsNewer('src/bin/psql/sql_help.h', 'src/bin/psql/create_help.pl')) + { + print "Generating sql_help.h...\n"; + my $psql = 'src/bin/psql'; + system( + "perl $psql/create_help.pl --docdir doc/src/sgml/ref --outdir $psql --basename sql_help" + ); + } + + if (IsNewer('src/common/kwlist_d.h', 'src/include/parser/kwlist.h')) + { + print "Generating kwlist_d.h...\n"; + system( + 'perl -I src/tools src/tools/gen_keywordlist.pl --extern -o src/common src/include/parser/kwlist.h' + ); + } + + if (IsNewer( + 'src/pl/plpgsql/src/pl_reserved_kwlist_d.h', + 'src/pl/plpgsql/src/pl_reserved_kwlist.h') + || IsNewer( + 'src/pl/plpgsql/src/pl_unreserved_kwlist_d.h', + 'src/pl/plpgsql/src/pl_unreserved_kwlist.h')) + { + print + "Generating pl_reserved_kwlist_d.h and pl_unreserved_kwlist_d.h...\n"; + chdir('src/pl/plpgsql/src'); + system( + 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname ReservedPLKeywords pl_reserved_kwlist.h' + ); + system( + 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname UnreservedPLKeywords pl_unreserved_kwlist.h' + ); + chdir('../../../..'); + } + + if (IsNewer( + 'src/interfaces/ecpg/preproc/c_kwlist_d.h', + 'src/interfaces/ecpg/preproc/c_kwlist.h') + || IsNewer( + 'src/interfaces/ecpg/preproc/ecpg_kwlist_d.h', + 'src/interfaces/ecpg/preproc/ecpg_kwlist.h')) + { + print "Generating c_kwlist_d.h and ecpg_kwlist_d.h...\n"; + chdir('src/interfaces/ecpg/preproc'); + system( + 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname ScanCKeywords --no-case-fold c_kwlist.h' + ); + system( + 'perl -I ../../../tools ../../../tools/gen_keywordlist.pl --varname ScanECPGKeywords ecpg_kwlist.h' + ); + chdir('../../../..'); + } + + if (IsNewer( + 'src/interfaces/ecpg/preproc/preproc.y', + 'src/backend/parser/gram.y')) + { + print "Generating preproc.y...\n"; + my $ecpg = 'src/interfaces/ecpg'; + system( + "perl $ecpg/preproc/parse.pl --srcdir $ecpg/preproc --parser src/backend/parser/gram.y --output $ecpg/preproc/preproc.y" + ); + } + + unless (-f "src/port/pg_config_paths.h") + { + print "Generating pg_config_paths.h...\n"; + open(my $o, '>', 'src/port/pg_config_paths.h') + || confess "Could not open pg_config_paths.h"; + print $o <<EOF; +#define PGBINDIR "/bin" +#define PGSHAREDIR "/share" +#define SYSCONFDIR "/etc" +#define INCLUDEDIR "/include" +#define PKGINCLUDEDIR "/include" +#define INCLUDEDIRSERVER "/include/server" +#define LIBDIR "/lib" +#define PKGLIBDIR "/lib" +#define LOCALEDIR "/share/locale" +#define DOCDIR "/doc" +#define HTMLDIR "/doc" +#define MANDIR "/man" +EOF + close($o); + } + + my $mf = Project::read_file('src/backend/catalog/Makefile'); + $mf =~ s{\\\r?\n}{}g; + $mf =~ /^CATALOG_HEADERS\s*:?=(.*)$/gm + || croak "Could not find CATALOG_HEADERS in Makefile\n"; + my @bki_srcs = split /\s+/, $1; + $mf =~ /^POSTGRES_BKI_DATA\s*:?=[^,]+,(.*)\)$/gm + || croak "Could not find POSTGRES_BKI_DATA in Makefile\n"; + my @bki_data = split /\s+/, $1; + + my $need_genbki = 0; + foreach my $bki (@bki_srcs, @bki_data) + { + next if $bki eq ""; + if (IsNewer( + 'src/backend/catalog/bki-stamp', + "src/include/catalog/$bki")) + { + $need_genbki = 1; + last; + } + } + $need_genbki = 1 + if IsNewer('src/backend/catalog/bki-stamp', + 'src/backend/catalog/genbki.pl'); + $need_genbki = 1 + if IsNewer('src/backend/catalog/bki-stamp', + 'src/backend/catalog/Catalog.pm'); + if ($need_genbki) + { + chdir('src/backend/catalog'); + my $bki_srcs = join(' ../../../src/include/catalog/', @bki_srcs); + system( + "perl genbki.pl --include-path ../../../src/include/ --set-version=$majorver $bki_srcs" + ); + open(my $f, '>', 'bki-stamp') + || confess "Could not touch bki-stamp"; + close($f); + chdir('../../..'); + } + + if (IsNewer( + 'src/include/catalog/header-stamp', + 'src/backend/catalog/bki-stamp')) + { + # Copy generated headers to include directory. + opendir(my $dh, 'src/backend/catalog/') + || die "Can't opendir src/backend/catalog/ $!"; + my @def_headers = grep { /pg_\w+_d\.h$/ } readdir($dh); + closedir $dh; + foreach my $def_header (@def_headers) + { + copyFile( + "src/backend/catalog/$def_header", + "src/include/catalog/$def_header"); + } + copyFile( + 'src/backend/catalog/schemapg.h', + 'src/include/catalog/schemapg.h'); + copyFile( + 'src/backend/catalog/system_fk_info.h', + 'src/include/catalog/system_fk_info.h'); + open(my $chs, '>', 'src/include/catalog/header-stamp') + || confess "Could not touch header-stamp"; + close($chs); + } + + my $nmf = Project::read_file('src/backend/nodes/Makefile'); + $nmf =~ s{\\\r?\n}{}g; + $nmf =~ /^node_headers\s*:?=(.*)$/gm + || croak "Could not find node_headers in Makefile\n"; + my @node_headers = split /\s+/, $1; + @node_headers = grep { $_ ne '' } @node_headers; + my @node_files = map { "src/include/$_" } @node_headers; + + my $need_node_support = 0; + foreach my $nodefile (@node_files) + { + if (IsNewer('src/backend/nodes/node-support-stamp', $nodefile)) + { + $need_node_support = 1; + last; + } + } + $need_node_support = 1 + if IsNewer( + 'src/backend/nodes/node-support-stamp', + 'src/backend/nodes/gen_node_support.pl'); + + if ($need_node_support) + { + system( + "perl src/backend/nodes/gen_node_support.pl --outdir src/backend/nodes @node_files" + ); + open(my $f, '>', 'src/backend/nodes/node-support-stamp') + || confess "Could not touch node-support-stamp"; + close($f); + } + + if (IsNewer( + 'src/include/nodes/nodetags.h', + 'src/backend/nodes/nodetags.h')) + { + copyFile('src/backend/nodes/nodetags.h', + 'src/include/nodes/nodetags.h'); + } + + open(my $o, '>', "doc/src/sgml/version.sgml") + || croak "Could not write to version.sgml\n"; + print $o <<EOF; +<!ENTITY version "$package_version"> +<!ENTITY majorversion "$majorver"> +EOF + close($o); + return; +} + +# Read lines from input file and substitute symbols using the same +# logic that config.status uses. There should be one call of this for +# each AC_CONFIG_HEADERS call in configure.ac. +# +# If the "required" argument is true, we also keep track which of our +# defines have been found and error out if any are left unused at the +# end. That way we avoid accumulating defines in this file that are +# no longer used by configure. +sub GenerateConfigHeader +{ + my ($self, $config_header, $defines, $required) = @_; + + my $config_header_in = $config_header . '.in'; + + if ( IsNewer($config_header, $config_header_in) + || IsNewer($config_header, __FILE__)) + { + my %defines_copy = %$defines; + + open(my $i, '<', $config_header_in) + || confess "Could not open $config_header_in\n"; + open(my $o, '>', $config_header) + || confess "Could not write to $config_header\n"; + + print $o + "/* $config_header. Generated from $config_header_in by src/tools/msvc/Solution.pm. */\n"; + + while (<$i>) + { + if (m/^#(\s*)undef\s+(\w+)/) + { + my $ws = $1; + my $macro = $2; + if (exists $defines->{$macro}) + { + if (defined $defines->{$macro}) + { + print $o "#${ws}define $macro ", $defines->{$macro}, + "\n"; + } + else + { + print $o "/* #${ws}undef $macro */\n"; + } + delete $defines_copy{$macro}; + } + else + { + croak + "undefined symbol: $macro at $config_header line $."; + } + } + else + { + print $o $_; + } + } + close($o); + close($i); + + if ($required && scalar(keys %defines_copy) > 0) + { + croak "unused defines: " . join(' ', keys %defines_copy); + } + } +} + +sub GenerateDefFile +{ + my ($self, $deffile, $txtfile, $libname) = @_; + + if (IsNewer($deffile, $txtfile)) + { + print "Generating $deffile...\n"; + open(my $if, '<', $txtfile) || confess("Could not open $txtfile\n"); + open(my $of, '>', $deffile) || confess("Could not open $deffile\n"); + print $of "LIBRARY $libname\nEXPORTS\n"; + while (<$if>) + { + next if (/^#/); + next if (/^\s*$/); + my ($f, $o) = split; + print $of " $f @ $o\n"; + } + close($of); + close($if); + } + return; +} + +sub AddProject +{ + my ($self, $name, $type, $folder, $initialdir) = @_; + + my $proj = + VSObjectFactory::CreateProject($self->{vcver}, $name, $type, $self); + push @{ $self->{projects}->{$folder} }, $proj; + $proj->AddDir($initialdir) if ($initialdir); + if ($self->{options}->{zlib}) + { + $proj->AddIncludeDir($self->{options}->{zlib} . '\include'); + $proj->AddLibrary($self->{options}->{zlib} . '\lib\zdll.lib'); + } + if ($self->{options}->{openssl}) + { + $proj->AddIncludeDir($self->{options}->{openssl} . '\include'); + my ($digit1, $digit2, $digit3) = $self->GetOpenSSLVersion(); + + # Starting at version 1.1.0 the OpenSSL installers have + # changed their library names from: + # - libeay to libcrypto + # - ssleay to libssl + if ( ($digit1 >= '3' && $digit2 >= '0' && $digit3 >= '0') + || ($digit1 >= '1' && $digit2 >= '1' && $digit3 >= '0')) + { + my $dbgsuffix; + my $libsslpath; + my $libcryptopath; + + # The format name of the libraries is slightly + # different between the Win32 and Win64 platform, so + # adapt. + if (-e "$self->{options}->{openssl}/lib/VC/sslcrypto32MD.lib") + { + # Win32 here, with a debugging library set. + $dbgsuffix = 1; + $libsslpath = '\lib\VC\libssl32.lib'; + $libcryptopath = '\lib\VC\libcrypto32.lib'; + } + elsif (-e "$self->{options}->{openssl}/lib/VC/sslcrypto64MD.lib") + { + # Win64 here, with a debugging library set. + $dbgsuffix = 1; + $libsslpath = '\lib\VC\libssl64.lib'; + $libcryptopath = '\lib\VC\libcrypto64.lib'; + } + else + { + # On both Win32 and Win64 the same library + # names are used without a debugging context. + $dbgsuffix = 0; + $libsslpath = '\lib\libssl.lib'; + $libcryptopath = '\lib\libcrypto.lib'; + } + + $proj->AddLibrary($self->{options}->{openssl} . $libsslpath, + $dbgsuffix); + $proj->AddLibrary($self->{options}->{openssl} . $libcryptopath, + $dbgsuffix); + } + else + { + # Choose which set of libraries to use depending on if + # debugging libraries are in place in the installer. + if (-e "$self->{options}->{openssl}/lib/VC/ssleay32MD.lib") + { + $proj->AddLibrary( + $self->{options}->{openssl} . '\lib\VC\ssleay32.lib', 1); + $proj->AddLibrary( + $self->{options}->{openssl} . '\lib\VC\libeay32.lib', 1); + } + else + { + # We don't expect the config-specific library + # to be here, so don't ask for it in last + # parameter. + $proj->AddLibrary( + $self->{options}->{openssl} . '\lib\ssleay32.lib', 0); + $proj->AddLibrary( + $self->{options}->{openssl} . '\lib\libeay32.lib', 0); + } + } + } + if ($self->{options}->{nls}) + { + $proj->AddIncludeDir($self->{options}->{nls} . '\include'); + $proj->AddLibrary($self->{options}->{nls} . '\lib\libintl.lib'); + } + if ($self->{options}->{gss}) + { + $proj->AddIncludeDir($self->{options}->{gss} . '\include'); + $proj->AddIncludeDir($self->{options}->{gss} . '\include\krb5'); + if ($self->{platform} eq 'Win32') + { + $proj->AddLibrary( + $self->{options}->{gss} . '\lib\i386\krb5_32.lib'); + $proj->AddLibrary( + $self->{options}->{gss} . '\lib\i386\comerr32.lib'); + $proj->AddLibrary( + $self->{options}->{gss} . '\lib\i386\gssapi32.lib'); + } + else + { + $proj->AddLibrary( + $self->{options}->{gss} . '\lib\amd64\krb5_64.lib'); + $proj->AddLibrary( + $self->{options}->{gss} . '\lib\amd64\comerr64.lib'); + $proj->AddLibrary( + $self->{options}->{gss} . '\lib\amd64\gssapi64.lib'); + } + } + if ($self->{options}->{iconv}) + { + $proj->AddIncludeDir($self->{options}->{iconv} . '\include'); + $proj->AddLibrary($self->{options}->{iconv} . '\lib\iconv.lib'); + } + if ($self->{options}->{icu}) + { + $proj->AddIncludeDir($self->{options}->{icu} . '\include'); + if ($self->{platform} eq 'Win32') + { + $proj->AddLibrary($self->{options}->{icu} . '\lib\icuin.lib'); + $proj->AddLibrary($self->{options}->{icu} . '\lib\icuuc.lib'); + $proj->AddLibrary($self->{options}->{icu} . '\lib\icudt.lib'); + } + else + { + $proj->AddLibrary($self->{options}->{icu} . '\lib64\icuin.lib'); + $proj->AddLibrary($self->{options}->{icu} . '\lib64\icuuc.lib'); + $proj->AddLibrary($self->{options}->{icu} . '\lib64\icudt.lib'); + } + } + if ($self->{options}->{xml}) + { + $proj->AddIncludeDir($self->{options}->{xml} . '\include'); + $proj->AddIncludeDir($self->{options}->{xml} . '\include\libxml2'); + $proj->AddLibrary($self->{options}->{xml} . '\lib\libxml2.lib'); + } + if ($self->{options}->{xslt}) + { + $proj->AddIncludeDir($self->{options}->{xslt} . '\include'); + $proj->AddLibrary($self->{options}->{xslt} . '\lib\libxslt.lib'); + } + if ($self->{options}->{lz4}) + { + $proj->AddIncludeDir($self->{options}->{lz4} . '\include'); + $proj->AddLibrary($self->{options}->{lz4} . '\lib\liblz4.lib'); + } + if ($self->{options}->{zstd}) + { + $proj->AddIncludeDir($self->{options}->{zstd} . '\include'); + $proj->AddLibrary($self->{options}->{zstd} . '\lib\libzstd.lib'); + } + if ($self->{options}->{uuid}) + { + $proj->AddIncludeDir($self->{options}->{uuid} . '\include'); + $proj->AddLibrary($self->{options}->{uuid} . '\lib\uuid.lib'); + } + return $proj; +} + +sub Save +{ + my ($self) = @_; + my %flduid; + + $self->GenerateFiles(); + foreach my $fld (keys %{ $self->{projects} }) + { + foreach my $proj (@{ $self->{projects}->{$fld} }) + { + $proj->Save(); + } + } + + open(my $sln, '>', "pgsql.sln") || croak "Could not write to pgsql.sln\n"; + print $sln <<EOF; +Microsoft Visual Studio Solution File, Format Version $self->{solutionFileVersion} +# $self->{visualStudioName} +EOF + + print $sln $self->GetAdditionalHeaders(); + + foreach my $fld (keys %{ $self->{projects} }) + { + foreach my $proj (@{ $self->{projects}->{$fld} }) + { + print $sln <<EOF; +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "$proj->{name}", "$proj->{name}$proj->{filenameExtension}", "$proj->{guid}" +EndProject +EOF + } + if ($fld ne "") + { + $flduid{$fld} = $^O eq "MSWin32" ? Win32::GuidGen() : 'FAKE'; + print $sln <<EOF; +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "$fld", "$fld", "$flduid{$fld}" +EndProject +EOF + } + } + + print $sln <<EOF; +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|$self->{platform}= Debug|$self->{platform} + Release|$self->{platform} = Release|$self->{platform} + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution +EOF + + foreach my $fld (keys %{ $self->{projects} }) + { + foreach my $proj (@{ $self->{projects}->{$fld} }) + { + print $sln <<EOF; + $proj->{guid}.Debug|$self->{platform}.ActiveCfg = Debug|$self->{platform} + $proj->{guid}.Debug|$self->{platform}.Build.0 = Debug|$self->{platform} + $proj->{guid}.Release|$self->{platform}.ActiveCfg = Release|$self->{platform} + $proj->{guid}.Release|$self->{platform}.Build.0 = Release|$self->{platform} +EOF + } + } + + print $sln <<EOF; + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution +EOF + + foreach my $fld (keys %{ $self->{projects} }) + { + next if ($fld eq ""); + foreach my $proj (@{ $self->{projects}->{$fld} }) + { + print $sln "\t\t$proj->{guid} = $flduid{$fld}\n"; + } + } + + print $sln <<EOF; + EndGlobalSection +EndGlobal +EOF + close($sln); + return; +} + +sub GetFakeConfigure +{ + my $self = shift; + + my $cfg = '--enable-thread-safety'; + $cfg .= ' --enable-cassert' if ($self->{options}->{asserts}); + $cfg .= ' --enable-nls' if ($self->{options}->{nls}); + $cfg .= ' --enable-tap-tests' if ($self->{options}->{tap_tests}); + $cfg .= ' --with-ldap' if ($self->{options}->{ldap}); + $cfg .= ' --without-zlib' unless ($self->{options}->{zlib}); + $cfg .= ' --with-extra-version' if ($self->{options}->{extraver}); + $cfg .= ' --with-ssl=openssl' if ($self->{options}->{openssl}); + $cfg .= ' --with-uuid' if ($self->{options}->{uuid}); + $cfg .= ' --with-libxml' if ($self->{options}->{xml}); + $cfg .= ' --with-libxslt' if ($self->{options}->{xslt}); + $cfg .= ' --with-lz4' if ($self->{options}->{lz4}); + $cfg .= ' --with-zstd' if ($self->{options}->{zstd}); + $cfg .= ' --with-gssapi' if ($self->{options}->{gss}); + $cfg .= ' --with-icu' if ($self->{options}->{icu}); + $cfg .= ' --with-tcl' if ($self->{options}->{tcl}); + $cfg .= ' --with-perl' if ($self->{options}->{perl}); + $cfg .= ' --with-python' if ($self->{options}->{python}); + my $port = $self->{options}->{'--with-pgport'}; + $cfg .= " --with-pgport=$port" if defined($port); + + return $cfg; +} + +package VS2015Solution; + +# +# Package that encapsulates a Visual Studio 2015 solution file +# + +use Carp; +use strict; +use warnings; +use base qw(Solution); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{solutionFileVersion} = '12.00'; + $self->{vcver} = '14.00'; + $self->{visualStudioName} = 'Visual Studio 2015'; + $self->{VisualStudioVersion} = '14.0.24730.2'; + $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; + + return $self; +} + +package VS2017Solution; + +# +# Package that encapsulates a Visual Studio 2017 solution file +# + +use Carp; +use strict; +use warnings; +use base qw(Solution); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{solutionFileVersion} = '12.00'; + $self->{vcver} = '15.00'; + $self->{visualStudioName} = 'Visual Studio 2017'; + $self->{VisualStudioVersion} = '15.0.26730.3'; + $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; + + return $self; +} + +package VS2019Solution; + +# +# Package that encapsulates a Visual Studio 2019 solution file +# + +use Carp; +use strict; +use warnings; +use base qw(Solution); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{solutionFileVersion} = '12.00'; + $self->{vcver} = '16.00'; + $self->{visualStudioName} = 'Visual Studio 2019'; + $self->{VisualStudioVersion} = '16.0.28729.10'; + $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; + + return $self; +} + +package VS2022Solution; + +# +# Package that encapsulates a Visual Studio 2022 solution file +# + +use Carp; +use strict; +use warnings; +use base qw(Solution); + +no warnings qw(redefine); ## no critic + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::_new(@_); + bless($self, $classname); + + $self->{solutionFileVersion} = '12.00'; + $self->{vcver} = '17.00'; + $self->{visualStudioName} = 'Visual Studio 2022'; + $self->{VisualStudioVersion} = '17.0.31903.59'; + $self->{MinimumVisualStudioVersion} = '10.0.40219.1'; + + return $self; +} + +sub GetAdditionalHeaders +{ + my ($self, $f) = @_; + + return qq|VisualStudioVersion = $self->{VisualStudioVersion} +MinimumVisualStudioVersion = $self->{MinimumVisualStudioVersion} +|; +} + +1; diff --git a/src/tools/msvc/VSObjectFactory.pm b/src/tools/msvc/VSObjectFactory.pm new file mode 100644 index 0000000..9df2ab4 --- /dev/null +++ b/src/tools/msvc/VSObjectFactory.pm @@ -0,0 +1,174 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +package VSObjectFactory; + +# +# Package that creates Visual Studio wrapper objects for msvc build +# +# src/tools/msvc/VSObjectFactory.pm +# + +use Carp; +use strict; +use warnings; + +use Exporter; +use Project; +use Solution; +use MSBuildProject; + +our (@ISA, @EXPORT); +@ISA = qw(Exporter); +@EXPORT = qw(CreateSolution CreateProject DetermineVisualStudioVersion); + +no warnings qw(redefine); ## no critic + +sub CreateSolution +{ + my $visualStudioVersion = shift; + + if (!defined($visualStudioVersion)) + { + $visualStudioVersion = DetermineVisualStudioVersion(); + } + + if ($visualStudioVersion eq '14.00') + { + return new VS2015Solution(@_); + } + + # The version of nmake bundled in Visual Studio 2017 is greater + # than 14.10 and less than 14.20. And the version number is + # actually 15.00. + elsif ( + ($visualStudioVersion ge '14.10' && $visualStudioVersion lt '14.20') + || $visualStudioVersion eq '15.00') + { + return new VS2017Solution(@_); + } + + # The version of nmake bundled in Visual Studio 2019 is greater + # than 14.20 and less than 14.30. And the version number is + # actually 16.00. + elsif ( + ($visualStudioVersion ge '14.20' && $visualStudioVersion lt '14.30') + || $visualStudioVersion eq '16.00') + { + return new VS2019Solution(@_); + } + + # The version of nmake bundled in Visual Studio 2022 is greater + # than 14.30 and less than 14.40. And the version number is + # actually 17.00. + elsif ( + ($visualStudioVersion ge '14.30' && $visualStudioVersion lt '14.40') + || $visualStudioVersion eq '17.00') + { + return new VS2022Solution(@_); + } + else + { + croak + "The requested Visual Studio version $visualStudioVersion is not supported."; + } +} + +sub CreateProject +{ + my $visualStudioVersion = shift; + + if (!defined($visualStudioVersion)) + { + $visualStudioVersion = DetermineVisualStudioVersion(); + } + + if ($visualStudioVersion eq '14.00') + { + return new VC2015Project(@_); + } + + # The version of nmake bundled in Visual Studio 2017 is greater + # than 14.10 and less than 14.20. And the version number is + # actually 15.00. + elsif ( + ($visualStudioVersion ge '14.10' && $visualStudioVersion lt '14.20') + || $visualStudioVersion eq '15.00') + { + return new VC2017Project(@_); + } + + # The version of nmake bundled in Visual Studio 2019 is greater + # than 14.20 and less than 14.30. And the version number is + # actually 16.00. + elsif ( + ($visualStudioVersion ge '14.20' && $visualStudioVersion lt '14.30') + || $visualStudioVersion eq '16.00') + { + return new VC2019Project(@_); + } + + # The version of nmake bundled in Visual Studio 2022 is greater + # than 14.30 and less than 14.40. And the version number is + # actually 17.00. + elsif ( + ($visualStudioVersion ge '14.30' && $visualStudioVersion lt '14.40') + || $visualStudioVersion eq '17.00') + { + return new VC2022Project(@_); + } + else + { + croak + "The requested Visual Studio version $visualStudioVersion is not supported."; + } +} + +sub DetermineVisualStudioVersion +{ + if ($^O eq "MSWin32") + { + # To determine version of Visual Studio we use nmake as it has + # existed for a long time and still exists in current Visual + # Studio versions. + my $output = `nmake /? 2>&1`; + $? >> 8 == 0 + or croak + "Unable to determine Visual Studio version: The nmake command wasn't found."; + if ($output =~ /(\d+)\.(\d+)\.\d+(\.\d+)?/) + { + return _GetVisualStudioVersion($1, $2); + } + + croak + "Unable to determine Visual Studio version: The nmake version could not be determined."; + } + else + { + # fake version + return '17.00'; + } +} + +sub _GetVisualStudioVersion +{ + my ($major, $minor) = @_; + + # The major visual studio that is supported has nmake + # version <= 14.40, so stick with it as the latest version + # if bumping on something even newer. + if ($major >= 14 && $minor >= 40) + { + carp + "The determined version of Visual Studio is newer than the latest supported version. Returning the latest supported version instead."; + return '14.30'; + } + elsif ($major < 12) + { + croak + "Unable to determine Visual Studio version: Visual Studio versions before 12.0 aren't supported."; + } + return "$major.$minor"; +} + +1; diff --git a/src/tools/msvc/build.bat b/src/tools/msvc/build.bat new file mode 100755 index 0000000..171f749 --- /dev/null +++ b/src/tools/msvc/build.bat @@ -0,0 +1,6 @@ +@echo off +REM src/tools/msvc/build.bat +REM all the logic for this now belongs in build.pl. This file really +REM only exists so you don't have to type "perl build.pl" +REM Resist any temptation to add any logic here. +@perl %~dp0/build.pl %* diff --git a/src/tools/msvc/build.pl b/src/tools/msvc/build.pl new file mode 100644 index 0000000..9853e5c --- /dev/null +++ b/src/tools/msvc/build.pl @@ -0,0 +1,92 @@ +# -*-perl-*- hey - emacs - this is a perl file + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +# +# Script that provides 'make' functionality for msvc builds. +# +# src/tools/msvc/build.pl +# +use strict; +use warnings; + +use FindBin; +use lib $FindBin::RealBin; + +use Cwd; + +use Mkvcbuild; + +sub usage +{ + die( "Usage: build.pl [ [ <configuration> ] <component> ]\n" + . "Options are case-insensitive.\n" + . " configuration: Release | Debug. This sets the configuration\n" + . " to build. Default is Release.\n" + . " component: name of component to build. An empty value means\n" + . " to build all components.\n"); +} + +chdir('../../..') if (-d '../msvc' && -d '../../../src'); +die 'Must run from root or msvc directory' + unless (-d 'src/tools/msvc' && -d 'src'); + +usage() unless scalar(@ARGV) <= 2; + +# buildenv.pl is for specifying the build environment settings +# it should contain lines like: +# $ENV{PATH} = "c:/path/to/bison/bin;$ENV{PATH}"; + +if (-e "src/tools/msvc/buildenv.pl") +{ + do "./src/tools/msvc/buildenv.pl"; +} +elsif (-e "./buildenv.pl") +{ + do "./buildenv.pl"; +} + +# set up the project +our $config; +do "./src/tools/msvc/config_default.pl"; +do "./src/tools/msvc/config.pl" if (-f "src/tools/msvc/config.pl"); + +my $vcver = Mkvcbuild::mkvcbuild($config); + +# check what sort of build we are doing +my $bconf = $ENV{CONFIG} || "Release"; +my $msbflags = $ENV{MSBFLAGS} || ""; +my $buildwhat = $ARGV[1] || ""; + +if (defined($ARGV[0])) +{ + if (uc($ARGV[0]) eq 'DEBUG') + { + $bconf = "Debug"; + } + elsif (uc($ARGV[0]) ne "RELEASE") + { + $buildwhat = $ARGV[0] || ""; + } +} + +# ... and do it + +if ($buildwhat) +{ + system( + "msbuild $buildwhat.vcxproj /verbosity:normal $msbflags /p:Configuration=$bconf" + ); +} +else +{ + system( + "msbuild pgsql.sln /verbosity:normal $msbflags /p:Configuration=$bconf" + ); +} + +# report status + +my $status = $? >> 8; + +exit $status; diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat new file mode 100755 index 0000000..cf35764 --- /dev/null +++ b/src/tools/msvc/clean.bat @@ -0,0 +1,156 @@ +@echo off +REM src/tools/msvc/clean.bat + +set DIST=0 +if "%1"=="dist" set DIST=1 + +setlocal + +cd "%~dp0\..\..\.." + +if exist debug rd /s /q debug +if exist release rd /s /q release +for %%f in (*.vcproj) do del %%f +for %%f in (*.vcxproj) do del %%f +for %%f in (*.vcxproj.user) do del %%f +if exist pgsql.sln del /q pgsql.sln +if exist pgsql.sln.cache del /q pgsql.sln.cache +if exist pgsql.sdf del /q pgsql.sdf +if exist pgsql.suo del /q /a:H pgsql.suo +del /s /q src\bin\win32ver.rc 2> NUL +del /s /q src\interfaces\win32ver.rc 2> NUL +if exist src\backend\win32ver.rc del /q src\backend\win32ver.rc +if exist src\backend\replication\libpqwalreceiver\win32ver.rc del /q src\backend\replication\libpqwalreceiver\win32ver.rc +if exist src\backend\replication\pgoutput\win32ver.rc del /q src\backend\replication\pgoutput\win32ver.rc +if exist src\backend\snowball\win32ver.rc del /q src\backend\snowball\win32ver.rc +if exist src\interfaces\ecpg\test\win32ver.rc del /q src\interfaces\ecpg\test\win32ver.rc +if exist src\pl\plperl\win32ver.rc del /q src\pl\plperl\win32ver.rc +if exist src\pl\plpgsql\src\win32ver.rc del /q src\pl\plpgsql\src\win32ver.rc +if exist src\pl\plpython\win32ver.rc del /q src\pl\plpython\win32ver.rc +if exist src\pl\tcl\win32ver.rc del /q src\pl\tcl\win32ver.rc +if exist src\test\isolation\win32ver.rc del /q src\test\isolation\win32ver.rc +if exist src\test\regress\win32ver.rc del /q src\test\regress\win32ver.rc +if exist src\timezone\win32ver.rc del /q src\timezone\win32ver.rc + +for /d %%f in (src\interfaces\ecpg\*) do if exist %%f\win32ver.rc del /q %%f\win32ver.rc +for /d %%f in (contrib\*) do if exist %%f\win32ver.rc del /q %%f\win32ver.rc +for /d %%f in (src\backend\utils\mb\conversion_procs\*) do if exist %%f\win32ver.rc del /q %%f\win32ver.rc +for /d %%f in (src\test\modules\*) do if exist %%f\win32ver.rc del /q %%f\win32ver.rc + +REM Delete files created with GenerateFiles() in Solution.pm +if exist src\include\pg_config.h del /q src\include\pg_config.h +if exist src\include\pg_config_ext.h del /q src\include\pg_config_ext.h +if exist src\include\pg_config_os.h del /q src\include\pg_config_os.h +if exist src\include\nodes\nodetags.h del /q src\include\nodes\nodetags.h +if exist src\include\utils\errcodes.h del /q src\include\utils\errcodes.h +if exist src\include\utils\fmgroids.h del /q src\include\utils\fmgroids.h +if exist src\include\utils\fmgrprotos.h del /q src\include\utils\fmgrprotos.h +if exist src\include\storage\lwlocknames.h del /q src\include\storage\lwlocknames.h +if exist src\include\utils\probes.h del /q src\include\utils\probes.h +if exist src\include\catalog\schemapg.h del /q src\include\catalog\schemapg.h +if exist src\include\catalog\system_fk_info.h del /q src\include\catalog\system_fk_info.h +if exist src\include\catalog\pg_*_d.h del /q src\include\catalog\pg_*_d.h +if exist src\include\catalog\header-stamp del /q src\include\catalog\header-stamp +if exist doc\src\sgml\version.sgml del /q doc\src\sgml\version.sgml + +if %DIST%==1 if exist src\backend\utils\fmgroids.h del /q src\backend\utils\fmgroids.h +if %DIST%==1 if exist src\backend\utils\fmgrprotos.h del /q src\backend\utils\fmgrprotos.h +if %DIST%==1 if exist src\backend\utils\fmgrtab.c del /q src\backend\utils\fmgrtab.c +if %DIST%==1 if exist src\backend\utils\fmgr-stamp del /q src\backend\utils\fmgr-stamp +if %DIST%==1 if exist src\backend\utils\errcodes.h del /q src\backend\utils\errcodes.h +if %DIST%==1 if exist src\backend\nodes\node-support-stamp del /q src\backend\nodes\node-support-stamp +if %DIST%==1 for %%F IN (copy equal out queryjumble read) do if exist src\backend\nodes\%%Ffuncs.funcs.c del /q src\backend\nodes\%%Ffuncs.funcs.c +if %DIST%==1 for %%F IN (copy equal out queryjumble read) do if exist src\backend\nodes\%%Ffuncs.switch.c del /q src\backend\nodes\%%Ffuncs.switch.c +if %DIST%==1 if exist src\backend\nodes\nodetags.h del /q src\backend\nodes\nodetags.h +if %DIST%==1 if exist src\backend\storage\lmgr\lwlocknames.c del /q src\backend\storage\lmgr\lwlocknames.c +if %DIST%==1 if exist src\backend\storage\lmgr\lwlocknames.h del /q src\backend\storage\lmgr\lwlocknames.h +if %DIST%==1 if exist src\pl\plpython\spiexceptions.h del /q src\pl\plpython\spiexceptions.h +if %DIST%==1 if exist src\pl\plpgsql\src\plerrcodes.h del /q src\pl\plpgsql\src\plerrcodes.h +if %DIST%==1 if exist src\pl\tcl\pltclerrcodes.h del /q src\pl\tcl\pltclerrcodes.h +if %DIST%==1 if exist src\bin\psql\sql_help.c del /q src\bin\psql\sql_help.c +if %DIST%==1 if exist src\bin\psql\sql_help.h del /q src\bin\psql\sql_help.h +if %DIST%==1 if exist src\common\kwlist_d.h del /q src\common\kwlist_d.h +if %DIST%==1 if exist src\pl\plpgsql\src\pl_reserved_kwlist_d.h del /q src\pl\plpgsql\src\pl_reserved_kwlist_d.h +if %DIST%==1 if exist src\pl\plpgsql\src\pl_unreserved_kwlist_d.h del /q src\pl\plpgsql\src\pl_unreserved_kwlist_d.h +if %DIST%==1 if exist src\interfaces\ecpg\preproc\c_kwlist_d.h del /q src\interfaces\ecpg\preproc\c_kwlist_d.h +if %DIST%==1 if exist src\interfaces\ecpg\preproc\ecpg_kwlist_d.h del /q src\interfaces\ecpg\preproc\ecpg_kwlist_d.h +if %DIST%==1 if exist src\interfaces\ecpg\preproc\preproc.y del /q src\interfaces\ecpg\preproc\preproc.y +if %DIST%==1 if exist src\backend\catalog\postgres.bki del /q src\backend\catalog\postgres.bki +if %DIST%==1 if exist src\backend\catalog\system_constraints.sql del /q src\backend\catalog\system_constraints.sql +if %DIST%==1 if exist src\backend\catalog\schemapg.h del /q src\backend\catalog\schemapg.h +if %DIST%==1 if exist src\backend\catalog\system_fk_info.h del /q src\backend\catalog\system_fk_info.h +if %DIST%==1 if exist src\backend\catalog\pg_*_d.h del /q src\backend\catalog\pg_*_d.h +if %DIST%==1 if exist src\backend\catalog\bki-stamp del /q src\backend\catalog\bki-stamp +if %DIST%==1 if exist src\backend\parser\scan.c del /q src\backend\parser\scan.c +if %DIST%==1 if exist src\backend\parser\gram.c del /q src\backend\parser\gram.c +if %DIST%==1 if exist src\backend\parser\gram.h del /q src\backend\parser\gram.h +if %DIST%==1 if exist src\backend\bootstrap\bootscanner.c del /q src\backend\bootstrap\bootscanner.c +if %DIST%==1 if exist src\backend\bootstrap\bootparse.c del /q src\backend\bootstrap\bootparse.c +if %DIST%==1 if exist src\backend\bootstrap\bootparse.h del /q src\backend\bootstrap\bootparse.h +if %DIST%==1 if exist src\backend\utils\adt\jsonpath_gram.c del /q src\backend\utils\adt\jsonpath_gram.c +if %DIST%==1 if exist src\backend\utils\adt\jsonpath_gram.h del /q src\backend\utils\adt\jsonpath_gram.h +if %DIST%==1 if exist src\backend\utils\adt\jsonpath_scan.c del /q src\backend\utils\adt\jsonpath_scan.c +if %DIST%==1 if exist src\backend\utils\misc\guc-file.c del /q src\backend\utils\misc\guc-file.c +if %DIST%==1 if exist src\backend\replication\repl_scanner.c del /q src\backend\replication\repl_scanner.c +if %DIST%==1 if exist src\backend\replication\repl_gram.c del /q src\backend\replication\repl_gram.c +if %DIST%==1 if exist src\backend\replication\repl_gram.h del /q src\backend\replication\repl_gram.h +if %DIST%==1 if exist src\backend\replication\syncrep_scanner.c del /q src\backend\replication\syncrep_scanner.c +if %DIST%==1 if exist src\backend\replication\syncrep_gram.c del /q src\backend\replication\syncrep_gram.c +if %DIST%==1 if exist src\backend\replication\syncrep_gram.h del /q src\backend\replication\syncrep_gram.h + + +if exist src\interfaces\libpq\libpqdll.def del /q src\interfaces\libpq\libpqdll.def +if exist src\interfaces\ecpg\compatlib\compatlib.def del /q src\interfaces\ecpg\compatlib\compatlib.def +if exist src\interfaces\ecpg\ecpglib\ecpglib.def del /q src\interfaces\ecpg\ecpglib\ecpglib.def +if exist src\interfaces\ecpg\include\ecpg_config.h del /q src\interfaces\ecpg\include\ecpg_config.h +if exist src\interfaces\ecpg\pgtypeslib\pgtypeslib.def del /q src\interfaces\ecpg\pgtypeslib\pgtypeslib.def +if %DIST%==1 if exist src\interfaces\ecpg\preproc\pgc.c del /q src\interfaces\ecpg\preproc\pgc.c +if %DIST%==1 if exist src\interfaces\ecpg\preproc\preproc.c del /q src\interfaces\ecpg\preproc\preproc.c +if %DIST%==1 if exist src\interfaces\ecpg\preproc\preproc.h del /q src\interfaces\ecpg\preproc\preproc.h + +if exist src\port\pg_config_paths.h del /q src\port\pg_config_paths.h + +if exist src\pl\plperl\SPI.c del /q src\pl\plperl\SPI.c +if exist src\pl\plperl\Util.c del /q src\pl\plperl\Util.c +if exist src\pl\plperl\perlchunks.h del /q src\pl\plperl\perlchunks.h +if exist src\pl\plperl\plperl_opmask.h del /q src\pl\plperl\plperl_opmask.h +if %DIST%==1 if exist src\pl\plpgsql\src\pl_gram.c del /q src\pl\plpgsql\src\pl_gram.c +if %DIST%==1 if exist src\pl\plpgsql\src\pl_gram.h del /q src\pl\plpgsql\src\pl_gram.h + +if %DIST%==1 if exist src\fe_utils\psqlscan.c del /q src\fe_utils\psqlscan.c +if %DIST%==1 if exist src\bin\psql\psqlscanslash.c del /q src\bin\psql\psqlscanslash.c +if %DIST%==1 if exist src\bin\pgbench\exprscan.c del /q src\bin\pgbench\exprscan.c +if %DIST%==1 if exist src\bin\pgbench\exprparse.c del /q src\bin\pgbench\exprparse.c +if %DIST%==1 if exist src\bin\pgbench\exprparse.h del /q src\bin\pgbench\exprparse.h + +if %DIST%==1 if exist contrib\cube\cubescan.c del /q contrib\cube\cubescan.c +if %DIST%==1 if exist contrib\cube\cubeparse.c del /q contrib\cube\cubeparse.c +if %DIST%==1 if exist contrib\cube\cubeparse.h del /q contrib\cube\cubeparse.h +if %DIST%==1 if exist contrib\fuzzystrmatch\daitch_mokotoff.h del /q contrib\fuzzystrmatch\daitch_mokotoff.h +if %DIST%==1 if exist contrib\seg\segscan.c del /q contrib\seg\segscan.c +if %DIST%==1 if exist contrib\seg\segparse.c del /q contrib\seg\segparse.c +if %DIST%==1 if exist contrib\seg\segparse.h del /q contrib\seg\segparse.h + +if exist src\test\regress\tmp_check rd /s /q src\test\regress\tmp_check +if exist contrib\spi\refint.dll del /q contrib\spi\refint.dll +if exist contrib\spi\autoinc.dll del /q contrib\spi\autoinc.dll +if exist src\test\regress\regress.dll del /q src\test\regress\regress.dll +if exist src\test\regress\refint.dll del /q src\test\regress\refint.dll +if exist src\test\regress\autoinc.dll del /q src\test\regress\autoinc.dll +if %DIST%==1 if exist src\test\isolation\specscanner.c del /q src\test\isolation\specscanner.c +if %DIST%==1 if exist src\test\isolation\specparse.c del /q src\test\isolation\specparse.c +if %DIST%==1 if exist src\test\isolation\specparse.h del /q src\test\isolation\specparse.h + +for /d %%f in (contrib\* src\bin\* src\test\* src\test\modules\* + ) do if exist %%f\tmp_check rd /s /q %%f\tmp_check + +REM Clean up datafiles built with contrib +REM cd contrib +REM for /r %%f in (*.sql) do if exist %%f.in del %%f + +cd "%~dp0" + +REM Clean up ecpg regression test files +msbuild ecpg_regression.proj /NoLogo /v:q %MSBFLAGS% /t:clean + +goto :eof diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl new file mode 100644 index 0000000..8945e77 --- /dev/null +++ b/src/tools/msvc/config_default.pl @@ -0,0 +1,32 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +# Configuration arguments for vcbuild. +use strict; +use warnings; + +our $config = { + asserts => 0, # --enable-cassert + + # blocksize => 8, # --with-blocksize, 8kB by default + # wal_blocksize => 8, # --with-wal-blocksize, 8kB by default + ldap => 1, # --with-ldap + extraver => undef, # --with-extra-version=<string> + gss => undef, # --with-gssapi=<path> + icu => undef, # --with-icu=<path> + lz4 => undef, # --with-lz4=<path> + zstd => undef, # --with-zstd=<path> + nls => undef, # --enable-nls=<path> + tap_tests => undef, # --enable-tap-tests + tcl => undef, # --with-tcl=<path> + perl => undef, # --with-perl=<path> + python => undef, # --with-python=<path> + openssl => undef, # --with-ssl=openssl with <path> + uuid => undef, # --with-uuid=<path> + xml => undef, # --with-libxml=<path> + xslt => undef, # --with-libxslt=<path> + iconv => undef, # (not in configure, path to iconv) + zlib => undef # --with-zlib=<path> +}; + +1; diff --git a/src/tools/msvc/dummylib/README b/src/tools/msvc/dummylib/README new file mode 100644 index 0000000..7b63d0e --- /dev/null +++ b/src/tools/msvc/dummylib/README @@ -0,0 +1,13 @@ + +src/tools/msvc/dummylib + +This directory contains just enough of a dummy library to allow checking of +the programs in src/tools/msvc and src/tools/win32tzlist.pl with +perl -cw, even on machines that lack the Win32 perl infrastructure. + +invoke via: + +PERL5LIB=src/tools/msvc/dummylib perl -cw $file + +This is the only use that should be made of this directory. Attempting actually +running of any programs using this library will result in a lot of grief. diff --git a/src/tools/msvc/dummylib/Win32.pm b/src/tools/msvc/dummylib/Win32.pm new file mode 100644 index 0000000..df2d7a2 --- /dev/null +++ b/src/tools/msvc/dummylib/Win32.pm @@ -0,0 +1,7 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +package Win32; +use strict; +use warnings; +1; diff --git a/src/tools/msvc/dummylib/Win32/Registry.pm b/src/tools/msvc/dummylib/Win32/Registry.pm new file mode 100644 index 0000000..e14636e --- /dev/null +++ b/src/tools/msvc/dummylib/Win32/Registry.pm @@ -0,0 +1,16 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +package Win32::Registry; + +use strict; +use warnings; + +use vars qw($HKEY_LOCAL_MACHINE); + +use Exporter (); +our (@EXPORT, @ISA); +@ISA = qw(Exporter); +@EXPORT = qw($HKEY_LOCAL_MACHINE); + +1; diff --git a/src/tools/msvc/dummylib/Win32API/File.pm b/src/tools/msvc/dummylib/Win32API/File.pm new file mode 100644 index 0000000..7baf34c --- /dev/null +++ b/src/tools/msvc/dummylib/Win32API/File.pm @@ -0,0 +1,17 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +package Win32API::File; + +use strict; +use warnings; + +use constant { SEM_FAILCRITICALERRORS => 1, SEM_NOGPFAULTERRORBOX => 2 }; +sub SetErrormode { } +use Exporter; +our (@ISA, @EXPORT_OK, %EXPORT_TAGS); +@ISA = qw(Exporter); +@EXPORT_OK = qw(SetErrorMode SEM_FAILCRITICALERRORS SEM_NOGPFAULTERRORBOX); +%EXPORT_TAGS = (SEM_ => [qw(SEM_FAILCRITICALERRORS SEM_NOGPFAULTERRORBOX)]); + +1; diff --git a/src/tools/msvc/ecpg_regression.proj b/src/tools/msvc/ecpg_regression.proj new file mode 100644 index 0000000..ec2760b --- /dev/null +++ b/src/tools/msvc/ecpg_regression.proj @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="all"> +<!-- + MSBuild project file to build ecpg regression tests +--> + + <PropertyGroup> + <TESTDIR>..\..\interfaces\ecpg\test</TESTDIR> + <CONFIG>Debug</CONFIG> + <OUTDIR>..\..\..\..\..\$(CONFIG)\</OUTDIR> + </PropertyGroup> + <PropertyGroup Condition="'$(CONFIG)'=='DEBUG'"> + <!-- set debug runtime library if necessary to be compatible with the LIB files generated --> + <DEBUGLIB>d</DEBUGLIB> + </PropertyGroup> + + <ItemGroup> + <Pgc Include="$(TESTDIR)\**\*.pgc" Exclude="$(TESTDIR)\performance\perftest.pgc"/> + </ItemGroup> + + <ItemGroup> + <OutputToDelete Include="$(TESTDIR)\**\*.exe" /> + <OutputToDelete Include="$(TESTDIR)\**\*.exe.manifest" /> + <OutputToDelete Include="$(TESTDIR)\**\*.obj" /> + <OutputToDelete Include="$(TESTDIR)\**\*.c" Exclude="$(TESTDIR)\pg_regress_ecpg.c;$(TESTDIR)\expected\*.c" /> + </ItemGroup> + + <Target Name="all" Inputs="@(Pgc)" Outputs="%(RelativeDir)%(Filename).exe"> + <!-- Set special parameters for some tests --> + <CreateProperty Value="-C INFORMIX" Condition="'%(Pgc.RelativeDir)'=='$(TESTDIR)\compat_informix\'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-C INFORMIX -r no_indicator" Condition="'%(Pgc.FileName)'=='rnull'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-C ORACLE" Condition="'%(Pgc.RelativeDir)'=='$(TESTDIR)\compat_oracle\'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-c" Condition="'%(Pgc.FileName)'=='array_of_struct'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-c" Condition="'%(Pgc.FileName)'=='pointer_to_struct'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-r questionmarks" Condition="'%(Pgc.FileName)'=='oldexec'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-r prepare" Condition="'%(Pgc.FileName)'=='autoprep'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + <CreateProperty Value="-i" Condition="'%(Pgc.FileName)'=='strings'"> + <Output TaskParameter="Value" PropertyName="ECPGPARAM" /> + </CreateProperty> + + <!-- Run ECPG and the Visual C++ compiler on the files. Don't bother with dependency check between the steps --> + <Exec WorkingDirectory="%(Pgc.RelativeDir)" Command="$(OUTDIR)ecpg\ecpg -I ../../include --regression $(ECPGPARAM) -o %(Pgc.Filename).c %(Pgc.Filename).pgc" /> + <Exec WorkingDirectory="%(Pgc.RelativeDir)" Command="cl /nologo %(Pgc.FileName).c /TC /MD$(DEBUGLIB) /DENABLE_THREAD_SAFETY /DWIN32 /I. /I..\..\include /I..\..\..\libpq /I..\..\..\..\include /link /defaultlib:$(OUTDIR)libecpg\libecpg.lib /defaultlib:$(OUTDIR)libecpg_compat\libecpg_compat.lib /defaultlib:$(OUTDIR)libpgtypes\libpgtypes.lib" /> + </Target> + + <!-- Clean up all output files --> + <Target Name="clean"> + <Delete Files="@(OutputToDelete)" /> + </Target> +</Project> diff --git a/src/tools/msvc/gendef.pl b/src/tools/msvc/gendef.pl new file mode 100644 index 0000000..cf83d7d --- /dev/null +++ b/src/tools/msvc/gendef.pl @@ -0,0 +1,205 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +use strict; +use warnings; +use List::Util qw(min); +use Getopt::Long; + +my @def; + +# +# Script that generates a .DEF file for all objects in a directory +# +# src/tools/msvc/gendef.pl +# + +# Given a symbol file path, loops over its contents +# and returns a list of symbols of interest as a dictionary +# of 'symbolname' -> symtype, where symtype is: +# +# 0 a CODE symbol, left undecorated in the .DEF +# 1 A DATA symbol, i.e. global var export +# +sub extract_syms +{ + my ($symfile, $def) = @_; + open(my $f, '<', $symfile) || die "Could not open $symfile for $_: $!\n"; + while (<$f>) + { + + # Expected symbol lines look like: + # + # 0 1 2 3 4 5 6 + # IDX SYMBOL SECT SYMTYPE SYMSTATIC SYMNAME + # ------------------------------------------------------------------------ + # 02E 00000130 SECTA notype External | _standbyState + # 02F 00000009 SECT9 notype Static | _LocalRecoveryInProgress + # 064 00000020 SECTC notype () Static | _XLogCheckBuffer + # 065 00000000 UNDEF notype () External | _BufferGetTag + # + # See http://msdn.microsoft.com/en-us/library/b842y285.aspx + # + # We're not interested in the symbol index or offset. + # + # SECT[ION] is only examined to see whether the symbol is defined in a + # COFF section of the local object file; if UNDEF, it's a symbol to be + # resolved at link time from another object so we can't export it. + # + # SYMTYPE is always notype for C symbols as there's no typeinfo and no + # way to get the symbol type from name (de)mangling. However, we care + # if "notype" is suffixed by "()" or not. The presence of () means the + # symbol is a function, the absence means it isn't. + # + # SYMSTATIC indicates whether it's a compilation-unit local "static" + # symbol ("Static"), or whether it's available for use from other + # compilation units ("External"). We export all symbols that aren't + # static as part of the whole program DLL interface to produce UNIX-like + # default linkage. + # + # SYMNAME is, obviously, the symbol name. The leading underscore + # indicates that the _cdecl calling convention is used. See + # http://www.unixwiz.net/techtips/win32-callconv.html + # http://www.codeproject.com/Articles/1388/Calling-Conventions-Demystified + # + s/notype \(\)/func/g; + s/notype/data/g; + + my @pieces = split; + + # Skip file and section headers and other non-symbol entries + next unless defined($pieces[0]) and $pieces[0] =~ /^[A-F0-9]{3,}$/; + + # Skip blank symbol names + next unless $pieces[6]; + + # Skip externs used from another compilation unit + next if ($pieces[2] eq "UNDEF"); + + # Skip static symbols + next unless ($pieces[4] eq "External"); + + # Skip some more MSVC-generated crud + next if $pieces[6] =~ /^@/; + next if $pieces[6] =~ /^\(/; + + # __real and __xmm are out-of-line floating point literals and + # (for __xmm) their SIMD equivalents. They shouldn't be part + # of the DLL interface. + next if $pieces[6] =~ /^__real/; + next if $pieces[6] =~ /^__xmm/; + + # __imp entries are imports from other DLLs, eg __imp__malloc . + # (We should never have one of these that hasn't already been skipped + # by the UNDEF test above, though). + next if $pieces[6] =~ /^__imp/; + + # More under-documented internal crud + next if $pieces[6] =~ /NULL_THUNK_DATA$/; + next if $pieces[6] =~ /^__IMPORT_DESCRIPTOR/; + next if $pieces[6] =~ /^__NULL_IMPORT/; + + # Skip string literals + next if $pieces[6] =~ /^\?\?_C/; + + # We assume that if a symbol is defined as data, then as a function, + # the linker will reject the binary anyway. So it's OK to just pick + # whatever came last. + $def->{ $pieces[6] } = $pieces[3]; + } + close($f); + return; +} + +sub writedef +{ + my ($deffile, $arch, $def) = @_; + open(my $fh, '>', $deffile) || die "Could not write to $deffile\n"; + print $fh "EXPORTS\n"; + foreach my $f (sort keys %{$def}) + { + my $isdata = $def->{$f} eq 'data'; + + # Strip the leading underscore for win32, but not x64 + $f =~ s/^_// + unless ($arch eq "x86_64"); + + # Emit just the name if it's a function symbol, or emit the name + # decorated with the DATA option for variables. + if ($isdata) + { + print $fh " $f DATA\n"; + } + else + { + print $fh " $f\n"; + } + } + close($fh); + return; +} + + +sub usage +{ + die("Usage: gendef.pl --arch <arch> --deffile <deffile> --tempdir <tempdir> files-or-directories\n" + . " arch: x86 | x86_64\n" + . " deffile: path of the generated file\n" + . " tempdir: directory for temporary files\n" + . " files or directories: object files or directory containing object files\n" + ); +} + +my $arch; +my $deffile; +my $tempdir = '.'; + +GetOptions( + 'arch:s' => \$arch, + 'deffile:s' => \$deffile, + 'tempdir:s' => \$tempdir,) or usage(); + +usage("arch: $arch") + unless ($arch eq 'x86' || $arch eq 'x86_64'); + +my @files; + +foreach my $in (@ARGV) +{ + if (-d $in) + { + push @files, glob "$in/*.obj"; + } + else + { + push @files, $in; + } +} + +# if the def file exists and is newer than all input object files, skip +# its creation +if (-f $deffile + && (-M $deffile < min(map { -M } @files))) +{ + print "Not re-generating $deffile, file already exists.\n"; + exit(0); +} + +print "Generating $deffile in tempdir $tempdir\n"; + +my %def = (); + +my $symfile = "$tempdir/all.sym"; +my $tmpfile = "$tempdir/tmp.sym"; +mkdir($tempdir) unless -d $tempdir; + +my $cmd = "dumpbin /nologo /symbols /out:$tmpfile " . join(' ', @files); + +system($cmd) && die "Could not call dumpbin"; +rename($tmpfile, $symfile); +extract_syms($symfile, \%def); +print "\n"; + +writedef($deffile, $arch, \%def); + +print "Generated " . scalar(keys(%def)) . " symbols\n"; diff --git a/src/tools/msvc/install.bat b/src/tools/msvc/install.bat new file mode 100644 index 0000000..d02b808 --- /dev/null +++ b/src/tools/msvc/install.bat @@ -0,0 +1,6 @@ +@echo off +REM src/tools/msvc/install.bat +REM all the logic for this now belongs in install.pl. This file really +REM only exists so you don't have to type "perl install.pl" +REM Resist any temptation to add any logic here. +@perl %~dp0/install.pl %* diff --git a/src/tools/msvc/install.pl b/src/tools/msvc/install.pl new file mode 100755 index 0000000..8de7cee --- /dev/null +++ b/src/tools/msvc/install.pl @@ -0,0 +1,39 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +# +# Script that provides 'make install' functionality for msvc builds +# +# src/tools/msvc/install.pl +# +use strict; +use warnings; + +use FindBin; +use lib $FindBin::RealBin; + +use Install qw(Install); + +# buildenv.pl is for specifying the build environment settings +# it should contain lines like: +# $ENV{PATH} = "c:/path/to/bison/bin;$ENV{PATH}"; + +if (-e "src/tools/msvc/buildenv.pl") +{ + do "./src/tools/msvc/buildenv.pl"; +} +elsif (-e "./buildenv.pl") +{ + do "./buildenv.pl"; +} + +my $target = shift || Usage(); +my $insttype = shift; +Install($target, $insttype); + +sub Usage +{ + print "Usage: install.pl <targetdir> [installtype]\n"; + print "installtype: client\n"; + exit(1); +} diff --git a/src/tools/msvc/mkvcbuild.pl b/src/tools/msvc/mkvcbuild.pl new file mode 100644 index 0000000..7f94b1a --- /dev/null +++ b/src/tools/msvc/mkvcbuild.pl @@ -0,0 +1,31 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +# +# Script that parses Unix style build environment and generates build files +# for building with Visual Studio. +# +# src/tools/msvc/mkvcbuild.pl +# +use strict; +use warnings; + +use FindBin; +use lib $FindBin::RealBin; + +use Mkvcbuild; + +chdir('../../..') if (-d '../msvc' && -d '../../../src'); +die 'Must run from root or msvc directory' + unless (-d 'src/tools/msvc' && -d 'src'); + +die 'Could not find config_default.pl' + unless (-f 'src/tools/msvc/config_default.pl'); +print "Warning: no config.pl found, using default.\n" + unless (-f 'src/tools/msvc/config.pl'); + +our $config; +do './src/tools/msvc/config_default.pl'; +do './src/tools/msvc/config.pl' if (-f 'src/tools/msvc/config.pl'); + +Mkvcbuild::mkvcbuild($config); diff --git a/src/tools/msvc/pgbison.bat b/src/tools/msvc/pgbison.bat new file mode 100755 index 0000000..dc8ac4a --- /dev/null +++ b/src/tools/msvc/pgbison.bat @@ -0,0 +1,7 @@ +@echo off + +REM src/tools/msvc/pgbison.bat +REM all the logic for this now belongs in pgbison.pl. This file really +REM only exists so you don't have to type "perl src/tools/msvc/pgbison.pl" +REM Resist any temptation to add any logic here. +@perl %~dp0/pgbison.pl %* diff --git a/src/tools/msvc/pgbison.pl b/src/tools/msvc/pgbison.pl new file mode 100644 index 0000000..25df669 --- /dev/null +++ b/src/tools/msvc/pgbison.pl @@ -0,0 +1,55 @@ +# -*-perl-*- hey - emacs - this is a perl file + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +# src/tools/msvc/pgbison.pl + +use strict; +use warnings; + +use File::Basename; + +# assume we are in the postgres source root + +do './src/tools/msvc/buildenv.pl' if -e 'src/tools/msvc/buildenv.pl'; + +my ($bisonver) = `bison -V`; # grab first line +$bisonver = (split(/\s+/, $bisonver))[3]; # grab version number + +unless ($bisonver ge '2.3') +{ + print "WARNING! Bison install not found, or unsupported Bison version.\n"; + print "echo Attempting to build without.\n"; + exit 0; +} + +my $input = shift; +if ($input !~ /\.y$/) +{ + print "Input must be a .y file\n"; + exit 1; +} +elsif (!-e $input) +{ + print "Input file $input not found\n"; + exit 1; +} + +(my $output = $input) =~ s/\.y$/.c/; + +# plpgsql just has to be different +$output =~ s/gram\.c$/pl_gram.c/ if $input =~ /src.pl.plpgsql.src.gram\.y$/; + +my $makefile = dirname($input) . "/Makefile"; +my ($mf, $make); +open($mf, '<', $makefile); +local $/ = undef; +$make = <$mf>; +close($mf); +my $basetarg = basename($output); +my $headerflag = ($make =~ /^$basetarg:\s+BISONFLAGS\b.*-d/m ? '-d' : ''); + +my $nodep = $bisonver ge '3.0' ? "-Wno-deprecated" : ""; + +system("bison $nodep $headerflag $input -o $output"); +exit $? >> 8; diff --git a/src/tools/msvc/pgflex.bat b/src/tools/msvc/pgflex.bat new file mode 100755 index 0000000..f20834b --- /dev/null +++ b/src/tools/msvc/pgflex.bat @@ -0,0 +1,7 @@ +@echo off + +REM src/tools/msvc/pgflex.bat +REM all the logic for this now belongs in pgflex.pl. This file really +REM only exists so you don't have to type "perl src/tools/msvc/pgflex.pl" +REM Resist any temptation to add any logic here. +@perl %~dp0/pgflex.pl %* diff --git a/src/tools/msvc/pgflex.pl b/src/tools/msvc/pgflex.pl new file mode 100644 index 0000000..c308a08 --- /dev/null +++ b/src/tools/msvc/pgflex.pl @@ -0,0 +1,108 @@ +# -*-perl-*- hey - emacs - this is a perl file + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +# src/tools/msvc/pgflex.pl + +use strict; +use warnings; + +use File::Basename; + +# silence flex bleatings about file path style +$ENV{CYGWIN} = 'nodosfilewarning'; + +# assume we are in the postgres source root + +do './src/tools/msvc/buildenv.pl' if -e 'src/tools/msvc/buildenv.pl'; + +my ($flexver) = `flex -V`; # grab first line +$flexver = (split(/\s+/, $flexver))[1]; +$flexver =~ s/[^0-9.]//g; +my @verparts = split(/\./, $flexver); +unless ($verparts[0] == 2 + && ($verparts[1] > 5 || ($verparts[1] == 5 && $verparts[2] >= 35))) +{ + print "WARNING! Flex install not found, or unsupported Flex version.\n"; + print "echo Attempting to build without.\n"; + exit 0; +} + +my $input = shift; +if ($input !~ /\.l$/) +{ + print "Input must be a .l file\n"; + exit 1; +} +elsif (!-e $input) +{ + print "Input file $input not found\n"; + exit 1; +} + +(my $output = $input) =~ s/\.l$/.c/; + +# get flex flags from make file +my $makefile = dirname($input) . "/Makefile"; +my ($mf, $make); +open($mf, '<', $makefile); +local $/ = undef; +$make = <$mf>; +close($mf); +my $basetarg = basename($output); +my $flexflags = ($make =~ /^$basetarg:\s*FLEXFLAGS\s*=\s*(\S.*)/m ? $1 : ''); + +system("flex $flexflags -o$output $input"); +if ($? == 0) +{ + + # Check for "%option reentrant" in .l file. + my $lfile; + open($lfile, '<', $input) || die "opening $input for reading: $!"; + my $lcode = <$lfile>; + close($lfile); + if ($lcode =~ /\%option\sreentrant/) + { + + # Reentrant scanners usually need a fix to prevent + # "unused variable" warnings with older flex versions. + system("perl src\\tools\\fix-old-flex-code.pl $output"); + } + else + { + + # For non-reentrant scanners we need to fix up the yywrap + # macro definition to keep the MS compiler happy. + # For reentrant scanners (like the core scanner) we do not + # need to (and must not) change the yywrap definition. + my $cfile; + open($cfile, '<', $output) || die "opening $output for reading: $!"; + my $ccode = <$cfile>; + close($cfile); + $ccode =~ s/yywrap\(n\)/yywrap()/; + open($cfile, '>', $output) || die "opening $output for writing: $!"; + print $cfile $ccode; + close($cfile); + } + if ($flexflags =~ /\s-b\s/) + { + my $lexback = "lex.backup"; + open($lfile, '<', $lexback) || die "opening $lexback for reading: $!"; + my $lexbacklines = <$lfile>; + close($lfile); + my $linecount = $lexbacklines =~ tr /\n/\n/; + if ($linecount != 1) + { + print "Scanner requires backup, see lex.backup.\n"; + exit 1; + } + unlink $lexback; + } + + exit 0; + +} +else +{ + exit $? >> 8; +} diff --git a/src/tools/msvc/vcregress.bat b/src/tools/msvc/vcregress.bat new file mode 100644 index 0000000..7fba45c --- /dev/null +++ b/src/tools/msvc/vcregress.bat @@ -0,0 +1,6 @@ +@echo off +REM src/tools/msvc/vcregress.bat +REM all the logic for this now belongs in vcregress.pl. This file really +REM only exists so you don't have to type "perl vcregress.pl" +REM Resist any temptation to add any logic here. +@perl %~dp0/vcregress.pl %* diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl new file mode 100644 index 0000000..78170d1 --- /dev/null +++ b/src/tools/msvc/vcregress.pl @@ -0,0 +1,664 @@ +# -*-perl-*- hey - emacs - this is a perl file + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +# src/tools/msvc/vcregress.pl + +use strict; +use warnings; + +our $config; + +use Cwd; +use File::Basename; +use File::Copy; +use File::Find (); +use File::Path qw(rmtree); +use File::Spec qw(devnull); + +use FindBin; +use lib $FindBin::RealBin; + +use Install qw(Install); + +my $startdir = getcwd(); + +chdir "../../.." if (-d "../../../src/tools/msvc"); + +my $topdir = getcwd(); +my $tmp_installdir = "$topdir/tmp_install"; + +do './src/tools/msvc/config_default.pl'; +do './src/tools/msvc/config.pl' if (-f 'src/tools/msvc/config.pl'); + +my $devnull = File::Spec->devnull; + +# These values are defaults that can be overridden by the calling environment +# (see buildenv.pl processing below). We assume that the ones listed here +# always exist by default. Other values may optionally be set for bincheck +# or taptest, see set_command_env() below. +# c.f. src/Makefile.global.in and configure.ac +$ENV{TAR} ||= 'tar'; + +# buildenv.pl is for specifying the build environment settings +# it should contain lines like: +# $ENV{PATH} = "c:/path/to/bison/bin;$ENV{PATH}"; + +if (-e "src/tools/msvc/buildenv.pl") +{ + do "./src/tools/msvc/buildenv.pl"; +} + +my $what = shift || ""; +if ($what =~ + /^(check|installcheck|plcheck|contribcheck|modulescheck|ecpgcheck|isolationcheck|upgradecheck|bincheck|recoverycheck|taptest)$/i + ) +{ + $what = uc $what; +} +else +{ + usage(); +} + +# use a capital C here because config.pl has $config +my $Config = -e "release/postgres/postgres.exe" ? "Release" : "Debug"; + +copy("$Config/refint/refint.dll", "src/test/regress"); +copy("$Config/autoinc/autoinc.dll", "src/test/regress"); +copy("$Config/regress/regress.dll", "src/test/regress"); +copy("$Config/dummy_seclabel/dummy_seclabel.dll", "src/test/regress"); + +# Configuration settings used by TAP tests +$ENV{with_ssl} = $config->{openssl} ? 'openssl' : 'no'; +$ENV{with_ldap} = $config->{ldap} ? 'yes' : 'no'; +$ENV{with_icu} = $config->{icu} ? 'yes' : 'no'; +$ENV{with_gssapi} = $config->{gss} ? 'yes' : 'no'; +$ENV{with_krb_srvnam} = $config->{krb_srvnam} || 'postgres'; +$ENV{with_readline} = 'no'; + +$ENV{PATH} = "$topdir/$Config/libpq;$ENV{PATH}"; + +if ($ENV{PERL5LIB}) +{ + $ENV{PERL5LIB} = "$topdir/src/tools/msvc;$ENV{PERL5LIB}"; +} +else +{ + $ENV{PERL5LIB} = "$topdir/src/tools/msvc"; +} + +my $maxconn = ""; +$maxconn = "--max-connections=$ENV{MAX_CONNECTIONS}" + if $ENV{MAX_CONNECTIONS}; + +my $temp_config = ""; +$temp_config = "--temp-config=\"$ENV{TEMP_CONFIG}\"" + if $ENV{TEMP_CONFIG}; + +chdir "src/test/regress"; + +my %command = ( + CHECK => \&check, + PLCHECK => \&plcheck, + INSTALLCHECK => \&installcheck, + ECPGCHECK => \&ecpgcheck, + CONTRIBCHECK => \&contribcheck, + MODULESCHECK => \&modulescheck, + ISOLATIONCHECK => \&isolationcheck, + BINCHECK => \&bincheck, + RECOVERYCHECK => \&recoverycheck, + UPGRADECHECK => \&upgradecheck, # no-op + TAPTEST => \&taptest,); + +my $proc = $command{$what}; + +exit 3 unless $proc; + +&$proc(@ARGV); + +exit 0; + +######################################################################## + +# Helper function for set_command_env, to set one environment command. +sub set_single_env +{ + my $envname = shift; + my $envdefault = shift; + + # If a command is defined by the environment, just use it. + return if (defined($ENV{$envname})); + + # Nothing is defined, so attempt to assign a default. The command + # may not be in the current environment, hence check if it can be + # executed. + my $rc = system("$envdefault --version >$devnull 2>&1"); + + # Set the environment to the default if it exists, else leave it. + $ENV{$envname} = $envdefault if $rc == 0; + return; +} + +# Set environment values for various command types. These can be used +# in the TAP tests. +sub set_command_env +{ + set_single_env('GZIP_PROGRAM', 'gzip'); + set_single_env('LZ4', 'lz4'); + set_single_env('OPENSSL', 'openssl'); + set_single_env('ZSTD', 'zstd'); +} + +sub installcheck_internal +{ + my ($schedule, @EXTRA_REGRESS_OPTS) = @_; + # for backwards compatibility, "serial" runs the tests in + # parallel_schedule one by one. + my $maxconn = $maxconn; + $maxconn = "--max-connections=1" if $schedule eq 'serial'; + $schedule = 'parallel' if $schedule eq 'serial'; + + my @args = ( + "../../../$Config/pg_regress/pg_regress", + "--dlpath=.", + "--bindir=../../../$Config/psql", + "--schedule=${schedule}_schedule", + "--max-concurrent-tests=20"); + push(@args, $maxconn) if $maxconn; + push(@args, @EXTRA_REGRESS_OPTS); + system(@args); + my $status = $? >> 8; + exit $status if $status; + return; +} + +sub installcheck +{ + my $schedule = shift || 'serial'; + installcheck_internal($schedule); + return; +} + +sub check +{ + my $schedule = shift || 'parallel'; + my $encoding = $ENV{ENCODING} || "SQL_ASCII"; + # for backwards compatibility, "serial" runs the tests in + # parallel_schedule one by one. + my $maxconn = $maxconn; + $maxconn = "--max-connections=1" if $schedule eq 'serial'; + $schedule = 'parallel' if $schedule eq 'serial'; + + InstallTemp(); + chdir "${topdir}/src/test/regress"; + my @args = ( + "../../../$Config/pg_regress/pg_regress", + "--dlpath=.", + "--bindir=", + "--schedule=${schedule}_schedule", + "--max-concurrent-tests=20", + "--encoding=${encoding}", + "--no-locale", + "--temp-instance=./tmp_check"); + push(@args, $maxconn) if $maxconn; + push(@args, $temp_config) if $temp_config; + system(@args); + my $status = $? >> 8; + exit $status if $status; + return; +} + +sub ecpgcheck +{ + my $msbflags = $ENV{MSBFLAGS} || ""; + chdir $startdir; + system("msbuild ecpg_regression.proj $msbflags /p:config=$Config"); + my $status = $? >> 8; + exit $status if $status; + InstallTemp(); + chdir "$topdir/src/interfaces/ecpg/test"; + my $schedule = "ecpg"; + my @args = ( + "../../../../$Config/pg_regress_ecpg/pg_regress_ecpg", + "--bindir=", + "--dbname=ecpg1_regression,ecpg2_regression", + "--create-role=regress_ecpg_user1,regress_ecpg_user2", + "--schedule=${schedule}_schedule", + "--encoding=SQL_ASCII", + "--no-locale", + "--temp-instance=./tmp_chk"); + push(@args, $maxconn) if $maxconn; + system(@args); + $status = $? >> 8; + exit $status if $status; + return; +} + +sub isolationcheck +{ + chdir "../isolation"; + copy("../../../$Config/isolationtester/isolationtester.exe", + "../../../$Config/pg_isolation_regress"); + my @args = ( + "../../../$Config/pg_isolation_regress/pg_isolation_regress", + "--bindir=../../../$Config/psql", + "--inputdir=.", + "--schedule=./isolation_schedule"); + push(@args, $maxconn) if $maxconn; + system(@args); + my $status = $? >> 8; + exit $status if $status; + return; +} + +sub tap_check +{ + die "Tap tests not enabled in configuration" + unless $config->{tap_tests}; + + my @flags; + foreach my $arg (0 .. scalar(@_) - 1) + { + next unless $_[$arg] =~ /^PROVE_FLAGS=(.*)/; + @flags = split(/\s+/, $1); + splice(@_, $arg, 1); + last; + } + + my $dir = shift; + chdir $dir; + + # Fetch and adjust PROVE_TESTS, applying glob() to each element + # defined to build a list of all the tests matching patterns. + my $prove_tests_val = $ENV{PROVE_TESTS} || "t/*.pl"; + my @prove_tests_array = split(/\s+/, $prove_tests_val); + my @prove_tests = (); + foreach (@prove_tests_array) + { + push(@prove_tests, glob($_)); + } + + # Fetch and adjust PROVE_FLAGS, handling multiple arguments. + my $prove_flags_val = $ENV{PROVE_FLAGS} || ""; + my @prove_flags = split(/\s+/, $prove_flags_val); + + my @args = ("prove", @flags, @prove_tests, @prove_flags); + + # adjust the environment for just this test + local %ENV = %ENV; + $ENV{PERL5LIB} = "$topdir/src/test/perl;$ENV{PERL5LIB}"; + $ENV{PG_REGRESS} = "$topdir/$Config/pg_regress/pg_regress"; + $ENV{REGRESS_SHLIB} = "$topdir/src/test/regress/regress.dll"; + + $ENV{TESTDATADIR} = "$dir/tmp_check"; + $ENV{TESTLOGDIR} = "$dir/tmp_check/log"; + + my $module = basename $dir; + # add the module build dir as the second element in the PATH + $ENV{PATH} =~ s!;!;$topdir/$Config/$module;!; + + rmtree('tmp_check'); + system(@args); + my $status = $? >> 8; + return $status; +} + +sub bincheck +{ + InstallTemp(); + + set_command_env(); + + my $mstat = 0; + + # Find out all the existing TAP tests by looking for t/ directories + # in the tree. + my @bin_dirs = glob("$topdir/src/bin/*"); + + # Process each test + foreach my $dir (@bin_dirs) + { + next unless -d "$dir/t"; + + my $status = tap_check($dir); + $mstat ||= $status; + } + exit $mstat if $mstat; + return; +} + +sub taptest +{ + my $dir = shift; + my @args; + + if ($dir =~ /^PROVE_FLAGS=/) + { + push(@args, $dir); + $dir = shift; + } + + die "no tests found!" unless -d "$topdir/$dir/t"; + + push(@args, "$topdir/$dir"); + + InstallTemp(); + + set_command_env(); + + my $status = tap_check(@args); + exit $status if $status; + return; +} + +sub plcheck +{ + chdir "$topdir/src/pl"; + + foreach my $dir (glob("*/src *")) + { + next unless -d "$dir/sql" && -d "$dir/expected"; + my $lang; + if ($dir eq 'plpgsql/src') + { + $lang = 'plpgsql'; + } + elsif ($dir eq 'tcl') + { + $lang = 'pltcl'; + } + else + { + $lang = $dir; + } + if ($lang eq 'plpython') + { + next + unless -d "$topdir/$Config/plpython3"; + $lang = 'plpythonu'; + } + else + { + next unless -d "$topdir/$Config/$lang"; + } + my @lang_args = ("--load-extension=$lang"); + chdir $dir; + my @tests = fetchTests(); + if ($lang eq 'plperl') + { + + # plperl tests will install the extensions themselves + @lang_args = (); + + # assume we're using this perl to built postgres + # test if we can run two interpreters in one backend, and if so + # run the trusted/untrusted interaction tests + use Config; + if ($Config{usemultiplicity} eq 'define') + { + push(@tests, 'plperl_plperlu'); + } + } + elsif ($lang eq 'plpythonu' && -d "$topdir/$Config/plpython3") + { + @lang_args = (); + } + + # Move on if no tests are listed. + next if (scalar @tests == 0); + + print + "============================================================\n"; + print "Checking $lang\n"; + my @args = ( + "$topdir/$Config/pg_regress/pg_regress", + "--bindir=$topdir/$Config/psql", + "--dbname=pl_regression", @lang_args, @tests); + system(@args); + my $status = $? >> 8; + exit $status if $status; + chdir "$topdir/src/pl"; + } + + chdir "$topdir"; + return; +} + +sub subdircheck +{ + my $module = shift; + + if ( !-d "$module/sql" + || !-d "$module/expected" + || (!-f "$module/GNUmakefile" && !-f "$module/Makefile")) + { + return; + } + + chdir $module; + my @tests = fetchTests(); + + # Leave if no tests are listed in the module. + if (scalar @tests == 0) + { + chdir ".."; + return; + } + + my @opts = fetchRegressOpts(); + + print "============================================================\n"; + print "Checking $module\n"; + my @args = ( + "$topdir/$Config/pg_regress/pg_regress", + "--bindir=${topdir}/${Config}/psql", + "--dbname=contrib_regression", @opts, @tests); + print join(' ', @args), "\n"; + system(@args); + chdir ".."; + return; +} + +sub contribcheck +{ + chdir "../../../contrib"; + my $mstat = 0; + foreach my $module (glob("*")) + { + # these configuration-based exclusions must match Install.pm + next if ($module eq "uuid-ossp" && !defined($config->{uuid})); + next if ($module eq "sslinfo" && !defined($config->{openssl})); + next if ($module eq "pgcrypto" && !defined($config->{openssl})); + next if ($module eq "xml2" && !defined($config->{xml})); + next if ($module =~ /_plperl$/ && !defined($config->{perl})); + next if ($module =~ /_plpython$/ && !defined($config->{python})); + next if ($module eq "sepgsql"); + + subdircheck($module); + my $status = $? >> 8; + $mstat ||= $status; + } + exit $mstat if $mstat; + return; +} + +sub modulescheck +{ + chdir "../../../src/test/modules"; + my $mstat = 0; + foreach my $module (glob("*")) + { + subdircheck($module); + my $status = $? >> 8; + $mstat ||= $status; + } + exit $mstat if $mstat; + return; +} + +sub recoverycheck +{ + InstallTemp(); + + my $dir = "$topdir/src/test/recovery"; + my $status = tap_check($dir); + exit $status if $status; + return; +} + +# Run "initdb", then reconfigure authentication. +sub standard_initdb +{ + return ( + system('initdb', '-N') == 0 and system( + "$topdir/$Config/pg_regress/pg_regress", '--config-auth', + $ENV{PGDATA}) == 0); +} + +# This is similar to appendShellString(). Perl system(@args) bypasses +# cmd.exe, so omit the caret escape layer. +sub quote_system_arg +{ + my $arg = shift; + + # Change N >= 0 backslashes before a double quote to 2N+1 backslashes. + $arg =~ s/(\\*)"/${\($1 . $1)}\\"/gs; + + # Change N >= 1 backslashes at end of argument to 2N backslashes. + $arg =~ s/(\\+)$/${\($1 . $1)}/gs; + + # Wrap the whole thing in unescaped double quotes. + return "\"$arg\""; +} + +sub upgradecheck +{ + # pg_upgrade is now handled by bincheck, but keep this target for + # backward compatibility. + print "upgradecheck is a no-op, use bincheck instead.\n"; + return; +} + +sub fetchRegressOpts +{ + my $handle; + open($handle, '<', "GNUmakefile") + || open($handle, '<', "Makefile") + || die "Could not open Makefile"; + local ($/) = undef; + my $m = <$handle>; + close($handle); + my @opts; + + $m =~ s{\\\r?\n}{}g; + if ($m =~ /^\s*REGRESS_OPTS\s*\+?=(.*)/m) + { + + # Substitute known Makefile variables, then ignore options that retain + # an unhandled variable reference. Ignore anything that isn't an + # option starting with "--". + @opts = grep { !/\$\(/ && /^--/ } + map { (my $x = $_) =~ s/\Q$(top_builddir)\E/\"$topdir\"/; $x; } + split(/\s+/, $1); + } + if ($m =~ /^\s*ENCODING\s*=\s*(\S+)/m) + { + push @opts, "--encoding=$1"; + } + if ($m =~ /^\s*NO_LOCALE\s*=\s*\S+/m) + { + push @opts, "--no-locale"; + } + return @opts; +} + +# Fetch the list of tests by parsing a module's Makefile. An empty +# list is returned if the module does not need to run anything. +sub fetchTests +{ + my $handle; + open($handle, '<', "GNUmakefile") + || open($handle, '<', "Makefile") + || die "Could not open Makefile"; + local ($/) = undef; + my $m = <$handle>; + close($handle); + my $t = ""; + + $m =~ s{\\\r?\n}{}g; + + # A module specifying NO_INSTALLCHECK does not support installcheck, + # so bypass its run by returning an empty set of tests. + if ($m =~ /^\s*NO_INSTALLCHECK\s*=\s*\S+/m) + { + return (); + } + + if ($m =~ /^REGRESS\s*=\s*(.*)$/gm) + { + $t = $1; + $t =~ s/\s+/ /g; + + if ($m =~ /contrib\/pgcrypto/) + { + + # pgcrypto is special since some tests depend on the + # configuration of the build + + my $pgptests = + $config->{zlib} + ? GetTests("ZLIB_TST", $m) + : GetTests("ZLIB_OFF_TST", $m); + $t =~ s/\$\(CF_PGP_TESTS\)/$pgptests/; + } + } + + return split(/\s+/, $t); +} + +sub GetTests +{ + my $testname = shift; + my $m = shift; + if ($m =~ /^$testname\s*=\s*(.*)$/gm) + { + return $1; + } + return ""; +} + +sub InstallTemp +{ + unless ($ENV{NO_TEMP_INSTALL}) + { + print "Setting up temp install\n\n"; + Install("$tmp_installdir", "all", $config); + } + $ENV{PATH} = "$tmp_installdir/bin;$ENV{PATH}"; + return; +} + +sub usage +{ + print STDERR + "Usage: vcregress.pl <mode> [<arg>]\n\n", + "Options for <mode>:\n", + " bincheck run tests of utilities in src/bin/\n", + " check deploy instance and run regression tests on it\n", + " contribcheck run tests of modules in contrib/\n", + " ecpgcheck run regression tests of ECPG\n", + " installcheck run regression tests on existing instance\n", + " isolationcheck run isolation tests\n", + " modulescheck run tests of modules in src/test/modules/\n", + " plcheck run tests of PL languages\n", + " recoverycheck run recovery test suite\n", + " taptest run an arbitrary TAP test set\n", + " upgradecheck run tests of pg_upgrade (no-op)\n", + "\nOptions for <arg>: (used by check and installcheck)\n", + " serial serial mode\n", + " parallel parallel mode\n", + "\nOption for <arg>: for taptest\n", + " TEST_DIR (required) directory where tests reside\n"; + exit(1); +} diff --git a/src/tools/perlcheck/find_perl_files b/src/tools/perlcheck/find_perl_files new file mode 100644 index 0000000..20dceb8 --- /dev/null +++ b/src/tools/perlcheck/find_perl_files @@ -0,0 +1,18 @@ +# src/tools/perlcheck/find_perl_files + +# shell function to find all perl files in the source tree + +find_perl_files () { + if [ $# -eq 0 ]; then + echo 'No files to process' 1>&2 + return + fi + { + # take all .pl and .pm files + find "$@" -type f -name '*.p[lm]' -print + # take executable files that file(1) thinks are perl files + find "$@" -type f -perm -100 -exec file {} \; -print | + egrep -i ':.*perl[0-9]*\>' | + cut -d: -f1 + } | sort -u | grep -v '^\./\.git/' +} diff --git a/src/tools/perlcheck/perlcriticrc b/src/tools/perlcheck/perlcriticrc new file mode 100644 index 0000000..49ac9ee --- /dev/null +++ b/src/tools/perlcheck/perlcriticrc @@ -0,0 +1,31 @@ +###################################################################### +# +# src/tools/perlcheck/perlcriticrc +# +# config file for perlcritic for Postgres project +# +##################################################################### + +severity = 5 + +theme = core + +# print the policy name as well as the normal output +verbose = %f: %m at line %l, column %c. %e. ([%p] Severity: %s)\n + +# Note: for policy descriptions see https://metacpan.org/dist/Perl-Critic + + +# allow octal constants with leading zeros +[-ValuesAndExpressions::ProhibitLeadingZeros] + +# insist on use of the warnings pragma +[TestingAndDebugging::RequireUseWarnings] +severity = 5 + +# forbid grep and map in void context +[BuiltinFunctions::ProhibitVoidGrep] +severity = 5 + +[BuiltinFunctions::ProhibitVoidMap] +severity = 5 diff --git a/src/tools/perlcheck/pgperlcritic b/src/tools/perlcheck/pgperlcritic new file mode 100755 index 0000000..2ec6f20 --- /dev/null +++ b/src/tools/perlcheck/pgperlcritic @@ -0,0 +1,20 @@ +#!/bin/sh + +# src/tools/perlcheck/pgperlcritic + +test -f src/tools/perlcheck/perlcriticrc || { + echo could not find src/tools/perlcheck/perlcriticrc + exit 1 + } + +set -e + +# set this to override default perlcritic program: +PERLCRITIC=${PERLCRITIC:-perlcritic} + +. src/tools/perlcheck/find_perl_files + +find_perl_files "$@" | xargs $PERLCRITIC \ + --quiet \ + --program-extensions .pl \ + --profile=src/tools/perlcheck/perlcriticrc diff --git a/src/tools/perlcheck/pgperlsyncheck b/src/tools/perlcheck/pgperlsyncheck new file mode 100755 index 0000000..da59c97 --- /dev/null +++ b/src/tools/perlcheck/pgperlsyncheck @@ -0,0 +1,16 @@ +#!/bin/sh + +# script to detect compile time errors and warnings in all perl files + +INCLUDES="-I src/tools/msvc -I src/tools/msvc/dummylib -I src/backend/catalog" +INCLUDES="-I src/test/perl -I src/backend/utils/mb/Unicode $INCLUDES" +INCLUDES="-I src/bin/pg_rewind -I src/test/ssl/t $INCLUDES" + +set -e + +. src/tools/perlcheck/find_perl_files + +# for zsh +setopt shwordsplit 2>/dev/null || true + +find_perl_files "$@" | xargs -L 1 perl $INCLUDES -cw 2>&1 | grep -v OK diff --git a/src/tools/pg_bsd_indent/.gitignore b/src/tools/pg_bsd_indent/.gitignore new file mode 100644 index 0000000..b27e361 --- /dev/null +++ b/src/tools/pg_bsd_indent/.gitignore @@ -0,0 +1,4 @@ +/pg_bsd_indent +# Generated by test suite +/log/ +/tmp_check/ diff --git a/src/tools/pg_bsd_indent/Makefile b/src/tools/pg_bsd_indent/Makefile new file mode 100644 index 0000000..d176ceb --- /dev/null +++ b/src/tools/pg_bsd_indent/Makefile @@ -0,0 +1,57 @@ +#------------------------------------------------------------------------- +# +# src/tools/pg_bsd_indent/Makefile +# +# Copyright (c) 2017-2023, PostgreSQL Global Development Group +# +#------------------------------------------------------------------------- + +PGFILEDESC = "pg_bsd_indent - indent C code nicely" +PGAPPICON = win32 + +subdir = src/tools/pg_bsd_indent +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) + +OBJS = \ + $(WIN32RES) \ + args.o \ + err.o \ + indent.o \ + io.o \ + lexi.o \ + parse.o \ + pr_comment.o + +all: pg_bsd_indent + +pg_bsd_indent: $(OBJS) | submake-libpgport + $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) + +install: all installdirs + $(INSTALL_PROGRAM) pg_bsd_indent$(X) '$(DESTDIR)$(bindir)/pg_bsd_indent$(X)' + +installdirs: + $(MKDIR_P) '$(DESTDIR)$(bindir)' + +uninstall: + rm -f '$(DESTDIR)$(bindir)/pg_bsd_indent$(X)' + +clean distclean maintainer-clean: + rm -f pg_bsd_indent$(X) $(OBJS) + rm -rf log/ tmp_check/ + +check: pg_bsd_indent + $(prove_check) + +installcheck: + $(prove_installcheck) + +# Provide this alternate test name to allow testing pg_bsd_indent +# without building all of the surrounding Postgres installation. +.PHONY: test + +test: pg_bsd_indent + $(prove_installcheck) diff --git a/src/tools/pg_bsd_indent/README b/src/tools/pg_bsd_indent/README new file mode 100644 index 0000000..992d4fc --- /dev/null +++ b/src/tools/pg_bsd_indent/README @@ -0,0 +1,174 @@ +src/tools/pg_bsd_indent/README + +This is a lightly modified version of the "indent" program maintained +by the FreeBSD project. The modifications are mostly to make it portable +to non-BSD-ish platforms, though we do have one formatting switch we +couldn't convince upstream to take. + +To build it, configure the surrounding Postgres source tree, +then run "make" in this directory. +Optionally, run "make test" for some simple tests. + +You'll need to install pg_bsd_indent somewhere in your PATH before +using it. Most likely, if you're a developer, you don't want to +put it in the same place as where the surrounding Postgres build +gets installed. Therefore, do this part with something like + + make install prefix=/usr/local + +If you are using Meson to build, the standard build targets will +build pg_bsd_indent and also test it, but there is not currently +provision for installing it anywhere. Manually copy the built +executable from build/src/tools/pg_bsd_indent/pg_bsd_indent to +wherever you want to put it. + + +If you happen to be hacking upon the indent source code, the closest +approximation to the existing indentation style seems to be + + ./pg_bsd_indent -i4 -l79 -di12 -nfc1 -nlp -sac somefile.c + +although this has by no means been rigorously adhered to. +(What was that saw about the shoemaker's children?) +We're not planning to re-indent to Postgres style, because that +would make it difficult to compare to the FreeBSD sources. + +---------- + +The FreeBSD originals of the files in this directory bear the +"4-clause" version of the BSD license. We have removed the +"advertising" clauses, as per UC Berkeley's directive here: +ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change +which reads: + +July 22, 1999 + +To All Licensees, Distributors of Any Version of BSD: + +As you know, certain of the Berkeley Software Distribution ("BSD") source +code files require that further distributions of products containing all or +portions of the software, acknowledge within their advertising materials +that such products contain software developed by UC Berkeley and its +contributors. + +Specifically, the provision reads: + +" * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors." + +Effective immediately, licensees and distributors are no longer required to +include the acknowledgement within advertising materials. Accordingly, the +foregoing paragraph of those BSD Unix files containing it is hereby deleted +in its entirety. + +William Hoskins +Director, Office of Technology Licensing +University of California, Berkeley + +---------- + +What follows is the README file as maintained by FreeBSD indent. + +---------- + + $FreeBSD: head/usr.bin/indent/README 105244 2002-10-16 13:58:39Z charnier $ + +This is the C indenter, it originally came from the University of Illinois +via some distribution tape for PDP-11 Unix. It has subsequently been +hacked upon by James Gosling @ CMU. It isn't very pretty, and really needs +to be completely redone, but it is probably the nicest C pretty printer +around. + +Further additions to provide "Kernel Normal Form" were contributed +by the folks at Sun Microsystems. + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +> From mnetor!yunexus!oz@uunet.UU.NET Wed Mar 9 15:30:55 1988 +> Date: Tue, 8 Mar 88 18:36:25 EST +> From: yunexus!oz@uunet.UU.NET (Ozan Yigit) +> To: bostic@okeeffe.berkeley.edu +> Cc: ccvaxa!willcox@uunet.UU.NET, jag@sun.com, rsalz@uunet.UU.NET +> In-Reply-To: Keith Bostic's message of Tue, 16 Feb 88 16:09:06 PST +> Subject: Re: Indent... + +Thank you for your response about indent. I was wrong in my original +observation (or mis-observation :-). UCB did keep the Illinois +copyright intact. + +The issue still is whether we can distribute indent, and if we can, which +version. David Willcox (the author) states that: + +| Several people have asked me on what basis I claim that indent is in +| the public domain. I knew I would be sorry I made that posting. +| +| Some history. Way back in 1976, the project I worked on at the +| University of Illinois Center for Advanced Computation had a huge +| battle about how to format C code. After about a week of fighting, I +| got disgusted and wrote a program, which I called indent, to reformat C +| code. It had a bunch of different options that would let you format +| the output the way you liked. In particular, all of the different +| formats being championed were supported. +| +| It was my first big C program. It was ugly. It wasn't designed, it +| just sort of grew. But it pretty much worked, and it stopped most of +| the fighting. +| +| As a matter of form, I included a University of Illinois Copyright +| notice. However, my understanding was that, since the work was done +| on an ARPA contract, it was in the public domain. +| +| Time passed. Some years later, indent showed up on one of the early +| emacs distributions. +| +| Later still, someone from UC Berkeley called the UofI and asked if +| indent was in the public domain. They wanted to include it in their +| UNIX distributions, along with the emacs stuff. I was no longer at the +| UofI, but Rob Kolstad, who was, asked me about it. I told him I didn't +| care if they used it, and since then it has been on the BSD distributions. +| +| Somewhere along the way, several other unnamed people have had their +| hands in it. It was converted to understand version 7 C. (The +| original was version 6.) It was converted from its original filter +| interface to its current "blow away the user's file" interface. +| The $HOME/.indent.pro file parsing was added. Some more formatting +| options were added. +| +| The source I have right now has two copyright notices. One is the +| original from the UofI. One is from Berkeley. +| +| I am not a lawyer, and I certainly do not understand copyright law. As +| far as I am concerned, the bulk of this program, everything covered by +| the UofI copyright, is in the public domain, and worth every penny. +| Berkeley's copyright probably should only cover their changes, and I +| don't know their feelings about sending it out. + +In any case, there appears to be none at UofI to clarify/and change +that copyright, but I am confident (based on the statements of its +author) that the code, as it stands with its copyright, is +distributable, and will not cause any legal problems. + +Hence, the issue reduces to *which* one to distribute through +comp.sources.unix. I would suggest that with the permission of you +folks (given that you have parts copyrighted), we distribute the 4.3 +version of indent, which appears to be the most up-to-date version. I +happen to have just about every known version of indent, including the +very original submission from the author to a unix tape, later the +G-Emacs version, any 4.n version, sun version and the Unipress +version. I still think we should not have to "go-back-in-time" and +re-do all the work you people have done. + +I hope to hear from you as to what you think about this. You may of +course send 4.3 version to the moderator directly, or you can let me +know of your permission, and I will send the sources, or you can let +me know that 4.3 version is off-limits, in which case we would probably +have to revert to an older version. One way or another, I hope to get +a version of indent to comp.sources.unix. + +regards.. oz + +cc: ccvaxa!willcox + sun.com!jar + uunet!rsalz + diff --git a/src/tools/pg_bsd_indent/args.c b/src/tools/pg_bsd_indent/args.c new file mode 100644 index 0000000..38eaa5a --- /dev/null +++ b/src/tools/pg_bsd_indent/args.c @@ -0,0 +1,350 @@ +/*- + * Copyright (c) 1985 Sun Microsystems, Inc. + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)args.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif + +#include "c.h" + +/* + * Argument scanning and profile reading code. Default parameters are set + * here as well. + */ + +#include <ctype.h> +#include <err.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "indent_globs.h" +#include "indent.h" + +#define INDENT_VERSION "2.1.2" + +/* profile types */ +#define PRO_SPECIAL 1 /* special case */ +#define PRO_BOOL 2 /* boolean */ +#define PRO_INT 3 /* integer */ + +/* profile specials for booleans */ +#define ON 1 /* turn it on */ +#define OFF 0 /* turn it off */ + +/* profile specials for specials */ +#define IGN 1 /* ignore it */ +#define CLI 2 /* case label indent (float) */ +#define STDIN 3 /* use stdin */ +#define KEY 4 /* type (keyword) */ + +static void scan_profile(FILE *); + +#define KEY_FILE 5 /* only used for args */ +#define VERSION 6 /* only used for args */ + +const char *option_source = "?"; + +void add_typedefs_from_file(const char *str); + +/* + * N.B.: because of the way the table here is scanned, options whose names are + * substrings of other options must occur later; that is, with -lp vs -l, -lp + * must be first. Also, while (most) booleans occur more than once, the last + * default value is the one actually assigned. + */ +struct pro { + const char *p_name; /* name, e.g. -bl, -cli */ + int p_type; /* type (int, bool, special) */ + int p_default; /* the default value (if int) */ + int p_special; /* depends on type */ + int *p_obj; /* the associated variable */ +} pro[] = { + + {"T", PRO_SPECIAL, 0, KEY, 0}, + {"U", PRO_SPECIAL, 0, KEY_FILE, 0}, + {"-version", PRO_SPECIAL, 0, VERSION, 0}, + {"P", PRO_SPECIAL, 0, IGN, 0}, + {"bacc", PRO_BOOL, false, ON, &blanklines_around_conditional_compilation}, + {"badp", PRO_BOOL, false, ON, &blanklines_after_declarations_at_proctop}, + {"bad", PRO_BOOL, false, ON, &blanklines_after_declarations}, + {"bap", PRO_BOOL, false, ON, &blanklines_after_procs}, + {"bbb", PRO_BOOL, false, ON, &blanklines_before_blockcomments}, + {"bc", PRO_BOOL, true, OFF, &ps.leave_comma}, + {"bl", PRO_BOOL, true, OFF, &btype_2}, + {"br", PRO_BOOL, true, ON, &btype_2}, + {"bs", PRO_BOOL, false, ON, &Bill_Shannon}, + {"cdb", PRO_BOOL, true, ON, &comment_delimiter_on_blankline}, + {"cd", PRO_INT, 0, 0, &ps.decl_com_ind}, + {"ce", PRO_BOOL, true, ON, &cuddle_else}, + {"ci", PRO_INT, 0, 0, &continuation_indent}, + {"cli", PRO_SPECIAL, 0, CLI, 0}, + {"cp", PRO_INT, 0, 0, &else_endif_com_ind}, + {"c", PRO_INT, 33, 0, &ps.com_ind}, + {"di", PRO_INT, 16, 0, &ps.decl_indent}, + {"dj", PRO_BOOL, false, ON, &ps.ljust_decl}, + {"d", PRO_INT, 0, 0, &ps.unindent_displace}, + {"eei", PRO_BOOL, false, ON, &extra_expression_indent}, + {"ei", PRO_BOOL, true, ON, &ps.else_if}, + {"fbs", PRO_BOOL, true, ON, &function_brace_split}, + {"fc1", PRO_BOOL, true, ON, &format_col1_comments}, + {"fcb", PRO_BOOL, true, ON, &format_block_comments}, + {"ip", PRO_BOOL, true, ON, &ps.indent_parameters}, + {"i", PRO_INT, 8, 0, &ps.ind_size}, + {"lc", PRO_INT, 0, 0, &block_comment_max_col}, + {"ldi", PRO_INT, -1, 0, &ps.local_decl_indent}, + {"lpl", PRO_BOOL, false, ON, &lineup_to_parens_always}, + {"lp", PRO_BOOL, true, ON, &lineup_to_parens}, + {"l", PRO_INT, 78, 0, &max_col}, + {"nbacc", PRO_BOOL, false, OFF, &blanklines_around_conditional_compilation}, + {"nbadp", PRO_BOOL, false, OFF, &blanklines_after_declarations_at_proctop}, + {"nbad", PRO_BOOL, false, OFF, &blanklines_after_declarations}, + {"nbap", PRO_BOOL, false, OFF, &blanklines_after_procs}, + {"nbbb", PRO_BOOL, false, OFF, &blanklines_before_blockcomments}, + {"nbc", PRO_BOOL, true, ON, &ps.leave_comma}, + {"nbs", PRO_BOOL, false, OFF, &Bill_Shannon}, + {"ncdb", PRO_BOOL, true, OFF, &comment_delimiter_on_blankline}, + {"nce", PRO_BOOL, true, OFF, &cuddle_else}, + {"ndj", PRO_BOOL, false, OFF, &ps.ljust_decl}, + {"neei", PRO_BOOL, false, OFF, &extra_expression_indent}, + {"nei", PRO_BOOL, true, OFF, &ps.else_if}, + {"nfbs", PRO_BOOL, true, OFF, &function_brace_split}, + {"nfc1", PRO_BOOL, true, OFF, &format_col1_comments}, + {"nfcb", PRO_BOOL, true, OFF, &format_block_comments}, + {"nip", PRO_BOOL, true, OFF, &ps.indent_parameters}, + {"nlpl", PRO_BOOL, false, OFF, &lineup_to_parens_always}, + {"nlp", PRO_BOOL, true, OFF, &lineup_to_parens}, + {"npcs", PRO_BOOL, false, OFF, &proc_calls_space}, + {"npro", PRO_SPECIAL, 0, IGN, 0}, + {"npsl", PRO_BOOL, true, OFF, &procnames_start_line}, + {"nsac", PRO_BOOL, false, OFF, &space_after_cast}, + {"nsc", PRO_BOOL, true, OFF, &star_comment_cont}, + {"nsob", PRO_BOOL, false, OFF, &swallow_optional_blanklines}, + {"ntpg", PRO_BOOL, false, OFF, &postgres_tab_rules}, + {"nut", PRO_BOOL, true, OFF, &use_tabs}, + {"nv", PRO_BOOL, false, OFF, &verbose}, + {"pcs", PRO_BOOL, false, ON, &proc_calls_space}, + {"psl", PRO_BOOL, true, ON, &procnames_start_line}, + {"sac", PRO_BOOL, false, ON, &space_after_cast}, + {"sc", PRO_BOOL, true, ON, &star_comment_cont}, + {"sob", PRO_BOOL, false, ON, &swallow_optional_blanklines}, + {"st", PRO_SPECIAL, 0, STDIN, 0}, + {"ta", PRO_BOOL, false, ON, &auto_typedefs}, + {"tpg", PRO_BOOL, false, ON, &postgres_tab_rules}, + {"ts", PRO_INT, 8, 0, &tabsize}, + {"ut", PRO_BOOL, true, ON, &use_tabs}, + {"v", PRO_BOOL, false, ON, &verbose}, + /* whew! */ + {0, 0, 0, 0, 0} +}; + +/* + * set_profile reads $HOME/.indent.pro and ./.indent.pro and handles arguments + * given in these files. + */ +void +set_profile(const char *profile_name) +{ + FILE *f; + char fname[MAXPGPATH]; + static char prof[] = ".indent.pro"; + + if (profile_name == NULL) + snprintf(fname, sizeof(fname), "%s/%s", getenv("HOME"), prof); + else + snprintf(fname, sizeof(fname), "%s", profile_name + 2); + if ((f = fopen(option_source = fname, "r")) != NULL) { + scan_profile(f); + (void) fclose(f); + } + if ((f = fopen(option_source = prof, "r")) != NULL) { + scan_profile(f); + (void) fclose(f); + } + option_source = "Command line"; +} + +static void +scan_profile(FILE *f) +{ + int comment, i; + char *p; + char buf[BUFSIZ]; + + while (1) { + p = buf; + comment = 0; + while ((i = getc(f)) != EOF) { + if (i == '*' && !comment && p > buf && p[-1] == '/') { + comment = p - buf; + *p++ = i; + } else if (i == '/' && comment && p > buf && p[-1] == '*') { + p = buf + comment - 1; + comment = 0; + } else if (isspace((unsigned char)i)) { + if (p > buf && !comment) + break; + } else { + *p++ = i; + } + } + if (p != buf) { + *p++ = 0; + if (verbose) + printf("profile: %s\n", buf); + set_option(buf); + } + else if (i == EOF) + return; + } +} + +static const char * +eqin(const char *s1, const char *s2) +{ + while (*s1) { + if (*s1++ != *s2++) + return (NULL); + } + return (s2); +} + +/* + * Set the defaults. + */ +void +set_defaults(void) +{ + struct pro *p; + + /* + * Because ps.case_indent is a float, we can't initialize it from the + * table: + */ + ps.case_indent = 0.0; /* -cli0.0 */ + for (p = pro; p->p_name; p++) + if (p->p_type != PRO_SPECIAL) + *p->p_obj = p->p_default; +} + +void +set_option(char *arg) +{ + struct pro *p; + const char *param_start; + + arg++; /* ignore leading "-" */ + for (p = pro; p->p_name; p++) + if (*p->p_name == *arg && (param_start = eqin(p->p_name, arg)) != NULL) + goto found; + errx(1, "%s: unknown parameter \"%s\"", option_source, arg - 1); +found: + switch (p->p_type) { + + case PRO_SPECIAL: + switch (p->p_special) { + + case IGN: + break; + + case CLI: + if (*param_start == 0) + goto need_param; + ps.case_indent = atof(param_start); + break; + + case STDIN: + if (input == NULL) + input = stdin; + if (output == NULL) + output = stdout; + break; + + case KEY: + if (*param_start == 0) + goto need_param; + add_typename(param_start); + break; + + case KEY_FILE: + if (*param_start == 0) + goto need_param; + add_typedefs_from_file(param_start); + break; + + case VERSION: + printf("pg_bsd_indent %s (based on FreeBSD indent)\n", INDENT_VERSION); + exit(0); + + default: + errx(1, "set_option: internal error: p_special %d", p->p_special); + } + break; + + case PRO_BOOL: + if (p->p_special == OFF) + *p->p_obj = false; + else + *p->p_obj = true; + break; + + case PRO_INT: + if (!isdigit((unsigned char)*param_start)) { + need_param: + errx(1, "%s: ``%s'' requires a parameter", option_source, p->p_name); + } + *p->p_obj = atoi(param_start); + break; + + default: + errx(1, "set_option: internal error: p_type %d", p->p_type); + } +} + +void +add_typedefs_from_file(const char *str) +{ + FILE *file; + char line[BUFSIZ]; + + if ((file = fopen(str, "r")) == NULL) { + fprintf(stderr, "indent: cannot open file %s\n", str); + exit(1); + } + while ((fgets(line, BUFSIZ, file)) != NULL) { + /* Remove trailing whitespace */ + line[strcspn(line, " \t\n\r")] = '\0'; + add_typename(line); + } + fclose(file); +} diff --git a/src/tools/pg_bsd_indent/err.c b/src/tools/pg_bsd_indent/err.c new file mode 100644 index 0000000..8073193 --- /dev/null +++ b/src/tools/pg_bsd_indent/err.c @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This is cut down to just the minimum that we need to build indent. + */ +#include "c.h" + +#include <err.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void +err(int eval, const char *fmt, ...) +{ + int code = errno; + va_list ap; + va_start(ap, fmt); + if (fmt != NULL) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, ": "); + } + fprintf(stderr, "%s\n", strerror(code)); + va_end(ap); + exit(eval); +} + +void +errx(int eval, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (fmt != NULL) + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(eval); +} diff --git a/src/tools/pg_bsd_indent/err.h b/src/tools/pg_bsd_indent/err.h new file mode 100644 index 0000000..a3e8f97 --- /dev/null +++ b/src/tools/pg_bsd_indent/err.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)err.h 8.1 (Berkeley) 6/2/93 + * $FreeBSD: stable/11/include/err.h 203964 2010-02-16 19:39:50Z imp $ + */ + +#ifndef _ERR_H_ +#define _ERR_H_ + +/* + * This is cut down to just the minimum that we need to build indent. + */ + +void err(int, const char *, ...) + pg_attribute_noreturn() pg_attribute_printf(2, 3); +void errx(int, const char *, ...) + pg_attribute_noreturn() pg_attribute_printf(2, 3); + +#endif /* !_ERR_H_ */ diff --git a/src/tools/pg_bsd_indent/indent.1 b/src/tools/pg_bsd_indent/indent.1 new file mode 100644 index 0000000..131abfe --- /dev/null +++ b/src/tools/pg_bsd_indent/indent.1 @@ -0,0 +1,618 @@ +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" Copyright (c) 1976 Board of Trustees of the University of Illinois. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)indent.1 8.1 (Berkeley) 7/1/93 +.\" $FreeBSD: head/usr.bin/indent/indent.1 309524 2016-12-04 03:10:25Z pfg $ +.\" +.Dd December 2, 2016 +.Dt INDENT 1 +.Os +.Sh NAME +.Nm indent +.Nd indent and format C program source +.Sh SYNOPSIS +.Nm +.Op Ar input-file Op Ar output-file +.Op Fl bacc | Fl nbacc +.Op Fl bad | Fl nbad +.Op Fl badp | Fl nbadp +.Op Fl bap | Fl nbap +.Op Fl bbb | Fl nbbb +.Op Fl \&bc | Fl nbc +.Op Fl \&bl | Fl \&br +.Op Fl bs | Fl nbs +.Op Fl c Ns Ar n +.Op Fl cp Ns Ar n +.Op Fl \&cd Ns Ar n +.Bk -words +.Op Fl cdb | Fl ncdb +.Ek +.Op Fl \&ce | Fl nce +.Op Fl \&ci Ns Ar n +.Op Fl cli Ns Ar n +.Op Fl d Ns Ar n +.Op Fl \&di Ns Ar n +.Op Fl dj | Fl ndj +.Bk -words +.Op Fl ei | Fl nei +.Op Fl eei | Fl neei +.Ek +.Bk -words +.Op Fl fbs | Fl nfbs +.Op Fl fc1 | Fl nfc1 +.Op Fl fcb | Fl nfcb +.Ek +.Op Fl i Ns Ar n +.Op Fl \&ip | Fl nip +.Op Fl l Ns Ar n +.Op Fl \&lc Ns Ar n +.Op Fl \&ldi Ns Ar n +.Op Fl \&lp | Fl nlp +.Op Fl \&lpl | Fl nlpl +.Op Fl npro +.Op Fl P Ns Ar file +.Op Fl pcs | Fl npcs +.Op Fl psl | Fl npsl +.Op Fl sac | Fl nsac +.Op Fl \&sc | Fl nsc +.Bk -words +.Op Fl sob | Fl nsob +.Ek +.Op Fl \&st +.Op Fl \&ta +.Op Fl T Ns Ar typename +.Op Fl tpg | Fl ntpg +.Op Fl ts Ns Ar n +.Op Fl U Ns Ar file +.Op Fl ut | Fl nut +.Op Fl v | Fl \&nv +.Op Fl -version +.Sh DESCRIPTION +The +.Nm +utility is a +.Em C +program formatter. +It reformats the +.Em C +program in the +.Ar input-file +according to the switches. +The switches which can be +specified are described below. +They may appear before or after the file +names. +.Pp +.Sy NOTE : +If you only specify an +.Ar input-file , +the formatting is +done `in-place', that is, the formatted file is written back into +.Ar input-file +and a backup copy of +.Ar input-file +is written in the current directory. +If +.Ar input-file +is named +.Sq Pa /blah/blah/file , +the backup file is named +.Sq Pa file.BAK . +.Pp +If +.Ar output-file +is specified, +.Nm +checks to make sure that it is different from +.Ar input-file . +.Pp +The options listed below control the formatting style imposed by +.Nm . +.Bl -tag -width Op +.It Fl bacc , nbacc +If +.Fl bacc +is specified, a blank line is forced around every conditional +compilation block. +For example, in front of every #ifdef and after every #endif. +Other blank lines surrounding such blocks will be swallowed. +Default: +.Fl nbacc . +.It Fl bad , nbad +If +.Fl bad +is specified, a blank line is forced after every block of +declarations. +Default: +.Fl nbad . +.It Fl badp , nbadp +This is vaguely similar to +.Fl bad +except that it only applies to the first set of declarations +in a procedure (just after the first `{') and it causes a blank +line to be generated even if there are no declarations. +The default is +.Fl nbadp. +.It Fl bap , nbap +If +.Fl bap +is specified, a blank line is forced after every procedure body. +Default: +.Fl nbap . +.It Fl bbb , nbbb +If +.Fl bbb +is specified, a blank line is forced before every block comment. +Default: +.Fl nbbb . +.It Fl \&bc , nbc +If +.Fl \&bc +is specified, then a newline is forced after each comma in a declaration. +.Fl nbc +turns off this option. +Default: +.Fl \&nbc . +.It Fl \&bl , \&br +Specifying +.Fl \&bl +lines up compound statements like this: +.Bd -literal -offset indent +if (...) +{ + code +} +.Ed +.Pp +Specifying +.Fl \&br +(the default) makes them look like this: +.Bd -literal -offset indent +if (...) { + code +} +.Ed +.It Fl bs , nbs +Whether a blank should always be inserted after sizeof. +The default is +.Fl nbs. +.It Fl c Ns Ar n +The column in which comments on code start. +The default is 33. +.It Fl cp Ns Ar n +The column in which comments on #else and #endif start. +The default is 0, which effectively separates the directives from comments by +a single space. +.It Fl cd Ns Ar n +The column in which comments on declarations start. +The default +is for these comments to start in the same column as those on code. +.It Fl cdb , ncdb +Enables (disables) the placement of comment delimiters on blank lines. +With +this option enabled, comments look like this: +.Bd -literal -offset indent + /* + * this is a comment + */ +.Ed +.Pp +Rather than like this: +.Bd -literal -offset indent + /* this is a comment */ +.Ed +.Pp +This only affects block comments, not comments to the right of +code. +The default is +.Fl cdb . +.It Fl ce , nce +Enables (disables) forcing of `else's to cuddle up to the immediately preceding +`}'. +The default is +.Fl \&ce . +.It Fl \&ci Ns Ar n +Sets the continuation indent to be +.Ar n . +Continuation +lines will be indented that far from the beginning of the first line of the +statement. +Parenthesized expressions have extra indentation added to +indicate the nesting, unless +.Fl \&lp +is in effect +or the continuation indent is exactly half of the main indent. +.Fl \&ci +defaults to the same value as +.Fl i . +.It Fl cli Ns Ar n +Causes case labels to be indented +.Ar n +tab stops to the right of the containing +.Ic switch +statement. +.Fl cli0.5 +causes case labels to be indented half a tab stop. +The +default is +.Fl cli0 . +.It Fl d Ns Ar n +Controls the placement of comments which are not to the +right of code. +For example, +.Fl \&d\&1 +means that such comments are placed one indentation level to the +left of code. +Specifying the default +.Fl \&d\&0 +lines up these comments with the code. +See the section on comment +indentation below. +.It Fl \&di Ns Ar n +Specifies the indentation, in character positions, +of global variable names and all struct/union member names +relative to the beginning of their type declaration. +The default is +.Fl di16 . +.It Fl dj , ndj +.Fl \&dj +left justifies declarations. +.Fl ndj +indents declarations the same as code. +The default is +.Fl ndj . +.It Fl \&ei , nei +Enables (disables) special +.Ic else-if +processing. +If it is enabled, an +.Ic if +following an +.Ic else +will have the same indentation as the preceding +.Ic \&if +statement. +The default is +.Fl ei . +.It Fl eei , neei +Enables (disables) extra indentation on continuation lines of +the expression part of +.Ic if +and +.Ic while +statements. +These continuation lines will be indented one extra level. +The default is +.Fl neei . +.It Fl fbs , nfbs +Enables (disables) splitting the function declaration and opening brace +across two lines. +The default is +.Fl fbs . +.It Fl fc1 , nfc1 +Enables (disables) the formatting of comments that start in column 1. +Often, comments whose leading `/' is in column 1 have been carefully +hand formatted by the programmer. +In such cases, +.Fl nfc1 +should be +used. +The default is +.Fl fc1 . +.It Fl fcb , nfcb +Enables (disables) the formatting of block comments (ones that begin +with `/*\\n'). +Often, block comments have been not so carefully hand formatted by the +programmer, but reformatting that would just change the line breaks is not +wanted. +In such cases, +.Fl nfcb +should be used. +Block comments are then handled like box comments. +The default is +.Fl fcb . +.It Fl i Ns Ar n +The number of columns for one indentation level. +The default is 8. +.It Fl \&ip , nip +Enables (disables) the indentation of parameter declarations from the left +margin. +The default is +.Fl \&ip . +.It Fl l Ns Ar n +Maximum length of an output line. +The default is 78. +.It Fl lc Ns Ar n +Maximum length of an output line in a block comment. +The default is 0, which means to limit block comment lines in accordance with +.Fl l. +.It Fl \&ldi Ns Ar n +Specifies the indentation, in character positions, +of local variable names +relative to the beginning of their type declaration. +The default is for local variable names to be indented +by the same amount as global ones. +.It Fl \&lp , nlp +Lines up code surrounded by parentheses in continuation lines. +With +.Fl \&lp , +if a line +has a left paren which is not closed on that line, then continuation lines +will be lined up to start at the character position just after the left +paren. +For example, here is how a piece of continued code looks with +.Fl nlp +in effect: +.Bd -literal -offset indent +p1 = first_procedure(second_procedure(p2, p3), +\ \ third_procedure(p4, p5)); +.Ed +.Pp +With +.Fl lp +in effect (the default) the code looks somewhat clearer: +.Bd -literal -offset indent +p1\ =\ first_procedure(second_procedure(p2,\ p3), +\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ third_procedure(p4,\ p5)); +.Ed +.Pp +Inserting two more newlines we get: +.Bd -literal -offset indent +p1\ =\ first_procedure(second_procedure(p2, +\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ p3), +\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ third_procedure(p4, +\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ p5)); +.Ed +.It Fl \&lpl , nlpl +With +.Fl \&lpl , +code surrounded by parentheses in continuation lines is lined up even if it +would extend past the right margin. +With +.Fl \&nlpl +(the default), such a line that would extend past the right margin is moved +left to keep it within the margin, if that does not require placing it to +the left of the prevailing indentation level. +These switches have no effect if +.Fl nlp +is selected. +.It Fl npro +Causes the profile files, +.Sq Pa ./.indent.pro +and +.Sq Pa ~/.indent.pro , +to be ignored. +.It Fl P Ns Ar file +Read profile from +.Ar file . +.It Fl pcs , npcs +If true +.Pq Fl pcs +all procedure calls will have a space inserted between +the name and the `('. +The default is +.Fl npcs . +.It Fl psl , npsl +If true +.Pq Fl psl +the names of procedures being defined are placed in +column 1 \- their types, if any, will be left on the previous lines. +The +default is +.Fl psl . +.It Fl sac , nsac +Control whether parenthesized type names in casts are followed by a space or +not. +The default is +.Fl nsac . +.It Fl \&sc , nsc +Enables (disables) the placement of asterisks (`*'s) at the left edge of all +comments. +The default is +.Fl sc . +.It Fl sob , nsob +If +.Fl sob +is specified, indent will swallow optional blank lines. +You can use this to +get rid of blank lines after declarations. +Default: +.Fl nsob . +.It Fl \&st +Causes +.Nm +to take its input from stdin and put its output to stdout. +.It Fl ta +Automatically add all identifiers ending in "_t" to the list +of type keywords. +.It Fl T Ns Ar typename +Adds +.Ar typename +to the list of type keywords. +Names accumulate: +.Fl T +can be specified more than once. +You need to specify all the typenames that +appear in your program that are defined by +.Ic typedef +\- nothing will be +harmed if you miss a few, but the program will not be formatted as nicely as +it should. +This sounds like a painful thing to have to do, but it is really +a symptom of a problem in C: +.Ic typedef +causes a syntactic change in the +language and +.Nm +cannot find all +instances of +.Ic typedef . +.It Fl tpg , ntpg +If +.Fl tpg +is specified, follow Postgres rules about when to use spaces versus +tabs for indentation, that is, use a space instead of a tab if the +tab would move only one column and no tab will follow it. +Default: +.Fl ntpg . +.It Fl ts Ns Ar n +Assumed distance between tab stops. +The default is 8. +.It Fl U Ns Ar file +Adds type names from +.Ar file +to the list of type keywords. +.It Fl ut , nut +Enables (disables) the use of tab characters in the output. +The default is +.Fl ut . +.It Fl v , \&nv +.Fl v +turns on `verbose' mode; +.Fl \&nv +turns it off. +When in verbose mode, +.Nm +reports when it splits one line of input into two or more lines of output, +and gives some size statistics at completion. +The default is +.Fl \&nv . +.It Fl -version +Causes +.Nm +to print its version number and exit. +.El +.Pp +You may set up your own `profile' of defaults to +.Nm +by creating a file called +.Pa .indent.pro +in your login directory and/or the current directory and including +whatever switches you like. +A `.indent.pro' in the current directory takes +precedence over the one in your login directory. +If +.Nm +is run and a profile file exists, then it is read to set up the program's +defaults. +Switches on the command line, though, always override profile +switches. +The switches should be separated by spaces, tabs or newlines. +.Pp +.Ss Comments +.Sq Em Box +.Em comments . +The +.Nm +utility +assumes that any comment with a dash or star immediately after the start of +comment (that is, `/*\-' or `/**') is a comment surrounded by a box of stars. +Each line of such a comment is left unchanged, except that its indentation +may be adjusted to account for the change in indentation of the first line +of the comment. +.Pp +.Em Straight text . +All other comments are treated as straight text. +The +.Nm +utility fits as many words (separated by blanks, tabs, or newlines) on a +line as possible. +Blank lines break paragraphs. +.Ss Comment indentation +If a comment is on a line with code it is started in the `comment column', +which is set by the +.Fl c Ns Ns Ar n +command line parameter. +Otherwise, the comment is started at +.Ar n +indentation levels less than where code is currently being placed, where +.Ar n +is specified by the +.Fl d Ns Ns Ar n +command line parameter. +If the code on a line extends past the comment +column, the comment starts further to the right, and the right margin may be +automatically extended in extreme cases. +.Ss Preprocessor lines +In general, +.Nm +leaves preprocessor lines alone. +The only +reformatting that it will do is to straighten up trailing comments. +It +leaves embedded comments alone. +Conditional compilation +.Pq Ic #ifdef...#endif +is recognized and +.Nm +attempts to correctly +compensate for the syntactic peculiarities introduced. +.Ss C syntax +The +.Nm +utility understands a substantial amount about the syntax of C, but it +has a `forgiving' parser. +It attempts to cope with the usual sorts of +incomplete and malformed syntax. +In particular, the use of macros like: +.Pp +.Dl #define forever for(;;) +.Pp +is handled properly. +.Sh ENVIRONMENT +The +.Nm +utility uses the +.Ev HOME +environment variable. +.Sh FILES +.Bl -tag -width "./.indent.pro" -compact +.It Pa ./.indent.pro +profile file +.It Pa ~/.indent.pro +profile file +.El +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +.Sh BUGS +The +.Nm +utility has even more switches than +.Xr ls 1 . +.Pp +A common mistake is to try to indent all the +.Em C +programs in a directory by typing: +.Pp +.Dl indent *.c +.Pp +This is probably a bug, not a feature. diff --git a/src/tools/pg_bsd_indent/indent.c b/src/tools/pg_bsd_indent/indent.c new file mode 100644 index 0000000..923fc34 --- /dev/null +++ b/src/tools/pg_bsd_indent/indent.c @@ -0,0 +1,1275 @@ +/*- + * Copyright (c) 1985 Sun Microsystems, Inc. + * Copyright (c) 1976 Board of Trustees of the University of Illinois. + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)indent.c 5.17 (Berkeley) 6/7/93"; +#endif /* not lint */ +#endif + +#include "c.h" + +#include <sys/param.h> +#include <err.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +/* Tell indent_globs.h to define our global variables here */ +#define DECLARE_INDENT_GLOBALS 1 + +#include "indent_globs.h" +#include "indent_codes.h" +#include "indent.h" + +static void bakcopy(void); +static void indent_declaration(int, int); + +const char *in_name = "Standard Input"; /* will always point to name of input + * file */ +const char *out_name = "Standard Output"; /* will always point to name + * of output file */ +char bakfile[MAXPGPATH] = ""; + +int +main(int argc, char **argv) +{ + int dec_ind; /* current indentation for declarations */ + int di_stack[20]; /* a stack of structure indentation levels */ + int force_nl; /* when true, code must be broken */ + int hd_type = 0; /* used to store type of stmt for if (...), + * for (...), etc */ + int i; /* local loop counter */ + int scase; /* set to true when we see a case, so we will + * know what to do with the following colon */ + int sp_sw; /* when true, we are in the expression of + * if(...), while(...), etc. */ + int squest; /* when this is positive, we have seen a ? + * without the matching : in a <c>?<s>:<s> + * construct */ + const char *t_ptr; /* used for copying tokens */ + int tabs_to_var; /* true if using tabs to indent to var name */ + int type_code; /* the type of token, returned by lexi */ + + int last_else = 0; /* true iff last keyword was an else */ + const char *profile_name = NULL; + struct parser_state transient_state; /* a copy for lookup */ + + + /*-----------------------------------------------*\ + | INITIALIZATION | + \*-----------------------------------------------*/ + + found_err = 0; + + ps.p_stack[0] = stmt; /* this is the parser's stack */ + ps.last_nl = true; /* this is true if the last thing scanned was + * a newline */ + ps.last_token = semicolon; + combuf = (char *) malloc(bufsize); + if (combuf == NULL) + err(1, NULL); + labbuf = (char *) malloc(bufsize); + if (labbuf == NULL) + err(1, NULL); + codebuf = (char *) malloc(bufsize); + if (codebuf == NULL) + err(1, NULL); + tokenbuf = (char *) malloc(bufsize); + if (tokenbuf == NULL) + err(1, NULL); + alloc_typenames(); + l_com = combuf + bufsize - 5; + l_lab = labbuf + bufsize - 5; + l_code = codebuf + bufsize - 5; + l_token = tokenbuf + bufsize - 5; + combuf[0] = codebuf[0] = labbuf[0] = ' '; /* set up code, label, and + * comment buffers */ + combuf[1] = codebuf[1] = labbuf[1] = '\0'; + ps.else_if = 1; /* Default else-if special processing to on */ + s_lab = e_lab = labbuf + 1; + s_code = e_code = codebuf + 1; + s_com = e_com = combuf + 1; + s_token = e_token = tokenbuf + 1; + + in_buffer = (char *) malloc(10); + if (in_buffer == NULL) + err(1, NULL); + in_buffer_limit = in_buffer + 8; + buf_ptr = buf_end = in_buffer; + line_no = 1; + had_eof = ps.in_decl = ps.decl_on_line = break_comma = false; + sp_sw = force_nl = false; + ps.in_or_st = false; + ps.bl_line = true; + dec_ind = 0; + di_stack[ps.dec_nest = 0] = 0; + ps.want_blank = ps.in_stmt = ps.ind_stmt = false; + + scase = ps.pcase = false; + squest = 0; + sc_end = NULL; + bp_save = NULL; + be_save = NULL; + + output = NULL; + tabs_to_var = 0; + + /*--------------------------------------------------*\ + | COMMAND LINE SCAN | + \*--------------------------------------------------*/ + +#ifdef undef + max_col = 78; /* -l78 */ + lineup_to_parens = 1; /* -lp */ + lineup_to_parens_always = 0; /* -nlpl */ + ps.ljust_decl = 0; /* -ndj */ + ps.com_ind = 33; /* -c33 */ + star_comment_cont = 1; /* -sc */ + ps.ind_size = 8; /* -i8 */ + verbose = 0; + ps.decl_indent = 16; /* -di16 */ + ps.local_decl_indent = -1; /* if this is not set to some nonnegative value + * by an arg, we will set this equal to + * ps.decl_ind */ + ps.indent_parameters = 1; /* -ip */ + ps.decl_com_ind = 0; /* if this is not set to some positive value + * by an arg, we will set this equal to + * ps.com_ind */ + btype_2 = 1; /* -br */ + cuddle_else = 1; /* -ce */ + ps.unindent_displace = 0; /* -d0 */ + ps.case_indent = 0; /* -cli0 */ + format_block_comments = 1; /* -fcb */ + format_col1_comments = 1; /* -fc1 */ + procnames_start_line = 1; /* -psl */ + proc_calls_space = 0; /* -npcs */ + comment_delimiter_on_blankline = 1; /* -cdb */ + ps.leave_comma = 1; /* -nbc */ +#endif + + for (i = 1; i < argc; ++i) + if (strcmp(argv[i], "-npro") == 0) + break; + else if (argv[i][0] == '-' && argv[i][1] == 'P' && argv[i][2] != '\0') + profile_name = argv[i]; /* non-empty -P (set profile) */ + set_defaults(); + if (i >= argc) + set_profile(profile_name); + + for (i = 1; i < argc; ++i) { + + /* + * look thru args (if any) for changes to defaults + */ + if (argv[i][0] != '-') {/* no flag on parameter */ + if (input == NULL) { /* we must have the input file */ + in_name = argv[i]; /* remember name of input file */ + input = fopen(in_name, "r"); + if (input == NULL) /* check for open error */ + err(1, "%s", in_name); + continue; + } + else if (output == NULL) { /* we have the output file */ + out_name = argv[i]; /* remember name of output file */ + if (strcmp(in_name, out_name) == 0) { /* attempt to overwrite + * the file */ + errx(1, "input and output files must be different"); + } + output = fopen(out_name, "wb"); + if (output == NULL) /* check for create error */ + err(1, "%s", out_name); + continue; + } + errx(1, "unknown parameter: %s", argv[i]); + } + else + set_option(argv[i]); + } /* end of for */ + if (input == NULL) + input = stdin; + if (output == NULL) { + if (input == stdin) + output = stdout; + else { + out_name = in_name; + bakcopy(); + } + } + + if (ps.com_ind <= 1) + ps.com_ind = 2; /* dont put normal comments before column 2 */ + if (block_comment_max_col <= 0) + block_comment_max_col = max_col; + if (ps.local_decl_indent < 0) /* if not specified by user, set this */ + ps.local_decl_indent = ps.decl_indent; + if (ps.decl_com_ind <= 0) /* if not specified by user, set this */ + ps.decl_com_ind = ps.ljust_decl ? (ps.com_ind <= 10 ? 2 : ps.com_ind - 8) : ps.com_ind; + if (continuation_indent == 0) + continuation_indent = ps.ind_size; + fill_buffer(); /* get first batch of stuff into input buffer */ + + parse(semicolon); + { + char *p = buf_ptr; + int col = 1; + + while (1) { + if (*p == ' ') + col++; + else if (*p == '\t') + col = tabsize * (1 + (col - 1) / tabsize) + 1; + else + break; + p++; + } + if (col > ps.ind_size) + ps.ind_level = ps.i_l_follow = col / ps.ind_size; + } + + /* + * START OF MAIN LOOP + */ + + while (1) { /* this is the main loop. it will go until we + * reach eof */ + int comment_buffered = false; + + type_code = lexi(&ps); /* lexi reads one token. The actual + * characters read are stored in "token". lexi + * returns a code indicating the type of token */ + + /* + * The following code moves newlines and comments following an if (), + * while (), else, etc. up to the start of the following stmt to + * a buffer. This allows proper handling of both kinds of brace + * placement (-br, -bl) and cuddling "else" (-ce). + */ + + while (ps.search_brace) { + switch (type_code) { + case newline: + if (sc_end == NULL) { + save_com = sc_buf; + save_com[0] = save_com[1] = ' '; + sc_end = &save_com[2]; + } + *sc_end++ = '\n'; + /* + * We may have inherited a force_nl == true from the previous + * token (like a semicolon). But once we know that a newline + * has been scanned in this loop, force_nl should be false. + * + * However, the force_nl == true must be preserved if newline + * is never scanned in this loop, so this assignment cannot be + * done earlier. + */ + force_nl = false; + case form_feed: + break; + case comment: + if (sc_end == NULL) { + /* + * Copy everything from the start of the line, because + * pr_comment() will use that to calculate original + * indentation of a boxed comment. + */ + memcpy(sc_buf, in_buffer, buf_ptr - in_buffer - 4); + save_com = sc_buf + (buf_ptr - in_buffer - 4); + save_com[0] = save_com[1] = ' '; + sc_end = &save_com[2]; + } + comment_buffered = true; + *sc_end++ = '/'; /* copy in start of comment */ + *sc_end++ = '*'; + for (;;) { /* loop until we get to the end of the comment */ + *sc_end = *buf_ptr++; + if (buf_ptr >= buf_end) + fill_buffer(); + if (*sc_end++ == '*' && *buf_ptr == '/') + break; /* we are at end of comment */ + if (sc_end >= &save_com[sc_size]) { /* check for temp buffer + * overflow */ + diag2(1, "Internal buffer overflow - Move big comment from right after if, while, or whatever"); + fflush(output); + exit(1); + } + } + *sc_end++ = '/'; /* add ending slash */ + if (++buf_ptr >= buf_end) /* get past / in buffer */ + fill_buffer(); + break; + case lbrace: + /* + * Put KNF-style lbraces before the buffered up tokens and + * jump out of this loop in order to avoid copying the token + * again under the default case of the switch below. + */ + if (sc_end != NULL && btype_2) { + save_com[0] = '{'; + /* + * Originally the lbrace may have been alone on its own + * line, but it will be moved into "the else's line", so + * if there was a newline resulting from the "{" before, + * it must be scanned now and ignored. + */ + while (isspace((unsigned char)*buf_ptr)) { + if (++buf_ptr >= buf_end) + fill_buffer(); + if (*buf_ptr == '\n') + break; + } + goto sw_buffer; + } + /* FALLTHROUGH */ + default: /* it is the start of a normal statement */ + { + int remove_newlines; + + remove_newlines = + /* "} else" */ + (type_code == sp_nparen && *token == 'e' && + e_code != s_code && e_code[-1] == '}') + /* "else if" */ + || (type_code == sp_paren && *token == 'i' && + last_else && ps.else_if); + if (remove_newlines) + force_nl = false; + if (sc_end == NULL) { /* ignore buffering if + * comment wasn't saved up */ + ps.search_brace = false; + goto check_type; + } + while (sc_end > save_com && isblank((unsigned char)sc_end[-1])) { + sc_end--; + } + if (swallow_optional_blanklines || + (!comment_buffered && remove_newlines)) { + force_nl = !remove_newlines; + while (sc_end > save_com && sc_end[-1] == '\n') { + sc_end--; + } + } + if (force_nl) { /* if we should insert a nl here, put + * it into the buffer */ + force_nl = false; + --line_no; /* this will be re-increased when the + * newline is read from the buffer */ + *sc_end++ = '\n'; + *sc_end++ = ' '; + if (verbose) /* print error msg if the line was + * not already broken */ + diag2(0, "Line broken"); + } + for (t_ptr = token; *t_ptr; ++t_ptr) + *sc_end++ = *t_ptr; + + sw_buffer: + ps.search_brace = false; /* stop looking for start of + * stmt */ + bp_save = buf_ptr; /* save current input buffer */ + be_save = buf_end; + buf_ptr = save_com; /* fix so that subsequent calls to + * lexi will take tokens out of + * save_com */ + *sc_end++ = ' ';/* add trailing blank, just in case */ + buf_end = sc_end; + sc_end = NULL; + break; + } + } /* end of switch */ + /* + * We must make this check, just in case there was an unexpected + * EOF. + */ + if (type_code != 0) { + /* + * The only intended purpose of calling lexi() below is to + * categorize the next token in order to decide whether to + * continue buffering forthcoming tokens. Once the buffering + * is over, lexi() will be called again elsewhere on all of + * the tokens - this time for normal processing. + * + * Calling it for this purpose is a bug, because lexi() also + * changes the parser state and discards leading whitespace, + * which is needed mostly for comment-related considerations. + * + * Work around the former problem by giving lexi() a copy of + * the current parser state and discard it if the call turned + * out to be just a look ahead. + * + * Work around the latter problem by copying all whitespace + * characters into the buffer so that the later lexi() call + * will read them. + */ + if (sc_end != NULL) { + while (*buf_ptr == ' ' || *buf_ptr == '\t') { + *sc_end++ = *buf_ptr++; + if (sc_end >= &save_com[sc_size]) { + errx(1, "input too long"); + } + } + if (buf_ptr >= buf_end) { + fill_buffer(); + } + } + transient_state = ps; + type_code = lexi(&transient_state); /* read another token */ + if (type_code != newline && type_code != form_feed && + type_code != comment && !transient_state.search_brace) { + ps = transient_state; + } + } + } /* end of while (search_brace) */ + last_else = 0; +check_type: + if (type_code == 0) { /* we got eof */ + if (s_lab != e_lab || s_code != e_code + || s_com != e_com) /* must dump end of line */ + dump_line(); + if (ps.tos > 1) /* check for balanced braces */ + diag2(1, "Stuff missing from end of file"); + + if (verbose) { + printf("There were %d output lines and %d comments\n", + ps.out_lines, ps.out_coms); + printf("(Lines with comments)/(Lines with code): %6.3f\n", + (1.0 * ps.com_lines) / code_lines); + } + fflush(output); + exit(found_err); + } + if ( + (type_code != comment) && + (type_code != newline) && + (type_code != preesc) && + (type_code != form_feed)) { + if (force_nl && + (type_code != semicolon) && + (type_code != lbrace || !btype_2)) { + /* we should force a broken line here */ + if (verbose) + diag2(0, "Line broken"); + dump_line(); + ps.want_blank = false; /* dont insert blank at line start */ + force_nl = false; + } + ps.in_stmt = true; /* turn on flag which causes an extra level of + * indentation. this is turned off by a ; or + * '}' */ + if (s_com != e_com) { /* the turkey has embedded a comment + * in a line. fix it */ + int len = e_com - s_com; + + CHECK_SIZE_CODE(len + 3); + *e_code++ = ' '; + memcpy(e_code, s_com, len); + e_code += len; + *e_code++ = ' '; + *e_code = '\0'; /* null terminate code sect */ + ps.want_blank = false; + e_com = s_com; + } + } + else if (type_code != comment) /* preserve force_nl thru a comment */ + force_nl = false; /* cancel forced newline after newline, form + * feed, etc */ + + + + /*-----------------------------------------------------*\ + | do switch on type of token scanned | + \*-----------------------------------------------------*/ + CHECK_SIZE_CODE(3); /* maximum number of increments of e_code + * before the next CHECK_SIZE_CODE or + * dump_line() is 2. After that there's the + * final increment for the null character. */ + switch (type_code) { /* now, decide what to do with the token */ + + case form_feed: /* found a form feed in line */ + ps.use_ff = true; /* a form feed is treated much like a newline */ + dump_line(); + ps.want_blank = false; + break; + + case newline: + if (ps.last_token != comma || ps.p_l_follow > 0 + || !ps.leave_comma || ps.block_init || !break_comma || s_com != e_com) { + dump_line(); + ps.want_blank = false; + } + ++line_no; /* keep track of input line number */ + break; + + case lparen: /* got a '(' or '[' */ + /* count parens to make Healy happy */ + if (++ps.p_l_follow == nitems(ps.paren_indents)) { + diag3(0, "Reached internal limit of %d unclosed parens", + nitems(ps.paren_indents)); + ps.p_l_follow--; + } + if (*token == '[') + /* not a function pointer declaration or a function call */; + else if (ps.in_decl && !ps.block_init && !ps.dumped_decl_indent && + ps.procname[0] == '\0' && ps.paren_level == 0) { + /* function pointer declarations */ + indent_declaration(dec_ind, tabs_to_var); + ps.dumped_decl_indent = true; + } + else if (ps.want_blank && + ((ps.last_token != ident && ps.last_token != funcname) || + /* offsetof (1) is never allowed a space; sizeof (2) gets + * one iff -bs; all other keywords (>2) always get a space + * before lparen */ + ps.keyword + Bill_Shannon > 2)) + *e_code++ = ' '; + ps.want_blank = false; + *e_code++ = token[0]; + ps.paren_indents[ps.p_l_follow - 1] = count_spaces_until(1, s_code, e_code) - 1; + if (sp_sw && ps.p_l_follow == 1 && extra_expression_indent + && ps.paren_indents[0] < 2 * ps.ind_size) + ps.paren_indents[0] = 2 * ps.ind_size; + if (ps.in_or_st && *token == '(' && ps.tos <= 2) { + /* + * this is a kluge to make sure that declarations will be + * aligned right if proc decl has an explicit type on it, i.e. + * "int a(x) {..." + */ + parse(semicolon); /* I said this was a kluge... */ + ps.in_or_st = false; /* turn off flag for structure decl or + * initialization */ + } + /* + * parenthesized type following sizeof or offsetof is not a cast, + * and we assume the same for any other non-keyword identifier, + * to support macros that take types + */ + if (ps.last_token == ident && + (ps.keyword == 0 || ps.keyword == 1 || ps.keyword == 2)) + ps.not_cast_mask |= 1 << ps.p_l_follow; + break; + + case rparen: /* got a ')' or ']' */ + if (ps.cast_mask & (1 << ps.p_l_follow) & ~ps.not_cast_mask) { + ps.last_u_d = true; + ps.cast_mask &= (1 << ps.p_l_follow) - 1; + ps.want_blank = space_after_cast; + } else + ps.want_blank = true; + ps.not_cast_mask &= (1 << ps.p_l_follow) - 1; + if (--ps.p_l_follow < 0) { + ps.p_l_follow = 0; + diag3(0, "Extra %c", *token); + } + if (e_code == s_code) /* if the paren starts the line */ + ps.paren_level = ps.p_l_follow; /* then indent it */ + + *e_code++ = token[0]; + + if (sp_sw && (ps.p_l_follow == 0)) { /* check for end of if + * (...), or some such */ + sp_sw = false; + force_nl = true;/* must force newline after if */ + ps.last_u_d = true; /* inform lexi that a following + * operator is unary */ + ps.in_stmt = false; /* dont use stmt continuation + * indentation */ + + parse(hd_type); /* let parser worry about if, or whatever */ + } + ps.search_brace = btype_2; /* this should ensure that constructs + * such as main(){...} and int[]{...} + * have their braces put in the right + * place */ + break; + + case unary_op: /* this could be any unary operation */ + if (!ps.dumped_decl_indent && ps.in_decl && !ps.block_init && + ps.procname[0] == '\0' && ps.paren_level == 0) { + /* pointer declarations */ + + /* + * if this is a unary op in a declaration, we should indent + * this token + */ + for (i = 0; token[i]; ++i) + /* find length of token */; + indent_declaration(dec_ind - i, tabs_to_var); + ps.dumped_decl_indent = true; + } + else if (ps.want_blank) + *e_code++ = ' '; + + { + int len = e_token - s_token; + + CHECK_SIZE_CODE(len); + memcpy(e_code, token, len); + e_code += len; + } + ps.want_blank = false; + break; + + case binary_op: /* any binary operation */ + { + int len = e_token - s_token; + + CHECK_SIZE_CODE(len + 1); + if (ps.want_blank) + *e_code++ = ' '; + memcpy(e_code, token, len); + e_code += len; + } + ps.want_blank = true; + break; + + case postop: /* got a trailing ++ or -- */ + *e_code++ = token[0]; + *e_code++ = token[1]; + ps.want_blank = true; + break; + + case question: /* got a ? */ + squest++; /* this will be used when a later colon + * appears so we can distinguish the + * <c>?<n>:<n> construct */ + if (ps.want_blank) + *e_code++ = ' '; + *e_code++ = '?'; + ps.want_blank = true; + break; + + case casestmt: /* got word 'case' or 'default' */ + scase = true; /* so we can process the later colon properly */ + goto copy_id; + + case colon: /* got a ':' */ + if (squest > 0) { /* it is part of the <c>?<n>: <n> construct */ + --squest; + if (ps.want_blank) + *e_code++ = ' '; + *e_code++ = ':'; + ps.want_blank = true; + break; + } + if (ps.in_or_st) { + *e_code++ = ':'; + ps.want_blank = false; + break; + } + ps.in_stmt = false; /* seeing a label does not imply we are in a + * stmt */ + /* + * turn everything so far into a label + */ + { + int len = e_code - s_code; + + CHECK_SIZE_LAB(len + 3); + memcpy(e_lab, s_code, len); + e_lab += len; + *e_lab++ = ':'; + *e_lab = '\0'; + e_code = s_code; + } + force_nl = ps.pcase = scase; /* ps.pcase will be used by + * dump_line to decide how to + * indent the label. force_nl + * will force a case n: to be + * on a line by itself */ + scase = false; + ps.want_blank = false; + break; + + case semicolon: /* got a ';' */ + if (ps.dec_nest == 0) + ps.in_or_st = false;/* we are not in an initialization or + * structure declaration */ + scase = false; /* these will only need resetting in an error */ + squest = 0; + if (ps.last_token == rparen) + ps.in_parameter_declaration = 0; + ps.cast_mask = 0; + ps.not_cast_mask = 0; + ps.block_init = 0; + ps.block_init_level = 0; + ps.just_saw_decl--; + + if (ps.in_decl && s_code == e_code && !ps.block_init && + !ps.dumped_decl_indent && ps.paren_level == 0) { + /* indent stray semicolons in declarations */ + indent_declaration(dec_ind - 1, tabs_to_var); + ps.dumped_decl_indent = true; + } + + ps.in_decl = (ps.dec_nest > 0); /* if we were in a first level + * structure declaration, we + * arent any more */ + + if ((!sp_sw || hd_type != forstmt) && ps.p_l_follow > 0) { + + /* + * This should be true iff there were unbalanced parens in the + * stmt. It is a bit complicated, because the semicolon might + * be in a for stmt + */ + diag2(1, "Unbalanced parens"); + ps.p_l_follow = 0; + if (sp_sw) { /* this is a check for an if, while, etc. with + * unbalanced parens */ + sp_sw = false; + parse(hd_type); /* dont lose the if, or whatever */ + } + } + *e_code++ = ';'; + ps.want_blank = true; + ps.in_stmt = (ps.p_l_follow > 0); /* we are no longer in the + * middle of a stmt */ + + if (!sp_sw) { /* if not if for (;;) */ + parse(semicolon); /* let parser know about end of stmt */ + force_nl = true;/* force newline after an end of stmt */ + } + break; + + case lbrace: /* got a '{' */ + ps.in_stmt = false; /* dont indent the {} */ + if (!ps.block_init) + force_nl = true;/* force other stuff on same line as '{' onto + * new line */ + else if (ps.block_init_level <= 0) + ps.block_init_level = 1; + else + ps.block_init_level++; + + if (s_code != e_code && !ps.block_init) { + if (!btype_2) { + dump_line(); + ps.want_blank = false; + } + else if (ps.in_parameter_declaration && !ps.in_or_st) { + ps.i_l_follow = 0; + if (function_brace_split) { /* dump the line prior to the + * brace ... */ + dump_line(); + ps.want_blank = false; + } else /* add a space between the decl and brace */ + ps.want_blank = true; + } + } + if (ps.in_parameter_declaration) + prefix_blankline_requested = 0; + + if (ps.p_l_follow > 0) { /* check for preceding unbalanced + * parens */ + diag2(1, "Unbalanced parens"); + ps.p_l_follow = 0; + if (sp_sw) { /* check for unclosed if, for, etc. */ + sp_sw = false; + parse(hd_type); + ps.ind_level = ps.i_l_follow; + } + } + if (s_code == e_code) + ps.ind_stmt = false; /* dont put extra indentation on line + * with '{' */ + if (ps.in_decl && ps.in_or_st) { /* this is either a structure + * declaration or an init */ + di_stack[ps.dec_nest] = dec_ind; + if (++ps.dec_nest == nitems(di_stack)) { + diag3(0, "Reached internal limit of %d struct levels", + nitems(di_stack)); + ps.dec_nest--; + } + /* ? dec_ind = 0; */ + } + else { + ps.decl_on_line = false; /* we can't be in the middle of + * a declaration, so don't do + * special indentation of + * comments */ + if (blanklines_after_declarations_at_proctop + && ps.in_parameter_declaration) + postfix_blankline_requested = 1; + ps.in_parameter_declaration = 0; + ps.in_decl = false; + } + dec_ind = 0; + parse(lbrace); /* let parser know about this */ + if (ps.want_blank) /* put a blank before '{' if '{' is not at + * start of line */ + *e_code++ = ' '; + ps.want_blank = false; + *e_code++ = '{'; + ps.just_saw_decl = 0; + break; + + case rbrace: /* got a '}' */ + if (ps.p_stack[ps.tos] == decl && !ps.block_init) /* semicolons can be + * omitted in + * declarations */ + parse(semicolon); + if (ps.p_l_follow) {/* check for unclosed if, for, else. */ + diag2(1, "Unbalanced parens"); + ps.p_l_follow = 0; + sp_sw = false; + } + ps.just_saw_decl = 0; + ps.block_init_level--; + if (s_code != e_code && !ps.block_init) { /* '}' must be first on + * line */ + if (verbose) + diag2(0, "Line broken"); + dump_line(); + } + *e_code++ = '}'; + ps.want_blank = true; + ps.in_stmt = ps.ind_stmt = false; + if (ps.dec_nest > 0) { /* we are in multi-level structure + * declaration */ + dec_ind = di_stack[--ps.dec_nest]; + if (ps.dec_nest == 0 && !ps.in_parameter_declaration) + ps.just_saw_decl = 2; + ps.in_decl = true; + } + prefix_blankline_requested = 0; + parse(rbrace); /* let parser know about this */ + ps.search_brace = cuddle_else && ps.p_stack[ps.tos] == ifhead + && ps.il[ps.tos] >= ps.ind_level; + if (ps.tos <= 1 && blanklines_after_procs && ps.dec_nest <= 0) + postfix_blankline_requested = 1; + break; + + case swstmt: /* got keyword "switch" */ + sp_sw = true; + hd_type = swstmt; /* keep this for when we have seen the + * expression */ + goto copy_id; /* go move the token into buffer */ + + case sp_paren: /* token is if, while, for */ + sp_sw = true; /* the interesting stuff is done after the + * expression is scanned */ + hd_type = (*token == 'i' ? ifstmt : + (*token == 'w' ? whilestmt : forstmt)); + + /* + * remember the type of header for later use by parser + */ + goto copy_id; /* copy the token into line */ + + case sp_nparen: /* got else, do */ + ps.in_stmt = false; + if (*token == 'e') { + if (e_code != s_code && (!cuddle_else || e_code[-1] != '}')) { + if (verbose) + diag2(0, "Line broken"); + dump_line();/* make sure this starts a line */ + ps.want_blank = false; + } + force_nl = true;/* also, following stuff must go onto new line */ + last_else = 1; + parse(elselit); + } + else { + if (e_code != s_code) { /* make sure this starts a line */ + if (verbose) + diag2(0, "Line broken"); + dump_line(); + ps.want_blank = false; + } + force_nl = true;/* also, following stuff must go onto new line */ + last_else = 0; + parse(dolit); + } + goto copy_id; /* move the token into line */ + + case type_def: + case storage: + prefix_blankline_requested = 0; + goto copy_id; + + case structure: + if (ps.p_l_follow > 0) + goto copy_id; + /* FALLTHROUGH */ + case decl: /* we have a declaration type (int, etc.) */ + parse(decl); /* let parser worry about indentation */ + if (ps.last_token == rparen && ps.tos <= 1) { + if (s_code != e_code) { + dump_line(); + ps.want_blank = 0; + } + } + if (ps.in_parameter_declaration && ps.indent_parameters && ps.dec_nest == 0) { + ps.ind_level = ps.i_l_follow = 1; + ps.ind_stmt = 0; + } + ps.in_or_st = true; /* this might be a structure or initialization + * declaration */ + ps.in_decl = ps.decl_on_line = true; + if ( /* !ps.in_or_st && */ ps.dec_nest <= 0) + ps.just_saw_decl = 2; + prefix_blankline_requested = 0; + for (i = 0; token[i++];); /* get length of token */ + + if (ps.ind_level == 0 || ps.dec_nest > 0) { + /* global variable or struct member in local variable */ + dec_ind = ps.decl_indent > 0 ? ps.decl_indent : i; + tabs_to_var = (use_tabs ? ps.decl_indent > 0 : 0); + } else { + /* local variable */ + dec_ind = ps.local_decl_indent > 0 ? ps.local_decl_indent : i; + tabs_to_var = (use_tabs ? ps.local_decl_indent > 0 : 0); + } + goto copy_id; + + case funcname: + case ident: /* got an identifier or constant */ + if (ps.in_decl) { + if (type_code == funcname) { + ps.in_decl = false; + if (procnames_start_line && s_code != e_code) { + *e_code = '\0'; + dump_line(); + } + else if (ps.want_blank) { + *e_code++ = ' '; + } + ps.want_blank = false; + } + else if (!ps.block_init && !ps.dumped_decl_indent && + ps.paren_level == 0) { /* if we are in a declaration, we + * must indent identifier */ + indent_declaration(dec_ind, tabs_to_var); + ps.dumped_decl_indent = true; + ps.want_blank = false; + } + } + else if (sp_sw && ps.p_l_follow == 0) { + sp_sw = false; + force_nl = true; + ps.last_u_d = true; + ps.in_stmt = false; + parse(hd_type); + } + copy_id: + { + int len = e_token - s_token; + + CHECK_SIZE_CODE(len + 1); + if (ps.want_blank) + *e_code++ = ' '; + memcpy(e_code, s_token, len); + e_code += len; + } + if (type_code != funcname) + ps.want_blank = true; + break; + + case strpfx: + { + int len = e_token - s_token; + + CHECK_SIZE_CODE(len + 1); + if (ps.want_blank) + *e_code++ = ' '; + memcpy(e_code, token, len); + e_code += len; + } + ps.want_blank = false; + break; + + case period: /* treat a period kind of like a binary + * operation */ + *e_code++ = '.'; /* move the period into line */ + ps.want_blank = false; /* dont put a blank after a period */ + break; + + case comma: + ps.want_blank = (s_code != e_code); /* only put blank after comma + * if comma does not start the + * line */ + if (ps.in_decl && ps.procname[0] == '\0' && !ps.block_init && + !ps.dumped_decl_indent && ps.paren_level == 0) { + /* indent leading commas and not the actual identifiers */ + indent_declaration(dec_ind - 1, tabs_to_var); + ps.dumped_decl_indent = true; + } + *e_code++ = ','; + if (ps.p_l_follow == 0) { + if (ps.block_init_level <= 0) + ps.block_init = 0; + if (break_comma && (!ps.leave_comma || + count_spaces_until(compute_code_target(), s_code, e_code) > + max_col - tabsize)) + force_nl = true; + } + break; + + case preesc: /* got the character '#' */ + if ((s_com != e_com) || + (s_lab != e_lab) || + (s_code != e_code)) + dump_line(); + CHECK_SIZE_LAB(1); + *e_lab++ = '#'; /* move whole line to 'label' buffer */ + { + int in_comment = 0; + int com_start = 0; + char quote = 0; + int com_end = 0; + + while (*buf_ptr == ' ' || *buf_ptr == '\t') { + buf_ptr++; + if (buf_ptr >= buf_end) + fill_buffer(); + } + while (*buf_ptr != '\n' || (in_comment && !had_eof)) { + CHECK_SIZE_LAB(2); + *e_lab = *buf_ptr++; + if (buf_ptr >= buf_end) + fill_buffer(); + switch (*e_lab++) { + case BACKSLASH: + if (!in_comment) { + *e_lab++ = *buf_ptr++; + if (buf_ptr >= buf_end) + fill_buffer(); + } + break; + case '/': + if (*buf_ptr == '*' && !in_comment && !quote) { + in_comment = 1; + *e_lab++ = *buf_ptr++; + com_start = e_lab - s_lab - 2; + } + break; + case '"': + if (quote == '"') + quote = 0; + break; + case '\'': + if (quote == '\'') + quote = 0; + break; + case '*': + if (*buf_ptr == '/' && in_comment) { + in_comment = 0; + *e_lab++ = *buf_ptr++; + com_end = e_lab - s_lab; + } + break; + } + } + + while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t')) + e_lab--; + if (e_lab - s_lab == com_end && bp_save == NULL) { + /* comment on preprocessor line */ + if (sc_end == NULL) { /* if this is the first comment, + * we must set up the buffer */ + save_com = sc_buf; + sc_end = &save_com[0]; + } + else { + *sc_end++ = '\n'; /* add newline between + * comments */ + *sc_end++ = ' '; + --line_no; + } + if (sc_end - save_com + com_end - com_start > sc_size) + errx(1, "input too long"); + memmove(sc_end, s_lab + com_start, com_end - com_start); + sc_end += com_end - com_start; + e_lab = s_lab + com_start; + while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t')) + e_lab--; + bp_save = buf_ptr; /* save current input buffer */ + be_save = buf_end; + buf_ptr = save_com; /* fix so that subsequent calls to + * lexi will take tokens out of + * save_com */ + *sc_end++ = ' '; /* add trailing blank, just in case */ + buf_end = sc_end; + sc_end = NULL; + } + CHECK_SIZE_LAB(1); + *e_lab = '\0'; /* null terminate line */ + ps.pcase = false; + } + + if (strncmp(s_lab, "#if", 3) == 0) { /* also ifdef, ifndef */ + if ((size_t)ifdef_level < nitems(state_stack)) { + match_state[ifdef_level].tos = -1; + state_stack[ifdef_level++] = ps; + } + else + diag2(1, "#if stack overflow"); + } + else if (strncmp(s_lab, "#el", 3) == 0) { /* else, elif */ + if (ifdef_level <= 0) + diag2(1, s_lab[3] == 'i' ? "Unmatched #elif" : "Unmatched #else"); + else { + match_state[ifdef_level - 1] = ps; + ps = state_stack[ifdef_level - 1]; + } + } + else if (strncmp(s_lab, "#endif", 6) == 0) { + if (ifdef_level <= 0) + diag2(1, "Unmatched #endif"); + else + ifdef_level--; + } else { + struct directives { + int size; + const char *string; + } + recognized[] = { + {7, "include"}, + {6, "define"}, + {5, "undef"}, + {4, "line"}, + {5, "error"}, + {6, "pragma"} + }; + int d = nitems(recognized); + while (--d >= 0) + if (strncmp(s_lab + 1, recognized[d].string, recognized[d].size) == 0) + break; + if (d < 0) { + diag2(1, "Unrecognized cpp directive"); + break; + } + } + if (blanklines_around_conditional_compilation) { + postfix_blankline_requested++; + n_real_blanklines = 0; + } + else { + postfix_blankline_requested = 0; + prefix_blankline_requested = 0; + } + break; /* subsequent processing of the newline + * character will cause the line to be printed */ + + case comment: /* we have gotten a / followed by * this is a biggie */ + pr_comment(); + break; + } /* end of big switch stmt */ + + *e_code = '\0'; /* make sure code section is null terminated */ + if (type_code != comment && type_code != newline && type_code != preesc) + ps.last_token = type_code; + } /* end of main while (1) loop */ +} + +/* + * copy input file to backup file if in_name is /blah/blah/blah/file, then + * backup file will be ".Bfile" then make the backup file the input and + * original input file the output + */ +static void +bakcopy(void) +{ + int n, + bakchn; + char buff[8 * 1024]; + const char *p; + + /* construct file name .Bfile */ + for (p = in_name; *p; p++); /* skip to end of string */ + while (p > in_name && *p != '/') /* find last '/' */ + p--; + if (*p == '/') + p++; + sprintf(bakfile, "%s.BAK", p); + + /* copy in_name to backup file */ + bakchn = creat(bakfile, 0600); + if (bakchn < 0) + err(1, "%s", bakfile); + while ((n = read(fileno(input), buff, sizeof(buff))) > 0) + if (write(bakchn, buff, n) != n) + err(1, "%s", bakfile); + if (n < 0) + err(1, "%s", in_name); + close(bakchn); + fclose(input); + + /* re-open backup file as the input file */ + input = fopen(bakfile, "r"); + if (input == NULL) + err(1, "%s", bakfile); + /* now the original input file will be the output */ + output = fopen(in_name, "wb"); + if (output == NULL) { + unlink(bakfile); + err(1, "%s", in_name); + } +} + +static void +indent_declaration(int cur_dec_ind, int tabs_to_var) +{ + int pos = e_code - s_code; + char *startpos = e_code; + + /* + * get the tab math right for indentations that are not multiples of tabsize + */ + if ((ps.ind_level * ps.ind_size) % tabsize != 0) { + pos += (ps.ind_level * ps.ind_size) % tabsize; + cur_dec_ind += (ps.ind_level * ps.ind_size) % tabsize; + } + if (tabs_to_var) { + int tpos; + + CHECK_SIZE_CODE(cur_dec_ind / tabsize); + while ((tpos = tabsize * (1 + pos / tabsize)) <= cur_dec_ind) { + *e_code++ = (!postgres_tab_rules || + tpos != pos + 1 || + cur_dec_ind >= tpos + tabsize) ? '\t' : ' '; + pos = tpos; + } + } + CHECK_SIZE_CODE(cur_dec_ind - pos + 1); + while (pos < cur_dec_ind) { + *e_code++ = ' '; + pos++; + } + if (e_code == startpos && ps.want_blank) { + *e_code++ = ' '; + ps.want_blank = false; + } +} diff --git a/src/tools/pg_bsd_indent/indent.h b/src/tools/pg_bsd_indent/indent.h new file mode 100644 index 0000000..e9e71d6 --- /dev/null +++ b/src/tools/pg_bsd_indent/indent.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2001 Jens Schweikhardt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +__FBSDID("$FreeBSD: head/usr.bin/indent/indent.h 303746 2016-08-04 15:27:09Z pfg $"); +#endif + +#define nitems(x) (sizeof((x)) / sizeof((x)[0])) + +struct parser_state; /* allow forward reference */ + +void add_typename(const char *); +void alloc_typenames(void); +int compute_code_target(void); +int compute_label_target(void); +int count_spaces(int, char *); +int count_spaces_until(int, char *, char *); +int lexi(struct parser_state *); +void diag2(int, const char *); +void diag3(int, const char *, int); +void diag4(int, const char *, int, int); +void dump_line(void); +int lookahead(void); +void lookahead_reset(void); +void fill_buffer(void); +void parse(int); +void pr_comment(void); +void set_defaults(void); +void set_option(char *); +void set_profile(const char *); diff --git a/src/tools/pg_bsd_indent/indent_codes.h b/src/tools/pg_bsd_indent/indent_codes.h new file mode 100644 index 0000000..6576bef --- /dev/null +++ b/src/tools/pg_bsd_indent/indent_codes.h @@ -0,0 +1,71 @@ +/*- + * Copyright (c) 1985 Sun Microsystems, Inc. + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)indent_codes.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: head/usr.bin/indent/indent_codes.h 309380 2016-12-02 01:25:51Z pfg $ + */ + +#define newline 1 +#define lparen 2 +#define rparen 3 +#define unary_op 4 +#define binary_op 5 +#define postop 6 +#define question 7 +#define casestmt 8 +#define colon 9 +#define semicolon 10 +#define lbrace 11 +#define rbrace 12 +#define ident 13 +#define comma 14 +#define comment 15 +#define swstmt 16 +#define preesc 17 +#define form_feed 18 +#define decl 19 +#define sp_paren 20 +#define sp_nparen 21 +#define ifstmt 22 +#define whilestmt 23 +#define forstmt 24 +#define stmt 25 +#define stmtl 26 +#define elselit 27 +#define dolit 28 +#define dohead 29 +#define ifhead 30 +#define elsehead 31 +#define period 32 +#define strpfx 33 +#define storage 34 +#define funcname 35 +#define type_def 36 +#define structure 37 diff --git a/src/tools/pg_bsd_indent/indent_globs.h b/src/tools/pg_bsd_indent/indent_globs.h new file mode 100644 index 0000000..917961b --- /dev/null +++ b/src/tools/pg_bsd_indent/indent_globs.h @@ -0,0 +1,339 @@ +/*- + * Copyright (c) 1985 Sun Microsystems, Inc. + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)indent_globs.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: head/usr.bin/indent/indent_globs.h 303735 2016-08-03 22:08:07Z pfg $ + */ + +#define BACKSLASH '\\' +#define bufsize 200 /* size of internal buffers */ +#define sc_size 5000 /* size of save_com buffer */ +#define label_offset 2 /* number of levels a label is placed to left + * of code */ + + +#ifndef false +#define false 0 +#endif +#ifndef true +#define true 1 +#endif + +/* + * Exactly one calling file should define this symbol. The global variables + * will be defined in that file, and just referenced elsewhere. + */ +#ifdef DECLARE_INDENT_GLOBALS +#define extern +#endif + +extern FILE *input; /* the fid for the input file */ +extern FILE *output; /* the output file */ + +#define CHECK_SIZE_CODE(desired_size) \ + if (e_code + (desired_size) >= l_code) { \ + int nsize = l_code-s_code + 400 + desired_size; \ + int code_len = e_code-s_code; \ + codebuf = (char *) realloc(codebuf, nsize); \ + if (codebuf == NULL) \ + err(1, NULL); \ + e_code = codebuf + code_len + 1; \ + l_code = codebuf + nsize - 5; \ + s_code = codebuf + 1; \ + } +#define CHECK_SIZE_COM(desired_size) \ + if (e_com + (desired_size) >= l_com) { \ + int nsize = l_com-s_com + 400 + desired_size; \ + int com_len = e_com - s_com; \ + int blank_pos; \ + if (last_bl != NULL) \ + blank_pos = last_bl - combuf; \ + else \ + blank_pos = -1; \ + combuf = (char *) realloc(combuf, nsize); \ + if (combuf == NULL) \ + err(1, NULL); \ + e_com = combuf + com_len + 1; \ + if (blank_pos > 0) \ + last_bl = combuf + blank_pos; \ + l_com = combuf + nsize - 5; \ + s_com = combuf + 1; \ + } +#define CHECK_SIZE_LAB(desired_size) \ + if (e_lab + (desired_size) >= l_lab) { \ + int nsize = l_lab-s_lab + 400 + desired_size; \ + int label_len = e_lab - s_lab; \ + labbuf = (char *) realloc(labbuf, nsize); \ + if (labbuf == NULL) \ + err(1, NULL); \ + e_lab = labbuf + label_len + 1; \ + l_lab = labbuf + nsize - 5; \ + s_lab = labbuf + 1; \ + } +#define CHECK_SIZE_TOKEN(desired_size) \ + if (e_token + (desired_size) >= l_token) { \ + int nsize = l_token-s_token + 400 + desired_size; \ + int token_len = e_token - s_token; \ + tokenbuf = (char *) realloc(tokenbuf, nsize); \ + if (tokenbuf == NULL) \ + err(1, NULL); \ + e_token = tokenbuf + token_len + 1; \ + l_token = tokenbuf + nsize - 5; \ + s_token = tokenbuf + 1; \ + } + +extern char *labbuf; /* buffer for label */ +extern char *s_lab; /* start ... */ +extern char *e_lab; /* .. and end of stored label */ +extern char *l_lab; /* limit of label buffer */ + +extern char *codebuf; /* buffer for code section */ +extern char *s_code; /* start ... */ +extern char *e_code; /* .. and end of stored code */ +extern char *l_code; /* limit of code section */ + +extern char *combuf; /* buffer for comments */ +extern char *s_com; /* start ... */ +extern char *e_com; /* ... and end of stored comments */ +extern char *l_com; /* limit of comment buffer */ + +#define token s_token +extern char *tokenbuf; /* the last token scanned */ +extern char *s_token; +extern char *e_token; +extern char *l_token; + +extern char *in_buffer; /* input buffer */ +extern char *in_buffer_limit; /* the end of the input buffer */ +extern char *buf_ptr; /* ptr to next character to be taken from + * in_buffer */ +extern char *buf_end; /* ptr to first after last char in in_buffer */ + +extern char sc_buf[sc_size]; /* input text is saved here when looking for + * the brace after an if, while, etc */ +extern char *save_com; /* start of the comment stored in sc_buf */ +extern char *sc_end; /* pointer into save_com buffer */ + +extern char *bp_save; /* saved value of buf_ptr when taking input + * from save_com */ +extern char *be_save; /* similarly saved value of buf_end */ + + +extern int found_err; +extern int blanklines_after_declarations; +extern int blanklines_before_blockcomments; +extern int blanklines_after_procs; +extern int blanklines_around_conditional_compilation; +extern int swallow_optional_blanklines; +extern int n_real_blanklines; +extern int prefix_blankline_requested; +extern int postfix_blankline_requested; +extern int break_comma; /* when true and not in parens, break after a + * comma */ +extern int btype_2; /* when true, brace should be on same line as + * if, while, etc */ +extern float case_ind; /* indentation level to be used for a "case + * n:" */ +extern int code_lines; /* count of lines with code */ +extern int had_eof; /* set to true when input is exhausted */ +extern int line_no; /* the current line number. */ +extern int max_col; /* the maximum allowable line length */ +extern int verbose; /* when true, non-essential error messages are + * printed */ +extern int cuddle_else; /* true if else should cuddle up to '}' */ +extern int star_comment_cont; /* true iff comment continuation lines should + * have stars at the beginning of each line. */ +extern int comment_delimiter_on_blankline; +extern int troff; /* true iff were generating troff input */ +extern int procnames_start_line; /* if true, the names of procedures + * being defined get placed in column + * 1 (ie. a newline is placed between + * the type of the procedure and its + * name) */ +extern int proc_calls_space; /* If true, procedure calls look like: + * foo(bar) rather than foo (bar) */ +extern int format_block_comments; /* true if comments beginning with + * `/ * \n' are to be reformatted */ +extern int format_col1_comments; /* If comments which start in column 1 + * are to be magically reformatted + * (just like comments that begin in + * later columns) */ +extern int inhibit_formatting; /* true if INDENT OFF is in effect */ +extern int suppress_blanklines;/* set iff following blanklines should be + * suppressed */ +extern int continuation_indent;/* set to the indentation between the edge of + * code and continuation lines */ +extern int lineup_to_parens; /* if true, continued code within parens will + * be lined up to the open paren */ +extern int lineup_to_parens_always; /* if true, do not attempt to keep + * lined-up code within the margin */ +extern int Bill_Shannon; /* true iff a blank should always be inserted + * after sizeof */ +extern int blanklines_after_declarations_at_proctop; /* This is vaguely + * similar to + * blanklines_after_decla + * rations except that + * it only applies to + * the first set of + * declarations in a + * procedure (just after + * the first '{') and it + * causes a blank line + * to be generated even + * if there are no + * declarations */ +extern int block_comment_max_col; +extern int extra_expression_indent; /* true if continuation lines from the + * expression part of "if(e)", + * "while(e)", "for(e;e;e)" should be + * indented an extra tab stop so that + * they don't conflict with the code + * that follows */ +extern int function_brace_split; /* split function declaration and + * brace onto separate lines */ +extern int use_tabs; /* set true to use tabs for spacing, + * false uses all spaces */ +extern int auto_typedefs; /* set true to recognize identifiers + * ending in "_t" like typedefs */ +extern int space_after_cast; /* "b = (int) a" vs "b = (int)a" */ +extern int postgres_tab_rules; /* use Postgres tab-vs-space rules */ +extern int tabsize; /* the size of a tab */ +extern int else_endif_com_ind; /* the column in which comments to + * the right of #else and #endif + * should start */ + +extern int ifdef_level; + +struct parser_state { + int last_token; + int p_stack[256]; /* this is the parsers stack */ + int il[64]; /* this stack stores indentation levels */ + float cstk[32]; /* used to store case stmt indentation levels */ + int box_com; /* set to true when we are in a "boxed" + * comment. In that case, the first non-blank + * char should be lined up with the / in / followed by * */ + int comment_delta; /* used to set up indentation for all lines + * of a boxed comment after the first one */ + int n_comment_delta;/* remembers how many columns there were + * before the start of a box comment so that + * forthcoming lines of the comment are + * indented properly */ + int cast_mask; /* indicates which close parens potentially + * close off casts */ + int not_cast_mask; /* indicates which close parens definitely + * close off something else than casts */ + int block_init; /* true iff inside a block initialization */ + int block_init_level; /* The level of brace nesting in an + * initialization */ + int last_nl; /* this is true if the last thing scanned was + * a newline */ + int in_or_st; /* Will be true iff there has been a + * declarator (e.g. int or char) and no left + * paren since the last semicolon. When true, + * a '{' is starting a structure definition or + * an initialization list */ + int bl_line; /* set to 1 by dump_line if the line is blank */ + int col_1; /* set to true if the last token started in + * column 1 */ + int com_col; /* this is the column in which the current + * comment should start */ + int com_ind; /* the column in which comments to the right + * of code should start */ + int com_lines; /* the number of lines with comments, set by + * dump_line */ + int dec_nest; /* current nesting level for structure or init */ + int decl_com_ind; /* the column in which comments after + * declarations should be put */ + int decl_on_line; /* set to true if this line of code has part + * of a declaration on it */ + int i_l_follow; /* the level to which ind_level should be set + * after the current line is printed */ + int in_decl; /* set to true when we are in a declaration + * stmt. The processing of braces is then + * slightly different */ + int in_stmt; /* set to 1 while in a stmt */ + int ind_level; /* the current indentation level */ + int ind_size; /* the size of one indentation level */ + int ind_stmt; /* set to 1 if next line should have an extra + * indentation level because we are in the + * middle of a stmt */ + int last_u_d; /* set to true after scanning a token which + * forces a following operator to be unary */ + int leave_comma; /* if true, never break declarations after + * commas */ + int ljust_decl; /* true if declarations should be left + * justified */ + int out_coms; /* the number of comments processed, set by + * pr_comment */ + int out_lines; /* the number of lines written, set by + * dump_line */ + int p_l_follow; /* used to remember how to indent following + * statement */ + int paren_level; /* parenthesization level. used to indent + * within statements */ + short paren_indents[20]; /* column positions of each paren */ + int pcase; /* set to 1 if the current line label is a + * case. It is printed differently from a + * regular label */ + int search_brace; /* set to true by parse when it is necessary + * to buffer up all info up to the start of a + * stmt after an if, while, etc */ + int unindent_displace; /* comments not to the right of code + * will be placed this many + * indentation levels to the left of + * code */ + int use_ff; /* set to one if the current line should be + * terminated with a form feed */ + int want_blank; /* set to true when the following token should + * be prefixed by a blank. (Said prefixing is + * ignored in some cases.) */ + int else_if; /* True iff else if pairs should be handled + * specially */ + int decl_indent; /* column to indent declared identifiers to */ + int local_decl_indent; /* like decl_indent but for locals */ + int keyword; /* the type of a keyword or 0 */ + int dumped_decl_indent; + float case_indent; /* The distance to indent case labels from the + * switch statement */ + int in_parameter_declaration; + int indent_parameters; + int tos; /* pointer to top of stack */ + char procname[100]; /* The name of the current procedure */ + int just_saw_decl; +}; + +extern struct parser_state ps; +extern struct parser_state state_stack[5]; +extern struct parser_state match_state[5]; + +/* Undo previous hackery */ +#ifdef DECLARE_INDENT_GLOBALS +#undef extern +#endif diff --git a/src/tools/pg_bsd_indent/io.c b/src/tools/pg_bsd_indent/io.c new file mode 100644 index 0000000..9d64ca1 --- /dev/null +++ b/src/tools/pg_bsd_indent/io.c @@ -0,0 +1,605 @@ +/*- + * Copyright (c) 1985 Sun Microsystems, Inc. + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)io.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif + +#include "c.h" + +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "indent_globs.h" +#include "indent.h" + +int comment_open; +static int paren_target; + +static char *lookahead_buf; /* malloc'd buffer, or NULL initially */ +static char *lookahead_buf_end; /* end+1 of allocated space */ +static char *lookahead_start; /* => next char for fill_buffer() to fetch */ +static char *lookahead_ptr; /* => next char for lookahead() to fetch */ +static char *lookahead_end; /* last+1 valid char in lookahead_buf */ +static char *lookahead_bp_save; /* lookahead position in bp_save, if any */ + +static int pad_output(int current, int target); + +void +dump_line(void) +{ /* dump_line is the routine that actually + * effects the printing of the new source. It + * prints the label section, followed by the + * code section with the appropriate nesting + * level, followed by any comments */ + int cur_col, + target_col = 1; + static int not_first_line; + + if (ps.procname[0]) { + ps.ind_level = 0; + ps.procname[0] = 0; + } + if (s_code == e_code && s_lab == e_lab && s_com == e_com) { + if (suppress_blanklines > 0) + suppress_blanklines--; + else { + ps.bl_line = true; + n_real_blanklines++; + } + } + else if (!inhibit_formatting) { + suppress_blanklines = 0; + ps.bl_line = false; + if (prefix_blankline_requested && not_first_line) { + if (swallow_optional_blanklines) { + if (n_real_blanklines == 1) + n_real_blanklines = 0; + } + else { + if (n_real_blanklines == 0) + n_real_blanklines = 1; + } + } + while (--n_real_blanklines >= 0) + putc('\n', output); + n_real_blanklines = 0; + if (ps.ind_level == 0) + ps.ind_stmt = 0; /* this is a class A kludge. dont do + * additional statement indentation if we are + * at bracket level 0 */ + + if (e_lab != s_lab || e_code != s_code) + ++code_lines; /* keep count of lines with code */ + + + if (e_lab != s_lab) { /* print lab, if any */ + if (comment_open) { + comment_open = 0; + fprintf(output, ".*/\n"); + } + while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t')) + e_lab--; + *e_lab = '\0'; + cur_col = pad_output(1, compute_label_target()); + if (s_lab[0] == '#' && (strncmp(s_lab, "#else", 5) == 0 + || strncmp(s_lab, "#endif", 6) == 0)) { + char *s = s_lab; + if (e_lab[-1] == '\n') e_lab--; + do putc(*s++, output); + while (s < e_lab && 'a' <= *s && *s<='z'); + while ((*s == ' ' || *s == '\t') && s < e_lab) + s++; + if (s < e_lab) + fprintf(output, s[0]=='/' && s[1]=='*' ? "\t%.*s" : "\t/* %.*s */", + (int)(e_lab - s), s); + } + else fprintf(output, "%.*s", (int)(e_lab - s_lab), s_lab); + cur_col = count_spaces(cur_col, s_lab); + } + else + cur_col = 1; /* there is no label section */ + + ps.pcase = false; + + if (s_code != e_code) { /* print code section, if any */ + char *p; + + if (comment_open) { + comment_open = 0; + fprintf(output, ".*/\n"); + } + target_col = compute_code_target(); + { + int i; + + for (i = 0; i < ps.p_l_follow; i++) + if (ps.paren_indents[i] >= 0) + ps.paren_indents[i] = -(ps.paren_indents[i] + target_col); + } + cur_col = pad_output(cur_col, target_col); + for (p = s_code; p < e_code; p++) + if (*p == (char) 0200) + fprintf(output, "%d", target_col * 7); + else + putc(*p, output); + cur_col = count_spaces(cur_col, s_code); + } + if (s_com != e_com) { /* print comment, if any */ + int target = ps.com_col; + char *com_st = s_com; + + target += ps.comment_delta; + while (*com_st == '\t') /* consider original indentation in + * case this is a box comment */ + com_st++, target += tabsize; + while (target <= 0) + if (*com_st == ' ') + target++, com_st++; + else if (*com_st == '\t') + target = tabsize * (1 + (target - 1) / tabsize) + 1, com_st++; + else + target = 1; + if (cur_col > target) { /* if comment can't fit on this line, + * put it on next line */ + putc('\n', output); + cur_col = 1; + ++ps.out_lines; + } + while (e_com > com_st && isspace((unsigned char)e_com[-1])) + e_com--; + (void)pad_output(cur_col, target); + fwrite(com_st, e_com - com_st, 1, output); + ps.comment_delta = ps.n_comment_delta; + ++ps.com_lines; /* count lines with comments */ + } + if (ps.use_ff) + putc('\014', output); + else + putc('\n', output); + ++ps.out_lines; + if (ps.just_saw_decl == 1 && blanklines_after_declarations) { + prefix_blankline_requested = 1; + ps.just_saw_decl = 0; + } + else + prefix_blankline_requested = postfix_blankline_requested; + postfix_blankline_requested = 0; + } + ps.decl_on_line = ps.in_decl; /* if we are in the middle of a + * declaration, remember that fact for + * proper comment indentation */ + /* next line should be indented if we have not completed this stmt, and + * either we are not in a declaration or we are in an initialization + * assignment; but not if we're within braces in an initialization, + * because that scenario is handled by other rules. */ + ps.ind_stmt = ps.in_stmt && + (!ps.in_decl || (ps.block_init && ps.block_init_level <= 0)); + ps.use_ff = false; + ps.dumped_decl_indent = 0; + *(e_lab = s_lab) = '\0'; /* reset buffers */ + *(e_code = s_code) = '\0'; + *(e_com = s_com = combuf + 1) = '\0'; + ps.ind_level = ps.i_l_follow; + ps.paren_level = ps.p_l_follow; + if (ps.paren_level > 0) + paren_target = -ps.paren_indents[ps.paren_level - 1]; + not_first_line = 1; +} + +int +compute_code_target(void) +{ + int target_col = ps.ind_size * ps.ind_level + 1; + + if (ps.paren_level) + if (!lineup_to_parens) + target_col += continuation_indent + * (2 * continuation_indent == ps.ind_size ? 1 : ps.paren_level); + else if (lineup_to_parens_always) + target_col = paren_target; + else { + int w; + int t = paren_target; + + if ((w = count_spaces(t, s_code) - max_col) > 0 + && count_spaces(target_col, s_code) <= max_col) { + t -= w + 1; + if (t > target_col) + target_col = t; + } + else + target_col = t; + } + else if (ps.ind_stmt) + target_col += continuation_indent; + return target_col; +} + +int +compute_label_target(void) +{ + return + ps.pcase ? (int) (case_ind * ps.ind_size) + 1 + : *s_lab == '#' ? 1 + : ps.ind_size * (ps.ind_level - label_offset) + 1; +} + +/* + * Read data ahead of what has been collected into in_buffer. + * + * Successive calls get further and further ahead, until we hit EOF. + * Call lookahead_reset() to rescan from just beyond in_buffer. + * + * Lookahead is automatically reset whenever fill_buffer() reads beyond + * the lookahead buffer, i.e., you can't use this for "look behind". + * + * The standard pattern for potentially multi-line lookahead is to call + * lookahead_reset(), then enter a loop that scans forward from buf_ptr + * to buf_end, then (if necessary) calls lookahead() to read additional + * characters from beyond the end of the current line. + */ +int +lookahead(void) +{ + /* First read whatever's in bp_save area */ + if (lookahead_bp_save != NULL && lookahead_bp_save < be_save) + return (unsigned char) *lookahead_bp_save++; + /* Else, we have to examine and probably fill the main lookahead buffer */ + while (lookahead_ptr >= lookahead_end) { + int i = getc(input); + + if (i == EOF) + return i; + if (i == '\0') + continue; /* fill_buffer drops nulls, and so do we */ + + if (lookahead_end >= lookahead_buf_end) { + /* Need to allocate or enlarge lookahead_buf */ + char *new_buf; + size_t req; + + if (lookahead_buf == NULL) { + req = 64; + new_buf = malloc(req); + } else { + req = (lookahead_buf_end - lookahead_buf) * 2; + new_buf = realloc(lookahead_buf, req); + } + if (new_buf == NULL) + errx(1, "too much lookahead required"); + lookahead_start = new_buf + (lookahead_start - lookahead_buf); + lookahead_ptr = new_buf + (lookahead_ptr - lookahead_buf); + lookahead_end = new_buf + (lookahead_end - lookahead_buf); + lookahead_buf = new_buf; + lookahead_buf_end = new_buf + req; + } + + *lookahead_end++ = i; + } + return (unsigned char) *lookahead_ptr++; +} + +/* + * Reset so that lookahead() will again scan from just beyond what's in + * in_buffer. + */ +void +lookahead_reset(void) +{ + /* Reset the main lookahead buffer */ + lookahead_ptr = lookahead_start; + /* If bp_save isn't NULL, we need to scan that first */ + lookahead_bp_save = bp_save; +} + +/* + * Copyright (C) 1976 by the Board of Trustees of the University of Illinois + * + * All rights reserved + * + * + * NAME: fill_buffer + * + * FUNCTION: Reads one line of input into in_buffer, + * sets up buf_ptr and buf_end to point to the line's start and end+1. + * (Note that the buffer does not get null-terminated.) + * + * HISTORY: initial coding November 1976 D A Willcox of CAC 1/7/77 A + * Willcox of CAC Added check for switch back to partly full input + * buffer from temporary buffer + * + */ +void +fill_buffer(void) +{ /* this routine reads stuff from the input */ + char *p; + int i; + FILE *f = input; + + if (bp_save != NULL) { /* there is a partly filled input buffer left */ + buf_ptr = bp_save; /* do not read anything, just switch buffers */ + buf_end = be_save; + bp_save = be_save = NULL; + lookahead_bp_save = NULL; + if (buf_ptr < buf_end) + return; /* only return if there is really something in + * this buffer */ + } + for (p = in_buffer;;) { + if (p >= in_buffer_limit) { + int size = (in_buffer_limit - in_buffer) * 2 + 10; + int offset = p - in_buffer; + in_buffer = realloc(in_buffer, size); + if (in_buffer == NULL) + errx(1, "input line too long"); + p = in_buffer + offset; + in_buffer_limit = in_buffer + size - 2; + } + if (lookahead_start < lookahead_end) { + i = (unsigned char) *lookahead_start++; + } else { + lookahead_start = lookahead_ptr = lookahead_end = lookahead_buf; + if ((i = getc(f)) == EOF) { + *p++ = ' '; + *p++ = '\n'; + had_eof = true; + break; + } + } + if (i != '\0') + *p++ = i; + if (i == '\n') + break; + } + buf_ptr = in_buffer; + buf_end = p; + if (p - in_buffer > 2 && p[-2] == '/' && p[-3] == '*') { + if (in_buffer[3] == 'I' && strncmp(in_buffer, "/**INDENT**", 11) == 0) + fill_buffer(); /* flush indent error message */ + else { + int com = 0; + + p = in_buffer; + while (*p == ' ' || *p == '\t') + p++; + if (*p == '/' && p[1] == '*') { + p += 2; + while (*p == ' ' || *p == '\t') + p++; + if (p[0] == 'I' && p[1] == 'N' && p[2] == 'D' && p[3] == 'E' + && p[4] == 'N' && p[5] == 'T') { + p += 6; + while (*p == ' ' || *p == '\t') + p++; + if (*p == '*') + com = 1; + else if (*p == 'O') { + if (*++p == 'N') + p++, com = 1; + else if (*p == 'F' && *++p == 'F') + p++, com = 2; + } + while (*p == ' ' || *p == '\t') + p++; + if (p[0] == '*' && p[1] == '/' && p[2] == '\n' && com) { + if (s_com != e_com || s_lab != e_lab || s_code != e_code) + dump_line(); + if (!(inhibit_formatting = com - 1)) { + n_real_blanklines = 0; + postfix_blankline_requested = 0; + prefix_blankline_requested = 0; + suppress_blanklines = 1; + } + } + } + } + } + } + if (inhibit_formatting) { + p = in_buffer; + do + putc(*p, output); + while (*p++ != '\n'); + } +} + +/* + * Copyright (C) 1976 by the Board of Trustees of the University of Illinois + * + * All rights reserved + * + * + * NAME: pad_output + * + * FUNCTION: Writes tabs and spaces to move the current column up to the desired + * position. + * + * ALGORITHM: Put tabs and/or blanks into pobuf, then write pobuf. + * + * PARAMETERS: current integer The current column + * target integer The desired column + * + * RETURNS: Integer value of the new column. (If current >= target, no action is + * taken, and current is returned. + * + * GLOBALS: None + * + * CALLS: write (sys) + * + * CALLED BY: dump_line + * + * HISTORY: initial coding November 1976 D A Willcox of CAC + * + */ +static int +pad_output(int current, int target) + /* writes tabs and blanks (if necessary) to + * get the current output position up to the + * target column */ + /* current: the current column value */ + /* target: position we want it at */ +{ + int curr; /* internal column pointer */ + + if (current >= target) + return (current); /* line is already long enough */ + curr = current; + if (use_tabs) { + int tcur; + + while ((tcur = tabsize * (1 + (curr - 1) / tabsize) + 1) <= target) { + putc((!postgres_tab_rules || + tcur != curr + 1 || + target >= tcur + tabsize) ? '\t' : ' ', output); + curr = tcur; + } + } + while (curr++ < target) + putc(' ', output); /* pad with final blanks */ + + return (target); +} + +/* + * Copyright (C) 1976 by the Board of Trustees of the University of Illinois + * + * All rights reserved + * + * + * NAME: count_spaces + * + * FUNCTION: Find out where printing of a given string will leave the current + * character position on output. + * + * ALGORITHM: Run thru input string and add appropriate values to current + * position. + * + * RETURNS: Integer value of position after printing "buffer" starting in column + * "current". + * + * HISTORY: initial coding November 1976 D A Willcox of CAC + * + */ +int +count_spaces_until(int cur, char *buffer, char *end) +/* + * this routine figures out where the character position will be after + * printing the text in buffer starting at column "current" + */ +{ + char *buf; /* used to look thru buffer */ + + for (buf = buffer; *buf != '\0' && buf != end; ++buf) { + switch (*buf) { + + case '\n': + case 014: /* form feed */ + cur = 1; + break; + + case '\t': + cur = tabsize * (1 + (cur - 1) / tabsize) + 1; + break; + + case 010: /* backspace */ + --cur; + break; + + default: + ++cur; + break; + } /* end of switch */ + } /* end of for loop */ + return (cur); +} + +int +count_spaces(int cur, char *buffer) +{ + return (count_spaces_until(cur, buffer, NULL)); +} + +void +diag4(int level, const char *msg, int a, int b) +{ + if (level) + found_err = 1; + if (output == stdout) { + fprintf(stdout, "/**INDENT** %s@%d: ", level == 0 ? "Warning" : "Error", line_no); + fprintf(stdout, msg, a, b); + fprintf(stdout, " */\n"); + } + else { + fprintf(stderr, "%s@%d: ", level == 0 ? "Warning" : "Error", line_no); + fprintf(stderr, msg, a, b); + fprintf(stderr, "\n"); + } +} + +void +diag3(int level, const char *msg, int a) +{ + if (level) + found_err = 1; + if (output == stdout) { + fprintf(stdout, "/**INDENT** %s@%d: ", level == 0 ? "Warning" : "Error", line_no); + fprintf(stdout, msg, a); + fprintf(stdout, " */\n"); + } + else { + fprintf(stderr, "%s@%d: ", level == 0 ? "Warning" : "Error", line_no); + fprintf(stderr, msg, a); + fprintf(stderr, "\n"); + } +} + +void +diag2(int level, const char *msg) +{ + if (level) + found_err = 1; + if (output == stdout) { + fprintf(stdout, "/**INDENT** %s@%d: ", level == 0 ? "Warning" : "Error", line_no); + fprintf(stdout, "%s", msg); + fprintf(stdout, " */\n"); + } + else { + fprintf(stderr, "%s@%d: ", level == 0 ? "Warning" : "Error", line_no); + fprintf(stderr, "%s", msg); + fprintf(stderr, "\n"); + } +} + diff --git a/src/tools/pg_bsd_indent/lexi.c b/src/tools/pg_bsd_indent/lexi.c new file mode 100644 index 0000000..943bf7c --- /dev/null +++ b/src/tools/pg_bsd_indent/lexi.c @@ -0,0 +1,720 @@ +/*- + * Copyright (c) 1985 Sun Microsystems, Inc. + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)lexi.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif + +#include "c.h" + +/* + * Here we have the token scanner for indent. It scans off one token and puts + * it in the global variable "token". It returns a code, indicating the type + * of token scanned. + */ + +#include <err.h> +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include "indent_globs.h" +#include "indent_codes.h" +#include "indent.h" + +#define alphanum 1 +#ifdef undef +#define opchar 3 +#endif + +struct templ { + const char *rwd; + int rwcode; +}; + +/* + * This table has to be sorted alphabetically, because it'll be used in binary + * search. For the same reason, string must be the first thing in struct templ. + */ +struct templ specials[] = +{ + {"_Bool", 4}, + {"_Complex", 4}, + {"_Imaginary", 4}, + {"auto", 10}, + {"bool", 4}, + {"break", 9}, + {"case", 8}, + {"char", 4}, + {"complex", 4}, + {"const", 4}, + {"continue", 12}, + {"default", 8}, + {"do", 6}, + {"double", 4}, + {"else", 6}, + {"enum", 3}, + {"extern", 10}, + {"float", 4}, + {"for", 5}, + {"global", 4}, + {"goto", 9}, + {"if", 5}, + {"imaginary", 4}, + {"inline", 12}, + {"int", 4}, + {"long", 4}, + {"offsetof", 1}, + {"register", 10}, + {"restrict", 12}, + {"return", 9}, + {"short", 4}, + {"signed", 4}, + {"sizeof", 2}, + {"static", 10}, + {"struct", 3}, + {"switch", 7}, + {"typedef", 11}, + {"union", 3}, + {"unsigned", 4}, + {"void", 4}, + {"volatile", 4}, + {"while", 5} +}; + +const char **typenames; +int typename_count; +int typename_top = -1; + +char chartype[128] = +{ /* this is used to facilitate the decision of + * what type (alphanumeric, operator) each + * character is */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 0, 0, 1, 3, 3, 0, + 0, 0, 3, 3, 0, 3, 0, 3, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 3, 3, 3, 3, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 3, 1, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 3, 0, 3, 0 +}; + +static int +strcmp_type(const void *e1, const void *e2) +{ + return (strcmp(e1, *(const char * const *)e2)); +} + +/* + * Decide whether "foo(..." is a function definition or declaration. + * + * At call, we are looking at the '('. Look ahead to find the first + * '{', ';' or ',' that is not within parentheses or comments; then + * it's a definition if we found '{', otherwise a declaration. + * Note that this rule is fooled by K&R-style parameter declarations, + * but telling the difference between those and function attributes + * seems like more trouble than it's worth. This code could also be + * fooled by mismatched parens or apparent comment starts within string + * literals, but that seems unlikely in the context it's used in. + */ +static int +is_func_definition(char *tp) +{ + int paren_depth = 0; + int in_comment = false; + int in_slash_comment = false; + int lastc = 0; + + /* We may need to look past the end of the current buffer. */ + lookahead_reset(); + for (;;) { + int c; + + /* Fetch next character. */ + if (tp < buf_end) + c = *tp++; + else { + c = lookahead(); + if (c == EOF) + break; + } + /* Handle comments. */ + if (in_comment) { + if (lastc == '*' && c == '/') + in_comment = false; + } else if (lastc == '/' && c == '*' && !in_slash_comment) + in_comment = true; + else if (in_slash_comment) { + if (c == '\n') + in_slash_comment = false; + } else if (lastc == '/' && c == '/') + in_slash_comment = true; + /* Count nested parens properly. */ + else if (c == '(') + paren_depth++; + else if (c == ')') { + paren_depth--; + /* + * If we find unbalanced parens, we must have started inside a + * declaration. + */ + if (paren_depth < 0) + return false; + } else if (paren_depth == 0) { + /* We are outside any parentheses or comments. */ + if (c == '{') + return true; + else if (c == ';' || c == ',') + return false; + } + lastc = c; + } + /* Hit EOF --- for lack of anything better, assume "not a definition". */ + return false; +} + +int +lexi(struct parser_state *state) +{ + int unary_delim; /* this is set to 1 if the current token + * forces a following operator to be unary */ + int code; /* internal code to be returned */ + char qchar; /* the delimiter character for a string */ + + e_token = s_token; /* point to start of place to save token */ + unary_delim = false; + state->col_1 = state->last_nl; /* tell world that this token started + * in column 1 iff the last thing + * scanned was a newline */ + state->last_nl = false; + + while (*buf_ptr == ' ' || *buf_ptr == '\t') { /* get rid of blanks */ + state->col_1 = false; /* leading blanks imply token is not in column + * 1 */ + if (++buf_ptr >= buf_end) + fill_buffer(); + } + + /* Scan an alphanumeric token */ + if (chartype[*buf_ptr & 127] == alphanum || + (buf_ptr[0] == '.' && isdigit((unsigned char)buf_ptr[1]))) { + /* + * we have a character or number + */ + struct templ *p; + + if (isdigit((unsigned char)*buf_ptr) || + (buf_ptr[0] == '.' && isdigit((unsigned char)buf_ptr[1]))) { + int seendot = 0, + seenexp = 0, + seensfx = 0; + + /* + * base 2, base 8, base 16: + */ + if (buf_ptr[0] == '0' && buf_ptr[1] != '.') { + int len; + + if (buf_ptr[1] == 'b' || buf_ptr[1] == 'B') + len = strspn(buf_ptr + 2, "01") + 2; + else if (buf_ptr[1] == 'x' || buf_ptr[1] == 'X') + len = strspn(buf_ptr + 2, "0123456789ABCDEFabcdef") + 2; + else + len = strspn(buf_ptr + 1, "012345678") + 1; + if (len > 0) { + CHECK_SIZE_TOKEN(len); + memcpy(e_token, buf_ptr, len); + e_token += len; + buf_ptr += len; + } + else + diag2(1, "Unterminated literal"); + } + else /* base 10: */ + while (1) { + if (*buf_ptr == '.') { + if (seendot) + break; + else + seendot++; + } + CHECK_SIZE_TOKEN(3); + *e_token++ = *buf_ptr++; + if (!isdigit((unsigned char)*buf_ptr) && *buf_ptr != '.') { + if ((*buf_ptr != 'E' && *buf_ptr != 'e') || seenexp) + break; + else { + seenexp++; + seendot++; + *e_token++ = *buf_ptr++; + if (*buf_ptr == '+' || *buf_ptr == '-') + *e_token++ = *buf_ptr++; + } + } + } + + while (1) { + CHECK_SIZE_TOKEN(2); + if (!(seensfx & 1) && (*buf_ptr == 'U' || *buf_ptr == 'u')) { + *e_token++ = *buf_ptr++; + seensfx |= 1; + continue; + } + if (!(seensfx & 2) && (strchr("fFlL", *buf_ptr) != NULL)) { + if (buf_ptr[1] == buf_ptr[0]) + *e_token++ = *buf_ptr++; + *e_token++ = *buf_ptr++; + seensfx |= 2; + continue; + } + break; + } + } + else + while (chartype[*buf_ptr & 127] == alphanum || *buf_ptr == BACKSLASH) { + /* fill_buffer() terminates buffer with newline */ + if (*buf_ptr == BACKSLASH) { + if (*(buf_ptr + 1) == '\n') { + buf_ptr += 2; + if (buf_ptr >= buf_end) + fill_buffer(); + } else + break; + } + CHECK_SIZE_TOKEN(1); + /* copy it over */ + *e_token++ = *buf_ptr++; + if (buf_ptr >= buf_end) + fill_buffer(); + } + *e_token = '\0'; + + if (s_token[0] == 'L' && s_token[1] == '\0' && + (*buf_ptr == '"' || *buf_ptr == '\'')) + return (strpfx); + + while (*buf_ptr == ' ' || *buf_ptr == '\t') { /* get rid of blanks */ + if (++buf_ptr >= buf_end) + fill_buffer(); + } + state->keyword = 0; + if (state->last_token == structure && !state->p_l_follow) { + /* if last token was 'struct' and we're not + * in parentheses, then this token + * should be treated as a declaration */ + state->last_u_d = true; + return (decl); + } + /* + * Operator after identifier is binary unless last token was 'struct' + */ + state->last_u_d = (state->last_token == structure); + + p = bsearch(s_token, + specials, + sizeof(specials) / sizeof(specials[0]), + sizeof(specials[0]), + strcmp_type); + if (p == NULL) { /* not a special keyword... */ + char *u; + + /* ... so maybe a type_t or a typedef */ + if ((auto_typedefs && ((u = strrchr(s_token, '_')) != NULL) && + strcmp(u, "_t") == 0) || (typename_top >= 0 && + bsearch(s_token, typenames, typename_top + 1, + sizeof(typenames[0]), strcmp_type))) { + state->keyword = 4; /* a type name */ + state->last_u_d = true; + goto found_typename; + } + } else { /* we have a keyword */ + state->keyword = p->rwcode; + state->last_u_d = true; + switch (p->rwcode) { + case 7: /* it is a switch */ + return (swstmt); + case 8: /* a case or default */ + return (casestmt); + + case 3: /* a "struct" */ + /* FALLTHROUGH */ + case 4: /* one of the declaration keywords */ + found_typename: + if (state->p_l_follow) { + /* inside parens: cast, param list, offsetof or sizeof */ + state->cast_mask |= (1 << state->p_l_follow) & ~state->not_cast_mask; + } + if (state->last_token == period || state->last_token == unary_op) { + state->keyword = 0; + break; + } + if (p != NULL && p->rwcode == 3) + return (structure); + if (state->p_l_follow) + break; + return (decl); + + case 5: /* if, while, for */ + return (sp_paren); + + case 6: /* do, else */ + return (sp_nparen); + + case 10: /* storage class specifier */ + return (storage); + + case 11: /* typedef */ + return (type_def); + + default: /* all others are treated like any other + * identifier */ + return (ident); + } /* end of switch */ + } /* end of if (found_it) */ + if (*buf_ptr == '(' && state->tos <= 1 && state->ind_level == 0 && + state->in_parameter_declaration == 0 && state->block_init == 0) { + if (is_func_definition(buf_ptr)) { + strncpy(state->procname, token, sizeof state->procname - 1); + if (state->in_decl) + state->in_parameter_declaration = 1; + return (funcname); + } + } + /* + * The following hack attempts to guess whether or not the current + * token is in fact a declaration keyword -- one that has been + * typedefd + */ + else if (!state->p_l_follow && !state->block_init && + !state->in_stmt && + ((*buf_ptr == '*' && buf_ptr[1] != '=') || + isalpha((unsigned char)*buf_ptr)) && + (state->last_token == semicolon || state->last_token == lbrace || + state->last_token == rbrace)) { + state->keyword = 4; /* a type name */ + state->last_u_d = true; + return decl; + } + if (state->last_token == decl) /* if this is a declared variable, + * then following sign is unary */ + state->last_u_d = true; /* will make "int a -1" work */ + return (ident); /* the ident is not in the list */ + } /* end of processing for alphanum character */ + + /* Scan a non-alphanumeric token */ + + CHECK_SIZE_TOKEN(3); /* things like "<<=" */ + *e_token++ = *buf_ptr; /* if it is only a one-character token, it is + * moved here */ + *e_token = '\0'; + if (++buf_ptr >= buf_end) + fill_buffer(); + + switch (*token) { + case '\n': + unary_delim = state->last_u_d; + state->last_nl = true; /* remember that we just had a newline */ + code = (had_eof ? 0 : newline); + + /* + * if data has been exhausted, the newline is a dummy, and we should + * return code to stop + */ + break; + + case '\'': /* start of quoted character */ + case '"': /* start of string */ + qchar = *token; + do { /* copy the string */ + while (1) { /* move one character or [/<char>]<char> */ + if (*buf_ptr == '\n') { + diag2(1, "Unterminated literal"); + goto stop_lit; + } + CHECK_SIZE_TOKEN(2); + *e_token = *buf_ptr++; + if (buf_ptr >= buf_end) + fill_buffer(); + if (*e_token == BACKSLASH) { /* if escape, copy extra char */ + if (*buf_ptr == '\n') /* check for escaped newline */ + ++line_no; + *++e_token = *buf_ptr++; + ++e_token; /* we must increment this again because we + * copied two chars */ + if (buf_ptr >= buf_end) + fill_buffer(); + } + else + break; /* we copied one character */ + } /* end of while (1) */ + } while (*e_token++ != qchar); +stop_lit: + code = ident; + break; + + case ('('): + case ('['): + unary_delim = true; + code = lparen; + break; + + case (')'): + case (']'): + code = rparen; + break; + + case '#': + unary_delim = state->last_u_d; + code = preesc; + break; + + case '?': + unary_delim = true; + code = question; + break; + + case (':'): + code = colon; + unary_delim = true; + break; + + case (';'): + unary_delim = true; + code = semicolon; + break; + + case ('{'): + unary_delim = true; + + /* + * if (state->in_or_st) state->block_init = 1; + */ + /* ? code = state->block_init ? lparen : lbrace; */ + code = lbrace; + break; + + case ('}'): + unary_delim = true; + /* ? code = state->block_init ? rparen : rbrace; */ + code = rbrace; + break; + + case 014: /* a form feed */ + unary_delim = state->last_u_d; + state->last_nl = true; /* remember this so we can set 'state->col_1' + * right */ + code = form_feed; + break; + + case (','): + unary_delim = true; + code = comma; + break; + + case '.': + unary_delim = false; + code = period; + break; + + case '-': + case '+': /* check for -, +, --, ++ */ + code = (state->last_u_d ? unary_op : binary_op); + unary_delim = true; + + if (*buf_ptr == token[0]) { + /* check for doubled character */ + *e_token++ = *buf_ptr++; + /* buffer overflow will be checked at end of loop */ + if (state->last_token == ident || state->last_token == rparen) { + code = (state->last_u_d ? unary_op : postop); + /* check for following ++ or -- */ + unary_delim = false; + } + } + else if (*buf_ptr == '=') + /* check for operator += */ + *e_token++ = *buf_ptr++; + else if (*buf_ptr == '>') { + /* check for operator -> */ + *e_token++ = *buf_ptr++; + unary_delim = false; + code = unary_op; + state->want_blank = false; + } + break; /* buffer overflow will be checked at end of + * switch */ + + case '=': + if (state->in_or_st) + state->block_init = 1; +#ifdef undef + if (chartype[*buf_ptr & 127] == opchar) { /* we have two char assignment */ + e_token[-1] = *buf_ptr++; + if ((e_token[-1] == '<' || e_token[-1] == '>') && e_token[-1] == *buf_ptr) + *e_token++ = *buf_ptr++; + *e_token++ = '='; /* Flip =+ to += */ + *e_token = 0; + } +#else + if (*buf_ptr == '=') {/* == */ + *e_token++ = '='; /* Flip =+ to += */ + buf_ptr++; + *e_token = 0; + } +#endif + code = binary_op; + unary_delim = true; + break; + /* can drop thru!!! */ + + case '>': + case '<': + case '!': /* ops like <, <<, <=, !=, etc */ + if (*buf_ptr == '>' || *buf_ptr == '<' || *buf_ptr == '=') { + *e_token++ = *buf_ptr; + if (++buf_ptr >= buf_end) + fill_buffer(); + } + if (*buf_ptr == '=') + *e_token++ = *buf_ptr++; + code = (state->last_u_d ? unary_op : binary_op); + unary_delim = true; + break; + + case '*': + unary_delim = true; + if (!state->last_u_d) { + if (*buf_ptr == '=') + *e_token++ = *buf_ptr++; + code = binary_op; + break; + } + while (*buf_ptr == '*' || isspace((unsigned char)*buf_ptr)) { + if (*buf_ptr == '*') { + CHECK_SIZE_TOKEN(1); + *e_token++ = *buf_ptr; + } + if (++buf_ptr >= buf_end) + fill_buffer(); + } + code = unary_op; + break; + + default: + if (token[0] == '/' && *buf_ptr == '*') { + /* it is start of comment */ + *e_token++ = '*'; + + if (++buf_ptr >= buf_end) + fill_buffer(); + + code = comment; + unary_delim = state->last_u_d; + break; + } + while (*(e_token - 1) == *buf_ptr || *buf_ptr == '=') { + /* + * handle ||, &&, etc, and also things as in int *****i + */ + CHECK_SIZE_TOKEN(1); + *e_token++ = *buf_ptr; + if (++buf_ptr >= buf_end) + fill_buffer(); + } + code = (state->last_u_d ? unary_op : binary_op); + unary_delim = true; + + + } /* end of switch */ + if (buf_ptr >= buf_end) /* check for input buffer empty */ + fill_buffer(); + state->last_u_d = unary_delim; + CHECK_SIZE_TOKEN(1); + *e_token = '\0'; /* null terminate the token */ + return (code); +} + +void +alloc_typenames(void) +{ + + typenames = (const char **)malloc(sizeof(typenames[0]) * + (typename_count = 16)); + if (typenames == NULL) + err(1, NULL); +} + +void +add_typename(const char *key) +{ + int comparison; + const char *copy; + + if (typename_top + 1 >= typename_count) { + typenames = realloc((void *)typenames, + sizeof(typenames[0]) * (typename_count *= 2)); + if (typenames == NULL) + err(1, NULL); + } + if (typename_top == -1) + typenames[++typename_top] = copy = strdup(key); + else if ((comparison = strcmp(key, typenames[typename_top])) >= 0) { + /* take advantage of sorted input */ + if (comparison == 0) /* remove duplicates */ + return; + typenames[++typename_top] = copy = strdup(key); + } + else { + int p; + + for (p = 0; (comparison = strcmp(key, typenames[p])) > 0; p++) + /* find place for the new key */; + if (comparison == 0) /* remove duplicates */ + return; + memmove(&typenames[p + 1], &typenames[p], + sizeof(typenames[0]) * (++typename_top - p)); + typenames[p] = copy = strdup(key); + } + + if (copy == NULL) + err(1, NULL); +} diff --git a/src/tools/pg_bsd_indent/meson.build b/src/tools/pg_bsd_indent/meson.build new file mode 100644 index 0000000..5545c09 --- /dev/null +++ b/src/tools/pg_bsd_indent/meson.build @@ -0,0 +1,40 @@ +# Copyright (c) 2022-2023, PostgreSQL Global Development Group + +pg_bsd_indent_sources = files( + 'args.c', + 'err.c', + 'indent.c', + 'io.c', + 'lexi.c', + 'parse.c', + 'pr_comment.c', +) + +if host_system == 'windows' + pg_bsd_indent_sources += rc_bin_gen.process(win32ver_rc, extra_args: [ + '--NAME', 'pg_bsd_indent', + '--FILEDESC', 'pg_bsd_indent - indent C code nicely']) +endif + +pg_bsd_indent = executable('pg_bsd_indent', + pg_bsd_indent_sources, + dependencies: [frontend_code], + include_directories: include_directories('.'), + kwargs: default_bin_args + { + 'install': false, +# possibly at some point do this: +# 'install_dir': dir_pgxs / 'src/tools/pg_bsd_indent', + }, +) +bin_targets += pg_bsd_indent + +tests += { + 'name': 'pg_bsd_indent', + 'sd': meson.current_source_dir(), + 'bd': meson.current_build_dir(), + 'tap': { + 'tests': [ + 't/001_pg_bsd_indent.pl', + ], + }, +} diff --git a/src/tools/pg_bsd_indent/parse.c b/src/tools/pg_bsd_indent/parse.c new file mode 100644 index 0000000..e707da6 --- /dev/null +++ b/src/tools/pg_bsd_indent/parse.c @@ -0,0 +1,338 @@ +/*- + * Copyright (c) 1985 Sun Microsystems, Inc. + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif + +#include "c.h" + +#include <err.h> +#include <stdio.h> +#include "indent_globs.h" +#include "indent_codes.h" +#include "indent.h" + +static void reduce(void); + +void +parse(int tk) /* tk: the code for the construct scanned */ +{ + int i; + +#ifdef debug + printf("%2d - %s\n", tk, token); +#endif + + while (ps.p_stack[ps.tos] == ifhead && tk != elselit) { + /* true if we have an if without an else */ + ps.p_stack[ps.tos] = stmt; /* apply the if(..) stmt ::= stmt + * reduction */ + reduce(); /* see if this allows any reduction */ + } + + + switch (tk) { /* go on and figure out what to do with the + * input */ + + case decl: /* scanned a declaration word */ + ps.search_brace = btype_2; + /* indicate that following brace should be on same line */ + if (ps.p_stack[ps.tos] != decl) { /* only put one declaration + * onto stack */ + break_comma = true; /* while in declaration, newline should be + * forced after comma */ + ps.p_stack[++ps.tos] = decl; + ps.il[ps.tos] = ps.i_l_follow; + + if (ps.ljust_decl) {/* only do if we want left justified + * declarations */ + ps.ind_level = 0; + for (i = ps.tos - 1; i > 0; --i) + if (ps.p_stack[i] == decl) + ++ps.ind_level; /* indentation is number of + * declaration levels deep we are */ + ps.i_l_follow = ps.ind_level; + } + } + break; + + case ifstmt: /* scanned if (...) */ + if (ps.p_stack[ps.tos] == elsehead && ps.else_if) /* "else if ..." */ + /* + * Note that the stack pointer here is decremented, effectively + * reducing "else if" to "if". This saves a lot of stack space + * in case of a long "if-else-if ... else-if" sequence. + */ + ps.i_l_follow = ps.il[ps.tos--]; + /* the rest is the same as for dolit and forstmt */ + /* FALLTHROUGH */ + case dolit: /* 'do' */ + case forstmt: /* for (...) */ + ps.p_stack[++ps.tos] = tk; + ps.il[ps.tos] = ps.ind_level = ps.i_l_follow; + ++ps.i_l_follow; /* subsequent statements should be indented 1 */ + ps.search_brace = btype_2; + break; + + case lbrace: /* scanned { */ + break_comma = false; /* don't break comma in an initial list */ + if (ps.p_stack[ps.tos] == stmt || ps.p_stack[ps.tos] == decl + || ps.p_stack[ps.tos] == stmtl) + ++ps.i_l_follow; /* it is a random, isolated stmt group or a + * declaration */ + else { + if (s_code == e_code) { + /* + * only do this if there is nothing on the line + */ + --ps.ind_level; + /* + * it is a group as part of a while, for, etc. + */ + if (ps.p_stack[ps.tos] == swstmt && ps.case_indent >= 1) + --ps.ind_level; + /* + * for a switch, brace should be two levels out from the code + */ + } + } + + ps.p_stack[++ps.tos] = lbrace; + ps.il[ps.tos] = ps.ind_level; + ps.p_stack[++ps.tos] = stmt; + /* allow null stmt between braces */ + ps.il[ps.tos] = ps.i_l_follow; + break; + + case whilestmt: /* scanned while (...) */ + if (ps.p_stack[ps.tos] == dohead) { + /* it is matched with do stmt */ + ps.ind_level = ps.i_l_follow = ps.il[ps.tos]; + ps.p_stack[++ps.tos] = whilestmt; + ps.il[ps.tos] = ps.ind_level = ps.i_l_follow; + } + else { /* it is a while loop */ + ps.p_stack[++ps.tos] = whilestmt; + ps.il[ps.tos] = ps.i_l_follow; + ++ps.i_l_follow; + ps.search_brace = btype_2; + } + + break; + + case elselit: /* scanned an else */ + + if (ps.p_stack[ps.tos] != ifhead) + diag2(1, "Unmatched 'else'"); + else { + ps.ind_level = ps.il[ps.tos]; /* indentation for else should + * be same as for if */ + ps.i_l_follow = ps.ind_level + 1; /* everything following should + * be in 1 level */ + ps.p_stack[ps.tos] = elsehead; + /* remember if with else */ + ps.search_brace = btype_2 | ps.else_if; + } + break; + + case rbrace: /* scanned a } */ + /* stack should have <lbrace> <stmt> or <lbrace> <stmtl> */ + if (ps.tos > 0 && ps.p_stack[ps.tos - 1] == lbrace) { + ps.ind_level = ps.i_l_follow = ps.il[--ps.tos]; + ps.p_stack[ps.tos] = stmt; + } + else + diag2(1, "Statement nesting error"); + break; + + case swstmt: /* had switch (...) */ + ps.p_stack[++ps.tos] = swstmt; + ps.cstk[ps.tos] = case_ind; + /* save current case indent level */ + ps.il[ps.tos] = ps.i_l_follow; + case_ind = ps.i_l_follow + ps.case_indent; /* cases should be one + * level down from + * switch */ + ps.i_l_follow += ps.case_indent + 1; /* statements should be two + * levels in */ + ps.search_brace = btype_2; + break; + + case semicolon: /* this indicates a simple stmt */ + break_comma = false; /* turn off flag to break after commas in a + * declaration */ + ps.p_stack[++ps.tos] = stmt; + ps.il[ps.tos] = ps.ind_level; + break; + + default: /* this is an error */ + diag2(1, "Unknown code to parser"); + return; + + + } /* end of switch */ + + if (ps.tos >= nitems(ps.p_stack) - 1) + errx(1, "Parser stack overflow"); + + reduce(); /* see if any reduction can be done */ + +#ifdef debug + for (i = 1; i <= ps.tos; ++i) + printf("(%d %d)", ps.p_stack[i], ps.il[i]); + printf("\n"); +#endif + + return; +} + +/* + * NAME: reduce + * + * FUNCTION: Implements the reduce part of the parsing algorithm + * + * ALGORITHM: The following reductions are done. Reductions are repeated + * until no more are possible. + * + * Old TOS New TOS + * <stmt> <stmt> <stmtl> + * <stmtl> <stmt> <stmtl> + * do <stmt> "dostmt" + * if <stmt> "ifstmt" + * switch <stmt> <stmt> + * decl <stmt> <stmt> + * "ifelse" <stmt> <stmt> + * for <stmt> <stmt> + * while <stmt> <stmt> + * "dostmt" while <stmt> + * + * On each reduction, ps.i_l_follow (the indentation for the following line) + * is set to the indentation level associated with the old TOS. + * + * PARAMETERS: None + * + * RETURNS: Nothing + * + * GLOBALS: ps.cstk ps.i_l_follow = ps.il ps.p_stack = ps.tos = + * + * CALLS: None + * + * CALLED BY: parse + * + * HISTORY: initial coding November 1976 D A Willcox of CAC + * + */ +/*----------------------------------------------*\ +| REDUCTION PHASE | +\*----------------------------------------------*/ +static void +reduce(void) +{ + int i; + + for (;;) { /* keep looping until there is nothing left to + * reduce */ + + switch (ps.p_stack[ps.tos]) { + + case stmt: + switch (ps.p_stack[ps.tos - 1]) { + + case stmt: + case stmtl: + /* stmtl stmt or stmt stmt */ + ps.p_stack[--ps.tos] = stmtl; + break; + + case dolit: /* <do> <stmt> */ + ps.p_stack[--ps.tos] = dohead; + ps.i_l_follow = ps.il[ps.tos]; + break; + + case ifstmt: + /* <if> <stmt> */ + ps.p_stack[--ps.tos] = ifhead; + for (i = ps.tos - 1; + ( + ps.p_stack[i] != stmt + && + ps.p_stack[i] != stmtl + && + ps.p_stack[i] != lbrace + ); + --i); + ps.i_l_follow = ps.il[i]; + /* + * for the time being, we will assume that there is no else on + * this if, and set the indentation level accordingly. If an + * else is scanned, it will be fixed up later + */ + break; + + case swstmt: + /* <switch> <stmt> */ + case_ind = ps.cstk[ps.tos - 1]; + /* FALLTHROUGH */ + case decl: /* finish of a declaration */ + case elsehead: + /* <<if> <stmt> else> <stmt> */ + case forstmt: + /* <for> <stmt> */ + case whilestmt: + /* <while> <stmt> */ + ps.p_stack[--ps.tos] = stmt; + ps.i_l_follow = ps.il[ps.tos]; + break; + + default: /* <anything else> <stmt> */ + return; + + } /* end of section for <stmt> on top of stack */ + break; + + case whilestmt: /* while (...) on top */ + if (ps.p_stack[ps.tos - 1] == dohead) { + /* it is termination of a do while */ + ps.tos -= 2; + break; + } + else + return; + + default: /* anything else on top */ + return; + + } + } +} diff --git a/src/tools/pg_bsd_indent/pr_comment.c b/src/tools/pg_bsd_indent/pr_comment.c new file mode 100644 index 0000000..a9572b3 --- /dev/null +++ b/src/tools/pg_bsd_indent/pr_comment.c @@ -0,0 +1,354 @@ +/*- + * Copyright (c) 1985 Sun Microsystems, Inc. + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)pr_comment.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif + +#include "c.h" + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "indent_globs.h" +#include "indent_codes.h" +#include "indent.h" +/* + * NAME: + * pr_comment + * + * FUNCTION: + * This routine takes care of scanning and printing comments. + * + * ALGORITHM: + * 1) Decide where the comment should be aligned, and if lines should + * be broken. + * 2) If lines should not be broken and filled, just copy up to end of + * comment. + * 3) If lines should be filled, then scan thru input_buffer copying + * characters to com_buf. Remember where the last blank, tab, or + * newline was. When line is filled, print up to last blank and + * continue copying. + * + * HISTORY: + * November 1976 D A Willcox of CAC Initial coding + * 12/6/76 D A Willcox of CAC Modification to handle + * UNIX-style comments + * + */ + +/* + * this routine processes comments. It makes an attempt to keep comments from + * going over the max line length. If a line is too long, it moves everything + * from the last blank to the next comment line. Blanks and tabs from the + * beginning of the input line are removed + */ + +void +pr_comment(void) +{ + int now_col; /* column we are in now */ + int adj_max_col; /* Adjusted max_col for when we decide to + * spill comments over the right margin */ + char *last_bl; /* points to the last blank in the output + * buffer */ + char *t_ptr; /* used for moving string */ + int break_delim = comment_delimiter_on_blankline; + int l_just_saw_decl = ps.just_saw_decl; + adj_max_col = max_col; + ps.just_saw_decl = 0; + last_bl = NULL; /* no blanks found so far */ + ps.box_com = false; /* at first, assume that we are not in + * a boxed comment or some other + * comment that should not be touched */ + ++ps.out_coms; /* keep track of number of comments */ + + /* Figure where to align and how to treat the comment */ + + if (ps.col_1 && !format_col1_comments) { /* if comment starts in column + * 1 it should not be touched */ + ps.box_com = true; + break_delim = false; + ps.com_col = 1; + } + else { + if (*buf_ptr == '-' || *buf_ptr == '*' || + (*buf_ptr == '\n' && !format_block_comments)) { + ps.box_com = true; /* A comment with a '-' or '*' immediately + * after the /+* is assumed to be a boxed + * comment. A comment with a newline + * immediately after the /+* is assumed to + * be a block comment and is treated as a + * box comment unless format_block_comments + * is nonzero (the default). */ + break_delim = false; + } + if ( /* ps.bl_line && */ (s_lab == e_lab) && (s_code == e_code)) { + /* klg: check only if this line is blank */ + /* + * If this (*and previous lines are*) blank, dont put comment way + * out at left + */ + ps.com_col = (ps.ind_level - ps.unindent_displace) * ps.ind_size + 1; + adj_max_col = block_comment_max_col; + if (ps.com_col <= 1) + ps.com_col = 1 + !format_col1_comments; + } + else { + int target_col; + break_delim = false; + if (s_code != e_code) + target_col = count_spaces(compute_code_target(), s_code); + else { + target_col = 1; + if (s_lab != e_lab) + target_col = count_spaces(compute_label_target(), s_lab); + } + if (s_lab != e_lab && s_lab[1] == 'e' && + (strncmp(s_lab, "#endif", 6) == 0 || + strncmp(s_lab, "#else", 5) == 0)) + ps.com_col = else_endif_com_ind <= target_col + ? target_col + 1 : else_endif_com_ind; + else + ps.com_col = ps.decl_on_line || ps.ind_level == 0 + ? ps.decl_com_ind : ps.com_ind; + if (ps.com_col <= target_col) + ps.com_col = tabsize * (1 + (target_col - 1) / tabsize) + 1; + if (ps.com_col + 24 > adj_max_col) + adj_max_col = ps.com_col + 24; + } + } + if (ps.box_com) { + /* + * Find out how much indentation there was originally, because that + * much will have to be ignored by pad_output() in dump_line(). This + * is a box comment, so nothing changes -- not even indentation. + * + * The comment we're about to read usually comes from in_buffer, + * unless it has been copied into save_com. + */ + char *start; + + start = buf_ptr >= save_com && buf_ptr < save_com + sc_size ? + sc_buf : in_buffer; + ps.n_comment_delta = 1 - count_spaces_until(1, start, buf_ptr - 2); + } + else { + ps.n_comment_delta = 0; + while (*buf_ptr == ' ' || *buf_ptr == '\t') + buf_ptr++; + } + ps.comment_delta = 0; + *e_com++ = '/'; /* put '/' followed by '*' into buffer */ + *e_com++ = '*'; + if (*buf_ptr != ' ' && !ps.box_com) + *e_com++ = ' '; + + /* + * Don't put a break delimiter if this is a one-liner that won't wrap. + */ + if (break_delim) + for (t_ptr = buf_ptr; *t_ptr != '\0' && *t_ptr != '\n'; t_ptr++) { + if (t_ptr >= buf_end) + fill_buffer(); + if (t_ptr[0] == '*' && t_ptr[1] == '/') { + if (adj_max_col >= count_spaces_until(ps.com_col, buf_ptr, t_ptr + 2)) + break_delim = false; + break; + } + } + + if (break_delim) { + char *t = e_com; + e_com = s_com + 2; + *e_com = 0; + if (blanklines_before_blockcomments && ps.last_token != lbrace) + prefix_blankline_requested = 1; + dump_line(); + e_com = s_com = t; + if (!ps.box_com && star_comment_cont) + *e_com++ = ' ', *e_com++ = '*', *e_com++ = ' '; + } + + /* Start to copy the comment */ + + while (1) { /* this loop will go until the comment is + * copied */ + switch (*buf_ptr) { /* this checks for various spcl cases */ + case 014: /* check for a form feed */ + CHECK_SIZE_COM(3); + if (!ps.box_com) { /* in a text comment, break the line here */ + ps.use_ff = true; + /* fix so dump_line uses a form feed */ + dump_line(); + last_bl = NULL; + if (!ps.box_com && star_comment_cont) + *e_com++ = ' ', *e_com++ = '*', *e_com++ = ' '; + while (*++buf_ptr == ' ' || *buf_ptr == '\t') + ; + } + else { + if (++buf_ptr >= buf_end) + fill_buffer(); + *e_com++ = 014; + } + break; + + case '\n': + if (had_eof) { /* check for unexpected eof */ + printf("Unterminated comment\n"); + dump_line(); + return; + } + last_bl = NULL; + CHECK_SIZE_COM(4); + if (ps.box_com || ps.last_nl) { /* if this is a boxed comment, + * we dont ignore the newline */ + if (s_com == e_com) + *e_com++ = ' '; + if (!ps.box_com && e_com - s_com > 3) { + dump_line(); + if (star_comment_cont) + *e_com++ = ' ', *e_com++ = '*', *e_com++ = ' '; + } + dump_line(); + if (!ps.box_com && star_comment_cont) + *e_com++ = ' ', *e_com++ = '*', *e_com++ = ' '; + } + else { + ps.last_nl = 1; + if (*(e_com - 1) == ' ' || *(e_com - 1) == '\t') + last_bl = e_com - 1; + /* + * if there was a space at the end of the last line, remember + * where it was + */ + else { /* otherwise, insert one */ + last_bl = e_com; + *e_com++ = ' '; + } + } + ++line_no; /* keep track of input line number */ + if (!ps.box_com) { + int nstar = 1; + do { /* flush any blanks and/or tabs at start of + * next line */ + if (++buf_ptr >= buf_end) + fill_buffer(); + if (*buf_ptr == '*' && --nstar >= 0) { + if (++buf_ptr >= buf_end) + fill_buffer(); + if (*buf_ptr == '/') + goto end_of_comment; + } + } while (*buf_ptr == ' ' || *buf_ptr == '\t'); + } + else if (++buf_ptr >= buf_end) + fill_buffer(); + break; /* end of case for newline */ + + case '*': /* must check for possibility of being at end + * of comment */ + if (++buf_ptr >= buf_end) /* get to next char after * */ + fill_buffer(); + CHECK_SIZE_COM(4); + if (*buf_ptr == '/') { /* it is the end!!! */ + end_of_comment: + if (++buf_ptr >= buf_end) + fill_buffer(); + if (break_delim) { + if (e_com > s_com + 3) { + dump_line(); + } + else + s_com = e_com; + *e_com++ = ' '; + } + if (e_com[-1] != ' ' && e_com[-1] != '\t' && !ps.box_com) + *e_com++ = ' '; /* ensure blank before end */ + *e_com++ = '*', *e_com++ = '/', *e_com = '\0'; + ps.just_saw_decl = l_just_saw_decl; + return; + } + else /* handle isolated '*' */ + *e_com++ = '*'; + break; + default: /* we have a random char */ + now_col = count_spaces_until(ps.com_col, s_com, e_com); + do { + CHECK_SIZE_COM(1); + *e_com = *buf_ptr++; + if (buf_ptr >= buf_end) + fill_buffer(); + if (*e_com == ' ' || *e_com == '\t') + last_bl = e_com; /* remember we saw a blank */ + ++e_com; + now_col++; + } while (!memchr("*\n\r\b\t", *buf_ptr, 6) && + (now_col <= adj_max_col || !last_bl)); + ps.last_nl = false; + if (now_col > adj_max_col && !ps.box_com && e_com[-1] > ' ') { + /* + * the comment is too long, it must be broken up + */ + if (last_bl == NULL) { + dump_line(); + if (!ps.box_com && star_comment_cont) + *e_com++ = ' ', *e_com++ = '*', *e_com++ = ' '; + break; + } + *e_com = '\0'; + e_com = last_bl; + dump_line(); + if (!ps.box_com && star_comment_cont) + *e_com++ = ' ', *e_com++ = '*', *e_com++ = ' '; + for (t_ptr = last_bl + 1; *t_ptr == ' ' || *t_ptr == '\t'; + t_ptr++) + ; + last_bl = NULL; + /* + * t_ptr will be somewhere between e_com (dump_line() reset) + * and l_com. So it's safe to copy byte by byte from t_ptr + * to e_com without any CHECK_SIZE_COM(). + */ + while (*t_ptr != '\0') { + if (*t_ptr == ' ' || *t_ptr == '\t') + last_bl = e_com; + *e_com++ = *t_ptr++; + } + } + break; + } + } +} diff --git a/src/tools/pg_bsd_indent/t/001_pg_bsd_indent.pl b/src/tools/pg_bsd_indent/t/001_pg_bsd_indent.pl new file mode 100644 index 0000000..fef5c86 --- /dev/null +++ b/src/tools/pg_bsd_indent/t/001_pg_bsd_indent.pl @@ -0,0 +1,53 @@ +# pg_bsd_indent: some simple tests + +# The test cases come from FreeBSD upstream, but this test scaffolding is ours. +# Copyright (c) 2017-2023, PostgreSQL Global Development Group + +use strict; +use warnings; + +use Cwd qw(getcwd); +use File::Copy "cp"; +use File::Spec; + +use PostgreSQL::Test::Utils; +use Test::More; + +# We expect to be started in the source directory (even in a VPATH build); +# we want to run pg_bsd_indent in the tmp_check directory to reduce clutter. +# (Also, it's caller's responsibility that pg_bsd_indent be in the PATH.) +my $src_dir = getcwd; +chdir ${PostgreSQL::Test::Utils::tmp_check}; + +# Basic tests: pg_bsd_indent knows --version but not much else. +program_version_ok('pg_bsd_indent'); + +# Run pg_bsd_indent on pre-fab test cases. +# Any diffs in the generated files will be accumulated here. +my $diffs_file = "test.diffs"; + +# Copy support files to current dir, so *.pro files don't need to know path. +while (my $file = glob("$src_dir/tests/*.list")) +{ + cp($file, ".") || die "cp $file failed: $!"; +} + +while (my $test_src = glob("$src_dir/tests/*.0")) +{ + # extract test basename + my ($volume, $directories, $test) = File::Spec->splitpath($test_src); + $test =~ s/\.0$//; + # run pg_bsd_indent + command_ok( + [ + 'pg_bsd_indent', $test_src, + "$test.out", "-P$src_dir/tests/$test.pro" + ], + "pg_bsd_indent succeeds on $test"); + # check result matches, adding any diff to $diffs_file + my $result = run_log([ 'diff', '-upd', "$test_src.stdout", "$test.out" ], + '>>', $diffs_file); + ok($result, "pg_bsd_indent output matches for $test"); +} + +done_testing(); diff --git a/src/tools/pg_bsd_indent/tests/binary.0 b/src/tools/pg_bsd_indent/tests/binary.0 new file mode 100644 index 0000000..0c5ebf7 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/binary.0 @@ -0,0 +1,9 @@ +#define b00101010 -1 +void t(void) { + unsigned a[] = {0b00101010, 0x00005678, 02, 17U}; + float x[] = {.7f, 0.7f}; + unsigned long ul[] = {0b00001111UL, 0x01010101UL, 02UL, 17UL}; + + if (0 b00101010) + return; +} diff --git a/src/tools/pg_bsd_indent/tests/binary.0.stdout b/src/tools/pg_bsd_indent/tests/binary.0.stdout new file mode 100644 index 0000000..6118ac5 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/binary.0.stdout @@ -0,0 +1,11 @@ +#define b00101010 -1 +void +t(void) +{ + unsigned a[] = {0b00101010, 0x00005678, 02, 17U}; + float x[] = {.7f, 0.7f}; + unsigned long ul[] = {0b00001111UL, 0x01010101UL, 02UL, 17UL}; + + if (0 b00101010) + return; +} diff --git a/src/tools/pg_bsd_indent/tests/comments.0 b/src/tools/pg_bsd_indent/tests/comments.0 new file mode 100644 index 0000000..7b65c2e --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/comments.0 @@ -0,0 +1,52 @@ +/* $FreeBSD$ */ +typedef enum x { + aaaaaaaaaaaaaaaaaaaaaa = 1 << 0, /* test a */ + bbbbbbbbbbbbbbbbb = 1 << 1, /* test b */ + cccccccccccccc = 1 << 1, /* test c */ + dddddddddddddddddddddddddddddd = 1 << 2 /* test d */ +} x; + +/* See r303597, r303598, r309219, and r309343 */ +void t(void) { + /* + * Old indent wrapped the URL near where this sentence ends. + * + * https://www.freebsd.org/cgi/man.cgi?query=indent&apropos=0&sektion=0&manpath=FreeBSD+12-current&arch=default&format=html + */ + + /* + * Old indent did not wrap to column 78 + * + * aaaaaa bbbbbb cccccc dddddd eeeeee ffffff ggggg hhhhh iiiii jjjj kk + */ + + /* + * Old indent unnecessarily removed the star comment continuation on the next line. + * + * *test* + */ + + /* r309219 Go through linked list, freeing from the malloced (t[-1]) address. */ + + /* r309343 */ +} + +int c(void) +{ + if (1) { /*- a christmas tree * + *** + ***** */ + /*- another one * + *** + ***** */ + 7; + } + + if (1) /*- a christmas tree * + *** + ***** */ + /*- another one * + *** + ***** */ + 1; +} diff --git a/src/tools/pg_bsd_indent/tests/comments.0.stdout b/src/tools/pg_bsd_indent/tests/comments.0.stdout new file mode 100644 index 0000000..8ca5aa5 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/comments.0.stdout @@ -0,0 +1,60 @@ +/* $FreeBSD$ */ +typedef enum x { + aaaaaaaaaaaaaaaaaaaaaa = 1 << 0, /* test a */ + bbbbbbbbbbbbbbbbb = 1 << 1, /* test b */ + cccccccccccccc = 1 << 1, /* test c */ + dddddddddddddddddddddddddddddd = 1 << 2 /* test d */ +} x; + +/* See r303597, r303598, r309219, and r309343 */ +void +t(void) +{ + /* + * Old indent wrapped the URL near where this sentence ends. + * + * https://www.freebsd.org/cgi/man.cgi?query=indent&apropos=0&sektion=0&manpath=FreeBSD+12-current&arch=default&format=html + */ + + /* + * Old indent did not wrap to column 78 + * + * aaaaaa bbbbbb cccccc dddddd eeeeee ffffff ggggg hhhhh iiiii jjjj + * kk + */ + + /* + * Old indent unnecessarily removed the star comment continuation on + * the next line. + * + * *test* + */ + + /* + * r309219 Go through linked list, freeing from the malloced (t[-1]) + * address. + */ + + /* r309343 */ +} + +int +c(void) +{ + if (1) { /*- a christmas tree * + *** + ***** */ + /*- another one * + *** + ***** */ + 7; + } + + if (1) /*- a christmas tree * + *** + ***** */ + /*- another one * + *** + ***** */ + 1; +} diff --git a/src/tools/pg_bsd_indent/tests/comments.pro b/src/tools/pg_bsd_indent/tests/comments.pro new file mode 100644 index 0000000..0959711 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/comments.pro @@ -0,0 +1 @@ +-bbb
\ No newline at end of file diff --git a/src/tools/pg_bsd_indent/tests/cppelsecom.0 b/src/tools/pg_bsd_indent/tests/cppelsecom.0 new file mode 100644 index 0000000..79aed65 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/cppelsecom.0 @@ -0,0 +1,7 @@ +#if 1 /* if */ + +#elif defined(test) /* elif */ + +#else /* else */ + +#endif /* endif */ diff --git a/src/tools/pg_bsd_indent/tests/cppelsecom.0.stdout b/src/tools/pg_bsd_indent/tests/cppelsecom.0.stdout new file mode 100644 index 0000000..047fc3d --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/cppelsecom.0.stdout @@ -0,0 +1,7 @@ +#if 1 /* if */ + +#elif defined(test) /* elif */ + +#else /* else */ + +#endif /* endif */ diff --git a/src/tools/pg_bsd_indent/tests/declarations.0 b/src/tools/pg_bsd_indent/tests/declarations.0 new file mode 100644 index 0000000..8419494 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/declarations.0 @@ -0,0 +1,79 @@ +/* $FreeBSD$ */ +/* See r303570 */ + +typedef void (*voidptr) (int *); + +static const struct +{ + double x; + double y, z; +} n[m + 1] = +{ + { + .0, + .9, + 5 + } +}; + +typedef struct Complex +{ + double x; + double y; +} Complex; + +void +t1 (char *a, int b, + void (*fn)(void)) +{} + +void t2 (char *x, int y) +{ + int a, + b, + c; + int + *d, + *e, + *f; + int (*g)(), + (*h)(), + (*i)(); + int j, + k, + l; + int m + ,n + ,o + ; + int chars[ /* push the comma beyond column 74 .... */ ], x; +} + +const int int_minimum_size = +MAXALIGN(offsetof(int, test)) + MAXIMUM_ALIGNOF; + +int *int_create(void) +{ + +} + +static +_attribute_printf(1, 2) +void +print_error(const char *fmt,...) +{ + +} + +static LIST_HEAD(, alq) ald_active; +static int ald_shutingdown = 0; +struct thread *ald_thread; + +static int +do_execve( +struct thread *td, +struct image_args *args, +struct mac *mac_p) +{ + +} diff --git a/src/tools/pg_bsd_indent/tests/declarations.0.stdout b/src/tools/pg_bsd_indent/tests/declarations.0.stdout new file mode 100644 index 0000000..ab5a447 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/declarations.0.stdout @@ -0,0 +1,73 @@ +/* $FreeBSD$ */ +/* See r303570 */ + +typedef void (*voidptr) (int *); + +static const struct { + double x; + double y, z; +} n[m + 1] = +{ + { + .0, + .9, + 5 + } +}; + +typedef struct Complex { + double x; + double y; +} Complex; + +void +t1(char *a, int b, + void (*fn) (void)) +{ +} + +void +t2(char *x, int y) +{ + int a, b, c; + int + *d, *e, *f; + int (*g) (), (*h) (), (*i) (); + int j, k, l; + int m + ,n + ,o + ; + int chars[ /* push the comma beyond column 74 .... */ ], + x; +} + +const int int_minimum_size = +MAXALIGN(offsetof(int, test)) + MAXIMUM_ALIGNOF; + +int * +int_create(void) +{ + +} + +static +_attribute_printf(1, 2) +void +print_error(const char *fmt,...) +{ + +} + +static LIST_HEAD(, alq) ald_active; +static int ald_shutingdown = 0; +struct thread *ald_thread; + +static int +do_execve( + struct thread *td, + struct image_args *args, + struct mac *mac_p) +{ + +} diff --git a/src/tools/pg_bsd_indent/tests/elsecomment.0 b/src/tools/pg_bsd_indent/tests/elsecomment.0 new file mode 100644 index 0000000..c701f1b --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/elsecomment.0 @@ -0,0 +1,42 @@ +/* $FreeBSD$ */ +/* See r303484 and r309342 */ +void t(void) { + /* The two if statements below exercise two different code paths. */ + + if (1) /* a */ int a; else /* b */ int b; + + if (1) /* a */ + int a; + else /* b */ + int b; + + if (1) { + + } + + + + /* Old indent would remove the 3 blank lines above, awaiting "else". */ + + if (1) { + int a; + } + + + else if (0) { + int b; + } + /* test */ + else + ; + + if (1) + ; + else /* Old indent would get very confused here */ + /* We also mustn't assume that there's only one comment */ + /* before the left brace. */ + { + + + } +} diff --git a/src/tools/pg_bsd_indent/tests/elsecomment.0.stdout b/src/tools/pg_bsd_indent/tests/elsecomment.0.stdout new file mode 100644 index 0000000..6c3c10e --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/elsecomment.0.stdout @@ -0,0 +1,47 @@ +/* $FreeBSD$ */ +/* See r303484 and r309342 */ +void +t(void) +{ + /* The two if statements below exercise two different code paths. */ + + if (1) /* a */ + int a; + else /* b */ + int b; + + if (1) /* a */ + int a; + else /* b */ + int b; + + if (1) + { + + } + + + + /* Old indent would remove the 3 blank lines above, awaiting "else". */ + + if (1) + { + int a; + } else if (0) + { + int b; + } + /* test */ + else + ; + + if (1) + ; + else /* Old indent would get very confused here */ + /* We also mustn't assume that there's only one comment */ + /* before the left brace. */ + { + + + } +} diff --git a/src/tools/pg_bsd_indent/tests/elsecomment.pro b/src/tools/pg_bsd_indent/tests/elsecomment.pro new file mode 100644 index 0000000..115c476 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/elsecomment.pro @@ -0,0 +1 @@ +-bl
\ No newline at end of file diff --git a/src/tools/pg_bsd_indent/tests/enum.0 b/src/tools/pg_bsd_indent/tests/enum.0 new file mode 100644 index 0000000..15057dc --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/enum.0 @@ -0,0 +1,6 @@ +typedef enum +{ +PREWARM_PREFETCH, /* comment */ +PREWARM_READ, /* more comment */ +PREWARM_BUFFER /* more comment */ +} PrewarmType; diff --git a/src/tools/pg_bsd_indent/tests/enum.0.stdout b/src/tools/pg_bsd_indent/tests/enum.0.stdout new file mode 100644 index 0000000..fd4653b --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/enum.0.stdout @@ -0,0 +1,5 @@ +typedef enum { + PREWARM_PREFETCH, /* comment */ + PREWARM_READ, /* more comment */ + PREWARM_BUFFER /* more comment */ +} PrewarmType; diff --git a/src/tools/pg_bsd_indent/tests/f_decls.0 b/src/tools/pg_bsd_indent/tests/f_decls.0 new file mode 100644 index 0000000..aeef03b --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/f_decls.0 @@ -0,0 +1,27 @@ +char * x(void) +{ + type identifier; + type *pointer; + unused * value; + (void)unused * value; + + dmax = (double)3 * 10.0; + dmin = (double)dmax * 10.0; + davg = (double)dmax * dmin; + + return NULL; +} + +int * +y(void) { + +} + +int +z(void) { + +} + +int x; +int *y; +int * * * * z; diff --git a/src/tools/pg_bsd_indent/tests/f_decls.0.stdout b/src/tools/pg_bsd_indent/tests/f_decls.0.stdout new file mode 100644 index 0000000..bc21248 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/f_decls.0.stdout @@ -0,0 +1,30 @@ +char * +x(void) +{ + type identifier; + type *pointer; + unused *value; + (void)unused * value; + + dmax = (double)3 * 10.0; + dmin = (double)dmax * 10.0; + davg = (double)dmax * dmin; + + return NULL; +} + +int * +y(void) +{ + +} + +int +z(void) +{ + +} + +int x; +int *y; +int ****z; diff --git a/src/tools/pg_bsd_indent/tests/float.0 b/src/tools/pg_bsd_indent/tests/float.0 new file mode 100644 index 0000000..91f017f --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/float.0 @@ -0,0 +1,6 @@ +/* $FreeBSD$ */ +/* See r303499 */ +void t(void) { + unsigned long x = 314UL; + float y = 3.14f; +} diff --git a/src/tools/pg_bsd_indent/tests/float.0.stdout b/src/tools/pg_bsd_indent/tests/float.0.stdout new file mode 100644 index 0000000..0f21318 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/float.0.stdout @@ -0,0 +1,8 @@ +/* $FreeBSD$ */ +/* See r303499 */ +void +t(void) +{ + unsigned long x = 314UL; + float y = 3.14f; +} diff --git a/src/tools/pg_bsd_indent/tests/label.0 b/src/tools/pg_bsd_indent/tests/label.0 new file mode 100644 index 0000000..7798a4d --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/label.0 @@ -0,0 +1,13 @@ +/* $FreeBSD$ */ +/* See r303489 */ +void t(void) { + switch (1) + { + case 1: /* test */ + case 2: /* test */ + } +CLEANUP: + ; +V: ; +U: ; +} diff --git a/src/tools/pg_bsd_indent/tests/label.0.stdout b/src/tools/pg_bsd_indent/tests/label.0.stdout new file mode 100644 index 0000000..22ec122 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/label.0.stdout @@ -0,0 +1,14 @@ +/* $FreeBSD$ */ +/* See r303489 */ +void +t(void) +{ + switch (1) { + case 1: /* test */ + case 2: /* test */ + } +CLEANUP: + ; +V: ; +U: ; +} diff --git a/src/tools/pg_bsd_indent/tests/label.pro b/src/tools/pg_bsd_indent/tests/label.pro new file mode 100644 index 0000000..75d1fe1 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/label.pro @@ -0,0 +1 @@ +-nut
\ No newline at end of file diff --git a/src/tools/pg_bsd_indent/tests/list_head.0 b/src/tools/pg_bsd_indent/tests/list_head.0 new file mode 100644 index 0000000..35874eb --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/list_head.0 @@ -0,0 +1,15 @@ +/* $FreeBSD$ */ +/* See r309380 */ +static int +do_execve(struct thread *td, +struct image_args *args, +struct mac *mac_p) +{ + +} + +static LIST_HEAD(, alq) ald_active; +static int ald_shuttingdown = 0; +struct thread *ald_thread; + + diff --git a/src/tools/pg_bsd_indent/tests/list_head.0.stdout b/src/tools/pg_bsd_indent/tests/list_head.0.stdout new file mode 100644 index 0000000..2ebcca5 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/list_head.0.stdout @@ -0,0 +1,13 @@ +/* $FreeBSD$ */ +/* See r309380 */ +static int +do_execve(struct thread *td, + struct image_args *args, + struct mac *mac_p) +{ + +} + +static LIST_HEAD(, alq) ald_active; +static int ald_shuttingdown = 0; +struct thread *ald_thread; diff --git a/src/tools/pg_bsd_indent/tests/nsac.0 b/src/tools/pg_bsd_indent/tests/nsac.0 new file mode 100644 index 0000000..449eadf --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/nsac.0 @@ -0,0 +1,4 @@ +/* $FreeBSD$ */ +void t(void) { + int a = (double) 8; +} diff --git a/src/tools/pg_bsd_indent/tests/nsac.0.stdout b/src/tools/pg_bsd_indent/tests/nsac.0.stdout new file mode 100644 index 0000000..78f3b28 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/nsac.0.stdout @@ -0,0 +1,6 @@ +/* $FreeBSD$ */ +void +t(void) +{ + int a = (double)8; +} diff --git a/src/tools/pg_bsd_indent/tests/nsac.pro b/src/tools/pg_bsd_indent/tests/nsac.pro new file mode 100644 index 0000000..6bcbd2b --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/nsac.pro @@ -0,0 +1 @@ +-nsac
\ No newline at end of file diff --git a/src/tools/pg_bsd_indent/tests/offsetof.0 b/src/tools/pg_bsd_indent/tests/offsetof.0 new file mode 100644 index 0000000..078db19 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/offsetof.0 @@ -0,0 +1,5 @@ +/* $FreeBSD$ */ +/* See r303718 */ +void t(void) { + int n = malloc(offsetof(struct s, f) + 1); +} diff --git a/src/tools/pg_bsd_indent/tests/offsetof.0.stdout b/src/tools/pg_bsd_indent/tests/offsetof.0.stdout new file mode 100644 index 0000000..199bf0f --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/offsetof.0.stdout @@ -0,0 +1,7 @@ +/* $FreeBSD$ */ +/* See r303718 */ +void +t(void) +{ + int n = malloc(offsetof(struct s, f) + 1); +} diff --git a/src/tools/pg_bsd_indent/tests/parens.0 b/src/tools/pg_bsd_indent/tests/parens.0 new file mode 100644 index 0000000..366536c --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/parens.0 @@ -0,0 +1,26 @@ +typedef void (*pgstat_page) (pgstattuple_type *, Relation, BlockNumber, + BufferAccessStrategy); + +typedef char (*get_relation_stats_hook_type) (int *root, + unsigned *rte, + char attnum, + float *vardata); + +void similarity_dist(int m) +{ + char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, 3 /* sizeof gmt */ ), + *here)]; + + float4 res = DatumGetFloat4(FunctionCall2(similarity, + (here), + here)); + + if (es->verbose) + { + char *sql = strVal(list_nth(fdw_private, + here)); + } + + rb->allocfunc(1); + rb2.allocfunc(7); +} diff --git a/src/tools/pg_bsd_indent/tests/parens.0.stdout b/src/tools/pg_bsd_indent/tests/parens.0.stdout new file mode 100644 index 0000000..2258f8d --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/parens.0.stdout @@ -0,0 +1,26 @@ +typedef void (*pgstat_page) (pgstattuple_type *, Relation, BlockNumber, + BufferAccessStrategy); + +typedef char (*get_relation_stats_hook_type) (int *root, + unsigned *rte, + char attnum, + float *vardata); + +void +similarity_dist(int m) +{ + char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, 3 /* sizeof gmt */ ), + *here)]; + + float4 res = DatumGetFloat4(FunctionCall2(similarity, + (here), + here)); + + if (es->verbose) { + char *sql = strVal(list_nth(fdw_private, + here)); + } + + rb->allocfunc(1); + rb2.allocfunc(7); +} diff --git a/src/tools/pg_bsd_indent/tests/parens.pro b/src/tools/pg_bsd_indent/tests/parens.pro new file mode 100644 index 0000000..e860911 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/parens.pro @@ -0,0 +1 @@ +-ts4 -i4 -di12 -Tallocfunc diff --git a/src/tools/pg_bsd_indent/tests/sac.0 b/src/tools/pg_bsd_indent/tests/sac.0 new file mode 100644 index 0000000..449eadf --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/sac.0 @@ -0,0 +1,4 @@ +/* $FreeBSD$ */ +void t(void) { + int a = (double) 8; +} diff --git a/src/tools/pg_bsd_indent/tests/sac.0.stdout b/src/tools/pg_bsd_indent/tests/sac.0.stdout new file mode 100644 index 0000000..1849b28 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/sac.0.stdout @@ -0,0 +1,6 @@ +/* $FreeBSD$ */ +void +t(void) +{ + int a = (double) 8; +} diff --git a/src/tools/pg_bsd_indent/tests/sac.pro b/src/tools/pg_bsd_indent/tests/sac.pro new file mode 100644 index 0000000..2b21505 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/sac.pro @@ -0,0 +1 @@ +-sac
\ No newline at end of file diff --git a/src/tools/pg_bsd_indent/tests/struct.0 b/src/tools/pg_bsd_indent/tests/struct.0 new file mode 100644 index 0000000..83142bf --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/struct.0 @@ -0,0 +1,21 @@ +/* $FreeBSD$ */ + +int f(struct x *a); + +/* See r303485 */ +void +t(void) +{ + static const struct { + int a; + int b; + } c[] = { + { D, E }, + { F, G } + }; +} + +void u(struct x a) { + int b; + struct y c = (struct y *)&a; +} diff --git a/src/tools/pg_bsd_indent/tests/struct.0.stdout b/src/tools/pg_bsd_indent/tests/struct.0.stdout new file mode 100644 index 0000000..3861312 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/struct.0.stdout @@ -0,0 +1,23 @@ +/* $FreeBSD$ */ + +int f(struct x *a); + +/* See r303485 */ +void +t(void) +{ + static const struct { + int a; + int b; + } c[] = { + {D, E}, + {F, G} + }; +} + +void +u(struct x a) +{ + int b; + struct y c = (struct y *)&a; +} diff --git a/src/tools/pg_bsd_indent/tests/surplusbad.0 b/src/tools/pg_bsd_indent/tests/surplusbad.0 new file mode 100644 index 0000000..07d0702 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/surplusbad.0 @@ -0,0 +1,9 @@ +/* $FreeBSD$ */ +/* See r303599 */ +#if defined(__i386__) +int a; +#elif defined(__amd64__) +int b; +#else +#error "Port me" +#endif diff --git a/src/tools/pg_bsd_indent/tests/surplusbad.0.stdout b/src/tools/pg_bsd_indent/tests/surplusbad.0.stdout new file mode 100644 index 0000000..b288970 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/surplusbad.0.stdout @@ -0,0 +1,9 @@ +/* $FreeBSD$ */ +/* See r303599 */ +#if defined(__i386__) +int a; +#elif defined(__amd64__) +int b; +#else +#error "Port me" +#endif diff --git a/src/tools/pg_bsd_indent/tests/surplusbad.pro b/src/tools/pg_bsd_indent/tests/surplusbad.pro new file mode 100644 index 0000000..e828c81 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/surplusbad.pro @@ -0,0 +1 @@ +-bad
\ No newline at end of file diff --git a/src/tools/pg_bsd_indent/tests/types_from_file.0 b/src/tools/pg_bsd_indent/tests/types_from_file.0 new file mode 100644 index 0000000..6efca24 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/types_from_file.0 @@ -0,0 +1,3 @@ +/* $FreeBSD$ */ +/* See r303735 */ +void t(a *x, b *y, c *z); diff --git a/src/tools/pg_bsd_indent/tests/types_from_file.0.stdout b/src/tools/pg_bsd_indent/tests/types_from_file.0.stdout new file mode 100644 index 0000000..8776ca6 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/types_from_file.0.stdout @@ -0,0 +1,3 @@ +/* $FreeBSD$ */ +/* See r303735 */ +void t(a *x, b *y, c * z); diff --git a/src/tools/pg_bsd_indent/tests/types_from_file.list b/src/tools/pg_bsd_indent/tests/types_from_file.list new file mode 100644 index 0000000..5f73361 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/types_from_file.list @@ -0,0 +1,2 @@ +b +a
\ No newline at end of file diff --git a/src/tools/pg_bsd_indent/tests/types_from_file.pro b/src/tools/pg_bsd_indent/tests/types_from_file.pro new file mode 100644 index 0000000..39eb911 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/types_from_file.pro @@ -0,0 +1 @@ +-Utypes_from_file.list
\ No newline at end of file diff --git a/src/tools/pg_bsd_indent/tests/wchar.0 b/src/tools/pg_bsd_indent/tests/wchar.0 new file mode 100644 index 0000000..9910e77 --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/wchar.0 @@ -0,0 +1,6 @@ +/* $FreeBSD$ */ +/* See r309220 */ +#include <wchar.h> + +wchar_t *x = L"test"; +wchar_t y = L't'; diff --git a/src/tools/pg_bsd_indent/tests/wchar.0.stdout b/src/tools/pg_bsd_indent/tests/wchar.0.stdout new file mode 100644 index 0000000..92774ab --- /dev/null +++ b/src/tools/pg_bsd_indent/tests/wchar.0.stdout @@ -0,0 +1,6 @@ +/* $FreeBSD$ */ +/* See r309220 */ +#include <wchar.h> + +wchar_t *x = L"test"; +wchar_t y = L't'; diff --git a/src/tools/pgflex b/src/tools/pgflex new file mode 100755 index 0000000..baabe2d --- /dev/null +++ b/src/tools/pgflex @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 + +# +# Wrapper around flex that: +# - ensures lex.backup is created in a private directory +# - can error out if lex.backup is created (--no-backup) +# - can fix warnings (--fix-warnings) +# - works around concurrency issues with win_flex.exe: +# https://github.com/lexxmark/winflexbison/issues/86 + +import argparse +import os +import subprocess +import sys +from os.path import abspath + +parser = argparse.ArgumentParser() + +parser.add_argument('--flex', type=abspath, required=True) +parser.add_argument('--perl', type=abspath, required=True) +parser.add_argument('--builddir', type=abspath, required=True) +parser.add_argument('--srcdir', type=abspath, required=True) +parser.add_argument('--privatedir', type=abspath, required=True, + help='private directory for target') + +parser.add_argument('-o', dest='output_file', type=abspath, required=True, + help='output file') +parser.add_argument('-i', dest='input_file', type=abspath, help='input file') + + +parser.add_argument('--fix-warnings', action='store_true', + help='whether to fix warnings in generated file') +parser.add_argument('--no-backup', action='store_true', + help='whether no_backup is enabled or not') + +parser.add_argument('flex_flags', nargs='*', help='flags passed on to flex') + +args = parser.parse_args() + +# Since 'lex.backup' is always named that and ninja uses the top level build +# directory as current directory for all commands, change directory to +# temporary directory to avoid conflicts between concurrent flex +# invocations. Only unreleased versions of flex have an argument to change +# lex.filename to be named differently. +if not os.path.isdir(args.privatedir): + os.mkdir(args.privatedir) +os.chdir(args.privatedir) + +# win_flex.exe generates names in a racy way, sometimes leading to random +# "error deleting file" failures and sometimes to intermingled file +# contents. Set FLEX_TMP_DIR to the target private directory to avoid +# that. That environment variable isn't consulted on other platforms, so we +# don't even need to make this conditional. +env = {'FLEX_TMP_DIR': args.privatedir} + +# build flex invocation +command = [args.flex, '-o', args.output_file] +if args.no_backup: + command += ['-b'] +command += args.flex_flags +command += [args.input_file] + +# create .c file from .l file +sp = subprocess.run(command, env=env) +if sp.returncode != 0: + sys.exit(sp.returncode) + +# check lex.backup +if args.no_backup: + with open('lex.backup') as lex: + if len(lex.readlines()) != 1: + sys.exit('Scanner requires backup; see lex.backup.') + os.remove('lex.backup') + +# fix warnings +if args.fix_warnings: + fix_warning_script = os.path.join(args.srcdir, + 'src/tools/fix-old-flex-code.pl') + + command = [args.perl, fix_warning_script, args.output_file] + sp = subprocess.run(command) + if sp.returncode != 0: + sys.exit(sp.returncode) + +sys.exit(0) diff --git a/src/tools/pginclude/README b/src/tools/pginclude/README new file mode 100644 index 0000000..712eca7 --- /dev/null +++ b/src/tools/pginclude/README @@ -0,0 +1,110 @@ +src/tools/pginclude/README + +NOTE: headerscheck and cpluspluscheck are in current use, and any +problems they find should generally get fixed. The other scripts +in this directory have not been used in some time, and have issues. +pgrminclude in particular has a history of creating more problems +than it fixes. Be very wary of applying their results blindly. + + +pginclude +========= + +These utilities help clean up #include file usage. They should be run +in this order so that the include files have the proper includes before +the C files are tested. + +pgfixinclude change #include's to <> or "" + +pgcompinclude [-v] + report which #include files can not compile on their own + +pgrminclude [-v] + remove extra #include's + +pgcheckdefines + check for #ifdef tests on symbols defined in files that + weren't included --- this is a necessary sanity check on + pgrminclude + +pgdefine create macro calls for all defines in the file (used by + the above routines) + +It is also a good idea to sort the pg-specific include files in +alphabetic order. This is best done with a text editor. Typical usage +order would be: + + pgfixinclude + sort include references + run multiple times: + pgcompinclude + pgrminclude /src/include + pgrminclude / + pgcheckdefines + +There is a complexity when modifying /src/include. If include file 1 +includes file 2, and file 2 includes file 3, then when file 1 is +processed, it needs only file 2, not file 3. However, if later, include +file 2 is processed, and file 3 is not needed by file 2 and is removed, +file 1 might then need to include file 3. For this reason, the +pgcompinclude and pgrminclude /src/include steps must be run several +times until all includes compile cleanly. + +Also, tests should be done with configure settings of --enable-cassert +and EXEC_BACKEND on and off. It is also wise to test a WIN32 compile. + +Another tools that does a similar task is at: + + http://code.google.com/p/include-what-you-use/ + +An include file visualizer script is available at: + + http://archives.postgresql.org/pgsql-hackers/2011-09/msg00311.php + + +headerscheck +============ + +This script can be run to verify that all Postgres include files meet +the project convention that they will compile "standalone", that is +with no prerequisite headers other than postgres.h (or postgres_fe.h +or c.h, as appropriate). + +A small number of header files are exempted from this requirement, +and are skipped by the headerscheck script. + +The easy way to run the script is to say "make -s headerscheck" in +the top-level build directory after completing a build. You should +have included "--with-perl --with-python" in your configure options, +else you're likely to get errors about related headers not being found. + +A limitation of the current script is that it doesn't know exactly which +headers are for frontend or backend; when in doubt it uses postgres.h as +prerequisite, even if postgres_fe.h or c.h would be more appropriate. +Also note that the contents of macros are not checked; this is intentional. + + +cpluspluscheck +============== + +This script can be run to verify that all Postgres include files meet +the project convention that they will compile as C++ code. Although +the project's coding language is C, some people write extensions in C++, +so it's helpful for include files to be C++-clean. + +A small number of header files are exempted from this requirement, +and are skipped by the cpluspluscheck script. + +The easy way to run the script is to say "make -s cpluspluscheck" in +the top-level build directory after completing a build. You should +have included "--with-perl --with-python" in your configure options, +else you're likely to get errors about related headers not being found. + +If you are using a non-g++-compatible C++ compiler, you may need to +override the script's CXXFLAGS setting by setting a suitable environment +value. + +A limitation of the current script is that it doesn't know exactly which +headers are for frontend or backend; when in doubt it uses postgres.h as +prerequisite, even if postgres_fe.h or c.h would be more appropriate. +Also note that the contents of macros are not checked; this is intentional. diff --git a/src/tools/pginclude/cpluspluscheck b/src/tools/pginclude/cpluspluscheck new file mode 100755 index 0000000..4e09c46 --- /dev/null +++ b/src/tools/pginclude/cpluspluscheck @@ -0,0 +1,222 @@ +#!/bin/sh + +# Check (almost) all PostgreSQL include files for C++ compatibility. +# +# Argument 1 is the top-level source directory, argument 2 the +# top-level build directory (they might be the same). If not set, they +# default to the current directory. +# +# Needs to be run after configuring and creating all generated headers. +# It's advisable to configure --with-perl --with-python, else you're +# likely to get errors from associated headers. +# +# No output if everything is OK, else compiler errors. +# +# src/tools/pginclude/cpluspluscheck +# Copyright (c) 2009-2023, PostgreSQL Global Development Group + +if [ -z "$1" ]; then + srcdir="." +else + srcdir="$1" +fi + +if [ -z "$2" ]; then + builddir="." +else + builddir="$2" +fi + +me=`basename $0` + +# These switches are g++ specific, you may override if necessary. +CXXFLAGS=${CXXFLAGS:- -fsyntax-only -Wall} + +# Pull some info from configure's results. +MGLOB="$builddir/src/Makefile.global" +CPPFLAGS=`sed -n 's/^CPPFLAGS[ ]*=[ ]*//p' "$MGLOB"` +CXX=`sed -n 's/^CXX[ ]*=[ ]*//p' "$MGLOB"` +perl_includespec=`sed -n 's/^perl_includespec[ ]*=[ ]*//p' "$MGLOB"` +python_includespec=`sed -n 's/^python_includespec[ ]*=[ ]*//p' "$MGLOB"` + +# Extract any -I and -D switches from CPPFLAGS. +# (If necessary, user can pass more switches by presetting EXTRAFLAGS.) +for flag in $CPPFLAGS; do + case $flag in + -I*|-D*) EXTRAFLAGS="$EXTRAFLAGS $flag";; + esac +done + +# Create temp directory. +tmp=`mktemp -d /tmp/$me.XXXXXX` + +trap "ret=$?; rm -rf $tmp; exit $ret" 0 1 2 3 15 + +exit_status=0 + +# Scan all of src/ and contrib/ for header files. +for f in `cd "$srcdir" && find src contrib -name '*.h' -print` +do + # Ignore files that are unportable or intentionally not standalone. + + # These files are platform-specific, and c.h will include the + # one that's relevant for our current platform anyway. + test "$f" = src/include/port/aix.h && continue + test "$f" = src/include/port/cygwin.h && continue + test "$f" = src/include/port/darwin.h && continue + test "$f" = src/include/port/freebsd.h && continue + test "$f" = src/include/port/linux.h && continue + test "$f" = src/include/port/netbsd.h && continue + test "$f" = src/include/port/openbsd.h && continue + test "$f" = src/include/port/solaris.h && continue + test "$f" = src/include/port/win32.h && continue + + # Additional Windows-specific headers. + test "$f" = src/include/port/win32_port.h && continue + test "$f" = src/include/port/win32/netdb.h && continue + test "$f" = src/include/port/win32/sys/resource.h && continue + test "$f" = src/include/port/win32/sys/socket.h && continue + test "$f" = src/include/port/win32_msvc/dirent.h && continue + test "$f" = src/include/port/win32_msvc/utime.h && continue + test "$f" = src/include/port/win32ntdll.h && continue + test "$f" = src/port/pthread-win32.h && continue + + # Likewise, these files are platform-specific, and the one + # relevant to our platform will be included by atomics.h. + test "$f" = src/include/port/atomics/arch-arm.h && continue + test "$f" = src/include/port/atomics/arch-hppa.h && continue + test "$f" = src/include/port/atomics/arch-ppc.h && continue + test "$f" = src/include/port/atomics/arch-x86.h && continue + test "$f" = src/include/port/atomics/fallback.h && continue + test "$f" = src/include/port/atomics/generic.h && continue + test "$f" = src/include/port/atomics/generic-acc.h && continue + test "$f" = src/include/port/atomics/generic-gcc.h && continue + test "$f" = src/include/port/atomics/generic-msvc.h && continue + test "$f" = src/include/port/atomics/generic-sunpro.h && continue + + # sepgsql.h depends on headers that aren't there on most platforms. + test "$f" = contrib/sepgsql/sepgsql.h && continue + + # nodetags.h cannot be included standalone: it's just a code fragment. + test "$f" = src/include/nodes/nodetags.h && continue + test "$f" = src/backend/nodes/nodetags.h && continue + + # These files are not meant to be included standalone, because + # they contain lists that might have multiple use-cases. + test "$f" = src/include/access/rmgrlist.h && continue + test "$f" = src/include/parser/kwlist.h && continue + test "$f" = src/pl/plpgsql/src/pl_reserved_kwlist.h && continue + test "$f" = src/pl/plpgsql/src/pl_unreserved_kwlist.h && continue + test "$f" = src/interfaces/ecpg/preproc/c_kwlist.h && continue + test "$f" = src/interfaces/ecpg/preproc/ecpg_kwlist.h && continue + test "$f" = src/include/regex/regerrs.h && continue + test "$f" = src/include/tcop/cmdtaglist.h && continue + test "$f" = src/pl/plpgsql/src/plerrcodes.h && continue + test "$f" = src/pl/plpython/spiexceptions.h && continue + test "$f" = src/pl/tcl/pltclerrcodes.h && continue + + # Also not meant to be included standalone. + test "$f" = src/include/common/unicode_nonspacing_table.h && continue + test "$f" = src/include/common/unicode_east_asian_fw_table.h && continue + + # We can't make these Bison output files compilable standalone + # without using "%code require", which old Bison versions lack. + # parser/gram.h will be included by parser/gramparse.h anyway. + test "$f" = contrib/cube/cubeparse.h && continue + test "$f" = contrib/seg/segparse.h && continue + test "$f" = src/backend/bootstrap/bootparse.h && continue + test "$f" = src/backend/parser/gram.h && continue + test "$f" = src/backend/replication/repl_gram.h && continue + test "$f" = src/backend/replication/syncrep_gram.h && continue + test "$f" = src/backend/utils/adt/jsonpath_gram.h && continue + test "$f" = src/bin/pgbench/exprparse.h && continue + test "$f" = src/pl/plpgsql/src/pl_gram.h && continue + test "$f" = src/interfaces/ecpg/preproc/preproc.h && continue + test "$f" = src/test/isolation/specparse.h && continue + + # ppport.h is not under our control, so we can't make it standalone. + test "$f" = src/pl/plperl/ppport.h && continue + + # regression.h is not actually C, but ECPG code. + test "$f" = src/interfaces/ecpg/test/regression.h && continue + # printf_hack.h produces "unused function" warnings. + test "$f" = src/interfaces/ecpg/test/printf_hack.h && continue + + # pg_trace.h and utils/probes.h can include sys/sdt.h from SystemTap, + # which itself contains C++ code and so won't compile with a C++ + # compiler under extern "C" linkage. + test "$f" = src/include/pg_trace.h && continue + test "$f" = src/include/utils/probes.h && continue + + # pg_dump is not C++-clean because it uses "public" and "namespace" + # as field names, which is unfortunate but we won't change it now. + test "$f" = src/bin/pg_dump/compress_gzip.h && continue + test "$f" = src/bin/pg_dump/compress_io.h && continue + test "$f" = src/bin/pg_dump/compress_lz4.h && continue + test "$f" = src/bin/pg_dump/compress_none.h && continue + test "$f" = src/bin/pg_dump/compress_zstd.h && continue + test "$f" = src/bin/pg_dump/parallel.h && continue + test "$f" = src/bin/pg_dump/pg_backup_archiver.h && continue + test "$f" = src/bin/pg_dump/pg_dump.h && continue + + # OK, create .c file to include this .h file. + { + echo 'extern "C" {' + # Ideally we'd pre-include only the appropriate one of + # postgres.h, postgres_fe.h, or c.h. We don't always have enough + # info to guess which, but in some subdirectories there's a + # reasonable choice to make, and otherwise we use postgres.h. + # Also, those three files should compile with no pre-include, as + # should src/interfaces headers meant to be exposed to clients. + case "$f" in + src/include/postgres.h) ;; + src/include/postgres_fe.h) ;; + src/include/c.h) ;; + src/interfaces/libpq/libpq-fe.h) ;; + src/interfaces/libpq/libpq-events.h) ;; + src/interfaces/ecpg/ecpglib/ecpglib_extern.h) + echo '#include "postgres_fe.h"' ;; + src/interfaces/ecpg/ecpglib/*) ;; + src/interfaces/*) + echo '#include "postgres_fe.h"' ;; + src/bin/*) + echo '#include "postgres_fe.h"' ;; + src/fe_utils/*) + echo '#include "postgres_fe.h"' ;; + src/port/*) ;; + src/common/*) + echo '#include "c.h"' ;; + *) + echo '#include "postgres.h"' ;; + esac + echo "#include \"$f\"" + echo '};' + } >$tmp/test.cpp + + # Some subdirectories need extra -I switches. + case "$f" in + src/pl/plperl/*) + EXTRAINCLUDES="$perl_includespec" ;; + src/pl/plpython/*) + EXTRAINCLUDES="$python_includespec" ;; + src/interfaces/ecpg/*) + EXTRAINCLUDES="-I $builddir/src/interfaces/ecpg/include -I $srcdir/src/interfaces/ecpg/include" ;; + src/backend/parser/*) + EXTRAINCLUDES="-I $builddir/src/backend/parser/" ;; + src/backend/utils/adt/*) + EXTRAINCLUDES="-I $builddir/src/backend/utils/adt/" ;; + *) + EXTRAINCLUDES="" ;; + esac + + # Run the test. + if ! ${CXX:-g++} -I $builddir -I $srcdir \ + -I $builddir/src/include -I $srcdir/src/include \ + -I $builddir/src/interfaces/libpq -I $srcdir/src/interfaces/libpq \ + $EXTRAINCLUDES $EXTRAFLAGS $CXXFLAGS -c $tmp/test.cpp + then + exit_status=1 + fi +done + +exit $exit_status diff --git a/src/tools/pginclude/headerscheck b/src/tools/pginclude/headerscheck new file mode 100755 index 0000000..8dee1b5 --- /dev/null +++ b/src/tools/pginclude/headerscheck @@ -0,0 +1,201 @@ +#!/bin/sh + +# Check (almost) all PostgreSQL include files for standalone build. +# +# Argument 1 is the top-level source directory, argument 2 the +# top-level build directory (they might be the same). If not set, they +# default to the current directory. +# +# Needs to be run after configuring and creating all generated headers. +# It's advisable to configure --with-perl --with-python, else you're +# likely to get errors from associated headers. +# +# No output if everything is OK, else compiler errors. +# +# src/tools/pginclude/headerscheck +# Copyright (c) 2009-2023, PostgreSQL Global Development Group + +if [ -z "$1" ]; then + srcdir="." +else + srcdir="$1" +fi + +if [ -z "$2" ]; then + builddir="." +else + builddir="$2" +fi + +me=`basename $0` + +# Pull some info from configure's results. +MGLOB="$builddir/src/Makefile.global" +CPPFLAGS=`sed -n 's/^CPPFLAGS[ ]*=[ ]*//p' "$MGLOB"` +CFLAGS=`sed -n 's/^CFLAGS[ ]*=[ ]*//p' "$MGLOB"` +CC=`sed -n 's/^CC[ ]*=[ ]*//p' "$MGLOB"` +PG_SYSROOT=`sed -n 's/^PG_SYSROOT[ ]*=[ ]*//p' "$MGLOB"` +perl_includespec=`sed -n 's/^perl_includespec[ ]*=[ ]*//p' "$MGLOB"` +python_includespec=`sed -n 's/^python_includespec[ ]*=[ ]*//p' "$MGLOB"` + +# needed on Darwin +CPPFLAGS=`echo "$CPPFLAGS" | sed "s|\\\$(PG_SYSROOT)|$PG_SYSROOT|g"` + +# (EXTRAFLAGS is not set here, but user can pass it in if need be.) + +# Create temp directory. +tmp=`mktemp -d /tmp/$me.XXXXXX` + +trap "ret=$?; rm -rf $tmp; exit $ret" 0 1 2 3 15 + +exit_status=0 + +# Scan all of src/ and contrib/ for header files. +for f in `cd "$srcdir" && find src contrib -name '*.h' -print` +do + # Ignore files that are unportable or intentionally not standalone. + + # These files are platform-specific, and c.h will include the + # one that's relevant for our current platform anyway. + test "$f" = src/include/port/aix.h && continue + test "$f" = src/include/port/cygwin.h && continue + test "$f" = src/include/port/darwin.h && continue + test "$f" = src/include/port/freebsd.h && continue + test "$f" = src/include/port/linux.h && continue + test "$f" = src/include/port/netbsd.h && continue + test "$f" = src/include/port/openbsd.h && continue + test "$f" = src/include/port/solaris.h && continue + test "$f" = src/include/port/win32.h && continue + + # Additional Windows-specific headers. + test "$f" = src/include/port/win32_port.h && continue + test "$f" = src/include/port/win32/netdb.h && continue + test "$f" = src/include/port/win32/sys/socket.h && continue + test "$f" = src/include/port/win32_msvc/dirent.h && continue + test "$f" = src/include/port/win32_msvc/utime.h && continue + test "$f" = src/include/port/win32ntdll.h && continue + test "$f" = src/port/pthread-win32.h && continue + + # Likewise, these files are platform-specific, and the one + # relevant to our platform will be included by atomics.h. + test "$f" = src/include/port/atomics/arch-arm.h && continue + test "$f" = src/include/port/atomics/arch-hppa.h && continue + test "$f" = src/include/port/atomics/arch-ppc.h && continue + test "$f" = src/include/port/atomics/arch-x86.h && continue + test "$f" = src/include/port/atomics/fallback.h && continue + test "$f" = src/include/port/atomics/generic.h && continue + test "$f" = src/include/port/atomics/generic-acc.h && continue + test "$f" = src/include/port/atomics/generic-gcc.h && continue + test "$f" = src/include/port/atomics/generic-msvc.h && continue + test "$f" = src/include/port/atomics/generic-sunpro.h && continue + + # sepgsql.h depends on headers that aren't there on most platforms. + test "$f" = contrib/sepgsql/sepgsql.h && continue + + # nodetags.h cannot be included standalone: it's just a code fragment. + test "$f" = src/include/nodes/nodetags.h && continue + test "$f" = src/backend/nodes/nodetags.h && continue + + # These files are not meant to be included standalone, because + # they contain lists that might have multiple use-cases. + test "$f" = src/include/access/rmgrlist.h && continue + test "$f" = src/include/parser/kwlist.h && continue + test "$f" = src/pl/plpgsql/src/pl_reserved_kwlist.h && continue + test "$f" = src/pl/plpgsql/src/pl_unreserved_kwlist.h && continue + test "$f" = src/interfaces/ecpg/preproc/c_kwlist.h && continue + test "$f" = src/interfaces/ecpg/preproc/ecpg_kwlist.h && continue + test "$f" = src/include/regex/regerrs.h && continue + test "$f" = src/include/tcop/cmdtaglist.h && continue + test "$f" = src/pl/plpgsql/src/plerrcodes.h && continue + test "$f" = src/pl/plpython/spiexceptions.h && continue + test "$f" = src/pl/tcl/pltclerrcodes.h && continue + + # Also not meant to be included standalone. + test "$f" = src/include/common/unicode_nonspacing_table.h && continue + test "$f" = src/include/common/unicode_east_asian_fw_table.h && continue + + # We can't make these Bison output files compilable standalone + # without using "%code require", which old Bison versions lack. + # parser/gram.h will be included by parser/gramparse.h anyway. + test "$f" = contrib/cube/cubeparse.h && continue + test "$f" = contrib/seg/segparse.h && continue + test "$f" = src/backend/bootstrap/bootparse.h && continue + test "$f" = src/backend/parser/gram.h && continue + test "$f" = src/backend/replication/repl_gram.h && continue + test "$f" = src/backend/replication/syncrep_gram.h && continue + test "$f" = src/backend/utils/adt/jsonpath_gram.h && continue + test "$f" = src/bin/pgbench/exprparse.h && continue + test "$f" = src/pl/plpgsql/src/pl_gram.h && continue + test "$f" = src/interfaces/ecpg/preproc/preproc.h && continue + test "$f" = src/test/isolation/specparse.h && continue + + # This produces a "no previous prototype" warning. + test "$f" = src/include/storage/checksum_impl.h && continue + + # ppport.h is not under our control, so we can't make it standalone. + test "$f" = src/pl/plperl/ppport.h && continue + + # regression.h is not actually C, but ECPG code. + test "$f" = src/interfaces/ecpg/test/regression.h && continue + # printf_hack.h produces "unused function" warnings. + test "$f" = src/interfaces/ecpg/test/printf_hack.h && continue + + # OK, create .c file to include this .h file. + { + # Ideally we'd pre-include only the appropriate one of + # postgres.h, postgres_fe.h, or c.h. We don't always have enough + # info to guess which, but in some subdirectories there's a + # reasonable choice to make, and otherwise we use postgres.h. + # Also, those three files should compile with no pre-include, as + # should src/interfaces headers meant to be exposed to clients. + case "$f" in + src/include/postgres.h) ;; + src/include/postgres_fe.h) ;; + src/include/c.h) ;; + src/interfaces/libpq/libpq-fe.h) ;; + src/interfaces/libpq/libpq-events.h) ;; + src/interfaces/ecpg/ecpglib/ecpglib_extern.h) + echo '#include "postgres_fe.h"' ;; + src/interfaces/ecpg/ecpglib/*) ;; + src/interfaces/*) + echo '#include "postgres_fe.h"' ;; + src/bin/*) + echo '#include "postgres_fe.h"' ;; + src/fe_utils/*) + echo '#include "postgres_fe.h"' ;; + src/port/*) ;; + src/common/*) + echo '#include "c.h"' ;; + *) + echo '#include "postgres.h"' ;; + esac + echo "#include \"$f\"" + } >$tmp/test.c + + # Some subdirectories need extra -I switches. + case "$f" in + src/pl/plperl/*) + EXTRAINCLUDES="$perl_includespec" ;; + src/pl/plpython/*) + EXTRAINCLUDES="$python_includespec" ;; + src/interfaces/ecpg/*) + EXTRAINCLUDES="-I $builddir/src/interfaces/ecpg/include -I $srcdir/src/interfaces/ecpg/include" ;; + src/backend/parser/*) + EXTRAINCLUDES="-I $builddir/src/backend/parser/" ;; + src/backend/utils/adt/*) + EXTRAINCLUDES="-I $builddir/src/backend/utils/adt/" ;; + *) + EXTRAINCLUDES="" ;; + esac + + # Run the test. + if ! ${CC:-gcc} $CPPFLAGS $CFLAGS -I $builddir -I $srcdir \ + -I $builddir/src/include -I $srcdir/src/include \ + -I $builddir/src/interfaces/libpq -I $srcdir/src/interfaces/libpq \ + $EXTRAINCLUDES $EXTRAFLAGS -c $tmp/test.c -o $tmp/test.o + then + exit_status=1 + fi +done + +exit $exit_status diff --git a/src/tools/pginclude/pgcheckdefines b/src/tools/pginclude/pgcheckdefines new file mode 100755 index 0000000..a9fe79e --- /dev/null +++ b/src/tools/pginclude/pgcheckdefines @@ -0,0 +1,305 @@ +#! /usr/bin/perl + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +# +# This script looks for symbols that are referenced in #ifdef or defined() +# tests without having #include'd the file that defines them. Since this +# situation won't necessarily lead to any compiler message, it seems worth +# having an automated check for it. In particular, use this to audit the +# results of pgrminclude! +# +# Usage: configure and build a PG source tree (non-VPATH), then start this +# script at the top level. It's best to enable as many configure options +# as you can, especially --enable-cassert which is known to affect include +# requirements. NB: you MUST use gcc, unless you have another compiler that +# can be persuaded to spit out the names of referenced include files. +# +# The results are necessarily platform-dependent, so use care in interpreting +# them. We try to process all .c files, even those not intended for the +# current platform, so there will be some phony failures. +# +# src/tools/pginclude/pgcheckdefines +# + +use strict; +use warnings; + +use Cwd; +use File::Basename; + +my $topdir = cwd(); + +# Programs to use +my $FIND = "find"; +my $MAKE = "make"; + +# +# Build arrays of all the .c and .h files in the tree +# +# We ignore .h files under src/include/port/, since only the one exposed as +# src/include/port.h is interesting. (XXX Windows ports have additional +# files there?) Ditto for .h files in src/backend/port/ subdirectories. +# Including these .h files would clutter the list of define'd symbols and +# cause a lot of false-positive results. +# +my (@cfiles, @hfiles); + +open my $pipe, '-|', "$FIND * -type f -name '*.c'" + or die "can't fork: $!"; +while (<$pipe>) +{ + chomp; + push @cfiles, $_; +} +close $pipe or die "$FIND failed: $!"; + +open $pipe, '-|', "$FIND * -type f -name '*.h'" + or die "can't fork: $!"; +while (<$pipe>) +{ + chomp; + push @hfiles, $_ + unless m|^src/include/port/| + || m|^src/backend/port/\w+/|; +} +close $pipe or die "$FIND failed: $!"; + +# +# For each .h file, extract all the symbols it #define's, and add them to +# a hash table. To cover the possibility of multiple .h files defining +# the same symbol, we make each hash entry a hash of filenames. +# +my %defines; + +foreach my $hfile (@hfiles) +{ + open my $fh, '<', $hfile + or die "can't open $hfile: $!"; + while (<$fh>) + { + if (m/^\s*#\s*define\s+(\w+)/) + { + $defines{$1}{$hfile} = 1; + } + } + close $fh; +} + +# +# For each file (both .h and .c), run the compiler to get a list of what +# files it #include's. Then extract all the symbols it tests for defined-ness, +# and check each one against the previously built hashtable. +# +foreach my $file (@hfiles, @cfiles) +{ + my ($fname, $fpath) = fileparse($file); + chdir $fpath or die "can't chdir to $fpath: $!"; + + # + # Ask 'make' to parse the makefile so we can get the correct flags to + # use. CPPFLAGS in particular varies for each subdirectory. If we are + # processing a .h file, we might be in a subdirectory that has no + # Makefile, in which case we have to fake it. Note that there seems + # no easy way to prevent make from recursing into subdirectories and + # hence printing multiple definitions --- we keep the last one, which + # should come from the current Makefile. + # + my $MAKECMD; + + if (-f "Makefile" || -f "GNUmakefile") + { + $MAKECMD = "$MAKE -qp"; + } + else + { + my $subdir = $fpath; + chop $subdir; + my $top_builddir = ".."; + my $tmp = $fpath; + while (($tmp = dirname($tmp)) ne '.') + { + $top_builddir = $top_builddir . "/.."; + } + $MAKECMD = + "$MAKE -qp 'subdir=$subdir' 'top_builddir=$top_builddir' -f '$top_builddir/src/Makefile.global'"; + } + + my ($CPPFLAGS, $CFLAGS, $CFLAGS_SL, $PTHREAD_CFLAGS, $CC); + + open $pipe, '-|', "$MAKECMD" + or die "can't fork: $!"; + while (<$pipe>) + { + if (m/^CPPFLAGS :?= (.*)/) + { + $CPPFLAGS = $1; + } + elsif (m/^CFLAGS :?= (.*)/) + { + $CFLAGS = $1; + } + elsif (m/^CFLAGS_SL :?= (.*)/) + { + $CFLAGS_SL = $1; + } + elsif (m/^PTHREAD_CFLAGS :?= (.*)/) + { + $PTHREAD_CFLAGS = $1; + } + elsif (m/^CC :?= (.*)/) + { + $CC = $1; + } + } + + # If make exits with status 1, it's not an error, it just means make + # thinks some files may not be up-to-date. Only complain on status 2. + close PIPE; + die "$MAKE failed in $fpath\n" if $? != 0 && $? != 256; + + # Expand out stuff that might be referenced in CFLAGS + $CFLAGS =~ s/\$\(CFLAGS_SL\)/$CFLAGS_SL/; + $CFLAGS =~ s/\$\(PTHREAD_CFLAGS\)/$PTHREAD_CFLAGS/; + + # + # Run the compiler (which had better be gcc) to get the inclusions. + # "gcc -H" reports inclusions on stderr as "... filename" where the + # number of dots varies according to nesting depth. + # + my @includes = (); + my $COMPILE = "$CC $CPPFLAGS $CFLAGS -H -E $fname"; + open $pipe, '-|', "$COMPILE 2>&1 >/dev/null" + or die "can't fork: $!"; + while (<$pipe>) + { + if (m/^\.+ (.*)/) + { + my $include = $1; + + # Ignore system headers (absolute paths); but complain if a + # .c file includes a system header before any PG header. + if ($include =~ m|^/|) + { + warn "$file includes $include before any Postgres inclusion\n" + if $#includes == -1 && $file =~ m/\.c$/; + next; + } + + # Strip any "./" (assume this appears only at front) + $include =~ s|^\./||; + + # Make path relative to top of tree + my $ipath = $fpath; + while ($include =~ s|^\.\./||) + { + $ipath = dirname($ipath) . "/"; + } + $ipath =~ s|^\./||; + push @includes, $ipath . $include; + } + else + { + warn "$CC: $_"; + } + } + + # The compiler might fail, particularly if we are checking a file that's + # not supposed to be compiled at all on the current platform, so don't + # quit on nonzero status. + close PIPE or warn "$COMPILE failed in $fpath\n"; + + # + # Scan the file to find #ifdef, #ifndef, and #if defined() constructs + # We assume #ifdef isn't continued across lines, and that defined(foo) + # isn't split across lines either + # + open my $fh, '<', $fname + or die "can't open $file: $!"; + my $inif = 0; + while (<$fh>) + { + my $line = $_; + if ($line =~ m/^\s*#\s*ifdef\s+(\w+)/) + { + checkit($file, $1, @includes); + } + if ($line =~ m/^\s*#\s*ifndef\s+(\w+)/) + { + checkit($file, $1, @includes); + } + if ($line =~ m/^\s*#\s*if\s+/) + { + $inif = 1; + } + if ($inif) + { + while ($line =~ s/\bdefined(\s+|\s*\(\s*)(\w+)//) + { + checkit($file, $2, @includes); + } + if (!($line =~ m/\\$/)) + { + $inif = 0; + } + } + } + close $fh; + + chdir $topdir or die "can't chdir to $topdir: $!"; +} + +exit 0; + +# Check an is-defined reference +sub checkit +{ + my ($file, $symbol, @includes) = @_; + + # Ignore if symbol isn't defined in any PG include files + if (!defined $defines{$symbol}) + { + return; + } + + # + # Try to match source(s) of symbol to the inclusions of the current file + # (including itself). We consider it OK if any one matches. + # + # Note: these tests aren't bulletproof; in theory the inclusion might + # occur after the use of the symbol. Given our normal file layout, + # however, the risk is minimal. + # + foreach my $deffile (keys %{ $defines{$symbol} }) + { + return if $deffile eq $file; + foreach my $reffile (@includes) + { + return if $deffile eq $reffile; + } + } + + # + # If current file is a .h file, it's OK for it to assume that one of the + # base headers (postgres.h or postgres_fe.h) has been included. + # + if ($file =~ m/\.h$/) + { + foreach my $deffile (keys %{ $defines{$symbol} }) + { + return if $deffile eq 'src/include/c.h'; + return if $deffile eq 'src/include/postgres.h'; + return if $deffile eq 'src/include/postgres_fe.h'; + return if $deffile eq 'src/include/pg_config.h'; + return if $deffile eq 'src/include/pg_config_manual.h'; + } + } + + # + my @places = keys %{ $defines{$symbol} }; + print "$file references $symbol, defined in @places\n"; + + # print "includes: @includes\n"; + + return; +} diff --git a/src/tools/pginclude/pgcompinclude b/src/tools/pginclude/pgcompinclude new file mode 100755 index 0000000..12169db --- /dev/null +++ b/src/tools/pginclude/pgcompinclude @@ -0,0 +1,47 @@ +: +# report which #include files can not compile on their own +# takes -v option to display compile failure message and line numbers +# src/tools/pginclude/pgcompinclude + +: ${CC:=cc} +: ${PGSRC:=src} + +if ! pgdefine +then echo "pgdefine must be in your PATH" 1>&2 + exit 1 +fi + +trap "rm -f /tmp/$$.c /tmp/$$.o /tmp/$$ /tmp/$$a" 0 1 2 3 15 +find . \( -name .git -a -prune \) -o -name '*.h' -type f -print | while read FILE +do + sed 's/->[a-zA-Z0-9_\.]*//g' "$FILE" >/tmp/$$a + echo "#include \"postgres.h\"" >/tmp/$$.c + + # suppress fcinfo errors + echo "struct {Datum arg[1];} *fcinfo;" >>/tmp/$$.c + + echo "#include \"/tmp/$$a\"" >>/tmp/$$.c + + echo "Datum include_test(void);" >>/tmp/$$.c + echo "Datum include_test() {" >>/tmp/$$.c + + pgdefine "$FILE" >>/tmp/$$.c + + echo "return (Datum)0;" >>/tmp/$$.c + echo "}" >>/tmp/$$.c + + # Use -O1 to get warnings only generated by optimization, + # but -O2 is too slow. + $CC -fsyntax-only -Werror -Wall -Wmissing-prototypes \ + -Wmissing-declarations -I$PGSRC/include -I$PGSRC/backend \ + -I$PGSRC/interfaces/libpq -I`dirname $FILE` $CFLAGS -O1 -c /tmp/$$.c \ + -o /tmp/$$.o >/tmp/$$ 2>&1 + if [ "$?" -ne 0 ] + then echo "$FILE" + if [ "$1" = "-v" ] + then cat /tmp/$$ + nl /tmp/$$.c + echo + fi + fi +done diff --git a/src/tools/pginclude/pgdefine b/src/tools/pginclude/pgdefine new file mode 100755 index 0000000..242d035 --- /dev/null +++ b/src/tools/pginclude/pgdefine @@ -0,0 +1,25 @@ +: +# create macro calls for all defines in the file + +# src/tools/pginclude/pgdefine + +trap "rm -f /tmp/$$" 0 1 2 3 15 +for FILE +do + cat "$FILE" | grep "^#define" >/tmp/$$ + cat /tmp/$$ | sed -n 's/^#define[ ][ ]*\([a-zA-Z0-9_]*\)[ ][ ]*[^ ].*\\\\$/\1;/p' + cat /tmp/$$ | sed -n 's/^#define[ ][ ]*\([a-zA-Z0-9_]*\)[ ][ ]*[^ ].*[^\\\\]$/(void)\1;/p' + + ( + cat /tmp/$$ | sed -n 's/^#define[ ][ ]*\([a-zA-Z0-9_]*([^)]*)\).*\\\\$/\1;/p' + cat /tmp/$$ | sed -n 's/^#define[ ][ ]*\([a-zA-Z0-9_]*([^)]*)\).*[^\\\\]$/(=void)\1;/p' + ) | + sed 's/([a-zA-Z0-9_ ][a-zA-Z0-9_ ]*)/(0)/g' | + sed 's/([a-zA-Z0-9_ ]*,/(0,/g' | + sed 's/,[a-zA-Z0-9_ ]*,/,0,/g' | + sed 's/,[a-zA-Z0-9_ ]*)/,0)/g' | + # do not cast 'return' macros as (void) + sed 's/(=void)\(.*return\)/\1/g' | + sed 's/(=void)\(.*RETURN\)/\1/g' | + sed 's/(=void)/(void)/g' +done diff --git a/src/tools/pginclude/pgfixinclude b/src/tools/pginclude/pgfixinclude new file mode 100755 index 0000000..6721566 --- /dev/null +++ b/src/tools/pginclude/pgfixinclude @@ -0,0 +1,21 @@ +: +# change #include's to <> or "" +# src/tools/pginclude/pgfixinclude + +trap "rm -f /tmp/$$.c /tmp/$$.o /tmp/$$ /tmp/$$a /tmp/$$b" 0 1 2 3 15 +find . \( -name .git -a -prune \) -o -type f -name '*.[chyls]' -print | +while read FILE +do + cat "$FILE" | grep "^#include" | + sed 's/^#include[ ]*[<"]\([^>"]*\).*$/\1/g' | + while read INCLUDE + do + if [ -s /usr/include/"$INCLUDE" ] + then cat "$FILE" | + sed 's;^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]$;#include <'"$INCLUDE"'>;g' >/tmp/$$ + else cat "$FILE" | + sed 's;^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]$;#include "'"$INCLUDE"'";g' >/tmp/$$ + fi + cat /tmp/$$ > "$FILE" + done +done diff --git a/src/tools/pginclude/pgrminclude b/src/tools/pginclude/pgrminclude new file mode 100755 index 0000000..7cbd2e7 --- /dev/null +++ b/src/tools/pginclude/pgrminclude @@ -0,0 +1,149 @@ +: +# remove extra #include's + +# pgcompinclude must be run before and after pgrminclude. It must be +# run before because we don't want include dependencies to leak into +# the C program files, and after because removal of includes from headers +# can cause new include unfulfilled dependencies. +# +# Limitations: 2011-09-24 +# +# Pgrminclude, when processing header files, can cause includes to be +# removed that require the addition of new illogical header files. +# This is dependent on what order the header files are processed. +# Manual review of header files now needed to satisfy pgcompinclude is +# required. +# +# C program files that have #ifdef blocks that contain code that cannot +# be compiled on the platform from which pgrminclude is run cannot be +# processed, and are skipped. + +: ${CC:=cc} +: ${PGSRC:=src} + +if ! pgdefine +then echo "pgdefine must be in your PATH" 1>&2 + exit 1 +fi + +trap "rm -f /tmp/$$.c /tmp/$$.o /tmp/$$ /tmp/$$a /tmp/$$b" 0 1 2 3 15 + +if [ "$1" = "-v" ] +then VERBOSE="Y" +else VERBOSE="" +fi + +verbose_output() { + if [ "$VERBOSE" ] + then cat /tmp/$$ + cat /tmp/$$b + nl /tmp/$$.c + fi +} + +process_includes_in_file() { + # loop through all includes mentioned in the file + cat "$FILE" | + grep "^#include\>" | + grep -v '/\* *pgrminclude *ignore *\*/' | + sed 's/^#include[ ]*[<"]\([^>"]*\).*$/\1/g' | + grep -v 'parser/kwlist\.h' | + grep -v '\.c$' | + while read INCLUDE + do if [ "$VERBOSE" ] + then echo "checking $FILE $INCLUDE" + fi + compile_file + done +} + +compile_file() { + [ "$INCLUDE" -a -s /usr/include/"$INCLUDE" ] && continue + [ "$INCLUDE" = "postgres.h" ] && continue + [ "$INCLUDE" = "postgres_fe.h" ] && continue + [ "$INCLUDE" = "pg_config.h" ] && continue + [ "$INCLUDE" = "c.h" ] && continue + # Stringify macros will expand undefined identifiers, so skip files that use it + egrep -q '\<(CppAsString2?|CppConcat)\>' "$FILE" && continue + + # preserve configure-specific includes + # these includes are surrounded by #ifdef's + grep -B1 '^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]' "$FILE" | + egrep -q '^#if|^#else|^#elif' && continue + grep -A1 '^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]' "$FILE" | + egrep -q '^#else|^#elif|^#endif' && continue + + # Remove all #if and #ifdef blocks because the blocks + # might contain code that is not compiled on this platform. + cat "$FILE" | + grep -v "^#if" | + grep -v "^#else" | + grep -v "^#elif" | + grep -v "^#endif" | + # with #if blocks gone, now undef #defines to avoid redefine + # warning and failure + sed 's/#define[ ][ ]*\([A-Za-z0-9_]*\).*$/#undef \1\n&/' >/tmp/$$a + + # set up initial file contents + grep -v '^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]' \ + /tmp/$$a >/tmp/$$b + + if [ "$IS_INCLUDE" = "Y" ] + then echo "#include \"postgres.h\"" >/tmp/$$.c + # suppress fcinfo errors + echo "struct {Datum arg[1];} *fcinfo;" >>/tmp/$$.c + else >/tmp/$$.c + fi + + echo "#include \"/tmp/$$b\"" >>/tmp/$$.c + + if [ "$IS_INCLUDE" = "Y" ] + then echo "Datum include_test(void);" >>/tmp/$$.c + echo "Datum include_test() {" >>/tmp/$$.c + pgdefine "$FILE" >>/tmp/$$.c + echo "return (Datum)0;" >>/tmp/$$.c + echo "}" >>/tmp/$$.c + fi + + # Use -O1 to get warnings only generated by optimization, + # but -O2 is too slow. + $CC -fsyntax-only -Werror -Wall -Wmissing-prototypes \ + -Wmissing-declarations -I$PGSRC/include -I$PGSRC/backend \ + -I$PGSRC/interfaces/libpq -I`dirname $FILE` $CFLAGS -O1 -c /tmp/$$.c \ + -o /tmp/$$.o >/tmp/$$ 2>&1 + if [ "$?" -eq 0 ] + then [ "$INCLUDE" -o "$VERBOSE" ] && echo "$FILE $INCLUDE" + grep -v '^#include[ ][ ]*[<"]'"$INCLUDE"'[>"]' \ + "$FILE" >/tmp/$$b + mv /tmp/$$b "$FILE" + return 0 + else return 1 + fi +} + +# Process include files first because they can affect the compilation +# of *.c files. +(find . \( -name .git -a -prune \) -o -type f -name '*.h' -print | sort; + find . \( -name .git -a -prune \) -o -type f -name '*.c' -print | sort) | +grep -v '/postgres.h$' | +grep -v '/postgres_fe.h$' | +grep -v '/pg_config.h$' | +grep -v '\./c.h$' | +while read FILE +do + if [ `expr $FILE : '.*\.h$'` -ne 0 ] + then IS_INCLUDE="Y" + else IS_INCLUDE="N" + fi + + # Can we compile the file with all existing includes? + INCLUDE="" + compile_file + # If the file can't be compiled on its own, there is no sense + # trying to remove the include files. + if [ "$?" -ne 0 ] + then echo "cannot compile $FILE with existing includes" + verbose_output + else process_includes_in_file + fi +done diff --git a/src/tools/pgindent/README b/src/tools/pgindent/README new file mode 100644 index 0000000..f5fdfc5 --- /dev/null +++ b/src/tools/pgindent/README @@ -0,0 +1,161 @@ +pgindent'ing the PostgreSQL source tree +======================================= + +We run this process at least once in each development cycle, +to maintain uniform layout style in our C and Perl code. + +You might find this blog post interesting: +http://adpgtech.blogspot.com/2015/05/running-pgindent-on-non-core-code-or.html + + +PREREQUISITES: + +1) Install pg_bsd_indent in your PATH. Its source code is in the + sibling directory src/tools/pg_bsd_indent; see the directions + in that directory's README file. + +2) Install perltidy. Please be sure it is version 20230309 (older and newer + versions make different formatting choices, and we want consistency). + You can get the correct version from + https://cpan.metacpan.org/authors/id/S/SH/SHANCOCK/ + To install, follow the usual install process for a Perl module + ("man perlmodinstall" explains it). Or, if you have cpan installed, + this should work: + cpan SHANCOCK/Perl-Tidy-20230309.tar.gz + Or if you have cpanm installed, you can just use: + cpanm https://cpan.metacpan.org/authors/id/S/SH/SHANCOCK/Perl-Tidy-20230309.tar.gz + +DOING THE INDENT RUN: + +1) Change directory to the top of the source tree. + +2) Download the latest typedef file from the buildfarm: + + wget -O src/tools/pgindent/typedefs.list https://buildfarm.postgresql.org/cgi-bin/typedefs.pl + + (See https://buildfarm.postgresql.org/cgi-bin/typedefs.pl?show_list for a full + list of typedef files, if you want to indent some back branch.) + +3) Run pgindent on the C files: + + src/tools/pgindent/pgindent . + + If any files generate errors, restore their original versions with + "git checkout", and see below for cleanup ideas. + +4) Indent the Perl code using perltidy: + + src/tools/pgindent/pgperltidy . + + If you want to use some perltidy version that's not in your PATH, + first set the PERLTIDY environment variable to point to it. + +5) Reformat the bootstrap catalog data files: + + ./configure # "make" will not work in an unconfigured tree + cd src/include/catalog + make reformat-dat-files + cd ../../.. + +VALIDATION: + +1) Check for any newly-created files using "git status"; there shouldn't + be any. (pgindent leaves *.BAK files behind if it has trouble, while + perltidy leaves *.LOG files behind.) + +2) Do a full test build: + + make -s clean + make -s all # look for unexpected warnings, and errors of course + make check-world + + Your configure switches should include at least --enable-tap-tests + or else much of the Perl code won't get exercised. + The ecpg regression tests may well fail due to pgindent's updates of + header files that get copied into ecpg output; if so, adjust the + expected-files to match. + +3) If you have the patience, it's worth eyeballing the "git diff" output + for any egregiously ugly changes. See below for cleanup ideas. + + +When you're done, "git commit" everything including the typedefs.list file +you used. + +4) Add the newly created commits to the .git-blame-ignore-revs file so + that "git blame" ignores the commits (for anybody that has opted-in + to using the ignore file). Follow the instructions that appear at + the top of the .git-blame-ignore-revs file. + +Another "git commit" will be required for your ignore file changes. + +--------------------------------------------------------------------------- + +Cleaning up in case of failure or ugly output +--------------------------------------------- + +If you don't like the results for any particular file, "git checkout" +that file to undo the changes, patch the file as needed, then repeat +the indent process. + +pgindent will reflow any comment block that's not at the left margin. +If this messes up manual formatting that ought to be preserved, protect +the comment block with some dashes: + + /*---------- + * Text here will not be touched by pgindent. + *---------- + */ + +Odd spacing around typedef names might indicate an incomplete typedefs list. + +pgindent will mangle both declaration and definition of a C function whose +name matches a typedef. Currently the best workaround is to choose +non-conflicting names. + +pgindent can get confused by #if sequences that look correct to the compiler +but have mismatched braces/parentheses when considered as a whole. Usually +that looks pretty unreadable to humans too, so best practice is to rearrange +the #if tests to avoid it. + +Sometimes, if pgindent or perltidy produces odd-looking output, it's because +of minor bugs like extra commas. Don't hesitate to clean that up while +you're at it. + +--------------------------------------------------------------------------- + +BSD indent +---------- + +We have standardized on FreeBSD's indent, and renamed it pg_bsd_indent. +pg_bsd_indent does differ slightly from FreeBSD's version, mostly in +being more easily portable to non-BSD platforms. Find it in the +sibling directory src/tools/pg_bsd_indent. + +GNU indent, version 2.2.6, has several problems, and is not recommended. +These bugs become pretty major when you are doing >500k lines of code. +If you don't believe me, take a directory and make a copy. Run pgindent +on the copy using GNU indent, and do a diff -r. You will see what I +mean. GNU indent does some things better, but mangles too. For details, +see: + + http://archives.postgresql.org/pgsql-hackers/2003-10/msg00374.php + http://archives.postgresql.org/pgsql-hackers/2011-04/msg01436.php + +--------------------------------------------------------------------------- + +Which files are processed +------------------------- + +The pgindent run processes (nearly) all PostgreSQL *.c and *.h files, +but we currently exclude *.y and *.l files, as well as *.c and *.h files +derived from *.y and *.l files. Additional exceptions are listed +in exclude_file_patterns; see the notes therein for rationale. + +Note that we do not exclude ecpg's header files from the run. Some of them +get copied verbatim into ecpg's output, meaning that ecpg's expected files +may need to be updated to match. + +The perltidy run processes all *.pl and *.pm files, plus a few +executable Perl scripts that are not named that way. See the "find" +rules in pgperltidy for details. diff --git a/src/tools/pgindent/exclude_file_patterns b/src/tools/pgindent/exclude_file_patterns new file mode 100644 index 0000000..6405a00 --- /dev/null +++ b/src/tools/pgindent/exclude_file_patterns @@ -0,0 +1,58 @@ +# List of filename patterns to exclude from pgindent runs +# +# These contain assembly code that pgindent tends to mess up. +src/include/storage/s_lock\.h$ +src/include/port/atomics/ +# +# This contains C++ constructs that confuse pgindent. +src/include/jit/llvmjit\.h$ +# +# These are generated files with incomplete code fragments that +# confuse pgindent. +src/backend/nodes/\w+\.funcs\.c$ +src/backend/nodes/\w+\.switch\.c$ +# +# This confuses pgindent, and it's a derived file anyway. +src/backend/utils/fmgrtab\.c$ +# +# pgindent might mangle entries in this that match typedef names. +# Since it's a derived file anyway, just exclude it. +src/backend/utils/fmgrprotos\.h$ +# +# kwlist_d files are made by gen_keywordlist.pl. While we could insist that +# they match pgindent style, they'd look worse not better, so exclude them. +kwlist_d\.h$ +# +# These are generated by the scripts from src/common/unicode/. They use +# hash functions generated by PerfectHash.pm whose format looks worse with +# pgindent. +src/include/common/unicode_norm_hashfunc\.h$ +src/include/common/unicode_normprops_table\.h$ +# +# Exclude ecpg test files to avoid breaking the ecpg regression tests +# (but include files at the top level of the ecpg/test/ directory). +src/interfaces/ecpg/test/.*/ +# +# src/include/snowball/libstemmer/ and src/backend/snowball/libstemmer/ +# are excluded because those files are imported from an external project, +# rather than maintained locally, and they are machine-generated anyway. +/snowball/libstemmer/ +# +# These files are machine-generated by code not under our control, +# so we shouldn't expect them to conform to our style. +# (Some versions of dtrace build probes.h files that confuse pgindent, too.) +src/backend/utils/probes\.h$ +src/include/pg_config\.h$ +src/pl/plperl/ppport\.h$ +src/pl/plperl/SPI\.c$ +src/pl/plperl/Util\.c$ +# +# pg_bsd_indent has its own, idiosyncratic indentation style. +# We'll stick to that to permit comparison with the FreeBSD upstream. +src/tools/pg_bsd_indent/.* +# +# Exclude any temporary installations that may be in the tree. +/tmp_check/ +/tmp_install/ +# ... and for paranoia's sake, don't touch git stuff. +/\.git/ diff --git a/src/tools/pgindent/perltidyrc b/src/tools/pgindent/perltidyrc new file mode 100644 index 0000000..589d6e1 --- /dev/null +++ b/src/tools/pgindent/perltidyrc @@ -0,0 +1,17 @@ +--add-whitespace +--backup-and-modify-in-place +--backup-file-extension=/ +--delete-old-whitespace +--entab-leading-whitespace=4 +--keep-old-blank-lines=2 +--maximum-line-length=78 +--nooutdent-long-comments +--nooutdent-long-quotes +--nospace-for-semicolon +--opening-brace-on-new-line +--output-line-ending=unix +--paren-tightness=2 +--paren-vertical-tightness=2 +--paren-vertical-tightness-closing=2 +--noblanks-before-comments +--valign-exclusion-list=", = => =~ |= || && if or qw unless" diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent new file mode 100755 index 0000000..bce63d9 --- /dev/null +++ b/src/tools/pgindent/pgindent @@ -0,0 +1,435 @@ +#!/usr/bin/perl + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +use strict; +use warnings; + +use Cwd qw(abs_path getcwd); +use File::Find; +use File::Spec qw(devnull); +use File::Temp; +use IO::Handle; +use Getopt::Long; + +# Update for pg_bsd_indent version +my $INDENT_VERSION = "2.1.2"; + +# Our standard indent settings +my $indent_opts = + "-bad -bap -bbb -bc -bl -cli1 -cp33 -cdb -nce -d0 -di12 -nfc1 -i4 -l79 -lp -lpl -nip -npro -sac -tpg -ts4"; + +my $devnull = File::Spec->devnull; + +my ($typedefs_file, $typedef_str, @excludes, + $indent, $build, $show_diff, + $silent_diff, $help, @commits,); + +$help = 0; + +my %options = ( + "help" => \$help, + "commit=s" => \@commits, + "typedefs=s" => \$typedefs_file, + "list-of-typedefs=s" => \$typedef_str, + "excludes=s" => \@excludes, + "indent=s" => \$indent, + "show-diff" => \$show_diff, + "silent-diff" => \$silent_diff,); +GetOptions(%options) || usage("bad command line argument"); + +usage() if $help; + +usage("Cannot have both --silent-diff and --show-diff") + if $silent_diff && $show_diff; + +usage("Cannot use --commit with command line file list") + if (@commits && @ARGV); + +# command line option wins, then environment, then locations based on current +# dir, then default location +$typedefs_file ||= $ENV{PGTYPEDEFS}; + +# get indent location for environment or default +$indent ||= $ENV{PGINDENT} || $ENV{INDENT} || "pg_bsd_indent"; + +my $sourcedir = locate_sourcedir(); + +# if it's the base of a postgres tree, we will exclude the files +# postgres wants excluded +if ($sourcedir) +{ + my $exclude_candidate = "$sourcedir/exclude_file_patterns"; + push(@excludes, $exclude_candidate) if -f $exclude_candidate; +} + +# The typedef list that's mechanically extracted by the buildfarm may omit +# some names we want to treat like typedefs, e.g. "bool" (which is a macro +# according to <stdbool.h>), and may include some names we don't want +# treated as typedefs, although various headers that some builds include +# might make them so. For the moment we just hardwire a list of names +# to add and a list of names to exclude; eventually this may need to be +# easier to configure. Note that the typedefs need trailing newlines. +my @additional = ("bool\n"); + +my %excluded = map { +"$_\n" => 1 } qw( + ANY FD_SET U abs allocfunc boolean date digit ilist interval iterator other + pointer printfunc reference string timestamp type wrap +); + +# globals +my @files; +my $filtered_typedefs_fh; + +sub check_indent +{ + system("$indent -? < $devnull > $devnull 2>&1"); + if ($? >> 8 != 1) + { + print STDERR + "You do not appear to have $indent installed on your system.\n"; + exit 1; + } + + if (`$indent --version` !~ m/ $INDENT_VERSION /) + { + print STDERR + "You do not appear to have $indent version $INDENT_VERSION installed on your system.\n"; + exit 1; + } + + system("$indent -gnu < $devnull > $devnull 2>&1"); + if ($? == 0) + { + print STDERR + "You appear to have GNU indent rather than BSD indent.\n"; + exit 1; + } + + return; +} + +sub locate_sourcedir +{ + # try fairly hard to locate the sourcedir + my $sub = "./src/tools/pgindent"; + return $sub if -d $sub; + # try to find it from an ancestor directory + $sub = "../src/tools/pgindent"; + foreach (1 .. 4) + { + return $sub if -d $sub; + $sub = "../$sub"; + } + return; # undef if nothing found +} + +sub load_typedefs +{ + # try fairly hard to find the typedefs file if it's not set + + foreach my $try ('.', $sourcedir, '/usr/local/etc') + { + last if $typedefs_file; + next unless defined $try; + $typedefs_file = "$try/typedefs.list" + if (-f "$try/typedefs.list"); + } + + die "cannot locate typedefs file \"$typedefs_file\"\n" + unless $typedefs_file && -f $typedefs_file; + + open(my $typedefs_fh, '<', $typedefs_file) + || die "cannot open typedefs file \"$typedefs_file\": $!\n"; + my @typedefs = <$typedefs_fh>; + close($typedefs_fh); + + # add command-line-supplied typedefs? + if (defined($typedef_str)) + { + foreach my $typedef (split(m/[, \t\n]+/, $typedef_str)) + { + push(@typedefs, $typedef . "\n"); + } + } + + # add additional entries + push(@typedefs, @additional); + + # remove excluded entries + @typedefs = grep { !$excluded{$_} } @typedefs; + + # write filtered typedefs + my $filter_typedefs_fh = new File::Temp(TEMPLATE => "pgtypedefXXXXX"); + print $filter_typedefs_fh @typedefs; + $filter_typedefs_fh->close(); + + # temp file remains because we return a file handle reference + return $filter_typedefs_fh; +} + +sub process_exclude +{ + foreach my $excl (@excludes) + { + last unless @files; + open(my $eh, '<', $excl) + || die "cannot open exclude file \"$excl\"\n"; + while (my $line = <$eh>) + { + chomp $line; + next if $line =~ m/^#/; + my $rgx = qr!$line!; + @files = grep { $_ !~ /$rgx/ } @files if $rgx; + } + close($eh); + } + return; +} + +sub read_source +{ + my $source_filename = shift; + my $source; + + open(my $src_fd, '<', $source_filename) + || die "cannot open file \"$source_filename\": $!\n"; + local ($/) = undef; + $source = <$src_fd>; + close($src_fd); + + return $source; +} + +sub write_source +{ + my $source = shift; + my $source_filename = shift; + + open(my $src_fh, '>', $source_filename) + || die "cannot open file \"$source_filename\": $!\n"; + print $src_fh $source; + close($src_fh); + return; +} + +sub pre_indent +{ + my $source = shift; + + ## Comments + + # Convert // comments to /* */ + $source =~ s!^([ \t]*)//(.*)$!$1/* $2 */!gm; + + # Adjust dash-protected block comments so indent won't change them + $source =~ s!/\* +---!/*---X_X!g; + + ## Other + + # Prevent indenting of code in 'extern "C"' blocks. + # we replace the braces with comments which we'll reverse later + my $extern_c_start = '/* Open extern "C" */'; + my $extern_c_stop = '/* Close extern "C" */'; + $source =~ + s!(^#ifdef[ \t]+__cplusplus.*\nextern[ \t]+"C"[ \t]*\n)\{[ \t]*$!$1$extern_c_start!gm; + $source =~ s!(^#ifdef[ \t]+__cplusplus.*\n)\}[ \t]*$!$1$extern_c_stop!gm; + + # Protect wrapping in CATALOG() + $source =~ s!^(CATALOG\(.*)$!/*$1*/!gm; + + return $source; +} + +sub post_indent +{ + my $source = shift; + + # Restore CATALOG lines + $source =~ s!^/\*(CATALOG\(.*)\*/$!$1!gm; + + # Put back braces for extern "C" + $source =~ s!^/\* Open extern "C" \*/$!{!gm; + $source =~ s!^/\* Close extern "C" \*/$!}!gm; + + ## Comments + + # Undo change of dash-protected block comments + $source =~ s!/\*---X_X!/* ---!g; + + # Fix run-together comments to have a tab between them + $source =~ s!\*/(/\*.*\*/)$!*/\t$1!gm; + + ## Functions + + # Use a single space before '*' in function return types + $source =~ s!^([A-Za-z_]\S*)[ \t]+\*$!$1 *!gm; + + return $source; +} + +sub run_indent +{ + my $source = shift; + my $error_message = shift; + + my $cmd = "$indent $indent_opts -U" . $filtered_typedefs_fh->filename; + + my $tmp_fh = new File::Temp(TEMPLATE => "pgsrcXXXXX"); + my $filename = $tmp_fh->filename; + print $tmp_fh $source; + $tmp_fh->close(); + + $$error_message = `$cmd $filename 2>&1`; + + return "" if ($? || length($$error_message) > 0); + + unlink "$filename.BAK"; + + open(my $src_out, '<', $filename); + local ($/) = undef; + $source = <$src_out>; + close($src_out); + + return $source; +} + +sub show_diff +{ + my $indented = shift; + my $source_filename = shift; + + my $post_fh = new File::Temp(TEMPLATE => "pgdiffXXXXX"); + my $post_fh_filename = $post_fh->filename; + + print $post_fh $indented; + + $post_fh->close(); + + my $diff = `diff -upd "$source_filename" "$post_fh_filename" 2>&1`; + return $diff; +} + +sub usage +{ + my $message = shift; + my $helptext = <<'EOF'; +Usage: +pgindent [OPTION]... [FILE|DIR]... +Options: + --help show this message and quit + --commit=gitref use files modified by the named commit + --typedefs=FILE file containing a list of typedefs + --list-of-typedefs=STR string containing typedefs, space separated + --excludes=PATH file containing list of filename patterns to ignore + --indent=PATH path to pg_bsd_indent program + --show-diff show the changes that would be made + --silent-diff exit with status 2 if any changes would be made +The --excludes and --commit options can be given more than once. +EOF + if ($help) + { + print $helptext; + exit 0; + } + else + { + print STDERR "$message\n", $helptext; + exit 1; + } +} + +# main + +$filtered_typedefs_fh = load_typedefs(); + +check_indent(); + +my $wanted = sub { + my ($dev, $ino, $mode, $nlink, $uid, $gid); + (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_)) + && -f _ + && /^.*\.[ch]\z/s + && push(@files, $File::Find::name); +}; + +# any non-option arguments are files or directories to be processed +File::Find::find({ wanted => $wanted }, @ARGV) if @ARGV; + +# commit file locations are relative to the source root +chdir "$sourcedir/../../.." if @commits && $sourcedir; + +# process named commits by comparing each with their immediate ancestor +foreach my $commit (@commits) +{ + my $prev = "$commit~"; + my @affected = `git diff --diff-filter=ACMR --name-only $prev $commit`; + die "git error" if $?; + chomp(@affected); + push(@files, @affected); +} + +warn "No files to process" unless @files; + +# remove excluded files from the file list +process_exclude(); + +my %processed; + +foreach my $source_filename (@files) +{ + # skip duplicates + next if $processed{$source_filename}; + $processed{$source_filename} = 1; + + # ignore anything that's not a .c or .h file + next unless $source_filename =~ /\.[ch]$/; + + # don't try to indent a file that doesn't exist + unless (-f $source_filename) + { + warn "Could not find $source_filename"; + next; + } + # Automatically ignore .c and .h files that correspond to a .y or .l + # file. indent tends to get badly confused by Bison/flex output, + # and there's no value in indenting derived files anyway. + my $otherfile = $source_filename; + $otherfile =~ s/\.[ch]$/.y/; + next if $otherfile ne $source_filename && -f $otherfile; + $otherfile =~ s/\.y$/.l/; + next if $otherfile ne $source_filename && -f $otherfile; + + my $source = read_source($source_filename); + my $orig_source = $source; + my $error_message = ''; + + $source = pre_indent($source); + + $source = run_indent($source, \$error_message); + if ($source eq "") + { + print STDERR "Failure in $source_filename: " . $error_message . "\n"; + next; + } + + $source = post_indent($source); + + if ($source ne $orig_source) + { + if ($silent_diff) + { + exit 2; + } + elsif ($show_diff) + { + print show_diff($source, $source_filename); + } + else + { + write_source($source, $source_filename); + } + } +} + +exit 0; diff --git a/src/tools/pgindent/pgindent.man b/src/tools/pgindent/pgindent.man new file mode 100644 index 0000000..fe411ee --- /dev/null +++ b/src/tools/pgindent/pgindent.man @@ -0,0 +1,43 @@ +pgindent will indent .c and .h files according to the coding standards of +the PostgreSQL project. It needs several things to run, and tries to locate +or build them if possible. They can also be specified via command line switches +or the environment. + +You can see all the options by running: + + pgindent --help + +In its simplest form, if all the required objects are installed, simply run +it at the top of the source tree you want to process like this: + + pgindent . + +If your pg_bsd_indent program is not installed in your path, you can specify +it by setting the environment variable INDENT, or PGINDENT, or by giving the +command line option --indent: + + pgindent --indent=/opt/extras/bsdindent + +pgindent also needs a file containing a list of typedefs. This can be +specified using the PGTYPEDEFS environment variable, or via the command line +--typedefs option. If neither is used, it will look for it within the +current source tree, or in /usr/local/etc/typedefs.list. + +We don't want to indent certain files in the PostgreSQL source. pgindent +will honor a file containing a list of patterns of files to avoid. This +file can be specified using the --excludes command line option. If indenting +a PostgreSQL source tree, this option is usually not necessary, as it will +find the file src/tools/pgindent/exclude_file_patterns. The --excludes option +can be used more than once to specify multiple files containing exclusion +patterns. + +There are also two non-destructive modes of pgindent. If given the --show-diff +option pgindent will show the changes it would make, but doesn't actually make +them. If given instead the --silent-diff option, pgindent will exit with a +status of 2 if it finds any indent changes are required, but will not +make the changes or give any other information. This mode is intended for +possible use in a git pre-commit hook. An example of its use in a git hook +can be seen at https://wiki.postgresql.org/wiki/Working_with_Git#Using_git_hooks + +Any non-option arguments are taken as the names of files to be indented. In this +case only these files will be changed, and nothing else will be touched. diff --git a/src/tools/pgindent/pgperltidy b/src/tools/pgindent/pgperltidy new file mode 100755 index 0000000..6af27d2 --- /dev/null +++ b/src/tools/pgindent/pgperltidy @@ -0,0 +1,12 @@ +#!/bin/sh + +# src/tools/pgindent/pgperltidy + +set -e + +# set this to override default perltidy program: +PERLTIDY=${PERLTIDY:-perltidy} + +. src/tools/perlcheck/find_perl_files + +find_perl_files "$@" | xargs $PERLTIDY --profile=src/tools/pgindent/perltidyrc diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list new file mode 100644 index 0000000..4ec490e --- /dev/null +++ b/src/tools/pgindent/typedefs.list @@ -0,0 +1,3984 @@ +ACCESS_ALLOWED_ACE +ACL +ACL_SIZE_INFORMATION +AFFIX +ASN1_INTEGER +ASN1_OBJECT +ASN1_OCTET_STRING +ASN1_STRING +AV +A_ArrayExpr +A_Const +A_Expr +A_Expr_Kind +A_Indices +A_Indirection +A_Star +AbsoluteTime +AccessMethodInfo +AccessPriv +Acl +AclItem +AclMaskHow +AclMode +AclResult +AcquireSampleRowsFunc +ActionList +ActiveSnapshotElt +AddForeignUpdateTargets_function +AddrInfo +AffixNode +AffixNodeData +AfterTriggerEvent +AfterTriggerEventChunk +AfterTriggerEventData +AfterTriggerEventList +AfterTriggerShared +AfterTriggerSharedData +AfterTriggersData +AfterTriggersQueryData +AfterTriggersTableData +AfterTriggersTransData +Agg +AggClauseCosts +AggInfo +AggPath +AggSplit +AggState +AggStatePerAgg +AggStatePerGroup +AggStatePerHash +AggStatePerPhase +AggStatePerTrans +AggStrategy +AggTransInfo +Aggref +AggregateInstrumentation +AlenState +Alias +AllocBlock +AllocFreeListLink +AllocPointer +AllocSet +AllocSetContext +AllocSetFreeList +AllocateDesc +AllocateDescKind +AlterCollationStmt +AlterDatabaseRefreshCollStmt +AlterDatabaseSetStmt +AlterDatabaseStmt +AlterDefaultPrivilegesStmt +AlterDomainStmt +AlterEnumStmt +AlterEventTrigStmt +AlterExtensionContentsStmt +AlterExtensionStmt +AlterFdwStmt +AlterForeignServerStmt +AlterFunctionStmt +AlterObjectDependsStmt +AlterObjectSchemaStmt +AlterOpFamilyStmt +AlterOperatorStmt +AlterOwnerStmt +AlterPolicyStmt +AlterPublicationAction +AlterPublicationStmt +AlterRoleSetStmt +AlterRoleStmt +AlterSeqStmt +AlterStatsStmt +AlterSubscriptionStmt +AlterSubscriptionType +AlterSystemStmt +AlterTSConfigType +AlterTSConfigurationStmt +AlterTSDictionaryStmt +AlterTableCmd +AlterTableMoveAllStmt +AlterTableSpaceOptionsStmt +AlterTableStmt +AlterTableType +AlterTableUtilityContext +AlterTypeRecurseParams +AlterTypeStmt +AlterUserMappingStmt +AlteredTableInfo +AlternativeSubPlan +AmcheckOptions +AnalyzeAttrComputeStatsFunc +AnalyzeAttrFetchFunc +AnalyzeForeignTable_function +AnlExprData +AnlIndexData +AnyArrayType +Append +AppendPath +AppendRelInfo +AppendState +ApplyErrorCallbackArg +ApplyExecutionData +ApplySubXactData +Archive +ArchiveCheckConfiguredCB +ArchiveEntryPtrType +ArchiveFileCB +ArchiveFormat +ArchiveHandle +ArchiveMode +ArchiveModuleCallbacks +ArchiveModuleInit +ArchiveModuleState +ArchiveOpts +ArchiveShutdownCB +ArchiveStartupCB +ArchiveStreamState +ArchiverOutput +ArchiverStage +ArrayAnalyzeExtraData +ArrayBuildState +ArrayBuildStateAny +ArrayBuildStateArr +ArrayCoerceExpr +ArrayConstIterState +ArrayExpr +ArrayExprIterState +ArrayIOData +ArrayIterator +ArrayMapState +ArrayMetaState +ArrayParseState +ArraySubWorkspace +ArrayType +AsyncQueueControl +AsyncQueueEntry +AsyncRequest +AttInMetadata +AttStatsSlot +AttoptCacheEntry +AttoptCacheKey +AttrDefInfo +AttrDefault +AttrMap +AttrMissing +AttrNumber +AttributeOpts +AuthRequest +AuthToken +AutoPrewarmSharedState +AutoVacOpts +AutoVacuumShmemStruct +AutoVacuumWorkItem +AutoVacuumWorkItemType +AuxProcType +BF_ctx +BF_key +BF_word +BF_word_signed +BIGNUM +BIO +BIO_METHOD +BITVECP +BMS_Comparison +BMS_Membership +BN_CTX +BOOL +BOOLEAN +BOX +BTArrayKeyInfo +BTBuildState +BTCycleId +BTDedupInterval +BTDedupState +BTDedupStateData +BTDeletedPageData +BTIndexStat +BTInsertState +BTInsertStateData +BTLeader +BTMetaPageData +BTOneVacInfo +BTOptions +BTPS_State +BTPageOpaque +BTPageOpaqueData +BTPageStat +BTPageState +BTParallelScanDesc +BTPendingFSM +BTScanInsert +BTScanInsertData +BTScanOpaque +BTScanOpaqueData +BTScanPos +BTScanPosData +BTScanPosItem +BTShared +BTSortArrayContext +BTSpool +BTStack +BTStackData +BTVacInfo +BTVacState +BTVacuumPosting +BTVacuumPostingData +BTWriteState +BUF_MEM +BYTE +BY_HANDLE_FILE_INFORMATION +Backend +BackendId +BackendParameters +BackendState +BackendType +BackgroundWorker +BackgroundWorkerArray +BackgroundWorkerHandle +BackgroundWorkerSlot +BackupState +Barrier +BaseBackupCmd +BaseBackupTargetHandle +BaseBackupTargetType +BasicArchiveData +BeginDirectModify_function +BeginForeignInsert_function +BeginForeignModify_function +BeginForeignScan_function +BeginSampleScan_function +BernoulliSamplerData +BgWorkerStartTime +BgwHandleStatus +BinaryArithmFunc +BindParamCbData +BipartiteMatchState +BitString +BitmapAnd +BitmapAndPath +BitmapAndState +BitmapHeapPath +BitmapHeapScan +BitmapHeapScanState +BitmapIndexScan +BitmapIndexScanState +BitmapOr +BitmapOrPath +BitmapOrState +Bitmapset +Block +BlockId +BlockIdData +BlockInfoRecord +BlockNumber +BlockSampler +BlockSamplerData +BlockedProcData +BlockedProcsData +BloomBuildState +BloomFilter +BloomMetaPageData +BloomOpaque +BloomOptions +BloomPageOpaque +BloomPageOpaqueData +BloomScanOpaque +BloomScanOpaqueData +BloomSignatureWord +BloomState +BloomTuple +BoolAggState +BoolExpr +BoolExprType +BoolTestType +Boolean +BooleanTest +BpChar +BrinBuildState +BrinDesc +BrinMemTuple +BrinMetaPageData +BrinOpaque +BrinOpcInfo +BrinOptions +BrinRevmap +BrinSpecialSpace +BrinStatsData +BrinTuple +BrinValues +BtreeCheckState +BtreeLevel +Bucket +BufFile +Buffer +BufferAccessStrategy +BufferAccessStrategyType +BufferCachePagesContext +BufferCachePagesRec +BufferDesc +BufferDescPadded +BufferHeapTupleTableSlot +BufferLookupEnt +BufferManagerRelation +BufferStrategyControl +BufferTag +BufferUsage +BuildAccumulator +BuiltinScript +BulkInsertState +BulkInsertStateData +CACHESIGN +CAC_state +CCFastEqualFN +CCHashFN +CEOUC_WAIT_MODE +CFuncHashTabEntry +CHAR +CHECKPOINT +CHKVAL +CIRCLE +CMPDAffix +CONTEXT +COP +CRITICAL_SECTION +CRSSnapshotAction +CState +CTECycleClause +CTEMaterialize +CTESearchClause +CV +CachedExpression +CachedPlan +CachedPlanSource +CallContext +CallStmt +CancelRequestPacket +Cardinality +CaseExpr +CaseTestExpr +CaseWhen +Cash +CastInfo +CatCList +CatCTup +CatCache +CatCacheHeader +CatalogId +CatalogIdMapEntry +CatalogIndexState +ChangeVarNodes_context +CheckPoint +CheckPointStmt +CheckpointStatsData +CheckpointerRequest +CheckpointerShmemStruct +Chromosome +CkptSortItem +CkptTsStatus +ClientAuthentication_hook_type +ClientCertMode +ClientCertName +ClientConnectionInfo +ClientData +ClonePtrType +ClosePortalStmt +ClosePtrType +ClosestMatchState +Clump +ClusterInfo +ClusterParams +ClusterStmt +CmdType +CoalesceExpr +CoerceParamHook +CoerceToDomain +CoerceToDomainValue +CoerceViaIO +CoercionContext +CoercionForm +CoercionPathType +CollAliasData +CollInfo +CollParam +CollateClause +CollateExpr +CollateStrength +CollectedATSubcmd +CollectedCommand +CollectedCommandType +ColorTrgm +ColorTrgmInfo +ColumnCompareData +ColumnDef +ColumnIOData +ColumnRef +ColumnsHashData +CombinationGenerator +ComboCidEntry +ComboCidEntryData +ComboCidKey +ComboCidKeyData +Command +CommandDest +CommandId +CommandTag +CommandTagBehavior +CommentItem +CommentStmt +CommitTimestampEntry +CommitTimestampShared +CommonEntry +CommonTableExpr +CompareScalarsContext +CompiledExprState +CompositeIOData +CompositeTypeStmt +CompoundAffixFlag +CompressFileHandle +CompressionLocation +CompressorState +ComputeXidHorizonsResult +ConditionVariable +ConditionVariableMinimallyPadded +ConditionalStack +ConfigData +ConfigVariable +ConnCacheEntry +ConnCacheKey +ConnParams +ConnStatusType +ConnType +ConnectionStateEnum +ConsiderSplitContext +Const +ConstrCheck +ConstrType +Constraint +ConstraintCategory +ConstraintInfo +ConstraintsSetStmt +ControlData +ControlFileData +ConvInfo +ConvProcInfo +ConversionLocation +ConvertRowtypeExpr +CookedConstraint +CopyDest +CopyFormatOptions +CopyFromState +CopyFromStateData +CopyHeaderChoice +CopyInsertMethod +CopyMultiInsertBuffer +CopyMultiInsertInfo +CopySource +CopyStmt +CopyToState +CopyToStateData +Cost +CostSelector +Counters +CoverExt +CoverPos +CreateAmStmt +CreateCastStmt +CreateConversionStmt +CreateDBRelInfo +CreateDBStrategy +CreateDomainStmt +CreateEnumStmt +CreateEventTrigStmt +CreateExtensionStmt +CreateFdwStmt +CreateForeignServerStmt +CreateForeignTableStmt +CreateFunctionStmt +CreateOpClassItem +CreateOpClassStmt +CreateOpFamilyStmt +CreatePLangStmt +CreatePolicyStmt +CreatePublicationStmt +CreateRangeStmt +CreateReplicationSlotCmd +CreateRoleStmt +CreateSchemaStmt +CreateSchemaStmtContext +CreateSeqStmt +CreateStatsStmt +CreateStmt +CreateStmtContext +CreateSubscriptionStmt +CreateTableAsStmt +CreateTableSpaceStmt +CreateTransformStmt +CreateTrigStmt +CreateUserMappingStmt +CreatedbStmt +CredHandle +CteItem +CteScan +CteScanState +CteState +CtlCommand +CtxtHandle +CurrentOfExpr +CustomExecMethods +CustomOutPtrType +CustomPath +CustomScan +CustomScanMethods +CustomScanState +CycleCtr +DBState +DCHCacheEntry +DEADLOCK_INFO +DECountItem +DH +DIR +DNSServiceErrorType +DNSServiceRef +DR_copy +DR_intorel +DR_printtup +DR_sqlfunction +DR_transientrel +DWORD +DataDumperPtr +DataPageDeleteStack +DatabaseInfo +DateADT +DateTimeErrorExtra +Datum +DatumTupleFields +DbInfo +DbInfoArr +DbLocaleInfo +DeClonePtrType +DeadLockState +DeallocateStmt +DeclareCursorStmt +DecodedBkpBlock +DecodedXLogRecord +DecodingOutputState +DefElem +DefElemAction +DefaultACLInfo +DefineStmt +DeleteStmt +DependencyGenerator +DependencyGeneratorData +DependencyType +DeserialIOData +DestReceiver +DictISpell +DictInt +DictSimple +DictSnowball +DictSubState +DictSyn +DictThesaurus +DimensionInfo +DirectoryMethodData +DirectoryMethodFile +DisableTimeoutParams +DiscardMode +DiscardStmt +DistanceValue +DistinctExpr +DoStmt +DocRepresentation +DomainConstraintCache +DomainConstraintRef +DomainConstraintState +DomainConstraintType +DomainIOData +DropBehavior +DropOwnedStmt +DropReplicationSlotCmd +DropRoleStmt +DropStmt +DropSubscriptionStmt +DropTableSpaceStmt +DropUserMappingStmt +DropdbStmt +DumpComponents +DumpId +DumpOptions +DumpSignalInformation +DumpableAcl +DumpableObject +DumpableObjectType +DumpableObjectWithAcl +DynamicFileList +DynamicZoneAbbrev +EC_KEY +EDGE +ENGINE +EOM_flatten_into_method +EOM_get_flat_size_method +EPQState +EPlan +EState +EStatus +EVP_CIPHER +EVP_CIPHER_CTX +EVP_MD +EVP_MD_CTX +EVP_PKEY +EachState +Edge +EditableObjectType +ElementsState +EnableTimeoutParams +EndDataPtrType +EndDirectModify_function +EndForeignInsert_function +EndForeignModify_function +EndForeignScan_function +EndLOPtrType +EndLOsPtrType +EndOfWalRecoveryInfo +EndSampleScan_function +EnumItem +EolType +EphemeralNameRelationType +EphemeralNamedRelation +EphemeralNamedRelationData +EphemeralNamedRelationMetadata +EphemeralNamedRelationMetadataData +EquivalenceClass +EquivalenceMember +ErrorContextCallback +ErrorData +ErrorSaveContext +EstimateDSMForeignScan_function +EstimationInfo +EventTriggerCacheEntry +EventTriggerCacheItem +EventTriggerCacheStateType +EventTriggerData +EventTriggerEvent +EventTriggerInfo +EventTriggerQueryState +ExceptionLabelMap +ExceptionMap +ExecAuxRowMark +ExecEvalBoolSubroutine +ExecEvalSubroutine +ExecForeignBatchInsert_function +ExecForeignDelete_function +ExecForeignInsert_function +ExecForeignTruncate_function +ExecForeignUpdate_function +ExecParallelEstimateContext +ExecParallelInitializeDSMContext +ExecPhraseData +ExecProcNodeMtd +ExecRowMark +ExecScanAccessMtd +ExecScanRecheckMtd +ExecStatus +ExecStatusType +ExecuteStmt +ExecutorCheckPerms_hook_type +ExecutorEnd_hook_type +ExecutorFinish_hook_type +ExecutorRun_hook_type +ExecutorStart_hook_type +ExpandedArrayHeader +ExpandedObjectHeader +ExpandedObjectMethods +ExpandedRange +ExpandedRecordFieldInfo +ExpandedRecordHeader +ExplainDirectModify_function +ExplainForeignModify_function +ExplainForeignScan_function +ExplainFormat +ExplainOneQuery_hook_type +ExplainState +ExplainStmt +ExplainWorkersState +ExportedSnapshot +Expr +ExprContext +ExprContextCallbackFunction +ExprContext_CB +ExprDoneCond +ExprEvalOp +ExprEvalOpLookup +ExprEvalRowtypeCache +ExprEvalStep +ExprSetupInfo +ExprState +ExprStateEvalFunc +ExtensibleNode +ExtensibleNodeEntry +ExtensibleNodeMethods +ExtensionControlFile +ExtensionInfo +ExtensionVersionInfo +FDWCollateState +FD_SET +FILE +FILETIME +FSMAddress +FSMPage +FSMPageData +FakeRelCacheEntry +FakeRelCacheEntryData +FastPathStrongRelationLockData +FdwInfo +FdwRoutine +FetchDirection +FetchStmt +FieldSelect +FieldStore +File +FileFdwExecutionState +FileFdwPlanState +FileNameMap +FileSet +FileTag +FinalPathExtraData +FindColsContext +FindSplitData +FindSplitStrat +First +FixedParallelExecutorState +FixedParallelState +FixedParamState +FlagMode +Float +FlushPosition +FmgrBuiltin +FmgrHookEventType +FmgrInfo +ForBothCellState +ForBothState +ForEachState +ForFiveState +ForFourState +ForThreeState +ForeignAsyncConfigureWait_function +ForeignAsyncNotify_function +ForeignAsyncRequest_function +ForeignDataWrapper +ForeignKeyCacheInfo +ForeignKeyOptInfo +ForeignPath +ForeignScan +ForeignScanState +ForeignServer +ForeignServerInfo +ForeignTable +ForeignTruncateInfo +ForkNumber +FormData_pg_aggregate +FormData_pg_am +FormData_pg_amop +FormData_pg_amproc +FormData_pg_attrdef +FormData_pg_attribute +FormData_pg_auth_members +FormData_pg_authid +FormData_pg_cast +FormData_pg_class +FormData_pg_collation +FormData_pg_constraint +FormData_pg_conversion +FormData_pg_database +FormData_pg_default_acl +FormData_pg_depend +FormData_pg_enum +FormData_pg_event_trigger +FormData_pg_extension +FormData_pg_foreign_data_wrapper +FormData_pg_foreign_server +FormData_pg_foreign_table +FormData_pg_index +FormData_pg_inherits +FormData_pg_language +FormData_pg_largeobject +FormData_pg_largeobject_metadata +FormData_pg_namespace +FormData_pg_opclass +FormData_pg_operator +FormData_pg_opfamily +FormData_pg_partitioned_table +FormData_pg_policy +FormData_pg_proc +FormData_pg_publication +FormData_pg_publication_namespace +FormData_pg_publication_rel +FormData_pg_range +FormData_pg_replication_origin +FormData_pg_rewrite +FormData_pg_sequence +FormData_pg_sequence_data +FormData_pg_shdepend +FormData_pg_statistic +FormData_pg_statistic_ext +FormData_pg_statistic_ext_data +FormData_pg_subscription +FormData_pg_subscription_rel +FormData_pg_tablespace +FormData_pg_transform +FormData_pg_trigger +FormData_pg_ts_config +FormData_pg_ts_config_map +FormData_pg_ts_dict +FormData_pg_ts_parser +FormData_pg_ts_template +FormData_pg_type +FormData_pg_user_mapping +Form_pg_aggregate +Form_pg_am +Form_pg_amop +Form_pg_amproc +Form_pg_attrdef +Form_pg_attribute +Form_pg_auth_members +Form_pg_authid +Form_pg_cast +Form_pg_class +Form_pg_collation +Form_pg_constraint +Form_pg_conversion +Form_pg_database +Form_pg_default_acl +Form_pg_depend +Form_pg_enum +Form_pg_event_trigger +Form_pg_extension +Form_pg_foreign_data_wrapper +Form_pg_foreign_server +Form_pg_foreign_table +Form_pg_index +Form_pg_inherits +Form_pg_language +Form_pg_largeobject +Form_pg_largeobject_metadata +Form_pg_namespace +Form_pg_opclass +Form_pg_operator +Form_pg_opfamily +Form_pg_partitioned_table +Form_pg_policy +Form_pg_proc +Form_pg_publication +Form_pg_publication_namespace +Form_pg_publication_rel +Form_pg_range +Form_pg_replication_origin +Form_pg_rewrite +Form_pg_sequence +Form_pg_sequence_data +Form_pg_shdepend +Form_pg_statistic +Form_pg_statistic_ext +Form_pg_statistic_ext_data +Form_pg_subscription +Form_pg_subscription_rel +Form_pg_tablespace +Form_pg_transform +Form_pg_trigger +Form_pg_ts_config +Form_pg_ts_config_map +Form_pg_ts_dict +Form_pg_ts_parser +Form_pg_ts_template +Form_pg_type +Form_pg_user_mapping +FormatNode +FreeBlockNumberArray +FreeListData +FreePageBtree +FreePageBtreeHeader +FreePageBtreeInternalKey +FreePageBtreeLeafKey +FreePageBtreeSearchResult +FreePageManager +FreePageSpanLeader +From +FromCharDateMode +FromExpr +FullTransactionId +FuncCall +FuncCallContext +FuncCandidateList +FuncDetailCode +FuncExpr +FuncInfo +FuncLookupError +FunctionCallInfo +FunctionCallInfoBaseData +FunctionParameter +FunctionParameterMode +FunctionScan +FunctionScanPerFuncState +FunctionScanState +FuzzyAttrMatchState +GBT_NUMKEY +GBT_NUMKEY_R +GBT_VARKEY +GBT_VARKEY_R +GENERAL_NAME +GISTBuildBuffers +GISTBuildState +GISTDeletedPageContents +GISTENTRY +GISTInsertStack +GISTInsertState +GISTIntArrayBigOptions +GISTIntArrayOptions +GISTNodeBuffer +GISTNodeBufferPage +GISTPageOpaque +GISTPageOpaqueData +GISTPageSplitInfo +GISTSTATE +GISTScanOpaque +GISTScanOpaqueData +GISTSearchHeapItem +GISTSearchItem +GISTTYPE +GIST_SPLITVEC +GMReaderTupleBuffer +GROUP +GUCHashEntry +GV +Gather +GatherMerge +GatherMergePath +GatherMergeState +GatherPath +GatherState +Gene +GeneratePruningStepsContext +GenerationBlock +GenerationContext +GenerationPointer +GenericCosts +GenericXLogState +GeqoPrivateData +GetForeignJoinPaths_function +GetForeignModifyBatchSize_function +GetForeignPaths_function +GetForeignPlan_function +GetForeignRelSize_function +GetForeignRowMarkType_function +GetForeignUpperPaths_function +GetState +GiSTOptions +GinBtree +GinBtreeData +GinBtreeDataLeafInsertData +GinBtreeEntryInsertData +GinBtreeStack +GinBuildState +GinChkVal +GinEntries +GinEntryAccumulator +GinIndexStat +GinMetaPageData +GinNullCategory +GinOptions +GinPageOpaque +GinPageOpaqueData +GinPlaceToPageRC +GinPostingList +GinQualCounts +GinScanEntry +GinScanKey +GinScanOpaque +GinScanOpaqueData +GinState +GinStatsData +GinTernaryValue +GinTupleCollector +GinVacuumState +GistBuildMode +GistEntryVector +GistHstoreOptions +GistInetKey +GistNSN +GistOptBufferingMode +GistSortedBuildLevelState +GistSplitUnion +GistSplitVector +GistTsVectorOptions +GistVacState +GlobalTransaction +GlobalVisHorizonKind +GlobalVisState +GrantRoleOptions +GrantRoleStmt +GrantStmt +GrantTargetType +Group +GroupClause +GroupPath +GroupPathExtraData +GroupResultPath +GroupState +GroupVarInfo +GroupingFunc +GroupingSet +GroupingSetData +GroupingSetKind +GroupingSetsPath +GucAction +GucBoolAssignHook +GucBoolCheckHook +GucContext +GucEnumAssignHook +GucEnumCheckHook +GucIntAssignHook +GucIntCheckHook +GucRealAssignHook +GucRealCheckHook +GucShowHook +GucSource +GucStack +GucStackState +GucStringAssignHook +GucStringCheckHook +GzipCompressorState +HANDLE +HASHACTION +HASHBUCKET +HASHCTL +HASHELEMENT +HASHHDR +HASHSEGMENT +HASH_SEQ_STATUS +HE +HEntry +HIST_ENTRY +HKEY +HLOCAL +HMAC_CTX +HMODULE +HOldEntry +HRESULT +HSParser +HSpool +HStore +HTAB +HTSV_Result +HV +Hash +HashAggBatch +HashAggSpill +HashAllocFunc +HashBuildState +HashCompareFunc +HashCopyFunc +HashIndexStat +HashInstrumentation +HashJoin +HashJoinState +HashJoinTable +HashJoinTableData +HashJoinTuple +HashMemoryChunk +HashMetaPage +HashMetaPageData +HashOptions +HashPageOpaque +HashPageOpaqueData +HashPageStat +HashPath +HashScanOpaque +HashScanOpaqueData +HashScanPosData +HashScanPosItem +HashSkewBucket +HashState +HashValueFunc +HbaLine +HeadlineJsonState +HeadlineParsedText +HeadlineWordEntry +HeapCheckContext +HeapPageFreeze +HeapScanDesc +HeapTuple +HeapTupleData +HeapTupleFields +HeapTupleForceOption +HeapTupleFreeze +HeapTupleHeader +HeapTupleHeaderData +HeapTupleTableSlot +HistControl +HotStandbyState +I32 +ICU_Convert_Func +ID +INFIX +INT128 +INTERFACE_INFO +IO +IOContext +IOFuncSelector +IOObject +IOOp +IO_STATUS_BLOCK +IPCompareMethod +ITEM +IV +IdentLine +IdentifierLookup +IdentifySystemCmd +IfStackElem +ImportForeignSchemaStmt +ImportForeignSchemaType +ImportForeignSchema_function +ImportQual +InProgressEnt +IncludeWal +InclusionOpaque +IncrementVarSublevelsUp_context +IncrementalSort +IncrementalSortExecutionStatus +IncrementalSortGroupInfo +IncrementalSortInfo +IncrementalSortPath +IncrementalSortState +Index +IndexAMProperty +IndexAmRoutine +IndexArrayKeyInfo +IndexAttachInfo +IndexAttrBitmapKind +IndexBuildCallback +IndexBuildResult +IndexBulkDeleteCallback +IndexBulkDeleteResult +IndexClause +IndexClauseSet +IndexDeleteCounts +IndexDeletePrefetchState +IndexElem +IndexFetchHeapData +IndexFetchTableData +IndexInfo +IndexList +IndexOnlyScan +IndexOnlyScanState +IndexOptInfo +IndexOrderByDistance +IndexPath +IndexRuntimeKeyInfo +IndexScan +IndexScanDesc +IndexScanState +IndexStateFlagsAction +IndexStmt +IndexTuple +IndexTupleData +IndexUniqueCheck +IndexVacuumInfo +IndxInfo +InferClause +InferenceElem +InfoItem +InhInfo +InheritableSocket +InitSampleScan_function +InitializeDSMForeignScan_function +InitializeWorkerForeignScan_function +InlineCodeBlock +InsertStmt +Instrumentation +Int128AggState +Int8TransTypeData +IntRBTreeNode +Integer +IntegerSet +InternalDefaultACL +InternalGrant +Interval +IntoClause +InvalMessageArray +InvalidationMsgsGroup +IpcMemoryId +IpcMemoryKey +IpcMemoryState +IpcSemaphoreId +IpcSemaphoreKey +IsForeignPathAsyncCapable_function +IsForeignRelUpdatable_function +IsForeignScanParallelSafe_function +IsoConnInfo +IspellDict +Item +ItemId +ItemIdData +ItemPointer +ItemPointerData +IterateDirectModify_function +IterateForeignScan_function +IterateJsonStringValuesState +JEntry +JHashState +JOBOBJECT_BASIC_LIMIT_INFORMATION +JOBOBJECT_BASIC_UI_RESTRICTIONS +JOBOBJECT_SECURITY_LIMIT_INFORMATION +JitContext +JitInstrumentation +JitProviderCallbacks +JitProviderCompileExprCB +JitProviderInit +JitProviderReleaseContextCB +JitProviderResetAfterErrorCB +Join +JoinCostWorkspace +JoinDomain +JoinExpr +JoinHashEntry +JoinPath +JoinPathExtraData +JoinState +JoinTreeItem +JoinType +JsObject +JsValue +JsonAggConstructor +JsonAggState +JsonArrayAgg +JsonArrayConstructor +JsonArrayQueryConstructor +JsonBaseObjectInfo +JsonConstructorExpr +JsonConstructorExprState +JsonConstructorType +JsonEncoding +JsonFormat +JsonFormatType +JsonHashEntry +JsonIsPredicate +JsonIterateStringValuesAction +JsonKeyValue +JsonLexContext +JsonLikeRegexContext +JsonManifestFileField +JsonManifestParseContext +JsonManifestParseState +JsonManifestSemanticState +JsonManifestWALRangeField +JsonObjectAgg +JsonObjectConstructor +JsonOutput +JsonParseContext +JsonParseErrorType +JsonPath +JsonPathBool +JsonPathExecContext +JsonPathExecResult +JsonPathGinAddPathItemFunc +JsonPathGinContext +JsonPathGinExtractNodesFunc +JsonPathGinNode +JsonPathGinNodeType +JsonPathGinPath +JsonPathGinPathItem +JsonPathItem +JsonPathItemType +JsonPathKeyword +JsonPathParseItem +JsonPathParseResult +JsonPathPredicateCallback +JsonPathString +JsonReturning +JsonSemAction +JsonTokenType +JsonTransformStringValuesAction +JsonTypeCategory +JsonUniqueBuilderState +JsonUniqueCheckState +JsonUniqueHashEntry +JsonUniqueParsingState +JsonUniqueStackEntry +JsonValueExpr +JsonValueList +JsonValueListIterator +JsonValueType +Jsonb +JsonbAggState +JsonbContainer +JsonbInState +JsonbIterState +JsonbIterator +JsonbIteratorToken +JsonbPair +JsonbParseState +JsonbSubWorkspace +JsonbTypeCategory +JsonbValue +JumbleState +JunkFilter +KAXCompressReason +KeyAction +KeyActions +KeyArray +KeySuffix +KeyWord +LARGE_INTEGER +LDAP +LDAPMessage +LDAPURLDesc +LDAP_TIMEVAL +LINE +LLVMAttributeRef +LLVMBasicBlockRef +LLVMBuilderRef +LLVMErrorRef +LLVMIntPredicate +LLVMJITEventListenerRef +LLVMJitContext +LLVMJitHandle +LLVMMemoryBufferRef +LLVMModuleRef +LLVMOrcCLookupSet +LLVMOrcCSymbolMapPair +LLVMOrcCSymbolMapPairs +LLVMOrcDefinitionGeneratorRef +LLVMOrcExecutionSessionRef +LLVMOrcJITDylibLookupFlags +LLVMOrcJITDylibRef +LLVMOrcJITTargetAddress +LLVMOrcJITTargetMachineBuilderRef +LLVMOrcLLJITBuilderRef +LLVMOrcLLJITRef +LLVMOrcLookupKind +LLVMOrcLookupStateRef +LLVMOrcMaterializationUnitRef +LLVMOrcObjectLayerRef +LLVMOrcResourceTrackerRef +LLVMOrcSymbolStringPoolRef +LLVMOrcThreadSafeContextRef +LLVMOrcThreadSafeModuleRef +LLVMPassManagerBuilderRef +LLVMPassManagerRef +LLVMTargetMachineRef +LLVMTargetRef +LLVMTypeRef +LLVMValueRef +LOCALLOCK +LOCALLOCKOWNER +LOCALLOCKTAG +LOCALPREDICATELOCK +LOCK +LOCKMASK +LOCKMETHODID +LOCKMODE +LOCKTAG +LONG +LONG_PTR +LOOP +LPARAM +LPBYTE +LPCWSTR +LPSERVICE_STATUS +LPSTR +LPTHREAD_START_ROUTINE +LPTSTR +LPVOID +LPWSTR +LSEG +LUID +LVPagePruneState +LVRelState +LVSavedErrInfo +LWLock +LWLockHandle +LWLockMode +LWLockPadded +LZ4F_compressionContext_t +LZ4F_decompressOptions_t +LZ4F_decompressionContext_t +LZ4F_errorCode_t +LZ4F_preferences_t +LZ4State +LabelProvider +LagTracker +LargeObjectDesc +Latch +LauncherLastStartTimesEntry +LerpFunc +LexDescr +LexemeEntry +LexemeHashKey +LexemeInfo +LexemeKey +LexizeData +LibraryInfo +Limit +LimitOption +LimitPath +LimitState +LimitStateCond +List +ListCell +ListDictionary +ListParsedLex +ListenAction +ListenActionKind +ListenStmt +LoInfo +LoadStmt +LocalBufferLookupEnt +LocalPgBackendStatus +LocalTransactionId +LocationIndex +LocationLen +LockAcquireResult +LockClauseStrength +LockData +LockInfoData +LockInstanceData +LockMethod +LockMethodData +LockRelId +LockRows +LockRowsPath +LockRowsState +LockStmt +LockTagType +LockTupleMode +LockViewRecurse_context +LockWaitPolicy +LockingClause +LogOpts +LogStmtLevel +LogicalDecodeBeginCB +LogicalDecodeBeginPrepareCB +LogicalDecodeChangeCB +LogicalDecodeCommitCB +LogicalDecodeCommitPreparedCB +LogicalDecodeFilterByOriginCB +LogicalDecodeFilterPrepareCB +LogicalDecodeMessageCB +LogicalDecodePrepareCB +LogicalDecodeRollbackPreparedCB +LogicalDecodeShutdownCB +LogicalDecodeStartupCB +LogicalDecodeStreamAbortCB +LogicalDecodeStreamChangeCB +LogicalDecodeStreamCommitCB +LogicalDecodeStreamMessageCB +LogicalDecodeStreamPrepareCB +LogicalDecodeStreamStartCB +LogicalDecodeStreamStopCB +LogicalDecodeStreamTruncateCB +LogicalDecodeTruncateCB +LogicalDecodingContext +LogicalErrorCallbackState +LogicalOutputPluginInit +LogicalOutputPluginWriterPrepareWrite +LogicalOutputPluginWriterUpdateProgress +LogicalOutputPluginWriterWrite +LogicalRepBeginData +LogicalRepCommitData +LogicalRepCommitPreparedTxnData +LogicalRepCtxStruct +LogicalRepMsgType +LogicalRepPartMapEntry +LogicalRepPreparedTxnData +LogicalRepRelId +LogicalRepRelMapEntry +LogicalRepRelation +LogicalRepRollbackPreparedTxnData +LogicalRepStreamAbortData +LogicalRepTupleData +LogicalRepTyp +LogicalRepWorker +LogicalRewriteMappingData +LogicalTape +LogicalTapeSet +LsnReadQueue +LsnReadQueueNextFun +LsnReadQueueNextStatus +LtreeGistOptions +LtreeSignature +MAGIC +MBuf +MCVItem +MCVList +MEMORY_BASIC_INFORMATION +MGVTBL +MINIDUMPWRITEDUMP +MINIDUMP_TYPE +MJEvalResult +MTTargetRelLookup +MVDependencies +MVDependency +MVNDistinct +MVNDistinctItem +Material +MaterialPath +MaterialState +MdfdVec +Memoize +MemoizeEntry +MemoizeInstrumentation +MemoizeKey +MemoizePath +MemoizeState +MemoizeTuple +MemoryChunk +MemoryContext +MemoryContextCallback +MemoryContextCallbackFunction +MemoryContextCounters +MemoryContextData +MemoryContextMethodID +MemoryContextMethods +MemoryStatsPrintFunc +MergeAction +MergeActionState +MergeAppend +MergeAppendPath +MergeAppendState +MergeJoin +MergeJoinClause +MergeJoinState +MergePath +MergeScanSelCache +MergeStmt +MergeWhenClause +MetaCommand +MinMaxAggInfo +MinMaxAggPath +MinMaxExpr +MinMaxMultiOptions +MinMaxOp +MinimalTuple +MinimalTupleData +MinimalTupleTableSlot +MinmaxMultiOpaque +MinmaxOpaque +ModifyTable +ModifyTableContext +ModifyTablePath +ModifyTableState +MonotonicFunction +MorphOpaque +MsgType +MultiAssignRef +MultiSortSupport +MultiSortSupportData +MultiXactId +MultiXactMember +MultiXactOffset +MultiXactStateData +MultiXactStatus +MultirangeIOData +MultirangeParseState +MultirangeType +NDBOX +NLSVERSIONINFOEX +NODE +NTSTATUS +NUMCacheEntry +NUMDesc +NUMProc +NV +Name +NameData +NameHashEntry +NamedArgExpr +NamedLWLockTranche +NamedLWLockTrancheRequest +NamedTuplestoreScan +NamedTuplestoreScanState +NamespaceInfo +NestLoop +NestLoopParam +NestLoopState +NestPath +NewColumnValue +NewConstraint +NextSampleBlock_function +NextSampleTuple_function +NextValueExpr +Node +NodeTag +NonEmptyRange +Notification +NotificationHash +NotificationList +NotifyStmt +Nsrt +NtDllRoutine +NtFlushBuffersFileEx_t +NullIfExpr +NullTest +NullTestType +NullableDatum +NullingRelsMatch +Numeric +NumericAggState +NumericDigit +NumericSortSupport +NumericSumAccum +NumericVar +OM_uint32 +OP +OSAPerGroupState +OSAPerQueryState +OSInfo +OSSLCipher +OSSLDigest +OVERLAPPED +ObjectAccessDrop +ObjectAccessNamespaceSearch +ObjectAccessPostAlter +ObjectAccessPostCreate +ObjectAccessType +ObjectAddress +ObjectAddressAndFlags +ObjectAddressExtra +ObjectAddressStack +ObjectAddresses +ObjectClass +ObjectPropertyType +ObjectType +ObjectWithArgs +Offset +OffsetNumber +OffsetVarNodes_context +Oid +OidOptions +OkeysState +OldSnapshotControlData +OldSnapshotTimeMapping +OldToNewMapping +OldToNewMappingData +OnCommitAction +OnCommitItem +OnConflictAction +OnConflictClause +OnConflictExpr +OnConflictSetState +OpBtreeInterpretation +OpClassCacheEnt +OpExpr +OpFamilyMember +OpFamilyOpFuncGroup +OpclassInfo +Operator +OperatorElement +OpfamilyInfo +OprCacheEntry +OprCacheKey +OprInfo +OprProofCacheEntry +OprProofCacheKey +OuterJoinClauseInfo +OutputPluginCallbacks +OutputPluginOptions +OutputPluginOutputType +OverrideSearchPath +OverrideStackEntry +OverridingKind +PACE_HEADER +PACL +PATH +PCtxtHandle +PERL_CONTEXT +PERL_SI +PFN +PGAlignedBlock +PGAlignedXLogBlock +PGAsyncStatusType +PGCALL2 +PGChecksummablePage +PGContextVisibility +PGEvent +PGEventConnDestroy +PGEventConnReset +PGEventId +PGEventProc +PGEventRegister +PGEventResultCopy +PGEventResultCreate +PGEventResultDestroy +PGFInfoFunction +PGFileType +PGFunction +PGIOAlignedBlock +PGLZ_HistEntry +PGLZ_Strategy +PGLoadBalanceType +PGMessageField +PGModuleMagicFunction +PGNoticeHooks +PGOutputData +PGOutputTxnData +PGPROC +PGP_CFB +PGP_Context +PGP_MPI +PGP_PubKey +PGP_S2K +PGPing +PGQueryClass +PGRUsage +PGSemaphore +PGSemaphoreData +PGShmemHeader +PGTargetServerType +PGTernaryBool +PGTransactionStatusType +PGVerbosity +PG_Locale_Strategy +PG_Lock_Status +PG_init_t +PGcancel +PGcmdQueueEntry +PGconn +PGdataValue +PGlobjfuncs +PGnotify +PGpipelineStatus +PGresAttDesc +PGresAttValue +PGresParamDesc +PGresult +PGresult_data +PIO_STATUS_BLOCK +PLAINTREE +PLAssignStmt +PLcword +PLpgSQL_case_when +PLpgSQL_condition +PLpgSQL_datum +PLpgSQL_datum_type +PLpgSQL_diag_item +PLpgSQL_exception +PLpgSQL_exception_block +PLpgSQL_execstate +PLpgSQL_expr +PLpgSQL_func_hashkey +PLpgSQL_function +PLpgSQL_getdiag_kind +PLpgSQL_if_elsif +PLpgSQL_label_type +PLpgSQL_nsitem +PLpgSQL_nsitem_type +PLpgSQL_plugin +PLpgSQL_promise_type +PLpgSQL_raise_option +PLpgSQL_raise_option_type +PLpgSQL_rec +PLpgSQL_recfield +PLpgSQL_resolve_option +PLpgSQL_row +PLpgSQL_stmt +PLpgSQL_stmt_assert +PLpgSQL_stmt_assign +PLpgSQL_stmt_block +PLpgSQL_stmt_call +PLpgSQL_stmt_case +PLpgSQL_stmt_close +PLpgSQL_stmt_commit +PLpgSQL_stmt_dynexecute +PLpgSQL_stmt_dynfors +PLpgSQL_stmt_execsql +PLpgSQL_stmt_exit +PLpgSQL_stmt_fetch +PLpgSQL_stmt_forc +PLpgSQL_stmt_foreach_a +PLpgSQL_stmt_fori +PLpgSQL_stmt_forq +PLpgSQL_stmt_fors +PLpgSQL_stmt_getdiag +PLpgSQL_stmt_if +PLpgSQL_stmt_loop +PLpgSQL_stmt_open +PLpgSQL_stmt_perform +PLpgSQL_stmt_raise +PLpgSQL_stmt_return +PLpgSQL_stmt_return_next +PLpgSQL_stmt_return_query +PLpgSQL_stmt_rollback +PLpgSQL_stmt_type +PLpgSQL_stmt_while +PLpgSQL_trigtype +PLpgSQL_type +PLpgSQL_type_type +PLpgSQL_var +PLpgSQL_variable +PLwdatum +PLword +PLyArrayToOb +PLyCursorObject +PLyDatumToOb +PLyDatumToObFunc +PLyExceptionEntry +PLyExecutionContext +PLyObToArray +PLyObToDatum +PLyObToDatumFunc +PLyObToDomain +PLyObToScalar +PLyObToTransform +PLyObToTuple +PLyObject_AsString_t +PLyPlanObject +PLyProcedure +PLyProcedureEntry +PLyProcedureKey +PLyResultObject +PLySRFState +PLySavedArgs +PLyScalarToOb +PLySubtransactionData +PLySubtransactionObject +PLyTransformToOb +PLyTupleToOb +PLyUnicode_FromStringAndSize_t +PLy_elog_impl_t +PMINIDUMP_CALLBACK_INFORMATION +PMINIDUMP_EXCEPTION_INFORMATION +PMINIDUMP_USER_STREAM_INFORMATION +PMSignalData +PMSignalReason +PMState +POLYGON +PQArgBlock +PQEnvironmentOption +PQExpBuffer +PQExpBufferData +PQcommMethods +PQconninfoOption +PQnoticeProcessor +PQnoticeReceiver +PQprintOpt +PQsslKeyPassHook_OpenSSL_type +PREDICATELOCK +PREDICATELOCKTAG +PREDICATELOCKTARGET +PREDICATELOCKTARGETTAG +PROCESS_INFORMATION +PROCLOCK +PROCLOCKTAG +PROC_HDR +PSID +PSQL_COMP_CASE +PSQL_ECHO +PSQL_ECHO_HIDDEN +PSQL_ERROR_ROLLBACK +PTEntryArray +PTIterationArray +PTOKEN_PRIVILEGES +PTOKEN_USER +PUTENVPROC +PVIndStats +PVIndVacStatus +PVOID +PVShared +PX_Alias +PX_Cipher +PX_Combo +PX_HMAC +PX_MD +Page +PageData +PageGistNSN +PageHeader +PageHeaderData +PageXLogRecPtr +PagetableEntry +Pairs +ParallelAppendState +ParallelApplyWorkerEntry +ParallelApplyWorkerInfo +ParallelApplyWorkerShared +ParallelBitmapHeapState +ParallelBlockTableScanDesc +ParallelBlockTableScanWorker +ParallelBlockTableScanWorkerData +ParallelCompletionPtr +ParallelContext +ParallelExecutorInfo +ParallelHashGrowth +ParallelHashJoinBatch +ParallelHashJoinBatchAccessor +ParallelHashJoinState +ParallelIndexScanDesc +ParallelReadyList +ParallelSlot +ParallelSlotArray +ParallelSlotResultHandler +ParallelState +ParallelTableScanDesc +ParallelTableScanDescData +ParallelTransState +ParallelVacuumState +ParallelWorkerContext +ParallelWorkerInfo +Param +ParamCompileHook +ParamExecData +ParamExternData +ParamFetchHook +ParamKind +ParamListInfo +ParamPathInfo +ParamRef +ParamsErrorCbData +ParentMapEntry +ParseCallbackState +ParseExprKind +ParseNamespaceColumn +ParseNamespaceItem +ParseParamRefHook +ParseState +ParsedLex +ParsedScript +ParsedText +ParsedWord +ParserSetupHook +ParserState +PartClauseInfo +PartClauseMatchStatus +PartClauseTarget +PartialFileSetState +PartitionBoundInfo +PartitionBoundInfoData +PartitionBoundSpec +PartitionCmd +PartitionDesc +PartitionDescData +PartitionDirectory +PartitionDirectoryEntry +PartitionDispatch +PartitionElem +PartitionHashBound +PartitionKey +PartitionListValue +PartitionMap +PartitionPruneCombineOp +PartitionPruneContext +PartitionPruneInfo +PartitionPruneState +PartitionPruneStep +PartitionPruneStepCombine +PartitionPruneStepOp +PartitionPruningData +PartitionRangeBound +PartitionRangeDatum +PartitionRangeDatumKind +PartitionScheme +PartitionSpec +PartitionStrategy +PartitionTupleRouting +PartitionedRelPruneInfo +PartitionedRelPruningData +PartitionwiseAggregateType +PasswordType +Path +PathClauseUsage +PathCostComparison +PathHashStack +PathKey +PathKeysComparison +PathTarget +PatternInfo +PatternInfoArray +Pattern_Prefix_Status +Pattern_Type +PendingFsyncEntry +PendingRelDelete +PendingRelSync +PendingUnlinkEntry +PendingWriteback +PerLockTagEntry +PerlInterpreter +Perl_ppaddr_t +Permutation +PermutationStep +PermutationStepBlocker +PermutationStepBlockerType +PgArchData +PgBackendGSSStatus +PgBackendSSLStatus +PgBackendStatus +PgBenchExpr +PgBenchExprLink +PgBenchExprList +PgBenchExprType +PgBenchFunction +PgBenchValue +PgBenchValueType +PgChecksumMode +PgFdwAnalyzeState +PgFdwConnState +PgFdwDirectModifyState +PgFdwModifyState +PgFdwOption +PgFdwPathExtraData +PgFdwRelationInfo +PgFdwSamplingMethod +PgFdwScanState +PgIfAddrCallback +PgStatShared_Archiver +PgStatShared_BgWriter +PgStatShared_Checkpointer +PgStatShared_Common +PgStatShared_Database +PgStatShared_Function +PgStatShared_HashEntry +PgStatShared_IO +PgStatShared_Relation +PgStatShared_ReplSlot +PgStatShared_SLRU +PgStatShared_Subscription +PgStatShared_Wal +PgStat_ArchiverStats +PgStat_BackendSubEntry +PgStat_BgWriterStats +PgStat_BktypeIO +PgStat_CheckpointerStats +PgStat_Counter +PgStat_EntryRef +PgStat_EntryRefHashEntry +PgStat_FetchConsistency +PgStat_FunctionCallUsage +PgStat_FunctionCounts +PgStat_HashKey +PgStat_IO +PgStat_Kind +PgStat_KindInfo +PgStat_LocalState +PgStat_PendingDroppedStatsItem +PgStat_PendingIO +PgStat_PendingWalStats +PgStat_SLRUStats +PgStat_ShmemControl +PgStat_Snapshot +PgStat_SnapshotEntry +PgStat_StatDBEntry +PgStat_StatFuncEntry +PgStat_StatReplSlotEntry +PgStat_StatSubEntry +PgStat_StatTabEntry +PgStat_SubXactStatus +PgStat_TableCounts +PgStat_TableStatus +PgStat_TableXactStatus +PgStat_WalStats +PgXmlErrorContext +PgXmlStrictness +Pg_finfo_record +Pg_magic_struct +PipeProtoChunk +PipeProtoHeader +PlaceHolderInfo +PlaceHolderVar +Plan +PlanDirectModify_function +PlanForeignModify_function +PlanInvalItem +PlanRowMark +PlanState +PlannedStmt +PlannerGlobal +PlannerInfo +PlannerParamItem +Point +Pointer +PolicyInfo +PolyNumAggState +Pool +PopulateArrayContext +PopulateArrayState +PopulateRecordCache +PopulateRecordsetState +Port +Portal +PortalHashEnt +PortalStatus +PortalStrategy +PostParseColumnRefHook +PostgresPollingStatusType +PostingItem +PreParseColumnRefHook +PredClass +PredIterInfo +PredIterInfoData +PredXactList +PredicateLockData +PredicateLockTargetType +PrefetchBufferResult +PrepParallelRestorePtrType +PrepareStmt +PreparedStatement +PresortedKeyData +PrewarmType +PrintExtraTocPtrType +PrintTocDataPtrType +PrintfArgType +PrintfArgValue +PrintfTarget +PrinttupAttrInfo +PrivTarget +PrivateRefCountEntry +ProcArrayStruct +ProcLangInfo +ProcSignalBarrierType +ProcSignalHeader +ProcSignalReason +ProcSignalSlot +ProcState +ProcWaitStatus +ProcessUtilityContext +ProcessUtility_hook_type +ProcessingMode +ProgressCommandType +ProjectSet +ProjectSetPath +ProjectSetState +ProjectionInfo +ProjectionPath +PromptInterruptContext +ProtocolVersion +PrsStorage +PruneState +PruneStepResult +PsqlScanCallbacks +PsqlScanQuoteType +PsqlScanResult +PsqlScanState +PsqlScanStateData +PsqlSettings +Publication +PublicationActions +PublicationDesc +PublicationInfo +PublicationObjSpec +PublicationObjSpecType +PublicationPartOpt +PublicationRelInfo +PublicationSchemaInfo +PublicationTable +PullFilter +PullFilterOps +PushFilter +PushFilterOps +PushFunction +PyCFunction +PyMappingMethods +PyMethodDef +PyModuleDef +PyObject +PySequenceMethods +PyTypeObject +Py_ssize_t +QPRS_STATE +QTN2QTState +QTNode +QUERYTYPE +QualCost +QualItem +Query +QueryCompletion +QueryDesc +QueryEnvironment +QueryInfo +QueryItem +QueryItemType +QueryMode +QueryOperand +QueryOperator +QueryRepresentation +QueryRepresentationOperand +QuerySource +QueueBackendStatus +QueuePosition +QuitSignalReason +RBTNode +RBTOrderControl +RBTree +RBTreeIterator +REPARSE_JUNCTION_DATA_BUFFER +RIX +RI_CompareHashEntry +RI_CompareKey +RI_ConstraintInfo +RI_QueryHashEntry +RI_QueryKey +RTEKind +RTEPermissionInfo +RWConflict +RWConflictData +RWConflictPoolHeader +Range +RangeBound +RangeBox +RangeFunction +RangeIOData +RangeQueryClause +RangeSubselect +RangeTableFunc +RangeTableFuncCol +RangeTableSample +RangeTblEntry +RangeTblFunction +RangeTblRef +RangeType +RangeVar +RangeVarGetRelidCallback +Ranges +RawColumnDefault +RawParseMode +RawStmt +ReInitializeDSMForeignScan_function +ReScanForeignScan_function +ReadBufPtrType +ReadBufferMode +ReadBytePtrType +ReadExtraTocPtrType +ReadFunc +ReadLocalXLogPageNoWaitPrivate +ReadReplicationSlotCmd +ReassignOwnedStmt +RecheckForeignScan_function +RecordCacheArrayEntry +RecordCacheEntry +RecordCompareData +RecordIOData +RecoveryLockEntry +RecoveryLockXidEntry +RecoveryPauseState +RecoveryState +RecoveryTargetTimeLineGoal +RecoveryTargetType +RectBox +RecursionContext +RecursiveUnion +RecursiveUnionPath +RecursiveUnionState +RefetchForeignRow_function +RefreshMatViewStmt +RegProcedure +Regis +RegisNode +RegisteredBgWorker +ReindexErrorInfo +ReindexIndexInfo +ReindexObjectType +ReindexParams +ReindexStmt +ReindexType +RelFileLocator +RelFileLocatorBackend +RelFileNumber +RelIdCacheEnt +RelInfo +RelInfoArr +RelMapFile +RelMapping +RelOptInfo +RelOptKind +RelToCheck +RelToCluster +RelabelType +Relation +RelationData +RelationInfo +RelationPtr +RelationSyncEntry +RelcacheCallbackFunction +ReleaseMatchCB +RelfilenumberMapEntry +RelfilenumberMapKey +Relids +RelocationBufferInfo +RelptrFreePageBtree +RelptrFreePageManager +RelptrFreePageSpanLeader +RenameStmt +ReopenPtrType +ReorderBuffer +ReorderBufferApplyChangeCB +ReorderBufferApplyTruncateCB +ReorderBufferBeginCB +ReorderBufferChange +ReorderBufferChangeType +ReorderBufferCommitCB +ReorderBufferCommitPreparedCB +ReorderBufferDiskChange +ReorderBufferIterTXNEntry +ReorderBufferIterTXNState +ReorderBufferMessageCB +ReorderBufferPrepareCB +ReorderBufferRollbackPreparedCB +ReorderBufferStreamAbortCB +ReorderBufferStreamChangeCB +ReorderBufferStreamCommitCB +ReorderBufferStreamMessageCB +ReorderBufferStreamPrepareCB +ReorderBufferStreamStartCB +ReorderBufferStreamStopCB +ReorderBufferStreamTruncateCB +ReorderBufferTXN +ReorderBufferTXNByIdEnt +ReorderBufferToastEnt +ReorderBufferTupleBuf +ReorderBufferTupleCidEnt +ReorderBufferTupleCidKey +ReorderBufferUpdateProgressTxnCB +ReorderTuple +RepOriginId +ReparameterizeForeignPathByChild_function +ReplaceVarsFromTargetList_context +ReplaceVarsNoMatchOption +ReplicaIdentityStmt +ReplicationKind +ReplicationSlot +ReplicationSlotCtlData +ReplicationSlotInvalidationCause +ReplicationSlotOnDisk +ReplicationSlotPersistency +ReplicationSlotPersistentData +ReplicationState +ReplicationStateCtl +ReplicationStateOnDisk +ResTarget +ReservoirState +ReservoirStateData +ResourceArray +ResourceOwner +ResourceReleaseCallback +ResourceReleaseCallbackItem +ResourceReleasePhase +RestoreOptions +RestorePass +RestrictInfo +Result +ResultRelInfo +ResultState +ReturnSetInfo +ReturnStmt +RevmapContents +RevokeRoleGrantAction +RewriteMappingDataEntry +RewriteMappingFile +RewriteRule +RewriteState +RmgrData +RmgrDescData +RmgrId +RoleNameEntry +RoleNameItem +RoleSpec +RoleSpecType +RoleStmtType +RollupData +RowCompareExpr +RowCompareType +RowExpr +RowIdentityVarInfo +RowMarkClause +RowMarkType +RowSecurityDesc +RowSecurityPolicy +RtlGetLastNtStatus_t +RtlNtStatusToDosError_t +RuleInfo +RuleLock +RuleStmt +RunningTransactions +RunningTransactionsData +SC_HANDLE +SECURITY_ATTRIBUTES +SECURITY_STATUS +SEG +SERIALIZABLEXACT +SERIALIZABLEXID +SERIALIZABLEXIDTAG +SERVICE_STATUS +SERVICE_STATUS_HANDLE +SERVICE_TABLE_ENTRY +SID_AND_ATTRIBUTES +SID_IDENTIFIER_AUTHORITY +SID_NAME_USE +SISeg +SIZE_T +SMgrRelation +SMgrRelationData +SMgrSortArray +SOCKADDR +SOCKET +SPELL +SPICallbackArg +SPIExecuteOptions +SPIParseOpenOptions +SPIPlanPtr +SPIPrepareOptions +SPITupleTable +SPLITCOST +SPNode +SPNodeData +SPPageDesc +SQLDropObject +SQLFunctionCache +SQLFunctionCachePtr +SQLFunctionParseInfo +SQLFunctionParseInfoPtr +SQLValueFunction +SQLValueFunctionOp +SSL +SSLExtensionInfoContext +SSL_CTX +STARTUPINFO +STRLEN +SV +SYNCHRONIZATION_BARRIER +SampleScan +SampleScanGetSampleSize_function +SampleScanState +SavedTransactionCharacteristics +ScalarArrayOpExpr +ScalarArrayOpExprHashEntry +ScalarArrayOpExprHashTable +ScalarIOData +ScalarItem +ScalarMCVItem +Scan +ScanDirection +ScanKey +ScanKeyData +ScanKeywordHashFunc +ScanKeywordList +ScanState +ScanTypeControl +ScannerCallbackState +SchemaQuery +SecBuffer +SecBufferDesc +SecLabelItem +SecLabelStmt +SeenRelsEntry +SelectLimit +SelectStmt +Selectivity +SemTPadded +SemiAntiJoinFactors +SeqScan +SeqScanState +SeqTable +SeqTableData +SerCommitSeqNo +SerialControl +SerialIOData +SerializableXactHandle +SerializedActiveRelMaps +SerializedClientConnectionInfo +SerializedRanges +SerializedReindexState +SerializedSnapshotData +SerializedTransactionState +Session +SessionBackupState +SessionEndType +SetConstraintState +SetConstraintStateData +SetConstraintTriggerData +SetExprState +SetFunctionReturnMode +SetOp +SetOpCmd +SetOpPath +SetOpState +SetOpStatePerGroup +SetOpStrategy +SetOperation +SetOperationStmt +SetQuantifier +SetToDefault +SetupWorkerPtrType +ShDependObjectInfo +SharedAggInfo +SharedBitmapState +SharedDependencyObjectType +SharedDependencyType +SharedExecutorInstrumentation +SharedFileSet +SharedHashInfo +SharedIncrementalSortInfo +SharedInvalCatalogMsg +SharedInvalCatcacheMsg +SharedInvalRelcacheMsg +SharedInvalRelmapMsg +SharedInvalSmgrMsg +SharedInvalSnapshotMsg +SharedInvalidationMessage +SharedJitInstrumentation +SharedMemoizeInfo +SharedRecordTableEntry +SharedRecordTableKey +SharedRecordTypmodRegistry +SharedSortInfo +SharedTuplestore +SharedTuplestoreAccessor +SharedTuplestoreChunk +SharedTuplestoreParticipant +SharedTypmodTableEntry +Sharedsort +ShellTypeInfo +ShippableCacheEntry +ShippableCacheKey +ShmemIndexEnt +ShutdownForeignScan_function +ShutdownInformation +ShutdownMode +SignTSVector +SimpleActionList +SimpleActionListCell +SimpleEcontextStackEntry +SimpleOidList +SimpleOidListCell +SimplePtrList +SimplePtrListCell +SimpleStats +SimpleStringList +SimpleStringListCell +SingleBoundSortItem +Size +SkipPages +SlabBlock +SlabContext +SlabSlot +SlotNumber +SlruCtl +SlruCtlData +SlruErrorCause +SlruPageStatus +SlruScanCallback +SlruShared +SlruSharedData +SlruWriteAll +SlruWriteAllData +SnapBuild +SnapBuildOnDisk +SnapBuildState +Snapshot +SnapshotData +SnapshotType +SockAddr +Sort +SortBy +SortByDir +SortByNulls +SortCoordinate +SortGroupClause +SortItem +SortPath +SortShimExtra +SortState +SortSupport +SortSupportData +SortTuple +SortTupleComparator +SortedPoint +SpGistBuildState +SpGistCache +SpGistDeadTuple +SpGistDeadTupleData +SpGistInnerTuple +SpGistInnerTupleData +SpGistLUPCache +SpGistLastUsedPage +SpGistLeafTuple +SpGistLeafTupleData +SpGistMetaPageData +SpGistNodeTuple +SpGistNodeTupleData +SpGistOptions +SpGistPageOpaque +SpGistPageOpaqueData +SpGistScanOpaque +SpGistScanOpaqueData +SpGistSearchItem +SpGistState +SpGistTypeDesc +SpecialJoinInfo +SpinDelayStatus +SplitInterval +SplitLR +SplitPoint +SplitTextOutputData +SplitVar +SplitedPageLayout +StackElem +StartDataPtrType +StartLOPtrType +StartLOsPtrType +StartReplicationCmd +StartupStatusEnum +StatEntry +StatExtEntry +StateFileChunk +StatisticExtInfo +StatsBuildData +StatsData +StatsElem +StatsExtInfo +StdAnalyzeData +StdRdOptIndexCleanup +StdRdOptions +Step +StopList +StrategyNumber +StreamCtl +String +StringInfo +StringInfoData +StripnullState +SubLink +SubLinkType +SubOpts +SubPlan +SubPlanState +SubRemoveRels +SubTransactionId +SubXactCallback +SubXactCallbackItem +SubXactEvent +SubXactInfo +SubqueryScan +SubqueryScanPath +SubqueryScanState +SubqueryScanStatus +SubscriptExecSetup +SubscriptExecSteps +SubscriptRoutines +SubscriptTransform +SubscriptingRef +SubscriptingRefState +Subscription +SubscriptionInfo +SubscriptionRelState +SupportRequestCost +SupportRequestIndexCondition +SupportRequestOptimizeWindowClause +SupportRequestRows +SupportRequestSelectivity +SupportRequestSimplify +SupportRequestWFuncMonotonic +Syn +SyncOps +SyncRepConfigData +SyncRepStandbyData +SyncRequestHandler +SyncRequestType +SysFKRelationship +SysScanDesc +SyscacheCallbackFunction +SystemRowsSamplerData +SystemSamplerData +SystemTimeSamplerData +TAPtype +TAR_MEMBER +TBMIterateResult +TBMIteratingState +TBMIterator +TBMSharedIterator +TBMSharedIteratorState +TBMStatus +TBlockState +TIDBitmap +TM_FailureData +TM_IndexDelete +TM_IndexDeleteOp +TM_IndexStatus +TM_Result +TOKEN_DEFAULT_DACL +TOKEN_INFORMATION_CLASS +TOKEN_PRIVILEGES +TOKEN_USER +TParser +TParserCharTest +TParserPosition +TParserSpecial +TParserState +TParserStateAction +TParserStateActionItem +TQueueDestReceiver +TRGM +TSAnyCacheEntry +TSConfigCacheEntry +TSConfigInfo +TSDictInfo +TSDictionaryCacheEntry +TSExecuteCallback +TSLexeme +TSParserCacheEntry +TSParserInfo +TSQuery +TSQueryData +TSQueryParserState +TSQuerySign +TSReadPointer +TSTemplateInfo +TSTernaryValue +TSTokenTypeItem +TSTokenTypeStorage +TSVector +TSVectorBuildState +TSVectorData +TSVectorParseState +TSVectorStat +TState +TStatus +TStoreState +TU_UpdateIndexes +TXNEntryFile +TYPCATEGORY +T_Action +T_WorkerStatus +TableAmRoutine +TableAttachInfo +TableDataInfo +TableFunc +TableFuncRoutine +TableFuncScan +TableFuncScanState +TableInfo +TableLikeClause +TableSampleClause +TableScanDesc +TableScanDescData +TableSpaceCacheEntry +TableSpaceOpts +TablespaceList +TablespaceListCell +TapeBlockTrailer +TapeShare +TarMethodData +TarMethodFile +TargetEntry +TclExceptionNameMap +Tcl_DString +Tcl_FileProc +Tcl_HashEntry +Tcl_HashTable +Tcl_Interp +Tcl_NotifierProcs +Tcl_Obj +Tcl_Time +TempNamespaceStatus +TestDecodingData +TestDecodingTxnData +TestSpec +TextFreq +TextPositionState +TheLexeme +TheSubstitute +TidExpr +TidExprType +TidHashKey +TidOpExpr +TidPath +TidRangePath +TidRangeScan +TidRangeScanState +TidScan +TidScanState +TimeADT +TimeLineHistoryCmd +TimeLineHistoryEntry +TimeLineID +TimeOffset +TimeStamp +TimeTzADT +TimeZoneAbbrevTable +TimeoutId +TimeoutType +Timestamp +TimestampTz +TmFromChar +TmToChar +ToastAttrInfo +ToastCompressionId +ToastTupleContext +ToastedAttribute +TocEntry +TokenAuxData +TokenizedAuthLine +TrackItem +TransApplyAction +TransInvalidationInfo +TransState +TransactionId +TransactionState +TransactionStateData +TransactionStmt +TransactionStmtKind +TransformInfo +TransformJsonStringValuesState +TransitionCaptureState +TrgmArc +TrgmArcInfo +TrgmBound +TrgmColor +TrgmColorInfo +TrgmGistOptions +TrgmNFA +TrgmPackArcInfo +TrgmPackedArc +TrgmPackedGraph +TrgmPackedState +TrgmPrefix +TrgmState +TrgmStateKey +TrieChar +Trigger +TriggerData +TriggerDesc +TriggerEvent +TriggerFlags +TriggerInfo +TriggerTransition +TruncateStmt +TsmRoutine +TupOutputState +TupSortStatus +TupStoreStatus +TupleConstr +TupleConversionMap +TupleDesc +TupleHashEntry +TupleHashEntryData +TupleHashIterator +TupleHashTable +TupleQueueReader +TupleTableSlot +TupleTableSlotOps +TuplesortClusterArg +TuplesortDatumArg +TuplesortIndexArg +TuplesortIndexBTreeArg +TuplesortIndexHashArg +TuplesortInstrumentation +TuplesortMethod +TuplesortPublic +TuplesortSpaceType +Tuplesortstate +Tuplestorestate +TwoPhaseCallback +TwoPhaseFileHeader +TwoPhaseLockRecord +TwoPhasePgStatRecord +TwoPhasePredicateLockRecord +TwoPhasePredicateRecord +TwoPhasePredicateRecordType +TwoPhasePredicateXactRecord +TwoPhaseRecordOnDisk +TwoPhaseRmgrId +TwoPhaseStateData +Type +TypeCacheEntry +TypeCacheEnumData +TypeCast +TypeCat +TypeFuncClass +TypeInfo +TypeName +U32 +U8 +UChar +UCharIterator +UColAttributeValue +UCollator +UConverter +UErrorCode +UINT +ULARGE_INTEGER +ULONG +ULONG_PTR +UV +UVersionInfo +UnicodeNormalizationForm +UnicodeNormalizationQC +Unique +UniquePath +UniquePathMethod +UniqueState +UnlistenStmt +UnresolvedTup +UnresolvedTupData +UpdateContext +UpdateStmt +UpperRelationKind +UpperUniquePath +UserAuth +UserContext +UserMapping +UserOpts +VacAttrStats +VacAttrStatsP +VacDeadItems +VacErrPhase +VacObjFilter +VacOptValue +VacuumParams +VacuumRelation +VacuumStmt +ValidIOData +ValidateIndexState +ValuesScan +ValuesScanState +Var +VarBit +VarChar +VarParamState +VarString +VarStringSortSupport +Variable +VariableAssignHook +VariableCache +VariableCacheData +VariableSetKind +VariableSetStmt +VariableShowStmt +VariableSpace +VariableStatData +VariableSubstituteHook +Variables +Vector32 +Vector8 +VersionedQuery +Vfd +ViewCheckOption +ViewOptCheckOption +ViewOptions +ViewStmt +VirtualTransactionId +VirtualTupleTableSlot +VolatileFunctionStatus +Vsrt +WAIT_ORDER +WALAvailability +WALInsertLock +WALInsertLockPadded +WALOpenSegment +WALReadError +WALSegmentCloseCB +WALSegmentContext +WALSegmentOpenCB +WCHAR +WCOKind +WFW_WaitOption +WIDGET +WORD +WORKSTATE +WSABUF +WSADATA +WSANETWORKEVENTS +WSAPROTOCOL_INFO +WaitEvent +WaitEventActivity +WaitEventClient +WaitEventIO +WaitEventIPC +WaitEventSet +WaitEventTimeout +WaitPMResult +WalCloseMethod +WalCompression +WalLevel +WalRcvData +WalRcvExecResult +WalRcvExecStatus +WalRcvState +WalRcvStreamOptions +WalRcvWakeupReason +WalReceiverConn +WalReceiverFunctionsType +WalSnd +WalSndCtlData +WalSndSendDataCallback +WalSndState +WalTimeSample +WalUsage +WalWriteMethod +WalWriteMethodOps +Walfile +WindowAgg +WindowAggPath +WindowAggState +WindowAggStatus +WindowClause +WindowClauseSortData +WindowDef +WindowFunc +WindowFuncExprState +WindowFuncLists +WindowObject +WindowObjectData +WindowStatePerAgg +WindowStatePerAggData +WindowStatePerFunc +WithCheckOption +WithClause +WordEntry +WordEntryIN +WordEntryPos +WordEntryPosVector +WordEntryPosVector1 +WorkTableScan +WorkTableScanState +WorkerInfo +WorkerInfoData +WorkerInstrumentation +WorkerJobDumpPtrType +WorkerJobRestorePtrType +Working_State +WriteBufPtrType +WriteBytePtrType +WriteDataCallback +WriteDataPtrType +WriteExtraTocPtrType +WriteFunc +WriteManifestState +WriteTarState +WritebackContext +X509 +X509_EXTENSION +X509_NAME +X509_NAME_ENTRY +X509_STORE +X509_STORE_CTX +XLTW_Oper +XLogCtlData +XLogCtlInsert +XLogDumpConfig +XLogDumpPrivate +XLogLongPageHeader +XLogLongPageHeaderData +XLogPageHeader +XLogPageHeaderData +XLogPageReadCB +XLogPageReadPrivate +XLogPageReadResult +XLogPrefetchStats +XLogPrefetcher +XLogPrefetcherFilter +XLogReaderRoutine +XLogReaderState +XLogRecData +XLogRecPtr +XLogRecStats +XLogRecord +XLogRecordBlockCompressHeader +XLogRecordBlockHeader +XLogRecordBlockImageHeader +XLogRecordBuffer +XLogRecoveryCtlData +XLogRedoAction +XLogSegNo +XLogSource +XLogStats +XLogwrtResult +XLogwrtRqst +XPV +XPVIV +XPVMG +XactCallback +XactCallbackItem +XactEvent +XactLockTableWaitInfo +XidBoundsViolation +XidCacheStatus +XidCommitStatus +XidStatus +XmlExpr +XmlExprOp +XmlOptionType +XmlSerialize +XmlTableBuilderData +YYLTYPE +YYSTYPE +YY_BUFFER_STATE +ZSTD_CCtx +ZSTD_CStream +ZSTD_DCtx +ZSTD_DStream +ZSTD_cParameter +ZSTD_inBuffer +ZSTD_outBuffer +ZstdCompressorState +_SPI_connection +_SPI_plan +__m128i +__time64_t +_dev_t +_ino_t +_locale_t +_resultmap +_stringlist +acquireLocksOnSubLinks_context +add_nulling_relids_context +adjust_appendrel_attrs_context +allocfunc +amadjustmembers_function +ambeginscan_function +ambuild_function +ambuildempty_function +ambuildphasename_function +ambulkdelete_function +amcanreturn_function +amcostestimate_function +amendscan_function +amestimateparallelscan_function +amgetbitmap_function +amgettuple_function +aminitparallelscan_function +aminsert_function +ammarkpos_function +amoptions_function +amparallelrescan_function +amproperty_function +amrescan_function +amrestrpos_function +amvacuumcleanup_function +amvalidate_function +array_iter +array_unnest_fctx +assign_collations_context +auth_password_hook_typ +autovac_table +av_relation +avl_dbase +avl_node +avl_tree +avw_dbase +backslashResult +backup_manifest_info +backup_manifest_option +base_yy_extra_type +basebackup_options +bbsink +bbsink_copystream +bbsink_gzip +bbsink_lz4 +bbsink_ops +bbsink_server +bbsink_shell +bbsink_state +bbsink_throttle +bbsink_zstd +bbstreamer +bbstreamer_archive_context +bbstreamer_extractor +bbstreamer_gzip_decompressor +bbstreamer_gzip_writer +bbstreamer_lz4_frame +bbstreamer_member +bbstreamer_ops +bbstreamer_plain_writer +bbstreamer_recovery_injector +bbstreamer_tar_archiver +bbstreamer_tar_parser +bbstreamer_zstd_frame +bgworker_main_type +binaryheap +binaryheap_comparator +bitmapword +bits16 +bits32 +bits8 +bloom_filter +boolKEY +brin_column_state +brin_serialize_callback_type +bytea +cached_re_str +canonicalize_state +cashKEY +catalogid_hash +check_agg_arguments_context +check_function_callback +check_network_data +check_object_relabel_type +check_password_hook_type +check_ungrouped_columns_context +chr +cmpEntriesArg +codes_t +collation_cache_entry +color +colormaprange +compare_context +config_var_value +contain_aggs_of_level_context +contain_placeholder_references_context +convert_testexpr_context +copy_data_dest_cb +copy_data_source_cb +core_YYSTYPE +core_yy_extra_type +core_yyscan_t +corrupt_items +cost_qual_eval_context +cp_hash_func +create_upper_paths_hook_type +createdb_failure_params +crosstab_HashEnt +crosstab_cat_desc +datapagemap_iterator_t +datapagemap_t +dateKEY +datetkn +dce_uuid_t +dclist_head +decimal +deparse_columns +deparse_context +deparse_expr_cxt +deparse_namespace +destructor +dev_t +digit +disassembledLeaf +dlist_head +dlist_iter +dlist_mutable_iter +dlist_node +dm_code +dm_codes +dm_letter +dm_node +ds_state +dsa_area +dsa_area_control +dsa_area_pool +dsa_area_span +dsa_handle +dsa_pointer +dsa_pointer_atomic +dsa_segment_header +dsa_segment_index +dsa_segment_map +dshash_compare_function +dshash_hash +dshash_hash_function +dshash_parameters +dshash_partition +dshash_seq_status +dshash_table +dshash_table_control +dshash_table_handle +dshash_table_item +dsm_control_header +dsm_control_item +dsm_handle +dsm_op +dsm_segment +dsm_segment_detach_callback +eLogType +ean13 +eary +ec_matches_callback_type +ec_member_foreign_arg +ec_member_matches_arg +emit_log_hook_type +eval_const_expressions_context +exec_thread_arg +execution_state +explain_get_index_name_hook_type +f_smgr +fd_set +fe_scram_state +fe_scram_state_enum +fetch_range_request +file_action_t +file_entry_t +file_type_t +filehash_hash +filehash_iterator +filemap_t +fill_string_relopt +finalize_primnode_context +find_dependent_phvs_context +find_expr_references_context +fix_join_expr_context +fix_scan_expr_context +fix_upper_expr_context +fix_windowagg_cond_context +flatten_join_alias_vars_context +flatten_rtes_walker_context +float4 +float4KEY +float8 +float8KEY +floating_decimal_32 +floating_decimal_64 +fmAggrefPtr +fmExprContextCallbackFunction +fmNodePtr +fmStringInfo +fmgr_hook_type +foreign_glob_cxt +foreign_loc_cxt +freefunc +fsec_t +gbt_vsrt_arg +gbtree_ninfo +gbtree_vinfo +generate_series_fctx +generate_series_numeric_fctx +generate_series_timestamp_fctx +generate_series_timestamptz_fctx +generate_subscripts_fctx +get_attavgwidth_hook_type +get_index_stats_hook_type +get_relation_info_hook_type +get_relation_stats_hook_type +gid_t +gin_leafpage_items_state +ginxlogCreatePostingTree +ginxlogDeleteListPages +ginxlogDeletePage +ginxlogInsert +ginxlogInsertDataInternal +ginxlogInsertEntry +ginxlogInsertListPage +ginxlogRecompressDataLeaf +ginxlogSplit +ginxlogUpdateMeta +ginxlogVacuumDataLeafPage +gistxlogDelete +gistxlogPage +gistxlogPageDelete +gistxlogPageReuse +gistxlogPageSplit +gistxlogPageUpdate +grouping_sets_data +gseg_picksplit_item +gss_OID_set +gss_buffer_desc +gss_cred_id_t +gss_cred_usage_t +gss_ctx_id_t +gss_key_value_element_desc +gss_key_value_set_desc +gss_name_t +gtrgm_consistent_cache +gzFile +hashfunc +hbaPort +heap_page_items_state +help_handler +hlCheck +hstoreCheckKeyLen_t +hstoreCheckValLen_t +hstorePairs_t +hstoreUniquePairs_t +hstoreUpgrade_t +hyperLogLogState +ifState +import_error_callback_arg +indexed_tlist +inet +inetKEY +inet_struct +init_function +inline_cte_walker_context +inline_error_callback_arg +ino_t +instr_time +int128 +int16 +int16KEY +int2vector +int32 +int32KEY +int32_t +int64 +int64KEY +int8 +internalPQconninfoOption +intptr_t +intset_internal_node +intset_leaf_node +intset_node +intvKEY +io_stat_col +itemIdCompact +itemIdCompactData +iterator +jmp_buf +join_search_hook_type +json_aelem_action +json_manifest_error_callback +json_manifest_perfile_callback +json_manifest_perwalrange_callback +json_ofield_action +json_scalar_action +json_struct_action +keyEntryData +key_t +lclContext +lclTocEntry +leafSegmentInfo +leaf_item +libpq_source +line_t +lineno_t +list_sort_comparator +local_relopt +local_relopts +local_source +locale_t +locate_agg_of_level_context +locate_var_of_level_context +locate_windowfunc_context +logstreamer_param +lquery +lquery_level +lquery_variant +ltree +ltree_gist +ltree_level +ltxtquery +mXactCacheEnt +mac8KEY +macKEY +macaddr +macaddr8 +macaddr_sortsupport_state +manifest_file +manifest_files_hash +manifest_files_iterator +manifest_wal_range +map_variable_attnos_context +max_parallel_hazard_context +mb2wchar_with_len_converter +mbchar_verifier +mbcharacter_incrementer +mbdisplaylen_converter +mblen_converter +mbstr_verifier +memoize_hash +memoize_iterator +metastring +missing_cache_key +mix_data_t +mixedStruct +mode_t +movedb_failure_params +multirange_bsearch_comparison +multirange_unnest_fctx +mxact +mxtruncinfo +needs_fmgr_hook_type +network_sortsupport_state +nodeitem +normal_rand_fctx +ntile_context +numeric +object_access_hook_type +object_access_hook_type_str +off_t +oidKEY +oidvector +on_dsm_detach_callback +on_exit_nicely_callback +openssl_tls_init_hook_typ +ossl_EVP_cipher_func +other +output_type +pagetable_hash +pagetable_iterator +pairingheap +pairingheap_comparator +pairingheap_node +pam_handle_t +parallel_worker_main_type +parse_error_callback_arg +parser_context +partition_method_t +pendingPosition +pgParameterStatus +pg_atomic_flag +pg_atomic_uint32 +pg_atomic_uint64 +pg_be_sasl_mech +pg_checksum_context +pg_checksum_raw_context +pg_checksum_type +pg_compress_algorithm +pg_compress_specification +pg_conn_host +pg_conn_host_type +pg_conv_map +pg_crc32 +pg_crc32c +pg_cryptohash_ctx +pg_cryptohash_errno +pg_cryptohash_type +pg_ctype_cache +pg_enc +pg_enc2gettext +pg_enc2name +pg_encname +pg_fe_sasl_mech +pg_funcptr_t +pg_gssinfo +pg_hmac_ctx +pg_hmac_errno +pg_int64 +pg_local_to_utf_combined +pg_locale_t +pg_mb_radix_tree +pg_md5_ctx +pg_on_exit_callback +pg_prng_state +pg_re_flags +pg_saslprep_rc +pg_sha1_ctx +pg_sha224_ctx +pg_sha256_ctx +pg_sha384_ctx +pg_sha512_ctx +pg_snapshot +pg_stack_base_t +pg_time_t +pg_time_usec_t +pg_tz +pg_tz_cache +pg_tzenum +pg_unicode_decompinfo +pg_unicode_decomposition +pg_unicode_norminfo +pg_unicode_normprops +pg_unicode_recompinfo +pg_utf_to_local_combined +pg_uuid_t +pg_wc_probefunc +pg_wchar +pg_wchar_tbl +pgp_armor_headers_state +pgsocket +pgsql_thing_t +pgssEntry +pgssGlobalStats +pgssHashKey +pgssSharedState +pgssStoreKind +pgssVersion +pgstat_entry_ref_hash_hash +pgstat_entry_ref_hash_iterator +pgstat_page +pgstat_snapshot_hash +pgstattuple_type +pgthreadlock_t +pid_t +pivot_field +planner_hook_type +planstate_tree_walker_callback +plperl_array_info +plperl_call_data +plperl_interp_desc +plperl_proc_desc +plperl_proc_key +plperl_proc_ptr +plperl_query_desc +plperl_query_entry +plpgsql_CastExprHashEntry +plpgsql_CastHashEntry +plpgsql_CastHashKey +plpgsql_HashEnt +pltcl_call_state +pltcl_interp_desc +pltcl_proc_desc +pltcl_proc_key +pltcl_proc_ptr +pltcl_query_desc +pointer +polymorphic_actuals +pos_trgm +post_parse_analyze_hook_type +postprocess_result_function +pqbool +pqsigfunc +printQueryOpt +printTableContent +printTableFooter +printTableOpt +printTextFormat +printTextLineFormat +printTextLineWrap +printTextRule +printXheaderWidthType +printfunc +priv_map +process_file_callback_t +process_sublinks_context +proclist_head +proclist_mutable_iter +proclist_node +promptStatus_t +pthread_barrier_t +pthread_cond_t +pthread_key_t +pthread_mutex_t +pthread_once_t +pthread_t +ptrdiff_t +published_rel +pull_var_clause_context +pull_varattnos_context +pull_varnos_context +pull_vars_context +pullup_replace_vars_context +pushdown_safe_type +pushdown_safety_info +qc_hash_func +qsort_arg_comparator +qsort_comparator +query_pathkeys_callback +radius_attribute +radius_packet +rangeTableEntry_used_context +rank_context +rbt_allocfunc +rbt_combiner +rbt_comparator +rbt_freefunc +reduce_outer_joins_partial_state +reduce_outer_joins_pass1_state +reduce_outer_joins_pass2_state +reference +regex_arc_t +regex_t +regexp +regexp_matches_ctx +registered_buffer +regmatch_t +regoff_t +regproc +relopt_bool +relopt_enum +relopt_enum_elt_def +relopt_gen +relopt_int +relopt_kind +relopt_parse_elt +relopt_real +relopt_string +relopt_type +relopt_value +relopts_validator +remoteConn +remoteConnHashEnt +remoteDep +remove_nulling_relids_context +rendezvousHashEntry +replace_rte_variables_callback +replace_rte_variables_context +ret_type +rewind_source +rewrite_event +rf_context +rm_detail_t +role_auth_extra +rolename_hash +row_security_policy_hook_type +rsv_callback +saophash_hash +save_buffer +scram_state +scram_state_enum +sem_t +sequence_magic +set_join_pathlist_hook_type +set_rel_pathlist_hook_type +shm_mq +shm_mq_handle +shm_mq_iovec +shm_mq_result +shm_toc +shm_toc_entry +shm_toc_estimator +shmem_request_hook_type +shmem_startup_hook_type +sig_atomic_t +sigjmp_buf +signedbitmapword +sigset_t +size_t +slist_head +slist_iter +slist_mutable_iter +slist_node +slock_t +socket_set +socklen_t +spgBulkDeleteState +spgChooseIn +spgChooseOut +spgChooseResultType +spgConfigIn +spgConfigOut +spgInnerConsistentIn +spgInnerConsistentOut +spgLeafConsistentIn +spgLeafConsistentOut +spgNodePtr +spgPickSplitIn +spgPickSplitOut +spgVacPendingItem +spgxlogAddLeaf +spgxlogAddNode +spgxlogMoveLeafs +spgxlogPickSplit +spgxlogSplitTuple +spgxlogState +spgxlogVacuumLeaf +spgxlogVacuumRedirect +spgxlogVacuumRoot +split_pathtarget_context +split_pathtarget_item +sql_error_callback_arg +sqlparseInfo +sqlparseState +ss_lru_item_t +ss_scan_location_t +ss_scan_locations_t +ssize_t +standard_qp_extra +stemmer_module +stmtCacheEntry +storeInfo +storeRes_func +stream_stop_callback +string +substitute_actual_parameters_context +substitute_actual_srf_parameters_context +substitute_phv_relids_context +symbol +tablespaceinfo +teSection +temp_tablespaces_extra +test_re_flags +test_regex_ctx +test_shm_mq_header +test_spec +test_start_function +text +timeKEY +time_t +timeout_handler_proc +timeout_params +timerCA +tlist_vinfo +toast_compress_header +tokenize_error_callback_arg +transferMode +transfer_thread_arg +tree_mutator_callback +tree_walker_callback +trgm +trgm_mb_char +trivalue +tsKEY +ts_parserstate +ts_tokenizer +ts_tokentype +tsearch_readline_state +tuplehash_hash +tuplehash_iterator +type +tzEntry +u_char +u_int +ua_page_items +ua_page_stats +uchr +uid_t +uint128 +uint16 +uint16_t +uint32 +uint32_t +uint32x4_t +uint64 +uint64_t +uint8 +uint8_t +uint8x16_t +uintptr_t +unicodeStyleBorderFormat +unicodeStyleColumnFormat +unicodeStyleFormat +unicodeStyleRowFormat +unicode_linestyle +unit_conversion +unlogged_relation_entry +utf_local_conversion_func +uuidKEY +uuid_rc_t +uuid_sortsupport_state +uuid_t +va_list +vacuumingOptions +validate_string_relopt +varatt_expanded +varattrib_1b +varattrib_1b_e +varattrib_4b +vbits +verifier_context +walrcv_check_conninfo_fn +walrcv_connect_fn +walrcv_create_slot_fn +walrcv_disconnect_fn +walrcv_endstreaming_fn +walrcv_exec_fn +walrcv_get_backend_pid_fn +walrcv_get_conninfo_fn +walrcv_get_senderinfo_fn +walrcv_identify_system_fn +walrcv_readtimelinehistoryfile_fn +walrcv_receive_fn +walrcv_send_fn +walrcv_server_version_fn +walrcv_startstreaming_fn +wchar2mb_with_len_converter +wchar_t +win32_deadchild_waitinfo +wint_t +worker_state +worktable +wrap +xl_brin_createidx +xl_brin_desummarize +xl_brin_insert +xl_brin_revmap_extend +xl_brin_samepage_update +xl_brin_update +xl_btree_dedup +xl_btree_delete +xl_btree_insert +xl_btree_mark_page_halfdead +xl_btree_metadata +xl_btree_newroot +xl_btree_reuse_page +xl_btree_split +xl_btree_unlink_page +xl_btree_update +xl_btree_vacuum +xl_clog_truncate +xl_commit_ts_truncate +xl_dbase_create_file_copy_rec +xl_dbase_create_wal_log_rec +xl_dbase_drop_rec +xl_end_of_recovery +xl_hash_add_ovfl_page +xl_hash_delete +xl_hash_init_bitmap_page +xl_hash_init_meta_page +xl_hash_insert +xl_hash_move_page_contents +xl_hash_split_allocate_page +xl_hash_split_complete +xl_hash_squeeze_page +xl_hash_update_meta_page +xl_hash_vacuum_one_page +xl_heap_confirm +xl_heap_delete +xl_heap_freeze_page +xl_heap_freeze_plan +xl_heap_header +xl_heap_inplace +xl_heap_insert +xl_heap_lock +xl_heap_lock_updated +xl_heap_multi_insert +xl_heap_new_cid +xl_heap_prune +xl_heap_rewrite_mapping +xl_heap_truncate +xl_heap_update +xl_heap_vacuum +xl_heap_visible +xl_invalid_page +xl_invalid_page_key +xl_invalidations +xl_logical_message +xl_multi_insert_tuple +xl_multixact_create +xl_multixact_truncate +xl_overwrite_contrecord +xl_parameter_change +xl_relmap_update +xl_replorigin_drop +xl_replorigin_set +xl_restore_point +xl_running_xacts +xl_seq_rec +xl_smgr_create +xl_smgr_truncate +xl_standby_lock +xl_standby_locks +xl_tblspc_create_rec +xl_tblspc_drop_rec +xl_testcustomrmgrs_message +xl_xact_abort +xl_xact_assignment +xl_xact_commit +xl_xact_dbinfo +xl_xact_invals +xl_xact_origin +xl_xact_parsed_abort +xl_xact_parsed_commit +xl_xact_parsed_prepare +xl_xact_prepare +xl_xact_relfilelocators +xl_xact_stats_item +xl_xact_stats_items +xl_xact_subxacts +xl_xact_twophase +xl_xact_xinfo +xmlBuffer +xmlBufferPtr +xmlChar +xmlDocPtr +xmlErrorPtr +xmlExternalEntityLoader +xmlGenericErrorFunc +xmlNodePtr +xmlNodeSetPtr +xmlParserCtxtPtr +xmlParserInputPtr +xmlSaveCtxt +xmlSaveCtxtPtr +xmlStructuredErrorFunc +xmlTextWriter +xmlTextWriterPtr +xmlXPathCompExprPtr +xmlXPathContextPtr +xmlXPathObjectPtr +xmltype +xpath_workspace +xsltSecurityPrefsPtr +xsltStylesheetPtr +xsltTransformContextPtr +yy_parser +yy_size_t +yyscan_t +z_stream +z_streamp +zic_t diff --git a/src/tools/pgtest b/src/tools/pgtest new file mode 100755 index 0000000..fa665ff --- /dev/null +++ b/src/tools/pgtest @@ -0,0 +1,57 @@ +#!/bin/sh + +# src/tools/pgtest [-n] [...] + +# This runs a build/initdb/regression test suite +# +# This will start a temporary postmaster, so you have to +# have enough kernel resources to run two postmasters or +# stop your main postmaster before running this script. +# +# Use -n to prevent 'make clean' + +MAKE="make" + +[ ! -d src ] && echo "This must be run from the top of the PostgreSQL source tree" 1>&2 && exit 1 + +trap "ret=$?; rm -rf /tmp/$$; exit $ret" 0 1 2 3 15 +mkdir /tmp/$$ +TMP="/tmp/$$" + +if [ "X$1" != "X-n" ] +then CLEAN="Y" +else CLEAN="" + shift +fi + +rm -f tmp_install/log/install.log + +# Run "make check" and store return code in $TMP/ret. +# Display output but also capture it in $TMP/0. +( + if [ "$CLEAN" ] + then $MAKE "$@" clean 2>&1 + echo "$?" > $TMP/ret + fi + if [ $(cat $TMP/ret) -eq 0 ] + then $MAKE "$@" 2>&1 && $MAKE "$@" check 2>&1 + echo "$?" > $TMP/ret + fi +) | tee $TMP/0 + +# Grab possible warnings from install.log +[ -e tmp_install/log/install.log ] && cat tmp_install/log/install.log >> $TMP/0 + +# If success, display warnings +if [ $(cat $TMP/ret) -eq 0 ] +then cat $TMP/0 | + # The following grep's have to be adjusted for your setup because + # certain warnings are acceptable. + grep -i warning | + grep -v setproctitle | + grep -v find_rule | + grep -v yy_flex_realloc +fi + +# return original make error code +exit `cat $TMP/ret` diff --git a/src/tools/rcgen b/src/tools/rcgen new file mode 100755 index 0000000..0c84772 --- /dev/null +++ b/src/tools/rcgen @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 + +# Helper for building resource files when building for windows. Always +# generates a .rc from the input .rc file. When building with msvc we +# additionally generate a .res file with 'rc', when building with gcc, we use +# windres to directly generate a .o. Additionally we generate basic +# dependencies if depfile is specified. + +import argparse +import os +import subprocess +import sys + +parser = argparse.ArgumentParser(description='generate PostgreSQL rc file') + +parser.add_argument('--srcdir', type=os.path.abspath, + required=True) +parser.add_argument('--builddir', type=os.path.abspath, + required=True) + +binaries = parser.add_argument_group('binaries') +binaries.add_argument('--windres', type=os.path.abspath) +binaries.add_argument('--rc', type=os.path.abspath) + +inout = parser.add_argument_group('inout') +inout.add_argument('--depfile', type=argparse.FileType('w')) +inout.add_argument('--input', type=argparse.FileType('r'), + required=True) +inout.add_argument('--rcout', type=argparse.FileType('w'), + required=True) +inout.add_argument('--out', type=str, + required=True) + +replacements = parser.add_argument_group('replacements') +replacements.add_argument('--FILEDESC', type=str) +replacements.add_argument('--NAME', type=str, required=True) +replacements.add_argument('--VFT_TYPE', type=str, required=True) +replacements.add_argument('--FILEENDING', type=str, required=True) +replacements.add_argument('--ICO', type=str) + +args = parser.parse_args() + +# determine replacement strings + +internal_name = '"{0}"'.format(args.NAME) +original_name = '"{0}.{1}"'.format(args.NAME, args.FILEENDING) + +# if no description is passed in, generate one based on the name +if args.FILEDESC: + filedesc = args.FILEDESC +elif args.NAME: + if args.VFT_TYPE == 'VFT_DLL': + filedesc = 'PostgreSQL {0} library'.format(args.NAME) + else: + filedesc = 'PostgreSQL {0} binary'.format(args.NAME) +filedesc = '"{0}"'.format(filedesc) + + +if args.ICO: + ico = 'IDI_ICON ICON "{0}"'.format(args.ICO) + if args.depfile: + args.depfile.write("{0} : {1}\n".format(args.rcout.name, args.ICO)) +else: + ico = '' + + +data = args.input.read() + +data = data.replace('VFT_APP', args.VFT_TYPE) +data = data.replace('_INTERNAL_NAME_', internal_name) +data = data.replace('_ORIGINAL_NAME_', original_name) +data = data.replace('FILEDESC', filedesc) +data = data.replace("_ICO_", ico) + +args.rcout.write(data) +args.rcout.close() + +if args.windres: + cmd = [ + args.windres, + '-I{0}/src/include/'.format(args.builddir), + '-I{0}/src/include/'.format(args.srcdir), + '-o', args.out, '-i', args.rcout.name, + ] +elif args.rc: + cmd = [ + args.rc, '/nologo', + '-I{0}/src/include/'.format(args.builddir), + '-I{0}/src/include/'.format(args.srcdir), + '/fo', args.out, args.rcout.name, + ] +else: + sys.exit('either --windres or --rc needs to be specified') + +sp = subprocess.run(cmd) +if sp.returncode != 0: + sys.exit(sp.returncode) + +# It'd be nicer if we could generate correct dependencies here, but 'rc' +# doesn't support doing so. It's unlikely we'll ever need more, so... +if args.depfile: + args.depfile.write("{0} : {1}\n".format( + args.rcout.name, args.input.name)) + args.depfile.write("{0} : {1}/{2}\n".format( + args.out, args.builddir, 'src/include/pg_config.h')) diff --git a/src/tools/testint128.c b/src/tools/testint128.c new file mode 100644 index 0000000..7f6680c --- /dev/null +++ b/src/tools/testint128.c @@ -0,0 +1,170 @@ +/*------------------------------------------------------------------------- + * + * testint128.c + * Testbed for roll-our-own 128-bit integer arithmetic. + * + * This is a standalone test program that compares the behavior of an + * implementation in int128.h to an (assumed correct) int128 native type. + * + * Copyright (c) 2017-2023, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/tools/testint128.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +/* + * By default, we test the non-native implementation in int128.h; but + * by predefining USE_NATIVE_INT128 to 1, you can test the native + * implementation, just to be sure. + */ +#ifndef USE_NATIVE_INT128 +#define USE_NATIVE_INT128 0 +#endif + +#include "common/int128.h" +#include "common/pg_prng.h" + +/* + * We assume the parts of this union are laid out compatibly. + */ +typedef union +{ + int128 i128; + INT128 I128; + union + { +#ifdef WORDS_BIGENDIAN + int64 hi; + uint64 lo; +#else + uint64 lo; + int64 hi; +#endif + } hl; +} test128; + + +/* + * Control version of comparator. + */ +static inline int +my_int128_compare(int128 x, int128 y) +{ + if (x < y) + return -1; + if (x > y) + return 1; + return 0; +} + +/* + * Main program. + * + * Generates a lot of random numbers and tests the implementation for each. + * The results should be reproducible, since we use a fixed PRNG seed. + * + * You can give a loop count if you don't like the default 1B iterations. + */ +int +main(int argc, char **argv) +{ + long count; + + pg_prng_seed(&pg_global_prng_state, 0); + + if (argc >= 2) + count = strtol(argv[1], NULL, 0); + else + count = 1000000000; + + while (count-- > 0) + { + int64 x = pg_prng_uint64(&pg_global_prng_state); + int64 y = pg_prng_uint64(&pg_global_prng_state); + int64 z = pg_prng_uint64(&pg_global_prng_state); + test128 t1; + test128 t2; + + /* check unsigned addition */ + t1.hl.hi = x; + t1.hl.lo = y; + t2 = t1; + t1.i128 += (int128) (uint64) z; + int128_add_uint64(&t2.I128, (uint64) z); + + if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo) + { + printf("%016lX%016lX + unsigned %lX\n", x, y, z); + printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo); + printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo); + return 1; + } + + /* check signed addition */ + t1.hl.hi = x; + t1.hl.lo = y; + t2 = t1; + t1.i128 += (int128) z; + int128_add_int64(&t2.I128, z); + + if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo) + { + printf("%016lX%016lX + signed %lX\n", x, y, z); + printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo); + printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo); + return 1; + } + + /* check multiplication */ + t1.i128 = (int128) x * (int128) y; + + t2.hl.hi = t2.hl.lo = 0; + int128_add_int64_mul_int64(&t2.I128, x, y); + + if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo) + { + printf("%lX * %lX\n", x, y); + printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo); + printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo); + return 1; + } + + /* check comparison */ + t1.hl.hi = x; + t1.hl.lo = y; + t2.hl.hi = z; + t2.hl.lo = pg_prng_uint64(&pg_global_prng_state); + + if (my_int128_compare(t1.i128, t2.i128) != + int128_compare(t1.I128, t2.I128)) + { + printf("comparison failure: %d vs %d\n", + my_int128_compare(t1.i128, t2.i128), + int128_compare(t1.I128, t2.I128)); + printf("arg1 = %016lX%016lX\n", t1.hl.hi, t1.hl.lo); + printf("arg2 = %016lX%016lX\n", t2.hl.hi, t2.hl.lo); + return 1; + } + + /* check case with identical hi parts; above will hardly ever hit it */ + t2.hl.hi = x; + + if (my_int128_compare(t1.i128, t2.i128) != + int128_compare(t1.I128, t2.I128)) + { + printf("comparison failure: %d vs %d\n", + my_int128_compare(t1.i128, t2.i128), + int128_compare(t1.I128, t2.I128)); + printf("arg1 = %016lX%016lX\n", t1.hl.hi, t1.hl.lo); + printf("arg2 = %016lX%016lX\n", t2.hl.hi, t2.hl.lo); + return 1; + } + } + + return 0; +} diff --git a/src/tools/testwrap b/src/tools/testwrap new file mode 100755 index 0000000..7a64fe7 --- /dev/null +++ b/src/tools/testwrap @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +import argparse +import shutil +import subprocess +import os +import sys + +parser = argparse.ArgumentParser() + +parser.add_argument('--srcdir', help='source directory of test', type=str) +parser.add_argument('--basedir', help='base directory of test', type=str) +parser.add_argument('--testgroup', help='test group', type=str) +parser.add_argument('--testname', help='test name', type=str) +parser.add_argument('test_command', nargs='*') + +args = parser.parse_args() + +testdir = '{}/testrun/{}/{}'.format( + args.basedir, args.testgroup, args.testname) + +print('# executing test in {} group {} test {}'.format( + testdir, args.testgroup, args.testname)) +sys.stdout.flush() + +if os.path.exists(testdir) and os.path.isdir(testdir): + shutil.rmtree(testdir) +os.makedirs(testdir) + +os.chdir(args.srcdir) + +# mark test as having started +open(os.path.join(testdir, 'test.start'), 'x') + +env_dict = {**os.environ, + 'TESTDATADIR': os.path.join(testdir, 'data'), + 'TESTLOGDIR': os.path.join(testdir, 'log')} + +sp = subprocess.run(args.test_command, env=env_dict) + +if sp.returncode == 0: + print('# test succeeded') + open(os.path.join(testdir, 'test.success'), 'x') +else: + print('# test failed') + open(os.path.join(testdir, 'test.fail'), 'x') +sys.exit(sp.returncode) diff --git a/src/tools/valgrind.supp b/src/tools/valgrind.supp new file mode 100644 index 0000000..7ea464c --- /dev/null +++ b/src/tools/valgrind.supp @@ -0,0 +1,182 @@ +# This is a suppression file for use with Valgrind tools. File format +# documentation: +# http://valgrind.org/docs/manual/mc-manual.html#mc-manual.suppfiles + +# The libc symbol that implements a particular standard interface is +# implementation-dependent. For example, strncpy() shows up as "__GI_strncpy" +# on some platforms. Use wildcards to avoid mentioning such specific names. +# Avoid mentioning functions that are good candidates for inlining, +# particularly single-caller static functions. Suppressions mentioning them +# would be ineffective at higher optimization levels. + + +# We have occasion to write raw binary structures to disk or to the network. +# These may contain uninitialized padding bytes. Since recipients also ignore +# those bytes as padding, this is harmless. + +{ + padding_pgstat_write + Memcheck:Param + write(buf) + + ... + fun:pgstat_write_statsfiles +} + +{ + padding_XLogRecData_CRC + Memcheck:Value8 + + fun:pg_comp_crc32c* + fun:XLogRecordAssemble +} + +{ + padding_XLogRecData_write + Memcheck:Param + pwrite64(buf) + + ... + fun:XLogWrite +} + +{ + padding_relcache + Memcheck:Param + write(buf) + + ... + fun:write_relcache_init_file +} + +{ + padding_reorderbuffer_serialize + Memcheck:Param + write(buf) + + ... + fun:ReorderBufferSerializeTXN +} + +{ + padding_twophase_prepare + Memcheck:Param + write(buf) + + ... + fun:EndPrepare +} + + +{ + padding_twophase_CRC + Memcheck:Value8 + fun:pg_comp_crc32c* + fun:EndPrepare +} + +{ + padding_bootstrap_initial_xlog_write + Memcheck:Param + write(buf) + + ... + fun:BootStrapXLOG +} + +{ + padding_bootstrap_control_file_write + Memcheck:Param + write(buf) + + ... + fun:WriteControlFile + fun:BootStrapXLOG +} + +{ + bootstrap_write_relmap_overlap + Memcheck:Overlap + fun:memcpy* + fun:write_relmap_file + fun:RelationMapFinishBootstrap +} + + +# gcc on ppc64 can generate a four-byte read to fetch the final "char" fields +# of a FormData_pg_cast. This is valid compiler behavior, because a proper +# FormData_pg_cast has trailing padding. Tuples we treat as structures omit +# that padding, so Valgrind reports an invalid read. Practical trouble would +# entail the missing pad bytes falling in a different memory page. So long as +# the structure is aligned, that will not happen. +{ + overread_tuplestruct_pg_cast + Memcheck:Addr4 + + fun:IsBinaryCoercibleWithCast +} + +# Python's allocator does some low-level tricks for efficiency. Those +# can be disabled for better instrumentation; but few people testing +# postgres will have such a build of python. So add broad +# suppressions of the resulting errors. +# See also https://svn.python.org/projects/python/trunk/Misc/README.valgrind +{ + python_clever_allocator + Memcheck:Addr4 + fun:PyObject_Free +} + +{ + python_clever_allocator + Memcheck:Addr8 + fun:PyObject_Free +} + +{ + python_clever_allocator + Memcheck:Value4 + fun:PyObject_Free +} + +{ + python_clever_allocator + Memcheck:Value8 + fun:PyObject_Free +} + +{ + python_clever_allocator + Memcheck:Cond + fun:PyObject_Free +} + +{ + python_clever_allocator + Memcheck:Addr4 + fun:PyObject_Realloc +} + +{ + python_clever_allocator + Memcheck:Addr8 + fun:PyObject_Realloc +} + +{ + python_clever_allocator + Memcheck:Value4 + fun:PyObject_Realloc +} + +{ + python_clever_allocator + Memcheck:Value8 + fun:PyObject_Realloc +} + +{ + python_clever_allocator + Memcheck:Cond + fun:PyObject_Realloc +} diff --git a/src/tools/version_stamp.pl b/src/tools/version_stamp.pl new file mode 100755 index 0000000..326da37 --- /dev/null +++ b/src/tools/version_stamp.pl @@ -0,0 +1,119 @@ +#! /usr/bin/perl + +################################################################# +# version_stamp.pl -- update version stamps throughout the source tree +# +# Copyright (c) 2008-2023, PostgreSQL Global Development Group +# +# src/tools/version_stamp.pl +################################################################# + +# +# This script updates the version stamp in configure.ac, and also in assorted +# other files wherein it's not convenient to obtain the version number from +# configure's output. Note that you still have to run autoconf afterward +# to regenerate configure from the updated configure.ac. +# +# Usage: cd to top of source tree and issue +# src/tools/version_stamp.pl MINORVERSION +# where MINORVERSION can be a minor release number (0, 1, etc), or +# "devel", "alphaN", "betaN", "rcN". +# + +use strict; +use warnings; + +# Major version is hard-wired into the script. We update it when we branch +# a new development version. +my $majorversion = 16; + +# Validate argument and compute derived variables +my $minor = shift; +defined($minor) || die "$0: missing required argument: minor-version\n"; + +my ($dotneeded); + +if ($minor =~ m/^\d+$/) +{ + $dotneeded = 1; +} +elsif ($minor eq "devel") +{ + $dotneeded = 0; +} +elsif ($minor =~ m/^alpha\d+$/) +{ + $dotneeded = 0; +} +elsif ($minor =~ m/^beta\d+$/) +{ + $dotneeded = 0; +} +elsif ($minor =~ m/^rc\d+$/) +{ + $dotneeded = 0; +} +else +{ + die "$0: minor-version must be N, devel, alphaN, betaN, or rcN\n"; +} + +my $fullversion; + +# Create various required forms of the version number +if ($dotneeded) +{ + $fullversion = $majorversion . "." . $minor; +} +else +{ + $fullversion = $majorversion . $minor; +} + +# Get the autoconf version number for eventual nag message +# (this also ensures we're in the right directory) + +my $aconfver = ""; +open(my $fh, '<', "configure.ac") || die "could not read configure.ac: $!\n"; +while (<$fh>) +{ + if (m/^m4_if\(m4_defn\(\[m4_PACKAGE_VERSION\]\), \[(.*)\], \[\], \[m4_fatal/ + ) + { + $aconfver = $1; + last; + } +} +close($fh); +$aconfver ne "" + || die "could not find autoconf version number in configure.ac\n"; + +# Update configure.ac and other files that contain version numbers + +my $fixedfiles = ""; + +sed_file("configure.ac", + "-e 's/AC_INIT(\\[PostgreSQL\\], \\[[0-9a-z.]*\\]/AC_INIT([PostgreSQL], [$fullversion]/'" +); +sed_file("meson.build", + qq{-e "/^project(/,/^)/ s/ version: '[0-9a-z.]*',/ version: '$fullversion',/"} +); + +print "Stamped these files with version number $fullversion:\n$fixedfiles"; +print "Don't forget to run autoconf $aconfver before committing.\n"; + +exit 0; + +sub sed_file +{ + my ($filename, $sedargs) = @_; + my ($tmpfilename) = $filename . ".tmp"; + + system("sed $sedargs $filename >$tmpfilename") == 0 + or die "sed failed: $?"; + system("mv $tmpfilename $filename") == 0 + or die "mv failed: $?"; + + $fixedfiles .= "\t$filename\n"; + return; +} diff --git a/src/tools/win32tzlist.pl b/src/tools/win32tzlist.pl new file mode 100755 index 0000000..657f7d4 --- /dev/null +++ b/src/tools/win32tzlist.pl @@ -0,0 +1,140 @@ +################################################################# +# +# win32tzlist.pl -- compare Windows timezone information +# +# Copyright (c) 2008-2023, PostgreSQL Global Development Group +# +# src/tools/win32tzlist.pl +################################################################# + +# +# This script compares the timezone information in the Windows registry +# with that in src/bin/initdb/findtimezone.c. A list of changes will be +# written to stdout - no attempt is made to automatically edit the file. +# +# Run the script from the top-level PG source directory. +# + +use strict; +use warnings; + +use Win32::Registry; + +my $tzfile = 'src/bin/initdb/findtimezone.c'; + +# +# Fetch all timezones in the registry +# +my $basekey; +$HKEY_LOCAL_MACHINE->Open( + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones", $basekey) + or die $!; + +my @subkeys; +$basekey->GetKeys(\@subkeys); + +my @system_zones; + +foreach my $keyname (@subkeys) +{ + my $subkey; + my %vals; + + $basekey->Open($keyname, $subkey) or die $!; + $subkey->GetValues(\%vals) or die $!; + $subkey->Close(); + + die "Incomplete timezone data for $keyname!\n" + unless ($vals{Std} && $vals{Dlt} && $vals{Display}); + push @system_zones, + { + 'std' => $vals{Std}->[2], + 'dlt' => $vals{Dlt}->[2], + 'display' => clean_displayname($vals{Display}->[2]), + }; +} + +$basekey->Close(); + +# +# Fetch all timezones currently in the file +# +my @file_zones; +my $pgtz; +open(my $tzfh, '<', $tzfile) or die "Could not open $tzfile!\n"; +{ + local $/ = undef; + $pgtz = <$tzfh>; +} +close($tzfh); + +# Attempt to locate and extract the complete win32_tzmap struct +$pgtz =~ /win32_tzmap\[\] =\s+{\s+\/\*[^\/]+\*\/\s+(.+?)};/gs + or die "Could not locate struct win32_tzmap in $tzfile!"; +$pgtz = $1; + +# Extract each individual record from the struct +while ($pgtz =~ + m/{\s+\/\*(.+?)\*\/\s+"([^"]+)",\s+"([^"]+)",\s+"([^"]+)",?\s+},/gs) +{ + push @file_zones, + { + 'display' => clean_displayname($1), + 'std' => $2, + 'dlt' => $3, + 'match' => $4, + }; +} + +# +# Look for anything that has changed +# +my @add; + +for my $sys (@system_zones) +{ + my $match = 0; + for my $file (@file_zones) + { + if ($sys->{std} eq $file->{std}) + { + $match = 1; + if ($sys->{dlt} ne $file->{dlt}) + { + print + "Timezone $sys->{std}, changed name of daylight zone!\n"; + } + if ($sys->{display} ne $file->{display}) + { + print + "Timezone $sys->{std} changed displayname ('$sys->{display}' from '$file->{display}')!\n"; + } + last; + } + } + unless ($match) + { + push @add, $sys; + } +} + +if (@add) +{ + print "\n\nOther than that, add the following timezones:\n"; + for my $z (@add) + { + print + "\t{\n\t\t/* $z->{display} */\n\t\t\"$z->{std}\", \"$z->{dlt}\",\n\t\t\"FIXME\"\n\t},\n"; + } +} + +sub clean_displayname +{ + my $dn = shift; + + $dn =~ s/\*//gs; + $dn =~ s/\s+/ /gs; + $dn =~ s/^\s+//gs; + $dn =~ s/\s+$//gs; + return $dn; +} |