#!/usr/bin/perl -w

use strict;
use warnings;

my $header = <<'END';
/*
 * Copyright 2016-2018 Andrey Semashev
 *
 * Distributed under the Boost Software License, Version 1.0.
 * See http://www.boost.org/LICENSE_1_0.txt
 */

#ifndef BOOST_WINAPI_ERROR_CODES_HPP_INCLUDED_
#define BOOST_WINAPI_ERROR_CODES_HPP_INCLUDED_

#include <boost/winapi/basic_types.hpp>

#ifdef BOOST_HAS_PRAGMA_ONCE
#pragma once
#endif

namespace boost {
namespace winapi {

END

my $footer = <<'END';

} // namespace winapi
} // namespace boost

#endif // BOOST_WINAPI_ERROR_CODES_HPP_INCLUDED_
END

print $header;

while (<>)
{
    my $line = $_;
    chomp($line);
    if ($line =~ /^\s*#\s*define\s+([a-zA-Z_\d]+)\s+(0[xX][[:xdigit:]]+|\d+|[a-zA-Z_\d]+)[lLuU]*\s*(\/\/.*|\/\*.*)?$/)
    {
        # We define some of the constants in other headers
        if ($1 ne "FORCEINLINE" && $1 ne "WAIT_TIMEOUT")
        {
            my $value = $2;
            print "BOOST_CONSTEXPR_OR_CONST DWORD_ ", $1 , "_ = ";
            if ($value =~ /0[xX][[:xdigit:]]+|\d+/)
            {
                print $value;
            }
            else
            {
                print $value, "_";
            }
            print ";\n";
        }
    }
}

print $footer;


#!/usr/bin/perl -w

use strict;
use warnings;

my $header = <<'END';
/*
 *             Copyright Andrey Semashev 2018.
 * Distributed under the Boost Software License, Version 1.0.
 *    (See accompanying file LICENSE_1_0.txt or copy at
 *          http://www.boost.org/LICENSE_1_0.txt)
 */
/*!
 * \file   error_codes_abi.cpp
 * \author Andrey Semashev
 * \date   09.03.2018
 *
 * \brief  This file contains ABI test for error_codes.hpp
 */

#include <boost/winapi/error_codes.hpp>
#include <windows.h>
#include <winerror.h>
#include <boost/predef/platform/windows_uwp.h>
#include "abi_test_tools.hpp"

int main()
{
END

my $footer = <<'END';

    return boost::report_errors();
}
END

print $header;

while (<>)
{
    my $line = $_;
    chomp($line);
    if ($line =~ /^\s*BOOST_CONSTEXPR_OR_CONST\s+DWORD_\s+([a-zA-Z_\d]+)_\s+.*$/)
    {
        print "#if ";
        # Some constants have different values in different Windows SDKs
        if ($1 eq "ERROR_IPSEC_IKE_NEG_STATUS_END")
        {
            print "BOOST_PLAT_WINDOWS_SDK_VERSION >= BOOST_WINAPI_WINDOWS_SDK_6_0 && ";
        }
        print "defined(", $1 , ")\n";
        print "    BOOST_WINAPI_TEST_CONSTANT(", $1 , ");\n";
        print "#endif\n";
    }
}

print $footer;


#!/usr/bin/perl -w

# Copyright 2004 Aleksey Gurtovoy
# Copyright 2001 Jens Maurer
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)

use strict;

my $filename;
my $compiler;
my $time = 0;
my $ct = 0;
my $first = 2;

print "\n\n<title>\nCompile Times</title>\n\n\n";
print "\n";
print "";
print "
\n";
print "Compile time for each successful regression test in seconds.\n";
print "
\n";

print "
\n";
print "
Test	\n";

while(<>) {
  if(/^\*\*\* (.*) \*\*\*$/) {
    $filename = $1;
    $first = ($first == 0 ? 0 : $first-1);
    if($first == 0) {
      print "
\n\n
\n$filename	\n";
    }
  } elsif(/^\*\* (.*)/) {
    $compiler = $1;
    if($first) {
      print "$compiler	\n";
    } else {
      $ct = 1;
    }
  } elsif($ct && /^CPU time: ([.0-9]*) s user, ([.0-9]*) s system/) {
    $time = $1 + $2;
  } elsif($ct && /^Pass$/) {
    printf "%.02f	\n", $time;
    $ct = 0;
  } elsif($ct && /^Fail$/) {
    print "-	\n";
    $ct = 0;
  }
}

print "
\n";
print "
\n";
print "\n\n";


#!/usr/bin/perl -w
#
# ------------------------------------------------------------------
# This file is part of bzip2/libbzip2, a program and library for
# lossless, block-sorting data compression.
#
# bzip2/libbzip2 version 1.0.8 of 13 July 2019
# Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>
#
# Please read the WARNING, DISCLAIMER and PATENTS sections in the
# README file.
#
# This program is released under the terms of the license contained
# in the file LICENSE.
# ------------------------------------------------------------------
#
use strict;

# get command line values:
if ( $#ARGV !=1 ) {
    die "Usage:  $0 xml_infile xml_outfile\n";
}

my $infile = shift;
# check infile exists
die "Can't find file \"$infile\""
  unless -f $infile;
# check we can read infile
if (! -r $infile) {
    die "Can't read input $infile\n";
}
# check we can open infile
open( INFILE,"<$infile" ) or
    die "Can't input $infile $!";

#my $outfile = 'fmt-manual.xml';
my $outfile = shift;
#print "Infile: $infile, Outfile: $outfile\n";
# check we can write to outfile
open( OUTFILE,">$outfile" ) or
    die "Can't output $outfile $! for writing";

my ($prev, $curr, $str);
$prev = ''; $curr = '';
while ( <INFILE> ) {

		print OUTFILE $prev;
    $prev = $curr;
    $curr = $_;
    $str = '';

    if ( $prev =~ /<programlisting>$|<screen>$/ ) {
        chomp $prev;
        $curr = join( '', $prev, "<![CDATA[", $curr );
				$prev = '';
        next;
    }
    elsif ( $curr =~ /<\/programlisting>|<\/screen>/ ) {
        chomp $prev;
        $curr = join( '', $prev, "]]>", $curr );
				$prev = '';
        next;
    }
}
print OUTFILE $curr;
close INFILE;
close OUTFILE;
exit;


#!/usr/bin/env perl
#***************************************************************************
#                                  _   _ ____  _
#  Project                     ___| | | |  _ \| |
#                             / __| | | | |_) | |
#                            | (__| |_| |  _ <| |___
#                             \___|\___/|_| \_\_____|
#
# Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################

=begin comment

This script generates the manpage.

Example: gen.pl <command> [files] > curl.1

Dev notes:

We open *input* files in :crlf translation (a no-op on many platforms) in
case we have CRLF line endings in Windows but a perl that defaults to LF.
Unfortunately it seems some perls like msysgit can't handle a global input-only
:crlf so it has to be specified on each file open for text input.

=end comment
=cut

my %optshort;
my %optlong;
my %helplong;
my %arglong;
my %redirlong;
my %protolong;
my %catlong;

use POSIX qw(strftime);
my $date = strftime "%B %d %Y", localtime;
my $year = strftime "%Y", localtime;
my $version = "unknown";

open(INC, "<../../include/curl/curlver.h");
while(<INC>) {
    if($_ =~ /^#define LIBCURL_VERSION \"([0-9.]*)/) {
        $version = $1;
        last;
    }
}
close(INC);

# get the long name version, return the man page string
sub manpageify {
    my ($k)=@_;
    my $l;
    if($optlong{$k} ne "") {
        # both short + long
        $l = "\\fI-".$optlong{$k}.", --$k\\fP";
    }
    else {
        # only long
        $l = "\\fI--$k\\fP";
    }
    return $l;
}

sub printdesc {
    my @desc = @_;
    for my $d (@desc) {
        if($d =~ /\(Added in ([0-9.]+)\)/i) {
            my $ver = $1;
            if(too_old($ver)) {
                $d =~ s/ *\(Added in $ver\)//gi;
            }
        }
        if($d !~ /^.\\"/) {
            # **bold**
            $d =~ s/\*\*([^ ]*)\*\*/\\fB$1\\fP/g;
            # *italics*
            $d =~ s/\*([^ ]*)\*/\\fI$1\\fP/g;
        }
        # skip lines starting with space (examples)
        if($d =~ /^[^ ]/) {
            for my $k (keys %optlong) {
                my $l = manpageify($k);
                $d =~ s/--$k([^a-z0-9_-])/$l$1/;
            }
        }
        # quote "bare" minuses in the output
        $d =~ s/( |\\fI|^)--/$1\\-\\-/g;
        $d =~ s/([ -]|\\fI|^)-/$1\\-/g;
        # handle single quotes first on the line
        $d =~ s/(\s*)\'/$1\\(aq/;
        print $d;
    }
}

sub seealso {
    my($standalone, $data)=@_;
    if($standalone) {
        return sprintf
            ".SH \"SEE ALSO\"\n$data\n";
    }
    else {
        return "See also $data. ";
    }
}

sub overrides {
    my ($standalone, $data)=@_;
    if($standalone) {
        return ".SH \"OVERRIDES\"\n$data\n";
    }
    else {
        return $data;
    }
}

sub protocols {
    my ($standalone, $data)=@_;
    if($standalone) {
        return ".SH \"PROTOCOLS\"\n$data\n";
    }
    else {
        return "($data) ";
    }
}

sub too_old {
    my ($version)=@_;
    my $a = 999999;
    if($version =~ /^(\d+)\.(\d+)\.(\d+)/) {
        $a = $1 * 1000 + $2 * 10 + $3;
    }
    elsif($version =~ /^(\d+)\.(\d+)/) {
        $a = $1 * 1000 + $2 * 10;
    }
    if($a < 7300) {
        # we consider everything before 7.30.0 to be too old to mention
        # specific changes for
        return 1;
    }
    return 0;
}

sub added {
    my ($standalone, $data)=@_;
    if(too_old($data)) {
        # don't mention ancient additions
        return "";
    }
    if($standalone) {
        return ".SH \"ADDED\"\nAdded in curl version $data\n";
    }
    else {
        return "Added in $data. ";
    }
}

