1#!/usr/bin/env perl
2#***************************************************************************
3#                                  _   _ ____  _
4#  Project                     ___| | | |  _ \| |
5#                             / __| | | | |_) | |
6#                            | (__| |_| |  _ <| |___
7#                             \___|\___/|_| \_\_____|
8#
9# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
10#
11# This software is licensed as described in the file COPYING, which
12# you should have received as part of this distribution. The terms
13# are also available at https://curl.se/docs/copyright.html.
14#
15# You may opt to use, copy, modify, merge, publish, distribute and/or sell
16# copies of the Software, and permit persons to whom the Software is
17# furnished to do so, under the terms of the COPYING file.
18#
19# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20# KIND, either express or implied.
21#
22#***************************************************************************
23
24#=======================================================================
25# Read a test definition which exercises curl's --libcurl option.
26# Generate either compilable source code for a new test tool,
27# or a new test definition which runs the tool and expects the
28# same output.
29# This should verify that the --libcurl code really does perform
30# the same actions as the original curl invocation.
31#-----------------------------------------------------------------------
32# The output of curl's --libcurl option differs in several ways from
33# the code needed to integrate with the test tool environment:
34# - #include "test.h"
35# - no call of curl_global_init & curl_global_cleanup
36# - main() function vs. test() function
37# - no checking of curl_easy_setopt calls vs. test_setopt wrapper
38# - handling of stdout
39# - variable names ret & hnd vs. res & curl
40# - URL as literal string vs. passed as argument
41#=======================================================================
42use strict;
43require "getpart.pm";
44
45# Boilerplate code for test tool
46my $head =
47'#include "test.h"
48#include "memdebug.h"
49
50int test(char *URL)
51{
52  CURLcode res;
53  CURL *curl;
54';
55# Other declarations from --libcurl come here
56# e.g. curl_slist
57my $init =
58'
59  if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
60    fprintf(stderr, "curl_global_init() failed\n");
61    return TEST_ERR_MAJOR_BAD;
62  }
63
64  if ((curl = curl_easy_init()) == NULL) {
65    fprintf(stderr, "curl_easy_init() failed\n");
66    curl_global_cleanup();
67    return TEST_ERR_MAJOR_BAD;
68  }
69';
70# Option setting, perform and cleanup come here
71my $exit =
72'  curl_global_cleanup();
73
74  return (int)res;
75}
76';
77
78my $myname = leaf($0);
79sub usage {die "Usage: $myname -c|-test=num testfile\n";}
80
81sub main {
82    @ARGV == 2
83        or usage;
84    my($opt,$testfile) = @ARGV;
85
86    if(loadtest($testfile)) {
87        die "$myname: $testfile doesn't look like a test case\n";
88    }
89
90    my $comment = sprintf("DO NOT EDIT - generated from %s by %s",
91                          leaf($testfile), $myname);
92    if($opt eq '-c') {
93        generate_c($comment);
94    }
95    elsif(my($num) = $opt =~ /^-test=(\d+)$/) {
96        generate_test($comment, $num);
97    }
98    else {
99        usage;
100    }
101}
102
103sub generate_c {
104    my($comment) = @_;
105    # Fetch the generated code, which is the output file checked by
106    # the old test.
107    my @libcurl = getpart("verify", "file")
108        or die "$myname: no <verify><file> section found\n";
109
110    # Mangle the code into a suitable form for a test tool.
111    # We want to extract the important parts (declarations,
112    # URL, setopt calls, cleanup code) from the --libcurl
113    # boilerplate and insert them into a new boilerplate.
114    my(@decl,@code);
115    # First URL passed in as argument, others as global
116    my @urlvars = ('URL', 'libtest_arg2', 'libtest_arg3');
117    my($seen_main,$seen_setopt,$seen_return);
118    foreach (@libcurl) {
119        # Check state changes first (even though it
120        # duplicates some matches) so that the other tests
121        # are in a logical order).
122        if(/^int main/) {
123            $seen_main = 1;
124        }
125        if($seen_main and /curl_easy_setopt/) {
126            # Don't match 'curl_easy_setopt' in comment!
127            $seen_setopt = 1;
128        }
129        if(/^\s*return/) {
130            $seen_return = 1;
131        }
132
133        # Now filter the code according to purpose
134        if(! $seen_main) {
135            next;
136        }
137        elsif(! $seen_setopt) {
138            if(/^\s*(int main|\{|CURLcode |CURL |hnd = curl_easy_init)/) {
139                # Initialisations handled by boilerplate
140                next;
141            }
142            else {
143                push @decl, $_;
144            }
145        }
146        elsif(! $seen_return) {
147            if(/CURLOPT_URL/) {
148                # URL is passed in as argument or by global
149		my $var = shift @urlvars;
150                s/\"[^\"]*\"/$var/;
151            }
152	    s/\bhnd\b/curl/;
153            # Convert to macro wrapper
154            s/curl_easy_setopt/test_setopt/;
155	    if(/curl_easy_perform/) {
156		s/\bret\b/res/;
157		push @code, $_;
158		push @code, "test_cleanup:\n";
159	    }
160	    else {
161		push @code, $_;
162	    }
163        }
164    }
165
166    print ("/* $comment */\n",
167           $head,
168           @decl,
169           $init,
170           @code,
171           $exit);
172}
173
174# Read the original test data file and transform it
175# - add a "DO NOT EDIT comment"
176# - replace CURLOPT_URL string with URL variable
177# - remove <verify><file> section (was the --libcurl output)
178# - insert a <client><tool> section with our new C program name
179# - replace <client><command> section with the URL
180sub generate_test {
181    my($comment,$newnumber) = @_;
182    my @libcurl = getpart("verify", "file")
183        or die "$myname: no <verify><file> section found\n";
184    # Scan the --libcurl code to find the URL used.
185    my $url;
186    foreach (@libcurl) {
187        if(my($u) = /CURLOPT_URL, \"([^\"]*)\"/) {
188            $url = $u;
189        }
190    }
191    die "$myname: CURLOPT_URL not found\n"
192        unless defined $url;
193
194    # Traverse the pseudo-XML transforming as required
195    my @new;
196    my(@path,$path,$skip);
197    foreach (getall()) {
198        if(my($end) = /\s*<(\/?)testcase>/) {
199            push @new, $_;
200            push @new, "# $comment\n"
201                unless $end;
202        }
203        elsif(my($tag) = /^\s*<(\w+)/) {
204            push @path, $tag;
205            $path = join '/', @path;
206            if($path eq 'verify/file') {
207                $skip = 1;
208            }
209            push @new, $_
210                unless $skip;
211            if($path eq 'client') {
212                push @new, ("<tool>\n",
213                            "lib$newnumber\n",
214                            "</tool>\n");
215            }
216            elsif($path eq 'client/command') {
217                push @new, sh_quote($url)."\n";
218            }
219        }
220        elsif(my($etag) = /^\s*<\/(\w+)/) {
221            my $tag = pop @path;
222            die "$myname: mismatched </$etag>\n"
223                unless $tag eq $etag;
224            push @new, $_
225                unless $skip;
226            $skip --
227                if $path eq 'verify/file';
228            $path = join '/', @path;
229        }
230        else {
231            if($path eq 'client/command') {
232                # Replaced above
233            }
234            else {
235                push @new, $_
236                    unless $skip;
237            }
238        }
239    }
240    print @new;
241}
242
243sub leaf {
244    # Works for POSIX filenames
245    (my $path = shift) =~ s!.*/!!;
246    return $path;
247}
248
249sub sh_quote {
250    my $word = shift;
251    $word =~ s/[\$\"\'\\]/\\$&/g;
252    return '"' . $word . '"';
253}
254
255main;
256