Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/tools/pgindent/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 10 kB image not shown  

Quelle  pgindent

  Sprache: C
 

#!/usr/bin/perl

# Copyright (c) 2021-2025, PostgreSQL Global Development Group

# Program to maintain uniform layout style in our C code.
# Exit codes:
#   0 -- all OK
#   1 -- error invoking pgindent, nothing done
#   2 -- --check mode and at least one file requires changes
#   3 -- pg_bsd_indent failed on at least one file

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

use Cwd qw(abs_path getcwd);
use File::Find;
use File::Spec;
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, $diff,
 $check, $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,
 "diff" => \$diff,
 "check" => \$check,);
GetOptions(%options) || usage("bad command line argument");

usage() if $help;

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 = map { "$_\n" } qw(
  bool regex_t regmatch_t regoff
);

my %excluded = map { +"$_\n" => 1 } qw(
  FD_SET LookupSet boolean date duration
  element_type inquiry iterator other
  pointer reference rep string timestamp type wrap
);

# globals
my @files;
my $filtered_typedefs_fh;

sub check_indent
{
 if (system("$indent -? < $devnull > $devnull 2>&1") != 0)
 {
  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;
 }

 if (system("$indent -gnu < $devnull > $devnull 2>&1") == 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/^#/ || $line eq "";
   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) || die $!;
 local ($/) = undef;
 $source = <$src_out>;
 close($src_out);

 return $source;
}

sub 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
 --diff                  show the changes that would be made
 --check                 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;
my $status = 0;

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";
  $status = 3;
  next;
 }

 $source = post_indent($source);

 if ($source ne $orig_source)
 {
  if (!$diff && !$check)
  {
   write_source($source, $source_filename);
  }
  else
  {
   if ($diff)
   {
    print diff($source, $source_filename);
   }

   if ($check)
   {
    $status ||= 2;
    last unless $diff;
   }
  }
 }
}

exit $status;

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet am  2026-08-06) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.