sub single {
    my ($f, $standalone)=@_;
    open(F, "<:crlf", "$f") ||
        return 1;
    my $short;
    my $long;
    my $tags;
    my $added;
    my $protocols;
    my $arg;
    my $mutexed;
    my $requires;
    my $category;
    my $seealso;
    my @examples; # there can be more than one
    my $magic; # cmdline special option
    my $line;
    while(<F>) {
        $line++;
        if(/^Short: *(.)/i) {
            $short=$1;
        }
        elsif(/^Long: *(.*)/i) {
            $long=$1;
        }
        elsif(/^Added: *(.*)/i) {
            $added=$1;
        }
        elsif(/^Tags: *(.*)/i) {
            $tags=$1;
        }
        elsif(/^Arg: *(.*)/i) {
            $arg=$1;
        }
        elsif(/^Magic: *(.*)/i) {
            $magic=$1;
        }
        elsif(/^Mutexed: *(.*)/i) {
            $mutexed=$1;
        }
        elsif(/^Protocols: *(.*)/i) {
            $protocols=$1;
        }
        elsif(/^See-also: *(.*)/i) {
            $seealso=$1;
        }
        elsif(/^Requires: *(.*)/i) {
            $requires=$1;
        }
        elsif(/^Category: *(.*)/i) {
            $category=$1;
        }
        elsif(/^Example: *(.*)/i) {
            push @examples, $1;
        }
        elsif(/^Help: *(.*)/i) {
            ;
        }
        elsif(/^---/) {
            if(!$long) {
                print STDERR "ERROR: no 'Long:' in $f\n";
                exit 1;
            }
            if(!$category) {
                print STDERR "ERROR: no 'Category:' in $f\n";
                exit 2;
            }
            if(!$examples[0]) {
                print STDERR "$f:$line:1:ERROR: no 'Example:' present\n";
                exit 2;
            }
            if(!$added) {
                print STDERR "$f:$line:1:ERROR: no 'Added:' version present\n";
                exit 2;
            }
            last;
        }
        else {
            chomp;
            print STDERR "WARN: unrecognized line in $f, ignoring:\n:'$_';"
        }
    }
    my @desc;
    while(<F>) {
        push @desc, $_;
    }
    close(F);
    my $opt;
    if(defined($short) && $long) {
        $opt = "-$short, --$long";
    }
    elsif($short && !$long) {
        $opt = "-$short";
    }
    elsif($long && !$short) {
        $opt = "--$long";
    }

    if($arg) {
        $opt .= " $arg";
    }

    # quote "bare" minuses in opt
    $opt =~ s/( |^)--/$1\\-\\-/g;
    $opt =~ s/( |^)-/$1\\-/g;
    if($standalone) {
        print ".TH curl 1 \"30 Nov 2016\" \"curl 7.52.0\" \"curl manual\"\n";
        print ".SH OPTION\n";
        print "curl $opt\n";
    }
    else {
        print ".IP \"$opt\"\n";
    }
    if($protocols) {
        print protocols($standalone, $protocols);
    }

    if($standalone) {
        print ".SH DESCRIPTION\n";
    }

    printdesc(@desc);
    undef @desc;

    my @foot;
    if($seealso) {
        my @m=split(/ /, $seealso);
        my $mstr;
        my $and = 0;
        my $num = scalar(@m);
        if($num > 2) {
            # use commas up to this point
            $and = $num - 1;
        }
        my $i = 0;
        for my $k (@m) {
            if(!$helplong{$k}) {
                print STDERR "WARN: $f see-alsos a non-existing option: $k\n";
            }
            my $l = manpageify($k);
            my $sep = " and";
            if($and && ($i < $and)) {
                $sep = ",";
            }
            $mstr .= sprintf "%s$l", $mstr?"$sep ":"";
            $i++;
        }
        push @foot, seealso($standalone, $mstr);
    }
    if($requires) {
        my $l = manpageify($long);
        push @foot, "$l requires that the underlying libcurl".
            " was built to support $requires. ";
    }
    if($mutexed) {
        my @m=split(/ /, $mutexed);
        my $mstr;
        for my $k (@m) {
            if(!$helplong{$k}) {
                print STDERR "WARN: $f mutexes a non-existing option: $k\n";
            }
            my $l = manpageify($k);
            $mstr .= sprintf "%s$l", $mstr?" and ":"";
        }
        push @foot, overrides($standalone, "This option overrides $mstr. ");
    }
    if($examples[0]) {
        my $s ="";
        $s="s" if($examples[1]);
        print "\nExample$s:\n.nf\n";
        foreach my $e (@examples) {
            $e =~ s!\$URL!https://example.com!g;
            print " curl $e\n";
        }
        print ".fi\n";
    }
    if($added) {
        push @foot, added($standalone, $added);
    }
    if($foot[0]) {
        print "\n";
        my $f = join("", @foot);
        $f =~ s/ +\z//; # remove trailing space
        print "$f\n";
    }
    return 0;
}

sub getshortlong {
    my ($f)=@_;
    open(F, "<:crlf", "$f");
    my $short;
    my $long;
    my $help;
    my $arg;
    my $protocols;
    my $category;
    while(<F>) {
        if(/^Short: (.)/i) {
            $short=$1;
        }
        elsif(/^Long: (.*)/i) {
            $long=$1;
        }
        elsif(/^Help: (.*)/i) {
            $help=$1;
        }
        elsif(/^Arg: (.*)/i) {
            $arg=$1;
        }
        elsif(/^Protocols: (.*)/i) {
            $protocols=$1;
        }
        elsif(/^Category: (.*)/i) {
            $category=$1;
        }
        elsif(/^---/) {
            last;
        }
    }
    close(F);
    if($short) {
        $optshort{$short}=$long;
    }
    if($long) {
        $optlong{$long}=$short;
        $helplong{$long}=$help;
        $arglong{$long}=$arg;
        $protolong{$long}=$protocols;
        $catlong{$long}=$category;
    }
}

sub indexoptions {
    my (@files) = @_;
    foreach my $f (@files) {
        getshortlong($f);
    }
}

sub header {
    my ($f)=@_;
    open(F, "<:crlf", "$f");
    my @d;
    while(<F>) {
        s/%DATE/$date/g;
        s/%VERSION/$version/g;
        push @d, $_;
    }
    close(F);
    printdesc(@d);
}

sub listhelp {
    print <<HEAD
/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \\| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \\___|\\___/|_| \\_\\_____|
 *
 * Copyright (C) 1998 - $year, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at https://curl.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ***************************************************************************/
#include "tool_setup.h"
#include "tool_help.h"

/*
 * DO NOT edit tool_listhelp.c manually.
 * This source file is generated with the following command:

  cd \$srcroot/docs/cmdline-opts
  ./gen.pl listhelp *.d > \$srcroot/src/tool_listhelp.c
 */

const struct helptxt helptext[] = {
HEAD
        ;
    foreach my $f (sort keys %helplong) {
        my $long = $f;
        my $short = $optlong{$long};
        my @categories = split ' ', $catlong{$long};
        my $bitmask;
        my $opt;

        if(defined($short) && $long) {
            $opt = "-$short, --$long";
        }
        elsif($long && !$short) {
            $opt = "    --$long";
        }
        for my $i (0 .. $#categories) {
            $bitmask .= 'CURLHELP_' . uc $categories[$i];
            # If not last element, append |
            if($i < $#categories) {
                $bitmask .= ' | ';
            }
        }
        my $arg = $arglong{$long};
        if($arg) {
            $opt .= " $arg";
        }
        my $desc = $helplong{$f};
        $desc =~ s/\"/\\\"/g; # escape double quotes

        my $line = sprintf "  {\"%s\",\n   \"%s\",\n   %s},\n", $opt, $desc, $bitmask;

        if(length($opt) > 78) {
            print STDERR "WARN: the --$long name is too long\n";
        }
        elsif(length($desc) > 78) {
            print STDERR "WARN: the --$long description is too long\n";
        }
        print $line;
    }
    print <<FOOT
  { NULL, NULL, CURLHELP_HIDDEN }
};
FOOT
        ;
}

sub listcats {
    my %allcats;
    foreach my $f (sort keys %helplong) {
        my @categories = split ' ', $catlong{$f};
        foreach (@categories) {
            $allcats{$_} = undef;
        }
    }
    my @categories;
    foreach my $key (keys %allcats) {
        push @categories, $key;
    }
    @categories = sort @categories;
    unshift @categories, 'hidden';
    for my $i (0..$#categories) {
        print '#define ' . 'CURLHELP_' . uc($categories[$i]) . ' ' . "1u << " . $i . "u\n";
    }
}

sub mainpage {
    my (@files) = @_;
    # show the page header
    header("page-header");

    # output docs for all options
    foreach my $f (sort @files) {
        if(single($f, 0)) {
            print STDERR "Can't read $f?\n";
        }
    }

    header("page-footer");
}

sub showonly {
    my ($f) = @_;
    if(single($f, 1)) {
        print STDERR "$f: failed\n";
    }
}

sub showprotocols {
    my %prots;
    foreach my $f (keys %optlong) {
        my @p = split(/ /, $protolong{$f});
        for my $p (@p) {
            $prots{$p}++;
        }
    }
    for(sort keys %prots) {
        printf "$_ (%d options)\n", $prots{$_};
    }
}

sub getargs {
    my ($f, @s) = @_;
    if($f eq "mainpage") {
        mainpage(@s);
        return;
    }
    elsif($f eq "listhelp") {
        listhelp();
        return;
    }
    elsif($f eq "single") {
        showonly($s[0]);
        return;
    }
    elsif($f eq "protos") {
        showprotocols();
        return;
    }
    elsif($f eq "listcats") {
        listcats();
        return;
    }

    print "Usage: gen.pl <mainpage/listhelp/single FILE/protos/listcats> [files]\n";
}

#------------------------------------------------------------------------

my $cmd = shift @ARGV;
my @files = @ARGV; # the rest are the files

# learn all existing options
indexoptions(@files);

getargs($cmd, @files);


#!/usr/bin/env perl
#***************************************************************************
#                                  _   _ ____  _
#  Project                     ___| | | |  _ \| |
#                             / __| | | | |_) | |
#                            | (__| |_| |  _ <| |___
#                             \___|\___/|_| \_\_____|
#
# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################

# This script accepts a source file as input on the command line.
#
# It first loads the 'symbols-in-versions' document and stores a lookup
# table for all known symbols for which version they were introduced.
#
# It then scans the given source file to dig up all symbols starting with CURL.
# Finally, it sorts the internal list of found symbols (using the version
# number as sort key) and then it outputs the most recent version number and
# the symbols from that version that are used.
#
# Usage:
#
#    version-check.pl [source file]
#

open(S, "<../libcurl/symbols-in-versions") || die;

my %doc;
my %rem;
while(<S>) {
    if(/(^CURL[^ \n]*) *(.*)/) {
        my ($sym, $rest)=($1, $2);
        my @a=split(/ +/, $rest);

        $doc{$sym}=$a[0]; # when it was introduced

        if($a[2]) {
            # this symbol is documented to have been present the last time
            # in this release
            $rem{$sym}=$a[2];
        }
    }

}

close(S);

sub age {
    my ($ver)=@_;

    my @s=split(/\./, $ver);
    return $s[0]*10000+$s[1]*100+$s[2];
}

my %used;
open(C, "<$ARGV[0]") || die;

while(<C>) {
    if(/\W(CURL[_A-Z0-9v]+)\W/) {
        #print "$1\n";
        $used{$1}++;
    }
}

close(C);

sub sortversions {
    my $r = age($doc{$a}) <=> age($doc{$b});
    if(!$r) {
        $r = $a cmp $b;
    }
    return $r;
}

my @recent = reverse sort sortversions keys %used;

# the most recent symbol
my $newsym = $recent[0];
# the most recent version
my $newver = $doc{$newsym};

print "The scanned source uses these symbols introduced in $newver:\n";

for my $w (@recent) {
    if($doc{$w} eq $newver) {
        printf "  $w\n";
        next;
    }
    last;
}


#!/usr/bin/env perl
# ***************************************************************************
# *                                  _   _ ____  _
# *  Project                     ___| | | |  _ \| |
# *                             / __| | | | |_) | |
# *                            | (__| |_| |  _ <| |___
# *                             \___|\___/|_| \_\_____|
# *
# * Copyright (C) 2015 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
# *
# * This software is licensed as described in the file COPYING, which
# * you should have received as part of this distribution. The terms
# * are also available at https://curl.se/docs/copyright.html.
# *
# * You may opt to use, copy, modify, merge, publish, distribute and/or sell
# * copies of the Software, and permit persons to whom the Software is
# * furnished to do so, under the terms of the COPYING file.
# *
# * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# * KIND, either express or implied.
# *
# ***************************************************************************

my $version="7.41.0";

use POSIX qw(strftime);
my $date = strftime "%b %e, %Y", localtime;
my $year = strftime "%Y", localtime;

print <<HEADER
.\\" **************************************************************************
.\\" *                                  _   _ ____  _
.\\" *  Project                     ___| | | |  _ \\| |
.\\" *                             / __| | | | |_) | |
.\\" *                            | (__| |_| |  _ <| |___
.\\" *                             \\___|\\___/|_| \\_\\_____|
.\\" *
.\\" * Copyright (C) 1998 - $year, Daniel Stenberg, <daniel\@haxx.se>, et al.
.\\" *
.\\" * This software is licensed as described in the file COPYING, which
.\\" * you should have received as part of this distribution. The terms
.\\" * are also available at https://curl.se/docs/copyright.html.
.\\" *
.\\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
.\\" * copies of the Software, and permit persons to whom the Software is
.\\" * furnished to do so, under the terms of the COPYING file.
.\\" *
.\\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
.\\" * KIND, either express or implied.
.\\" *
.\\" **************************************************************************
.TH libcurl-symbols 3 "$date" "libcurl $version" "libcurl symbols"
.SH NAME
libcurl-symbols \\- libcurl symbol version information
.SH "libcurl symbols"
This man page details version information for public symbols provided in the
libcurl header files. This lists the first version in which the symbol was
introduced and for some symbols two additional information pieces:

The first version in which the symbol is marked "deprecated" - meaning that
since that version no new code should be written to use the symbol as it is
marked for getting removed in a future.

The last version that featured the specific symbol. Using the symbol in source
code will make it no longer compile error-free after that specified version.

This man page is automatically generated from the symbols-in-versions file.
HEADER
    ;

while(<STDIN>) {
    if($_ =~ /^(CURL[A-Z0-9_.]*) *(.*)/i) {
        my ($symbol, $rest)=($1,$2);
        my ($intro, $dep, $rem);
        if($rest =~ s/^([0-9.]*) *//) {
           $intro = $1;
        }
        if($rest =~ s/^([0-9.]*) *//) {
           $dep = $1;
        }
        if($rest =~ s/^([0-9.]*) *//) {
           $rem = $1;
        }
        print ".IP $symbol\nIntroduced in $intro\n";
        if($dep) {
          print "Deprecated since $dep\n";
        }
        if($rem) {
          print "Last used in $rem\n";
        }
    }

}


#!/usr/bin/env perl
#***************************************************************************
#                                  _   _ ____  _
#  Project                     ___| | | |  _ \| |
#                             / __| | | | |_) | |
#                            | (__| |_| |  _ <| |___
#                             \___|\___/|_| \_\_____|
#
# Copyright (C) 2011 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################
#
# Experience has shown that the symbols-in-versions file is very useful to
# applications that want to build with a wide range of libcurl versions.
# It is however easy to get it wrong and the source gets a bit messy with all
# the fixed numerical comparisons.
#
# The point of this script is to provide an easy-to-use macro for libcurl-
# using applications to do preprocessor checks for specific libcurl defines,
# and yet make the code clearly show what the macro is used for.
#
# Run this script and generate libcurl-symbols.h and then use that header in
# a fashion similar to:
#
# #include "libcurl-symbols.h"
#
# #if LIBCURL_HAS(CURLOPT_MUTE)
#   has mute
# #else
#   no mute
# #endif
#
#
open F, "<symbols-in-versions";

sub str2num {
    my ($str)=@_;
    if($str =~ /([0-9]*)\.([0-9]*)\.*([0-9]*)/) {
        return sprintf("0x%06x", $1<<16 | $2 << 8 | $3);
    }
}

print <<EOS

#include <curl/curl.h>

#define LIBCURL_HAS(x) \\
  (defined(x ## _FIRST) && (x ## _FIRST <= LIBCURL_VERSION_NUM) && \\
   (!defined(x ## _LAST) || ( x ## _LAST >= LIBCURL_VERSION_NUM)))

EOS
    ;

while(<F>) {
    if(/^(CURL[^ ]*)[ \t]*(.*)/) {
        my ($sym, $vers)=($1, $2);

        my $intr;
        my $rm;
        my $dep;

        # is there removed info?
        if($vers =~ /([\d.]+)[ \t-]+([\d.-]+)[ \t]+([\d.]+)/) {
            ($intr, $dep, $rm)=($1, $2, $3);
        }
        # is it a dep-only line?
        elsif($vers =~ /([\d.]+)[ \t-]+([\d.]+)/) {
            ($intr, $dep)=($1, $2);
        }
        else {
            $intr = $vers;
        }

        my $inum = str2num($intr);

        print <<EOS
#define ${sym}_FIRST $inum /* Added in $intr */
EOS
;
        my $irm = str2num($rm);
        if($rm) {
        print <<EOS
#define ${sym}_LAST $irm /* Last featured in $rm */
EOS
;
        }

    }
}


#!/usr/bin/env perl
#***************************************************************************
#                                  _   _ ____  _
#  Project                     ___| | | |  _ \| |
#                             / __| | | | |_) | |
#                            | (__| |_| |  _ <| |___
#                             \___|\___/|_| \_\_____|
#
# Copyright (C) 2011 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################

use strict;
use warnings;

my $max_column = 79;
my $indent = 2;

my $warnings = 0;
my $swarnings = 0;
my $errors = 0;
my $serrors = 0;
my $suppressed; # skipped problems
my $file;
my $dir=".";
my $wlist="";
my @alist;
my $windows_os = $^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys';
my $verbose;
my %skiplist;

my %ignore;
my %ignore_set;
my %ignore_used;
my @ignore_line;

my %warnings_extended = (
    'COPYRIGHTYEAR'    => 'copyright year incorrect',
    'STRERROR',        => 'strerror() detected',
    );

my %warnings = (
    'LONGLINE'         => "Line longer than $max_column",
    'TABS'             => 'TAB characters not allowed',
    'TRAILINGSPACE'    => 'Trailing whitespace on the line',
    'CPPCOMMENTS'      => '// comment detected',
    'SPACEBEFOREPAREN' => 'space before an open parenthesis',
    'SPACEAFTERPAREN'  => 'space after open parenthesis',
    'SPACEBEFORECLOSE' => 'space before a close parenthesis',
    'SPACEBEFORECOMMA' => 'space before a comma',
    'RETURNNOSPACE'    => 'return without space',
    'COMMANOSPACE'     => 'comma without following space',
    'BRACEELSE'        => '} else on the same line',
    'PARENBRACE'       => '){ without sufficient space',
    'SPACESEMICOLON'   => 'space before semicolon',
    'BANNEDFUNC'       => 'a banned function was used',
    'FOPENMODE'        => 'fopen needs a macro for the mode string',
    'BRACEPOS'         => 'wrong position for an open brace',
    'INDENTATION'      => 'wrong start column for code',
    'COPYRIGHT'        => 'file missing a copyright statement',
    'BADCOMMAND'       => 'bad !checksrc! instruction',
    'UNUSEDIGNORE'     => 'a warning ignore was not used',
    'OPENCOMMENT'      => 'file ended with a /* comment still "open"',
    'ASTERISKSPACE'    => 'pointer declared with space after asterisk',
    'ASTERISKNOSPACE'  => 'pointer declared without space before asterisk',
    'ASSIGNWITHINCONDITION' => 'assignment within conditional expression',
    'EQUALSNOSPACE'    => 'equals sign without following space',
    'NOSPACEEQUALS'    => 'equals sign without preceding space',
    'SEMINOSPACE'      => 'semicolon without following space',
    'MULTISPACE'       => 'multiple spaces used when not suitable',
    'SIZEOFNOPAREN'    => 'use of sizeof without parentheses',
    'SNPRINTF'         => 'use of snprintf',
    'ONELINECONDITION' => 'conditional block on the same line as the if()',
    'TYPEDEFSTRUCT'    => 'typedefed struct',
    'DOBRACE'          => 'A single space between do and open brace',
    'BRACEWHILE'       => 'A single space between open brace and while',
    'EXCLAMATIONSPACE' => 'Whitespace after exclamation mark in expression',
    'EMPTYLINEBRACE'   => 'Empty line before the open brace',
    'EQUALSNULL'       => 'if/while comparison with == NULL',
    'NOTEQUALSZERO',   => 'if/while comparison with != 0',
    );

sub readskiplist {
    open(W, "<$dir/checksrc.skip") or return;
    my @all=<W>;
    for(@all) {
        $windows_os ? $_ =~ s/\r?\n$// : chomp;
        $skiplist{$_}=1;
    }
    close(W);
}

# Reads the .checksrc in $dir for any extended warnings to enable locally.
# Currently there is no support for disabling warnings from the standard set,
# and since that's already handled via !checksrc! commands there is probably
# little use to add it.
sub readlocalfile {
    my $i = 0;

    open(my $rcfile, "<", "$dir/.checksrc") or return;

    while(<$rcfile>) {
        $i++;

        # Lines starting with '#' are considered comments
        if (/^\s*(#.*)/) {
            next;
        }
        elsif (/^\s*enable ([A-Z]+)$/) {
            if(!defined($warnings_extended{$1})) {
                print STDERR "invalid warning specified in .checksrc: \"$1\"\n";
                next;
            }
            $warnings{$1} = $warnings_extended{$1};
        }
        elsif (/^\s*disable ([A-Z]+)$/) {
            if(!defined($warnings{$1})) {
                print STDERR "invalid warning specified in .checksrc: \"$1\"\n";
                next;
            }
            # Accept-list
            push @alist, $1;
        }
        else {
            die "Invalid format in $dir/.checksrc on line $i\n";
        }
    }
    close($rcfile);
}

sub checkwarn {
    my ($name, $num, $col, $file, $line, $msg, $error) = @_;

    my $w=$error?"error":"warning";
    my $nowarn=0;

    #if(!$warnings{$name}) {
    #    print STDERR "Dev! there's no description for $name!\n";
    #}

    # checksrc.skip
    if($skiplist{$line}) {
        $nowarn = 1;
    }
    # !checksrc! controlled
    elsif($ignore{$name}) {
        $ignore{$name}--;
        $ignore_used{$name}++;
        $nowarn = 1;
        if(!$ignore{$name}) {
            # reached zero, enable again
            enable_warn($name, $num, $file, $line);
        }
    }

    if($nowarn) {
        $suppressed++;
        if($w) {
            $swarnings++;
        }
        else {
            $serrors++;
        }
        return;
    }

    if($w) {
        $warnings++;
    }
    else {
        $errors++;
    }

    $col++;
    print "$file:$num:$col: $w: $msg ($name)\n";
    print " $line\n";

    if($col < 80) {
        my $pref = (' ' x $col);
        print "${pref}^\n";
    }
}

$file = shift @ARGV;

while(defined $file) {

    if($file =~ /-D(.*)/) {
        $dir = $1;
        $file = shift @ARGV;
        next;
    }
    elsif($file =~ /-W(.*)/) {
        $wlist .= " $1 ";
        $file = shift @ARGV;
        next;
    }
    elsif($file =~ /-A(.+)/) {
        push @alist, $1;
        $file = shift @ARGV;
        next;
    }
    elsif($file =~ /-i([1-9])/) {
        $indent = $1 + 0;
        $file = shift @ARGV;
        next;
    }
    elsif($file =~ /-m([0-9]+)/) {
        $max_column = $1 + 0;
        $file = shift @ARGV;
        next;
    }
    elsif($file =~ /^(-h|--help)/) {
        undef $file;
        last;
    }

    last;
}

if(!$file) {
    print "checksrc.pl [option] <file1> [file2] ...\n";
    print " Options:\n";
    print "  -A[rule]  Accept this violation, can be used multiple times\n";
    print "  -D[DIR]   Directory to prepend file names\n";
    print "  -h        Show help output\n";
    print "  -W[file]  Skip the given file - ignore all its flaws\n";
    print "  -i<n>     Indent spaces. Default: 2\n";
    print "  -m<n>     Maximum line length. Default: 79\n";
    print "\nDetects and warns for these problems:\n";
    my @allw = keys %warnings;
    push @allw, keys %warnings_extended;
    for my $w (sort @allw) {
        if($warnings{$w}) {
            printf (" %-18s: %s\n", $w, $warnings{$w});
        }
        else {
            printf (" %-18s: %s[*]\n", $w, $warnings_extended{$w});
        }
    }
    print " [*] = disabled by default\n";
    exit;
}

readskiplist();
readlocalfile();

do {
    if("$wlist" !~ / $file /) {
        my $fullname = $file;
        $fullname = "$dir/$file" if ($fullname !~ '^\.?\.?/');
        scanfile($fullname);
    }
    $file = shift @ARGV;

} while($file);

sub accept_violations {
    for my $r (@alist) {
        if(!$warnings{$r}) {
            print "'$r' is not a warning to accept!\n";
            exit;
        }
        $ignore{$r}=999999;
        $ignore_used{$r}=0;
    }
}

sub checksrc_clear {
    undef %ignore;
    undef %ignore_set;
    undef @ignore_line;
}

sub checksrc_endoffile {
    my ($file) = @_;
    for(keys %ignore_set) {
        if($ignore_set{$_} && !$ignore_used{$_}) {
            checkwarn("UNUSEDIGNORE", $ignore_set{$_},
                      length($_)+11, $file,
                      $ignore_line[$ignore_set{$_}],
                      "Unused ignore: $_");
        }
    }
}

sub enable_warn {
    my ($what, $line, $file, $l) = @_;

    # switch it back on, but warn if not triggered!
    if(!$ignore_used{$what}) {
        checkwarn("UNUSEDIGNORE",
                  $line, length($what) + 11, $file, $l,
                  "No warning was inhibited!");
    }
    $ignore_set{$what}=0;
    $ignore_used{$what}=0;
    $ignore{$what}=0;
}
sub checksrc {
    my ($cmd, $line, $file, $l) = @_;
    if($cmd =~ / *([^ ]*) *(.*)/) {
        my ($enable, $what) = ($1, $2);
        $what =~ s: *\*/$::; # cut off end of C comment
        # print "ENABLE $enable WHAT $what\n";
        if($enable eq "disable") {
            my ($warn, $scope)=($1, $2);
            if($what =~ /([^ ]*) +(.*)/) {
                ($warn, $scope)=($1, $2);
            }
            else {
                $warn = $what;
                $scope = 1;
            }
            # print "IGNORE $warn for SCOPE $scope\n";
            if($scope eq "all") {
                $scope=999999;
            }

            # Comparing for a literal zero rather than the scalar value zero
            # covers the case where $scope contains the ending '*' from the
            # comment. If we use a scalar comparison (==) we induce warnings
            # on non-scalar contents.
            if($scope eq "0") {
                checkwarn("BADCOMMAND",
                          $line, 0, $file, $l,
                          "Disable zero not supported, did you mean to enable?");
            }
            elsif($ignore_set{$warn}) {
                checkwarn("BADCOMMAND",
                          $line, 0, $file, $l,
                          "$warn already disabled from line $ignore_set{$warn}");
            }
            else {
                $ignore{$warn}=$scope;
                $ignore_set{$warn}=$line;
                $ignore_line[$line]=$l;
            }
        }
        elsif($enable eq "enable") {
            enable_warn($what, $line, $file, $l);
        }
        else {
            checkwarn("BADCOMMAND",
                      $line, 0, $file, $l,
                      "Illegal !checksrc! command");
        }
    }
}

sub nostrings {
    my ($str) = @_;
    $str =~ s/\".*\"//g;
    return $str;
}

sub scanfile {
    my ($file) = @_;

    my $line = 1;
    my $prevl="";
    my $prevpl="";
    my $l = "";
    my $prep = 0;
    my $prevp = 0;
    open(R, "<$file") || die "failed to open $file";

    my $incomment=0;
    my @copyright=();
    checksrc_clear(); # for file based ignores
    accept_violations();

    while(<R>) {
        $windows_os ? $_ =~ s/\r?\n$// : chomp;
        my $l = $_;
        my $ol = $l; # keep the unmodified line for error reporting
        my $column = 0;

        # check for !checksrc! commands
        if($l =~ /\!checksrc\! (.*)/) {
            my $cmd = $1;
            checksrc($cmd, $line, $file, $l)
        }

        # check for a copyright statement and save the years
        if($l =~ /\* +copyright .* \d\d\d\d/i) {
            while($l =~ /([\d]{4})/g) {
                push @copyright, {
                  year => $1,
                  line => $line,
                  col => index($l, $1),
                  code => $l
                };
            }
        }

        # detect long lines
        if(length($l) > $max_column) {
            checkwarn("LONGLINE", $line, length($l), $file, $l,
                      "Longer than $max_column columns");
        }
        # detect TAB characters
        if($l =~ /^(.*)\t/) {
            checkwarn("TABS",
                      $line, length($1), $file, $l, "Contains TAB character", 1);
        }
        # detect trailing whitespace
        if($l =~ /^(.*)[ \t]+\z/) {
            checkwarn("TRAILINGSPACE",
                      $line, length($1), $file, $l, "Trailing whitespace");
        }

        # ------------------------------------------------------------
        # Above this marker, the checks were done on lines *including*
        # comments
        # ------------------------------------------------------------

        # strip off C89 comments

      comment:
        if(!$incomment) {
            if($l =~ s/\/\*.*\*\// /g) {
                # full /* comments */ were removed!
            }
            if($l =~ s/\/\*.*//) {
                # start of /* comment was removed
                $incomment = 1;
            }
        }
        else {
            if($l =~ s/.*\*\///) {
                # end of comment */ was removed
                $incomment = 0;
                goto comment;
            }
            else {
                # still within a comment
                $l="";
            }
        }

        # ------------------------------------------------------------
        # Below this marker, the checks were done on lines *without*
        # comments
        # ------------------------------------------------------------

        # prev line was a preprocessor **and** ended with a backslash
        if($prep && ($prevpl =~ /\\ *\z/)) {
            # this is still a preprocessor line
            $prep = 1;
            goto preproc;
        }
        $prep = 0;

        # crude attempt to detect // comments without too many false
        # positives
        if($l =~ /^(([^"\*]*)[^:"]|)\/\//) {
            checkwarn("CPPCOMMENTS",
                      $line, length($1), $file, $l, "\/\/ comment");
        }

        # detect and strip preprocessor directives
        if($l =~ /^[ \t]*\#/) {
            # preprocessor line
            $prep = 1;
            goto preproc;
        }

        my $nostr = nostrings($l);
        # check spaces after for/if/while/function call
        if($nostr =~ /^(.*)(for|if|while| ([a-zA-Z0-9_]+)) \((.)/) {
            if($1 =~ / *\#/) {
                # this is a #if, treat it differently
            }
            elsif(defined $3 && $3 eq "return") {
                # return must have a space
            }
            elsif(defined $3 && $3 eq "case") {
                # case must have a space
            }
            elsif($4 eq "*") {
                # (* beginning makes the space OK!
            }
            elsif($1 =~ / *typedef/) {
                # typedefs can use space-paren
            }
            else {
                checkwarn("SPACEBEFOREPAREN", $line, length($1)+length($2), $file, $l,
                          "$2 with space");
            }
        }
        # check for '== NULL' in if/while conditions but not if the thing on
        # the left of it is a function call
        if($nostr =~ /^(.*)(if|while)(\(.*[^)]) == NULL/) {
            checkwarn("EQUALSNULL", $line,
                      length($1) + length($2) + length($3),
                      $file, $l, "we prefer !variable instead of \"== NULL\" comparisons");
        }

        # check for '!= 0' in if/while conditions but not if the thing on
        # the left of it is a function call
        if($nostr =~ /^(.*)(if|while)(\(.*[^)]) != 0[^x]/) {
            checkwarn("NOTEQUALSZERO", $line,
                      length($1) + length($2) + length($3),
                      $file, $l, "we prefer if(rc) instead of \"rc != 0\" comparisons");
        }

        # check spaces in 'do {'
        if($nostr =~ /^( *)do( *)\{/ && length($2) != 1) {
            checkwarn("DOBRACE", $line, length($1) + 2, $file, $l, "one space after do before brace");
        }
        # check spaces in 'do {'
        elsif($nostr =~ /^( *)\}( *)while/ && length($2) != 1) {
            checkwarn("BRACEWHILE", $line, length($1) + 2, $file, $l, "one space between brace and while");
        }
        if($nostr =~ /^((.*\s)(if) *\()(.*)\)(.*)/) {
            my $pos = length($1);
            my $postparen = $5;
            my $cond = $4;
            if($cond =~ / = /) {
                checkwarn("ASSIGNWITHINCONDITION",
                          $line, $pos+1, $file, $l,
                          "assignment within conditional expression");
            }
            my $temp = $cond;
            $temp =~ s/\(//g; # remove open parens
            my $openc = length($cond) - length($temp);

            $temp = $cond;
            $temp =~ s/\)//g; # remove close parens
            my $closec = length($cond) - length($temp);
            my $even = $openc == $closec;

            if($l =~ / *\#/) {
                # this is a #if, treat it differently
            }
            elsif($even && $postparen &&
               ($postparen !~ /^ *$/) && ($postparen !~ /^ *[,{&|\\]+/)) {
                checkwarn("ONELINECONDITION",
                          $line, length($l)-length($postparen), $file, $l,
                          "conditional block on the same line");
            }
        }
        # check spaces after open parentheses
        if($l =~ /^(.*[a-z])\( /i) {
            checkwarn("SPACEAFTERPAREN",
                      $line, length($1)+1, $file, $l,
                      "space after open parenthesis");
        }

        # check spaces before close parentheses, unless it was a space or a
        # close parenthesis!
        if($l =~ /(.*[^\) ]) \)/) {
            checkwarn("SPACEBEFORECLOSE",
                      $line, length($1)+1, $file, $l,
                      "space before close parenthesis");
        }

        # check spaces before comma!
        if($l =~ /(.*[^ ]) ,/) {
            checkwarn("SPACEBEFORECOMMA",
                      $line, length($1)+1, $file, $l,
                      "space before comma");
        }

        # check for "return(" without space
        if($l =~ /^(.*)return\(/) {
            if($1 =~ / *\#/) {
                # this is a #if, treat it differently
            }
            else {
                checkwarn("RETURNNOSPACE", $line, length($1)+6, $file, $l,
                          "return without space before paren");
            }
        }

        # check for "sizeof" without parenthesis
        if(($l =~ /^(.*)sizeof *([ (])/) && ($2 ne "(")) {
            if($1 =~ / *\#/) {
                # this is a #if, treat it differently
            }
            else {
                checkwarn("SIZEOFNOPAREN", $line, length($1)+6, $file, $l,
                          "sizeof without parenthesis");
            }
        }

        # check for comma without space
        if($l =~ /^(.*),[^ \n]/) {
            my $pref=$1;
            my $ign=0;
            if($pref =~ / *\#/) {
                # this is a #if, treat it differently
                $ign=1;
            }
            elsif($pref =~ /\/\*/) {
                # this is a comment
                $ign=1;
            }
            elsif($pref =~ /[\"\']/) {
                $ign = 1;
                # There is a quote here, figure out whether the comma is
                # within a string or '' or not.
                if($pref =~ /\"/) {
                    # within a string
                }
                elsif($pref =~ /\'$/) {
                    # a single letter
                }
                else {
                    $ign = 0;
                }
            }
            if(!$ign) {
                checkwarn("COMMANOSPACE", $line, length($pref)+1, $file, $l,
                          "comma without following space");
            }
        }

        # check for "} else"
        if($l =~ /^(.*)\} *else/) {
            checkwarn("BRACEELSE",
                      $line, length($1), $file, $l, "else after closing brace on same line");
        }
        # check for "){"
        if($l =~ /^(.*)\)\{/) {
            checkwarn("PARENBRACE",
                      $line, length($1)+1, $file, $l, "missing space after close paren");
        }
        # check for "^{" with an empty line before it
        if(($l =~ /^\{/) && ($prevl =~ /^[ \t]*\z/)) {
            checkwarn("EMPTYLINEBRACE",
                      $line, 0, $file, $l, "empty line before open brace");
        }

        # check for space before the semicolon last in a line
        if($l =~ /^(.*[^ ].*) ;$/) {
            checkwarn("SPACESEMICOLON",
                      $line, length($1), $file, $ol, "no space before semicolon");
        }

        # scan for use of banned functions
        if($l =~ /^(.*\W)
                   (gmtime|localtime|
                    gets|
                    strtok|
                    v?sprintf|
                    (str|_mbs|_tcs|_wcs)n?cat|
                    LoadLibrary(Ex)?(A|W)?)
                   \s*\(
                 /x) {
            checkwarn("BANNEDFUNC",
                      $line, length($1), $file, $ol,
                      "use of $2 is banned");
        }
        if($warnings{"STRERROR"}) {
            # scan for use of banned strerror. This is not a BANNEDFUNC to
            # allow for individual enable/disable of this warning.
            if($l =~ /^(.*\W)(strerror)\s*\(/x) {
                if($1 !~ /^ *\#/) {
                    # skip preprocessor lines
                    checkwarn("STRERROR",
                              $line, length($1), $file, $ol,
                              "use of $2 is banned");
                }
            }
        }
        # scan for use of snprintf for curl-internals reasons
        if($l =~ /^(.*\W)(v?snprintf)\s*\(/x) {
            checkwarn("SNPRINTF",
                      $line, length($1), $file, $ol,
                      "use of $2 is banned");
        }

        # scan for use of non-binary fopen without the macro
        if($l =~ /^(.*\W)fopen\s*\([^,]*, *\"([^"]*)/) {
            my $mode = $2;
            if($mode !~ /b/) {
                checkwarn("FOPENMODE",
                          $line, length($1), $file, $ol,
                          "use of non-binary fopen without FOPEN_* macro: $mode");
            }
        }

        # check for open brace first on line but not first column only alert
        # if previous line ended with a close paren and it wasn't a cpp line
        if(($prevl =~ /\)\z/) && ($l =~ /^( +)\{/) && !$prevp) {
            checkwarn("BRACEPOS",
                      $line, length($1), $file, $ol, "badly placed open brace");
        }

        # if the previous line starts with if/while/for AND ends with an open
        # brace, or an else statement, check that this line is indented $indent
        # more steps, if not a cpp line
        if(!$prevp && ($prevl =~ /^( *)((if|while|for)\(.*\{|else)\z/)) {
            my $first = length($1);
            # this line has some character besides spaces
            if($l =~ /^( *)[^ ]/) {
                my $second = length($1);
                my $expect = $first+$indent;
                if($expect != $second) {
                    my $diff = $second - $first;
                    checkwarn("INDENTATION", $line, length($1), $file, $ol,
                              "not indented $indent steps (uses $diff)");

                }
            }
        }

        # check for 'char * name'
        if(($l =~ /(^.*(char|int|long|void|CURL|CURLM|CURLMsg|[cC]url_[A-Za-z_]+|struct [a-zA-Z_]+) *(\*+)) (\w+)/) && ($4 !~ /^(const|volatile)$/)) {
            checkwarn("ASTERISKSPACE",
                      $line, length($1), $file, $ol,
                      "space after declarative asterisk");
        }
        # check for 'char*'
        if(($l =~ /(^.*(char|int|long|void|curl_slist|CURL|CURLM|CURLMsg|curl_httppost|sockaddr_in|FILE)\*)/)) {
            checkwarn("ASTERISKNOSPACE",
                      $line, length($1)-1, $file, $ol,
                      "no space before asterisk");
        }

        # check for 'void func() {', but avoid false positives by requiring
        # both an open and closed parentheses before the open brace
        if($l =~ /^((\w).*)\{\z/) {
            my $k = $1;
            $k =~ s/const *//;
            $k =~ s/static *//;
            if($k =~ /\(.*\)/) {
                checkwarn("BRACEPOS",
                          $line, length($l)-1, $file, $ol,
                          "wrongly placed open brace");
            }
        }

        # check for equals sign without spaces next to it
        if($nostr =~ /(.*)\=[a-z0-9]/i) {
            checkwarn("EQUALSNOSPACE",
                      $line, length($1)+1, $file, $ol,
                      "no space after equals sign");
        }
        # check for equals sign without spaces before it
        elsif($nostr =~ /(.*)[a-z0-9]\=/i) {
            checkwarn("NOSPACEEQUALS",
                      $line, length($1)+1, $file, $ol,
                      "no space before equals sign");
        }

        # check for plus signs without spaces next to it
        if($nostr =~ /(.*)[^+]\+[a-z0-9]/i) {
            checkwarn("PLUSNOSPACE",
                      $line, length($1)+1, $file, $ol,
                      "no space after plus sign");
        }
        # check for plus sign without spaces before it
        elsif($nostr =~ /(.*)[a-z0-9]\+[^+]/i) {
            checkwarn("NOSPACEPLUS",
                      $line, length($1)+1, $file, $ol,
                      "no space before plus sign");
        }

        # check for semicolons without space next to it
        if($nostr =~ /(.*)\;[a-z0-9]/i) {
            checkwarn("SEMINOSPACE",
                      $line, length($1)+1, $file, $ol,
                      "no space after semicolon");
        }

        # typedef struct ... {
        if($nostr =~ /^(.*)typedef struct.*{/) {
            checkwarn("TYPEDEFSTRUCT",
                      $line, length($1)+1, $file, $ol,
                      "typedef'ed struct");
        }

        if($nostr =~ /(.*)! +(\w|\()/) {
            checkwarn("EXCLAMATIONSPACE",
                      $line, length($1)+1, $file, $ol,
                      "space after exclamation mark");
        }

        # check for more than one consecutive space before open brace or
        # question mark. Skip lines containing strings since they make it hard
        # due to artificially getting multiple spaces
        if(($l eq $nostr) &&
           $nostr =~ /^(.*(\S)) + [{?]/i) {
            checkwarn("MULTISPACE",
                      $line, length($1)+1, $file, $ol,
                      "multiple spaces");
        }
      preproc:
        $line++;
        $prevp = $prep;
        $prevl = $ol if(!$prep);
        $prevpl = $ol if($prep);
    }

    if(!scalar(@copyright)) {
        checkwarn("COPYRIGHT", 1, 0, $file, "", "Missing copyright statement", 1);
    }

    # COPYRIGHTYEAR is a extended warning so we must first see if it has been
    # enabled in .checksrc
    if(defined($warnings{"COPYRIGHTYEAR"})) {
        # The check for updated copyrightyear is overly complicated in order to
        # not punish current hacking for past sins. The copyright years are
        # right now a bit behind, so enforcing copyright year checking on all
        # files would cause hundreds of errors. Instead we only look at files
        # which are tracked in the Git repo and edited in the workdir, or
        # committed locally on the branch without being in upstream master.
        #
        # The simple and naive test is to simply check for the current year,
        # but updating the year even without an edit is against project policy
        # (and it would fail every file on January 1st).
        #
        # A rather more interesting, and correct, check would be to not test
        # only locally committed files but inspect all files wrt the year of
        # their last commit. Removing the `git rev-list origin/master..HEAD`
        # condition below will enfore copyright year checks against the year
        # the file was last committed (and thus edited to some degree).
        my $commityear = undef;
        @copyright = sort {$$b{year} cmp $$a{year}} @copyright;

        # if the file is modified, assume commit year this year
        if(`git status -s -- $file` =~ /^ [MARCU]/) {
            $commityear = (localtime(time))[5] + 1900;
        }
        else {
            # min-parents=1 to ignore wrong initial commit in truncated repos
            my $grl = `git rev-list --max-count=1 --min-parents=1 --timestamp HEAD -- $file`;
            if($grl) {
                chomp $grl;
                $commityear = (localtime((split(/ /, $grl))[0]))[5] + 1900;
            }
        }

        if(defined($commityear) && scalar(@copyright) &&
           $copyright[0]{year} != $commityear) {
            checkwarn("COPYRIGHTYEAR", $copyright[0]{line}, $copyright[0]{col},
                      $file, $copyright[0]{code},
                      "Copyright year out of date, should be $commityear, " .
                      "is $copyright[0]{year}", 1);
        }
    }

    if($incomment) {
        checkwarn("OPENCOMMENT", 1, 0, $file, "", "Missing closing comment", 1);
    }

    checksrc_endoffile($file);

    close(R);

}

if($errors || $warnings || $verbose) {
    printf "checksrc: %d errors and %d warnings\n", $errors, $warnings;
    if($suppressed) {
        printf "checksrc: %d errors and %d warnings suppressed\n",
        $serrors,
        $swarnings;
    }
    exit 5; # return failure
}


#!/usr/bin/env perl
#***************************************************************************
#                                  _   _ ____  _
#  Project                     ___| | | |  _ \| |
#                             / __| | | | |_) | |
#                            | (__| |_| |  _ <| |___
#                             \___|\___/|_| \_\_____|
#
# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################

use strict;
use warnings;
use Getopt::Long();
use Pod::Usage();

my $curl = 'curl';
my $shell = 'zsh';
my $help = 0;
Getopt::Long::GetOptions(
    'curl=s' => \$curl,
    'shell=s' => \$shell,
    'help' => \$help,
) or Pod::Usage::pod2usage();
Pod::Usage::pod2usage() if $help;

my $regex = '\s+(?:(-[^\s]+),\s)?(--[^\s]+)\s*(\<.+?\>)?\s+(.*)';
my @opts = parse_main_opts('--help all', $regex);

if ($shell eq 'fish') {
    print "# curl fish completion\n\n";
    print qq{$_ \n} foreach (@opts);
} elsif ($shell eq 'zsh') {
    my $opts_str;

    $opts_str .= qq{  $_ \\\n} foreach (@opts);
    chomp $opts_str;

my $tmpl = <<"EOS";
#compdef curl

# curl zsh completion

local curcontext="\$curcontext" state state_descr line
typeset -A opt_args

local rc=1

_arguments -C -S \\
$opts_str
  '*:URL:_urls' && rc=0

return rc
EOS

    print $tmpl;
} else {
    die("Unsupported shell: $shell");
}

sub parse_main_opts {
    my ($cmd, $regex) = @_;

    my @list;
    my @lines = call_curl($cmd);

    foreach my $line (@lines) {
        my ($short, $long, $arg, $desc) = ($line =~ /^$regex/) or next;

        my $option = '';

        $arg =~ s/\:/\\\:/g if defined $arg;

        $desc =~ s/'/'\\''/g if defined $desc;
        $desc =~ s/\[/\\\[/g if defined $desc;
        $desc =~ s/\]/\\\]/g if defined $desc;
        $desc =~ s/\:/\\\:/g if defined $desc;

        if ($shell eq 'fish') {
            $option .= "complete --command curl";
            $option .= " --short-option '" . strip_dash(trim($short)) . "'"
                if defined $short;
            $option .= " --long-option '" . strip_dash(trim($long)) . "'"
                if defined $long;
            $option .= " --description '" . strip_dash(trim($desc)) . "'"
                if defined $desc;
        } elsif ($shell eq 'zsh') {
            $option .= '{' . trim($short) . ',' if defined $short;
            $option .= trim($long)  if defined $long;
            $option .= '}' if defined $short;
            $option .= '\'[' . trim($desc) . ']\'' if defined $desc;

            $option .= ":'$arg'" if defined $arg;

            $option .= ':_files'
                if defined $arg and ($arg eq '<file>' || $arg eq '<filename>'
                    || $arg eq '<dir>');
        }

        push @list, $option;
    }

    # Sort longest first, because zsh won't complete an option listed
    # after one that's a prefix of it.
    @list = sort {
        $a =~ /([^=]*)/; my $ma = $1;
        $b =~ /([^=]*)/; my $mb = $1;

        length($mb) <=> length($ma)
    } @list if $shell eq 'zsh';

    return @list;
}

sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };
sub strip_dash { my $s = shift; $s =~ s/^-+//g; return $s };

sub call_curl {
    my ($cmd) = @_;
    my $output = `"$curl" $cmd`;
    if ($? == -1) {
        die "Could not run curl: $!";
    } elsif ((my $exit_code = $? >> 8) != 0) {
        die "curl returned $exit_code with output:\n$output";
    }
    return split /\n/, $output;
}

__END__

=head1 NAME

completion.pl - Generates tab-completion files for various shells

=head1 SYNOPSIS

completion.pl [options...]

    --curl   path to curl executable
    --shell  zsh/fish
    --help   prints this help

=cut


#!/usr/bin/env perl
#***************************************************************************
#                                  _   _ ____  _
#  Project                     ___| | | |  _ \| |
#                             / __| | | | |_) | |
#                            | (__| |_| |  _ <| |___
#                             \___|\___/|_| \_\_____|
#
# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################

# Update man pages.

use strict;
use warnings;
use Tie::File;

# Data from the command line.

my $curlver = $ARGV[0];
my $curldate = $ARGV[1];

# Directories and extensions.

my @dirlist = ("docs/", "docs/libcurl/", "docs/libcurl/opts/", "tests/");
my @extlist = (".1", ".3");
my @excludelist = ("mk-ca-bundle.1", "template.3");

# Subroutines

sub printargs{
  # Print arguments and exit.

  print "usage: updatemanpages.pl <version> <date>\n";
  exit;
}

sub getthline{
  # Process file looking for .TH section.

  my $filename = shift;
  my $file_handle;
  my $file_line;

  # Open the file.

  open($file_handle, $filename);

  # Look for the .TH section, process it into an array,
  # modify it and write to file.

  tie(my @file_data, 'Tie::File', $filename);
  foreach my $file_data_line(@file_data) {
    if($file_data_line =~ /^.TH/) {
      $file_line = $file_data_line;
      last;
    }
  }

  # Close the file.

  close($file_handle);
  return $file_line;
}

sub extractth{
  # Extract .TH section as an array.

  my $input = shift;

  # Split the line into an array.

  my @tharray;
  my $inputsize = length($input);
  my $inputcurrent = "";
  my $quotemode = 0;

  for(my $inputseek = 0; $inputseek < $inputsize; $inputseek++) {

    if(substr($input, $inputseek, 1) eq " " && $quotemode eq 0) {
      push(@tharray, $inputcurrent);
      $inputcurrent = "";
      next;
    }

    $inputcurrent = $inputcurrent . substr($input, $inputseek, 1);

    if(substr($input, $inputseek, 1) eq "\"") {
      if($quotemode eq 0) {
        $quotemode = 1;
      }
      else {
        $quotemode = 0;
      }
    }
  }

  if($inputcurrent ne "") {
    push(@tharray, $inputcurrent);
  }

  return @tharray;
}

sub getdate{
  # Get the date from the .TH section.

  my $filename = shift;
  my $thline;
  my @tharray;
  my $date = "";

  $thline = getthline($filename);

  # Return nothing if there is no .TH section found.

  if(!$thline || $thline eq "") {
    return "";
  }

  @tharray = extractth($thline);

  # Remove the quotes at the start and end.

  $date = substr($tharray[3], 1, -1);
  return $date;
}

sub processth{
  # Process .TH section.

  my $input = shift;
  my $date = shift;

  # Split the line into an array.

  my @tharray = extractth($input);

  # Alter the date.

  my $itemdate = "\"";
  $itemdate .= $date;
  $itemdate .= "\"";
  $tharray[3] = $itemdate;

  # Alter the item version.

  my $itemver = $tharray[4];
  my $itemname = "";

  for(my $itemnameseek = 1;
    $itemnameseek < length($itemver);
    $itemnameseek++) {
    if(substr($itemver, $itemnameseek, 1) eq " " ||
      substr($itemver, $itemnameseek, 1) eq "\"") {
      last;
    }
    $itemname .= substr($itemver, $itemnameseek, 1);
  }

  $itemver = "\"";
  $itemver .= $itemname;
  $itemver .= " ";
  $itemver .= $curlver;
  $itemver .= "\"";

  $tharray[4] = $itemver;

  my $thoutput = "";

  foreach my $thvalue (@tharray) {
    $thoutput .= $thvalue;
    $thoutput .= " ";
  }
  $thoutput =~ s/\s+$//;
  $thoutput .= "\n";

  # Return updated string.

  return $thoutput;
}

sub processfile{
  # Process file looking for .TH section.

  my $filename = shift;
  my $date = shift;
  my $file_handle;
  my $file_dist_handle;
  my $filename_dist;

  # Open a handle for the original file and a second file handle
  # for the dist file.

  $filename_dist = $filename . ".dist";

  open($file_handle, $filename);
  open($file_dist_handle, ">" . $filename_dist);

  # Look for the .TH section, process it into an array,
  # modify it and write to file.

  tie(my @file_data, 'Tie::File', $filename);
  foreach my $file_data_line (@file_data) {
    if($file_data_line =~ /^.TH/) {
      my $file_dist_line = processth($file_data_line, $date);
      print $file_dist_handle $file_dist_line . "\n";
    }
    else {
      print $file_dist_handle $file_data_line . "\n";
    }
  }

  # Close the file.

  close($file_handle);
  close($file_dist_handle);
}

# Check that $curlver is set, otherwise print arguments and exit.

if(!$curlver) {
  printargs();
}

# check to see that the git command works, it requires git 2.6 something
my $gitcheck = `git log -1 --date="format:%B %d, %Y" $dirlist[0] 2>/dev/null`;
if(length($gitcheck) < 1) {
    print "git version too old or $dirlist[0] is a bad argument\n";
    exit;
}

# Look in each directory.

my $dir_handle;

foreach my $dirname (@dirlist) {
  foreach my $extname (@extlist) {
    # Go through the directory looking for files ending with
    # the current extension.

    opendir($dir_handle, $dirname);
    my @filelist = grep(/.$extname$/i, readdir($dir_handle));

    foreach my $file (@filelist) {
      # Skip if file is in exclude list.

      if(grep(/^$file$/, @excludelist)) {
        next;
      }

      # Load the file and get the date.

      my $filedate;

      # Check if dist version exists and load date from that
      # file if it does.

      if(-e ($dirname . $file . ".dist")) {
        $filedate = getdate(($dirname . $file . ".dist"));
      }
      else {
        $filedate = getdate(($dirname . $file));
      }

      # Skip if value is empty.

      if(!$filedate || $filedate eq "") {
        next;
      }

      # Check the man page in the git repository.

      my $repodata = `LC_TIME=C git log -1 --date="format:%B %d, %Y" \\
                       --since="$filedate" $dirname$file | grep ^Date:`;

      # If there is output then update the man page
      # with the new date/version.

      # Process the file if there is output.

      if($repodata) {
        my $thisdate;
        if(!$curldate) {
          if($repodata =~ /^Date: +(.*)/) {
            $thisdate = $1;
          }
          else {
            print STDERR "Warning: " . ($dirname . $file) . ": found no " .
                           "date\n";
          }
        }
        else {
          $thisdate = $curldate;
        }
        processfile(($dirname . $file), $thisdate);
        print $dirname . $file . " page updated to $thisdate\n";
      }
    }
    closedir($dir_handle);
  }
}

__END__

=pod

=head1 updatemanpages.pl

Updates the man pages with the version number and optional date. If the date
isn't provided, the last modified date from git is used.

=head2 USAGE

updatemanpages.pl version [date]

=head3 version

Specifies version (required)

=head3 date

Specifies date (optional)

=head2 SETTINGS

=head3 @dirlist

Specifies the list of directories to look for files in.

=head3 @extlist

Specifies the list of files with extensions to process.

=head3 @excludelist

Specifies the list of files to not process.

=head2 NOTES

This script is used during maketgz.

=cut


#!/usr/bin/env perl
#***************************************************************************
#                                  _   _ ____  _
#  Project                     ___| | | |  _ \| |
#                             / __| | | | |_) | |
#                            | (__| |_| |  _ <| |___
#                             \___|\___/|_| \_\_____|
#
# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################

# Yeah, I know, probably 1000 other persons already wrote a script like
# this, but I'll tell ya:

# THEY DON'T FIT ME :-)

# Get readme file as parameter:

if($ARGV[0] eq "-c") {
    $c=1;
    shift @ARGV;
}

push @out, "                                  _   _ ____  _\n";
push @out, "  Project                     ___| | | |  _ \\| |\n";
push @out, "                             / __| | | | |_) | |\n";
push @out, "                            | (__| |_| |  _ <| |___\n";
push @out, "                             \\___|\\___/|_| \\_\\_____|\n";

my $olen=0;
while (<STDIN>) {
    my $line = $_;

    # this should be removed:
    $line =~ s/(.|_)//g;

    # remove trailing CR from line. msysgit checks out files as line+CRLF
    $line =~ s/\r$//;

    if($line =~ /^([ \t]*\n|curl)/i) {
        # cut off headers and empty lines
        $wline++; # count number of cut off lines
        next;
    }

    my $text = $line;
    $text =~ s/^\s+//g; # cut off preceding...
    $text =~ s/\s+$//g; # and trailing whitespaces

    $tlen = length($text);

    if($wline && ($olen == $tlen)) {
        # if the previous line with contents was exactly as long as
        # this line, then we ignore the newlines!

        # We do this magic because a header may abort a paragraph at
        # any line, but we don't want that to be noticed in the output
        # here
        $wline=0;
    }
    $olen = $tlen;

    if($wline) {
        # we only make one empty line max
        $wline = 0;
        push @out, "\n";
    }
    push @out, $line;
}
push @out, "\n"; # just an extra newline

print <<HEAD
/*
 * NEVER EVER edit this manually, fix the mkhelp.pl script instead!
 */
#ifdef USE_MANUAL
#include "tool_hugehelp.h"
HEAD
    ;
if($c) {
    # If compression requested, check that the Gzip module is available
    # or else disable compression
    $c = eval
    {
      require IO::Compress::Gzip;
      IO::Compress::Gzip->import();
      1;
    };
    print STDERR "Warning: compression requested but Gzip is not available\n" if (!$c)
}

if($c)
{
    my $content = join("", @out);
    my $gzippedContent;
    IO::Compress::Gzip::gzip(
        \$content, \$gzippedContent, Level => 9, TextFlag => 1, Time=>0) or die "gzip failed:";
    $gzip = length($content);
    $gzipped = length($gzippedContent);

    print <<HEAD
#include <zlib.h>
#include "memdebug.h" /* keep this as LAST include */
static const unsigned char hugehelpgz[] = {
  /* This mumbo-jumbo is the huge help text compressed with gzip.
     Thanks to this operation, the size of this data shrank from $gzip
     to $gzipped bytes. You can disable the use of compressed help
     texts by NOT passing -c to the mkhelp.pl tool. */
HEAD
;

    my $c=0;
    print " ";
    for(split(//, $gzippedContent)) {
        my $num=ord($_);
        printf(" 0x%02x,", 0+$num);
        if(!(++$c % 12)) {
            print "\n ";
        }
    }
    print "\n};\n";

    print <<EOF
#define BUF_SIZE 0x10000
static voidpf zalloc_func(voidpf opaque, unsigned int items, unsigned int size)
{
  (void) opaque;
  /* not a typo, keep it calloc() */
  return (voidpf) calloc(items, size);
}
static void zfree_func(voidpf opaque, voidpf ptr)
{
  (void) opaque;
  free(ptr);
}
/* Decompress and send to stdout a gzip-compressed buffer */
void hugehelp(void)
{
  unsigned char* buf;
  int status,headerlen;
  z_stream z;

  /* Make sure no gzip options are set */
  if (hugehelpgz[3] & 0xfe)
    return;

  headerlen = 10;
  memset(&z, 0, sizeof(z_stream));
  z.zalloc = (alloc_func)zalloc_func;
  z.zfree = (free_func)zfree_func;
  z.avail_in = (unsigned int)(sizeof(hugehelpgz) - headerlen);
  z.next_in = (unsigned char *)hugehelpgz + headerlen;

  if (inflateInit2(&z, -MAX_WBITS) != Z_OK)
    return;

  buf = malloc(BUF_SIZE);
  if (buf) {
    while(1) {
      z.avail_out = BUF_SIZE;
      z.next_out = buf;
      status = inflate(&z, Z_SYNC_FLUSH);
      if (status == Z_OK || status == Z_STREAM_END) {
        fwrite(buf, BUF_SIZE - z.avail_out, 1, stdout);
        if (status == Z_STREAM_END)
          break;
      }
      else
        break;    /* Error */
    }
    free(buf);
  }
  inflateEnd(&z);
}
EOF
    ;
foot();
exit;
}
else {
    print <<HEAD
void hugehelp(void)
{
   fputs(
HEAD
         ;
}

$outsize=0;
for(@out) {
    chop;

    $new = $_;

    $outsize += length($new)+1; # one for the newline

    $new =~ s/\\/\\\\/g;
    $new =~ s/\"/\\\"/g;

    # gcc 2.96 claims ISO C89 only is required to support 509 letter strings
    if($outsize > 500) {
        # terminate and make another fputs() call here
        print ", stdout);\n fputs(\n";
        $outsize=length($new)+1;
    }
    printf("\"%s\\n\"\n", $new);

}

print ", stdout) ;\n}\n";

foot();

sub foot {
  print <<FOOT
#else /* !USE_MANUAL */
/* built-in manual is disabled, blank function */
#include "tool_hugehelp.h"
void hugehelp(void) {}
#endif /* USE_MANUAL */
FOOT
  ;
}


#!/usr/bin/env perl
#***************************************************************************
#                                  _   _ ____  _
#  Project                     ___| | | |  _ \| |
#                             / __| | | | |_) | |
#                            | (__| |_| |  _ <| |___
#                             \___|\___/|_| \_\_____|
#
# Copyright (C) 2010-2021, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################
#
# This script grew out of help from Przemyslaw Iskra and Balint Szilakszi
# a late evening in the #curl IRC channel.
#

use strict;
use warnings;
use vars qw($Cpreprocessor);

#
# configurehelp perl module is generated by configure script
#
my $rc = eval {
    require configurehelp;
    configurehelp->import(qw(
        $Cpreprocessor
    ));
    1;
};
# Set default values if configure has not generated a configurehelp.pm file.
# This is the case with cmake.
if (!$rc) {
    $Cpreprocessor = 'cpp';
}

my $verbose=0;

# verbose mode when -v is the first argument
if($ARGV[0] eq "-v") {
    $verbose=1;
    shift;
}

# we may get the dir root pointed out
my $root=$ARGV[0] || ".";

# need an include directory when building out-of-tree
my $i = ($ARGV[1]) ? "-I$ARGV[1] " : '';

my $incdir = "$root/include/curl";

my $summary=0;
my $misses=0;

my @syms;
my %doc;
my %rem;

sub scanenums {
    my ($file)=@_;
    my $skipit = 0;

    open H_IN, "-|", "$Cpreprocessor $i$file" || die "Cannot preprocess $file";
    while ( <H_IN> ) {
        my ($line, $linenum) = ($_, $.);
        if( /^#(line|) (\d+) \"(.*)\"/) {
            # if the included file isn't in our incdir, then we skip this section
            # until next #line
            #
            if($3 !~ /^$incdir/) {
                $skipit = 1;
                next;
            }
            # parse this!
            $skipit = 0;
            next;
        }
        if($skipit) {
            next;
        }
        if (/^#/) {
            next;
        }
        if ( /enum\s+(\S+\s+)?{/ .. /}/ ) {
            s/^\s+//;
            chomp;
            s/[,\s].*//;
            if(($_ !~ /\}(;|)/) &&
               ($_ ne "typedef") &&
               ($_ ne "enum") &&
               ($_ !~ /^[ \t]*$/)) {
                if($verbose) {
                    print "Source: $Cpreprocessor $i$file\n";
                    print "Symbol: $_\n";
                    print "Line #$linenum: $line\n\n";
                }
                push @syms, $_;
            }
        }
    }
    close H_IN || die "Error preprocessing $file";
}

sub scanheader {
    my ($f)=@_;
    scanenums($f);
    open H, "<$f";
    while(<H>) {
        my ($line, $linenum) = ($_, $.);
        if (/^#define +([^ \n]*)/) {
            if($verbose) {
                print "Source: $f\n";
                print "Symbol: $1\n";
                print "Line #$linenum: $line\n\n";
            }
            push @syms, $1;
        }
    }
    close H;
}

opendir(my $dh, $incdir) || die "Can't opendir $incdir: $!";
my @hfiles = grep { /\.h$/ } readdir($dh);
closedir $dh;

for(@hfiles) {
    scanheader("$incdir/$_");
}

my $errors = 0;
for my $s (@syms) {
    if($s !~ /^(lib|)curl/i) {
        print "Bad symbols in public header files:\n" if(!$errors);
        $errors++;
        print "  $s\n";
    }
}
if($errors) {
    exit 1;
}
printf "%d fine symbols found\n", scalar(@syms);


#!/usr/bin/env perl
#***************************************************************************
#                                  _   _ ____  _
#  Project                     ___| | | |  _ \| |
#                             / __| | | | |_) | |
#                            | (__| |_| |  _ <| |___
#                             \___|\___/|_| \_\_____|
#
# Copyright (C) 2010 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################
#

use strict;
use warnings;

# the DISABLE options that can be set by configure
my %disable;
# the DISABLE options that are used in C files
my %file;
# the DISABLE options that are documented
my %docs;

# we may get the dir root pointed out
my $root=$ARGV[0] || ".";
my $DOCS="CURL-DISABLE.md";

sub scanconf {
    my ($f)=@_;
    open S, "<$f";
    while(<S>) {
        if(/(CURL_DISABLE_[A-Z_]+)/g) {
            my ($sym)=($1);
            $disable{$sym} = 1;
        }
    }
    close S;
}

sub scan_configure {
    opendir(my $m, "$root/m4") || die "Can't opendir $root/m4: $!";
    my @m4 = grep { /\.m4$/ } readdir($m);
    closedir $m;
    scanconf("$root/configure.ac");
    # scan all m4 files too
    for my $e (@m4) {
        scanconf("$root/m4/$e");
    }
}

sub scan_file {
    my ($source)=@_;
    open F, "<$source";
    while(<F>) {
        if(/(CURL_DISABLE_[A-Z_]+)/g) {
            my ($sym)=($1);
            $file{$sym} = $source;
        }
    }
    close F;
}

sub scan_dir {
    my ($dir)=@_;
    opendir(my $dh, $dir) || die "Can't opendir $dir: $!";
    my @cfiles = grep { /\.[ch]\z/ && -f "$dir/$_" } readdir($dh);
    closedir $dh;
    for my $f (sort @cfiles) {
        scan_file("$dir/$f");
    }
}

sub scan_sources {
    scan_dir("$root/src");
    scan_dir("$root/lib");
    scan_dir("$root/lib/vtls");
    scan_dir("$root/lib/vauth");
}

sub scan_docs {
    open F, "<$root/docs/$DOCS";
    my $line = 0;
    while(<F>) {
        $line++;
        if(/^## (CURL_DISABLE_[A-Z_]+)/g) {
            my ($sym)=($1);
            $docs{$sym} = $line;
        }
    }
    close F;
}

scan_configure();
scan_sources();
scan_docs();

my $error = 0;
# Check the configure symbols for use in code
for my $s (sort keys %disable) {
    if(!$file{$s}) {
        printf "Present in configure.ac, not used by code: %s\n", $s;
        $error++;
    }
    if(!$docs{$s}) {
        printf "Present in configure.ac, not documented in $DOCS: %s\n", $s;
        $error++;
    }
}

# Check the code symbols for use in configure
for my $s (sort keys %file) {
    if(!$disable{$s}) {
        printf "Not set by configure: %s (%s)\n", $s, $file{$s};
        $error++;
    }
    if(!$docs{$s}) {
        printf "Used in code, not documented in $DOCS: %s\n", $s;
        $error++;
    }
}

# Check the documented symbols
for my $s (sort keys %docs) {
    if(!$disable{$s}) {
        printf "Documented but not in configure: %s\n", $s;
        $error++;
    }
    if(!$file{$s}) {
        printf "Documented, but not used by code: %s\n", $s;
        $error++;
    }
}

exit $error;


#!/usr/bin/env perl
#***************************************************************************
#                                  _   _ ____  _
#  Project                     ___| | | |  _ \| |
#                             / __| | | | |_) | |
#                            | (__| |_| |  _ <| |___
#                             \___|\___/|_| \_\_____|
#
# Copyright (C) 2010 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################
#
#

use strict;
use warnings;

# we may get the dir root pointed out
my $root=$ARGV[0] || ".";

my %error; # from the include file
my %docs; # from libcurl-errors.3

sub getdocserrors {
    open(F, "<$root/docs/libcurl/libcurl-errors.3");
    while(<F>) {
        if($_ =~ /^.IP \"(CURL[EM]_[^ \t\"]*)/) {
            my ($symbol) = ($1);
            if($symbol =~ /OBSOLETE/) {
                ;
            }
            else {
                $docs{$symbol}=1;
            }
        }
    }
    close(F);
}

sub getincludeerrors {
    open(F, "<$root/docs/libcurl/symbols-in-versions");
    while(<F>) {
        if($_ =~ /^(CURL[EM]_[^ \t]*)[ \t]*([0-9.]+)[ \t]*(.*)/) {
            my ($symbol, $added, $rest) = ($1,$2,$3);
            if($rest =~ /^([0-9.]+)/) {
                # removed!
            }
            else {
                $error{$symbol}=$added;
            }
        }
    }
    close(F);
}

getincludeerrors();
getdocserrors();

for(sort keys %error) {
    if($error{$_} && !$docs{$_}) {
        print "$_ is not in libcurl-errors.3\n";
    }
}

for(sort keys %docs) {
    if($docs{$_} && !$error{$_}) {
        print "$_ is not in symbols-in-versions\n";
    }
}


#!/usr/bin/env perl
#***************************************************************************
#                                  _   _ ____  _
#  Project                     ___| | | |  _ \| |
#                             / __| | | | |_) | |
#                            | (__| |_| |  _ <| |___
#                             \___|\___/|_| \_\_____|
#
# Copyright (C) 2010 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################
#
#

use strict;
use warnings;

# we may get the dir root pointed out
my $root=$ARGV[0] || ".";

my @incs = (
    "$root/include/curl/curl.h",
    "$root/include/curl/easy.h",
    "$root/include/curl/mprintf.h",
    "$root/include/curl/multi.h",
    );

my $verbose=0;
my $summary=0;
my $misses=0;

my @syms;
my %doc;
my %rem;

sub scanheader {
    my ($f)=@_;
    open H, "<$f" || die;
    while(<H>) {
        if (/^(CURL_EXTERN.*)/) {
            my $decl = $1;
            $decl =~ s/\r$//;
            print "$decl\n";
        }
    }
    close H;
}

foreach my $i (@incs) {
    scanheader($i);
}