1#!@PERL@
2
3use Getopt::Long;
4Getopt::Long::Configure("bundling", "no_ignore_case", "pass_through");
5
6use strict;
7
8my $valgrind = 0;
9my $callgrind = 0;
10my $gdb_attach = 0;
11my $calc_md5_file = undef;
12my $calc_md5_dir = undef;
13my $dontrun = 0;
14my $retval = 0;
15my $testpattern_command;
16my @printer_list = ();
17my @special_options = ();
18my @standard_options = qw(InkType InkSet,RawChannels DitherAlgorithm Duplex MediaType InputSlot ColorCorrection ImageType FullBleed Quality UseGloss Weave PrintingDirection Density CyanDensity);
19my $global_status = 1;
20my $run_installed = 0;
21my $use_min_res = 0;
22my $help = 0;
23my $output = undef;
24my $hsize = 0.1;
25my $vsize = 0.1;
26my $left = 0.15;
27my $top = 0.15;
28my $geometry = "";
29
30my @extras = ();
31my @job_extras = ();
32my @messages = ();
33my @global_messages = ();
34my @families = ();
35my %stpdata = ();
36my %models_found = ();
37my %models;
38my %families;
39my $skip_duplicate_printers = 0;
40my $std_pages = 1;
41my $duplex_pages = 4;
42my $skip_resolutions = 0;
43my $quiet = 0;
44my $single = 0;
45my $rerun = 0;
46
47my @default_options = ();
48my %base_settings = ("DitherAlgorithm" => "Fast");
49
50my %param_types;
51my %param_classes;
52my %param_levels;
53my %stp_float_values;
54my %stp_int_values;
55my %rerun_cases;
56
57GetOptions("v+"  => \$valgrind,
58	   "c"   => \$callgrind,
59	   "g"   => \$gdb_attach,
60	   "G=s" => \$geometry,
61	   "n"   => \$dontrun,
62	   "p:i" => \$std_pages,
63	   "P:i" => \$duplex_pages,
64	   "s!"  => \$skip_duplicate_printers,
65	   "S!"  => \$single,
66	   "i!"  => \$run_installed,
67	   "r!"  => \$skip_resolutions,
68	   "R+"  => \$use_min_res,
69	   "q!"  => \$quiet,
70	   "o=s" => \@special_options,
71	   "f=s" => \@families,
72	   "O=s" => \$output,
73	   "d=s" => \@default_options,
74	   "m:s" => \$calc_md5_dir,
75	   "M:s" => \$calc_md5_file,
76	   "X"   => \$rerun,
77	   "h"   => \$help);
78
79sub print_help_and_exit() {
80    my $options = join("\n                        ", sort @standard_options);
81    print STDERR <<EOF;
82Usage: run-testpattern-2 [opts] [model ...]
83
84  Options:
85    -c              Use callgrind (incompatible with -v)
86    -f family       Test printers only in the specified family.  Multiple
87                    families may be tested.  By default, all printer families
88		    are tested.
89    -g              Attach testpattern job to gdb
90    -G geometry     Specify geometry (=(HxV)(+L+T))
91    -i              Use the installed Gutenprint libraries rather than
92                    source tree
93    -m md5_dir      Generate MD5 checksums and place them in the specified
94                    directory, one file per run.  Directory will be created
95		    if necessary.
96    -M md5_file     Generate MD5 checksums and place them in the specified
97                    file, one line per run.
98    -n              Do not actually run testpattern
99    -O out_dir      Generate actual output and place it in the specified
100                    directory, one file per run.  Directory will be created
101		    if necessary.
102    -o option       Test this option.  Multiple -o options may be specified.
103                    option1,option2 indicates to test each value of option1
104		    in combination with each value of option2.  For floating
105		    point and integer options, a selection of values is
106		    tested.
107		    Default:
108		        $options
109    -d option=value Test this option with only the specified (default) value,
110		    if available
111    -p page count   Specify number of pages to print per printer.  Default $std_pages.
112    -P page count   Specify number of pages to print per printer when testing
113                    duplex mode.  Default $duplex_pages.
114    -q              Do not print progress messages
115    -r              Do not test each resolution (test only the default)
116    -R              Use the minimum resolution (rather than the default)
117                    to test other options.
118    -RR             Use only the minimum resolution, and do not test other
119                    resolutions.
120    -s              Skip duplicate printers (with the same model number
121                    in printers.xml)
122    -S              Run a separate testpattern command for each printer
123    -X              Repeat specified cases listed on standard input
124    -v[v[v[v]]]     Use valgrind.  Number of -v options controls usage:
125                    -v    Basic valgrind checking, no leak checking
126                    -vv   Additional leak checking
127                    -vvv  Show reachable data also
128                    -vvvv Minimal checks only
129EOF
130exit 1;
131}
132
133if ($help) {
134    print_help_and_exit();
135}
136
137my $pages = $std_pages;
138
139if (! @special_options) {
140    @special_options = @standard_options;
141}
142
143my $bad_opt = 0;
144
145foreach my $opt (@default_options) {
146    if (! ($opt =~ /=/)) {
147	print STDERR "Malformed default option `$opt'\n";
148	$bad_opt = 1;
149    }
150    my ($option, $value) = split(/=/, $opt);
151    if (! $value) {
152	delete $base_settings{$option};
153    } else {
154	$base_settings{$option} = $value;
155    }
156}
157
158if ($bad_opt) {
159    print_help_and_exit();
160}
161
162my $pwd = `pwd`;
163chomp $pwd;
164
165my $srcdir = $ENV{"srcdir"};
166my $sdir;
167
168$geometry =~ s/^=*//;
169if ($geometry =~ /^(([01]?(\.[0-9]*)?)x([01]?(\.[0-9]*)?))?(\+([01]?(\.[0-9]*)?)\+([01]?(\.[0-9]*)?))?$/) {
170    my ($H) = $2;
171    my ($V) = $4;
172    my ($L) = $7;
173    my ($T) = $9;
174    if ($H) {
175	$hsize = $H;
176	if ($hsize > 1) {
177	    $hsize = 1;
178	}
179    }
180    if ($V) {
181	$vsize = $V;
182	if ($vsize > 1) {
183	    $vsize = 1;
184	}
185    }
186    if ($L) {
187	$left = $L;
188    }
189    if ($T) {
190	$top = $T;
191    }
192    if ($left + $hsize > 1) {
193	$left = 1 - $hsize;
194    }
195    if ($top + $vsize > 1) {
196	$top = 1 - $vsize;
197    }
198}
199
200if ("$srcdir" eq "" || "$srcdir" eq ".") {
201    $sdir = $pwd;
202} elsif ($srcdir =~ /^\//) {
203    $sdir = "$srcdir";
204} else {
205    $sdir = "$pwd/$srcdir";
206}
207
208if (! $run_installed && ! defined $ENV{"STP_DATA_PATH"}) {
209    $ENV{"STP_DATA_PATH"} = "${sdir}/../xml";
210}
211
212if (! defined $ENV{"STP_MODULE_PATH"}) {
213    $ENV{"STP_MODULE_PATH"} = "${sdir}/../main:${sdir}/../main/.libs";
214}
215
216sub set_opt($$$) {
217    my ($opt, $val, $printer) = @_;
218    my ($type) = $param_types{$printer}{$opt};
219    if ($type == 1) {
220	push @extras, "parameter_int \"$opt\" $val;\n";
221    } elsif ($type == 2) {
222	my ($xval) = $val;
223	if ($val =~ /true/i) {
224	    $xval = 1;
225	} elsif ($val =~ /false/i) {
226	    $xval = 0;
227	}
228	push @extras, "parameter_bool \"$opt\" $xval;\n";
229    } elsif ($type == 3) {
230	push @extras, "parameter_float \"$opt\" $val;\n";
231    } elsif ($type == 4) {
232	push @extras, "parameter_curve \"$opt\" \"$val\";\n";
233    } else {
234	if ($opt eq "PageSize" && $val =~ /^([^.]+)\.([0-9]+)x([0-9]+)$/) {
235	    push @extras, "parameter \"PageSize\" \"$1\";\n";
236	    push @extras, "page_size $2 $3;\n";
237	} else {
238	    push @extras, "parameter \"$opt\" \"$val\";\n";
239	}
240    }
241}
242
243sub set_message($) {
244    my ($message) = @_;
245    push @messages, "message \"$message\";\n" if (! $quiet);
246}
247
248sub set_global_message($) {
249    my ($message) = @_;
250    push @global_messages, "message \"$message\";\n" if (! $quiet);
251}
252
253sub print_one_testpattern($;$) {
254    my ($printer, $raw) = @_;
255    my $stuff = join "", @global_messages;
256    $stuff .= join "", @job_extras;
257    foreach my $page (0..$pages - 1) {
258    	$stuff .= "printer \"$printer\";\n";
259	$stuff .= "parameter \"PageSize\" \"Auto\";\n";
260	$stuff .= join "", @extras, @messages;
261	if (! $quiet && $pages > 1) {
262	    $stuff .= "message \"(page $page)\";\n";
263	}
264	$stuff .= "parameter_int \"PageNumber\" $page;\n";
265	if ($page == 0) {
266	    $stuff .= "start_job;\n";
267	}
268	if ($page == $pages - 1) {
269	    $stuff .= "end_job;\n";
270	}
271	$stuff .= sprintf("hsize %f;\n", $hsize);
272	$stuff .= sprintf("vsize %f;\n", $vsize);
273	$stuff .= sprintf("left %f;\n", $left);
274	$stuff .= sprintf("top %f;\n", $top);
275	$stuff .= "blackline 0;\n";
276	$stuff .= "steps 16;\n";
277	if ($raw > 0) {
278	    $stuff .= "mode extended $raw 16;\n";
279	    $stuff .= "xpattern ";
280	    for (my $i = 0; $i < $raw; $i++) {
281		$stuff .= "0.0 0.0 1.0  ";
282	    }
283	    $stuff .= ";\n";
284	    for (my $i = 0; $i < $raw; $i++) {
285		$stuff .= "xpattern ";
286		for (my $j = 0; $j < $raw; $j++) {
287		    if ($i == $j) {
288			$stuff .= "0.0 1.0 1.0  ";
289		    } else {
290			$stuff .= "0.0 0.0 1.0  ";
291		    }
292		}
293		$stuff .= ";\n";
294	    }
295	    $stuff .= "xpattern ";
296	    for (my $i = 0; $i < $raw; $i++) {
297		$stuff .= "0.0 0.0 1.0  ";
298	    }
299	    $stuff .= ";\n";
300	    $stuff .= "end;\n";
301	} else {
302	    $stuff .= << 'EOF';
303mode rgb 8;
304pattern 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0  0.0 0.0 1.0  0.0 0.0 1.0  0.0 0.0 1.0 ;
305pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0  0.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
306pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0  0.0 0.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0;
307pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 1.0 1.0;
308pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0  0.0 1.0 1.0 0.0 1.0 1.0 0.0 1.0 1.0;
309pattern 0.0 0.0 1.0 1.0 1.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
310pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0  0.0 -2.0 1.0 0.0 -2.0 1.0 0.0 -2.0 1.0;
311pattern 1.0 1.0 1.0 1.0 1.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
312pattern 1.0 1.0 -2.0 -2.0 -2.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
313pattern 0.1 0.3 1.0 1.0 1.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
314pattern 0.3 0.7 -2.0 -2.0 -2.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
315pattern 0.1 0.999 1.0 1.0 1.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
316pattern 0.3 0.999 1.0 1.0 1.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
317pattern 0.5 0.999 1.0 1.0 1.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
318pattern 0.1 0.3 -2.0 -2.0 -2.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
319pattern 0.3 0.7 1.0 1.0 1.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
320pattern 0.1 0.999 -2.0 -2.0 -2.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
321pattern 0.3 0.999 -2.0 -2.0 -2.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
322pattern 0.5 0.999 -2.0 -2.0 -2.0 0.0 1.0 1.0  0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0;
323pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0  0.0 0.0 1.0 0.0 1.0 1.0 0.0 1.0 1.0;
324pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.25 1.0  0.0 0.0 1.0 0.0 0.75 1.0 0.0 0.75 1.0;
325pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.25 1.0  0.0 0.0 1.0 0.0 0.75 1.0 0.0 0.75 1.0;
326pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.5 1.0  0.0 0.0 1.0 0.0 0.5 1.0 0.0 0.5 1.0;
327pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.5 1.0  0.0 0.0 1.0 0.0 0.5 1.0 0.0 0.5 1.0;
328pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.75 1.0  0.0 0.0 1.0 0.0 0.25 1.0 0.0 0.25 1.0;
329pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.75 1.0  0.0 0.0 1.0 0.0 0.25 1.0 0.0 0.25 1.0;
330pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.9 1.0  0.0 0.0 1.0 0.0 0.1 1.0 0.0 0.1 1.0;
331pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.9 1.0  0.0 0.0 1.0 0.0 0.1 1.0 0.0 0.1 1.0;
332pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0  0.0 1.0 1.0 0.0 0.0 1.0 0.0 1.0 1.0;
333pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.25 1.0  0.0 0.75 1.0 0.0 0.0 1.0 0.0 0.75 1.0;
334pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.25 1.0  0.0 0.75 1.0 0.0 0.0 1.0 0.0 0.75 1.0;
335pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.5 1.0  0.0 0.5 1.0 0.0 0.0 1.0 0.0 0.5 1.0;
336pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.5 1.0  0.0 0.5 1.0 0.0 0.0 1.0 0.0 0.5 1.0;
337pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.75 1.0  0.0 0.25 1.0 0.0 0.0 1.0 0.0 0.25 1.0;
338pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.75 1.0  0.0 0.25 1.0 0.0 0.0 1.0 0.0 0.25 1.0;
339pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.9 1.0  0.0 0.1 1.0 0.0 0.0 1.0 0.0 0.1 1.0;
340pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.9 1.0  0.0 0.1 1.0 0.0 0.0 1.0 0.0 0.1 1.0;
341pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.0 1.0  0.0 1.0 1.0 0.0 1.0 1.0 0.0 0.0 1.0;
342pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.25 1.0  0.0 0.75 1.0 0.0 0.75 1.0 0.0 0.0 1.0;
343pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.25 1.0  0.0 0.75 1.0 0.0 0.75 1.0 0.0 0.0 1.0;
344pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.5 1.0  0.0 0.5 1.0 0.0 0.5 1.0 0.0 0.0 1.0;
345pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.5 1.0  0.0 0.5 1.0 0.0 0.5 1.0 0.0 0.0 1.0;
346pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.75 1.0  0.0 0.25 1.0 0.0 0.25 1.0 0.0 0.0 1.0;
347pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.75 1.0  0.0 0.25 1.0 0.0 0.25 1.0 0.0 0.0 1.0;
348pattern 1.0 1.0 1.0 1.0 1.0 0.0 0.9 1.0  0.0 0.1 1.0 0.0 0.1 1.0 0.0 0.0 1.0;
349pattern 0.0 0.0 1.0 1.0 1.0 0.0 0.9 1.0  0.0 0.1 1.0 0.0 0.1 1.0 0.0 0.0 1.0;
350end;
351EOF
352	}
353    }
354    return $stuff;
355}
356
357my $extra_arg = "";
358if ($#ARGV >= 0) {
359    @printer_list = @ARGV;
360    $extra_arg = join " ", @printer_list;
361} else {
362    open PIPE, "./printers|" or die "Cannot run printers: $!\n";
363    while(<PIPE>) {
364	next if m!^#!;
365	chomp;
366	push @printer_list, $_;
367    }
368    close PIPE;
369}
370
371open PIPE, "./printer_options $extra_arg|" or die "Cannot run printer_options: $!\n";
372while(<PIPE>) {
373    next if m!^#!;
374    eval $_;
375}
376close PIPE or die "Cannot run printer_options: $!\n";
377
378sub do_print {
379    my ($output, $fh) = @_;
380    if ($dontrun) {
381	print $output;
382    } else {
383	print $fh $output;
384    }
385}
386
387sub do_output($) {
388    my ($outkey) = @_;
389    if (defined $output || defined $calc_md5_dir ||
390	defined $calc_md5_file) {
391	my $md5_cmd;
392	my $outbase = "${outkey}.prn";
393	my $outfile = "$output/$outbase";
394	if (defined $calc_md5_file) {
395	    $md5_cmd = "md5sum -b | sed 's/-/$outbase/' >> '$calc_md5_file'";
396	} elsif (defined $calc_md5_dir) {
397	    my $md5_dir = $calc_md5_dir;
398	    if (defined $calc_md5_dir && $calc_md5_dir eq "") {
399		$md5_dir = $output;
400	    }
401	    my $md5file = "$md5_dir/${outkey}.md5";
402	    if (! $md5_dir) {
403		$md5file = "${outkey}.md5";
404	    }
405	    $md5_cmd = "md5sum -b | sed 's/-/$outbase/' > '$md5file'";
406	}
407	if ($output && $md5_cmd ne "") {
408	    push @job_extras, "output \"|tee '$outfile' | $md5_cmd\";\n";
409	} elsif ($output) {
410	    push @job_extras, "output \"$outfile\";\n";
411	} else {
412	    push @job_extras, "output \"|$md5_cmd\";\n";
413	}
414    }
415}
416
417sub build_list($$) {
418    my ($keys, $printer) = @_;
419    my (@keys) = split (/,/, $keys);
420    my ($key) = $keys[0];
421    my ($rest);
422    my (@stuff);
423    if ($#keys > 0) {
424	$rest = join ",", @keys[1..$#keys];
425	@stuff = build_list($rest, $printer);
426    }
427    my (@tmp);
428    if ($param_types{$printer}{$key} == 3) {
429	my ($minv) = $stp_float_values{$printer}{'MINVAL'}{$key};
430	my ($defv) = $stp_float_values{$printer}{'DEFVAL'}{$key};
431	my ($maxv) = $stp_float_values{$printer}{'MAXVAL'}{$key};
432	push @tmp, $minv;
433	push @tmp, ($minv + $defv) / 2.0;
434	push @tmp, $defv;
435	push @tmp, ($defv + $maxv) / 2.0;
436	push @tmp, $maxv;
437    } elsif ($param_types{$printer}{$key} == 1) {
438	my ($minv) = $stp_int_values{$printer}{'MINVAL'}{$key};
439	my ($maxv) = $stp_int_values{$printer}{'MAXVAL'}{$key};
440	push @tmp, ($minv..$maxv);
441    } elsif ($param_types{$printer}{$key} == 2) {
442	push @tmp, 0;
443	push @tmp, 1;
444    } elsif (defined($param_types{$printer}{$key})) {
445	@tmp = keys %{$stpdata{$printer}{$key}};
446    }
447    if (! @tmp) {
448	return @stuff;
449    } elsif (! @stuff) {
450	return @tmp;
451    } else {
452	my (@answer);
453	foreach my $i (@tmp) {
454	    foreach my $j (@stuff) {
455		push @answer, "$i,$j";
456	    }
457	}
458	return @answer;
459    }
460    return @tmp;
461}
462
463sub build_key($$) {
464    my ($keys, $printer) = @_;
465    my (@keys) = split (/,/, $keys);
466    my (@answer) = grep { defined $param_types{$printer}{$_} } @keys;
467    return join ",", @answer;
468}
469
470sub do_printer($$) {
471    my ($printer, $fh) = @_;
472    my $tmp;
473    my $min_res_name;
474    my $min_res_value = 0;
475    my $first_time = 1;
476    my $key;
477    my %opt_vals = {};
478    $tmp = $stpdata{$printer}{'Resolution'};
479    my (@resolutions) = grep {$_ ne 'None' } keys %$tmp;
480    $tmp = $stpdata{$printer}{'PrintingMode'};
481    my (@printing_modes) = grep {$_ ne 'None' } keys %$tmp;
482    if ($base_settings{'PrintingMode'}) {
483	if ($base_settings{'PrintingMode'} eq 'Color' &&
484	    grep { $_ eq 'Color' } @printing_modes) {
485	    @printing_modes = 'Color';
486	} elsif ($base_settings{'PrintingMode'} eq 'BW' &&
487		 grep { $_ eq 'BW' } @printing_modes) {
488	    @printing_modes = 'BW';
489	} else {
490	    return;
491	}
492    }
493
494    foreach $key (@special_options) {
495	my $nkey = build_key($key, $printer);
496	if ($nkey ne "") {
497	    my (@vals) = build_list($nkey, $printer);
498	    $opt_vals{$nkey} = \@vals;
499	}
500    }
501    foreach $tmp (sort @resolutions) {
502	my $res_value = ($stpdata{$printer}{'x_resolution'}{$tmp} *
503			 $stpdata{$printer}{'y_resolution'}{$tmp});
504	if ($min_res_value == 0 || $res_value < $min_res_value) {
505	    $min_res_value = $res_value;
506	    $min_res_name = $tmp;
507	}
508    }
509    if ($use_min_res > 1) {
510	@resolutions = ($min_res_name);
511    }
512    # We want to do all resolutions and all ink types in both color modes.
513    # We don't need to do both resolutions and ink types.
514    my $pmode;
515    foreach $pmode (@printing_modes) {
516	my ($resolution);
517	$pages = $std_pages;
518	if (! $skip_resolutions) {
519	    foreach $resolution (@resolutions) {
520		@extras = ();
521		@job_extras = ();
522		@messages = ();
523		@global_messages = ();
524		if ($first_time) {
525		    set_global_message("$printer\n");
526		}
527		set_opt("PrintingMode", $pmode, $printer);
528		set_opt("Resolution", $resolution, $printer);
529		map { set_opt($_, $base_settings{$_}, $printer)} keys %base_settings;
530		my ($case) = "${printer}_PrintingMode_${pmode}_Resolution_${resolution}";
531		if (! $rerun || $rerun_cases{$case}) {
532		    $first_time = 0;
533		    do_output($case);
534		    set_message("    ${pmode}+${resolution}");
535		    my $output = print_one_testpattern($printer);
536		    do_print( $output, $fh );
537		}
538	    }
539	}
540	foreach $key (keys %opt_vals) {
541	    next if ($key eq "RawChannels" && $pmode ne "Color");
542	    my (@subkeys) = split (/,/, $key);
543	    $tmp = $opt_vals{$key};
544	    if (defined $tmp && $tmp >= 1) {
545		my (@opts) = @$tmp;
546		my $opt;
547		my $rawval;
548		my $set_resolution = 0;
549		foreach $opt (@opts) {
550		    my (@subopts) = split (/,/, $opt);
551		    @extras = ();
552		    @job_extras = ();
553		    @messages = ();
554		    @global_messages = ();
555		    set_opt("PrintingMode", $pmode, $printer);
556		    if ($use_min_res) {
557			set_opt("Resolution", $min_res_name, $printer);
558		    }
559		    my (@mvals);
560		    my (@ovals);
561		    map {
562			my $k = $subkeys[$_];
563			my $v = $subopts[$_];
564			if ($k eq "RawChannels") {
565			    next if ($v ne "None" && $pmode ne "Color");
566			    $rawval = $v;
567			} else {
568			    set_opt($k, $v, $printer);
569			}
570			push @mvals, "${k}_${v}";
571			push @ovals, "${k}=${v}";
572		    } (0..$#subkeys);
573		    if ($first_time) {
574			set_global_message("$printer\n");
575			$first_time = 0;
576		    }
577		    # FIXME! need to handle Duplex as a subkey
578		    map {
579			$pages = $std_pages;
580			if ($key ne $_) {
581			    set_opt($_, $base_settings{$_}, $printer);
582			    push @mvals, "${_}_$base_settings{$_}";
583			    push @ovals, "${_}=$base_settings{$_}";
584			    if ($_ eq "Resolution") {
585				$set_resolution = 1;
586			    }
587			    if ($_ eq "Duplex") {
588				set_opt("JobMode", "Job", $printer);
589				$pages = $duplex_pages;
590			    }
591			}
592		    } keys %base_settings;
593		    my ($mstring) = join "_", @mvals;
594		    my ($ostring) = join "+", @ovals;
595		    my $case;
596		    if ($use_min_res && ! $set_resolution) {
597			$case = "${printer}_PrintingMode_${pmode}_Resolution_${min_res_name}_${mstring}";
598		    } else {
599			$case = "${printer}_PrintingMode_${pmode}_${mstring}";
600		    }
601		    if (! $rerun || $rerun_cases{$case}) {
602			do_output($case);
603			if ($use_min_res && ! $set_resolution) {
604			    set_message("    ${ostring}+${pmode}+${min_res_name}");
605			} else {
606			    set_message("    ${ostring}+${pmode}");
607			}
608			my $output = print_one_testpattern($printer, $rawval);
609			do_print( $output, $fh );
610		    }
611		}
612	    }
613	}
614    }
615}
616
617if ($rerun) {
618    while (<>) {
619	chomp;
620	s/^[^a-zA-Z]*//;
621	s/\.prn//;
622	$rerun_cases{$_} = 1;
623    }
624}
625
626my (@nprinter_list);
627foreach my $printer (@printer_list) {
628    my $model_id = $models{$printer};
629    my $family_id = $families{$printer};
630    if (($skip_duplicate_printers && $models_found{$family_id}{$model_id}) ||
631	(@families && ! grep { $family_id eq $_ } @families)) {
632    } else {
633	$models_found{$family_id}{$model_id} = 1;
634	push @nprinter_list, $printer;
635    }
636}
637@printer_list = @nprinter_list;
638
639if ($dontrun) {
640    map { do_printer($_, \*STDOUT) } @printer_list;
641    exit 0;
642} else {
643    my $valgrind_command;
644    my $valopts;
645    if ($callgrind) {
646	$valopts = '--tool=callgrind --dump-instr=yes --trace-jump=yes';
647	$valgrind = 4;
648    } elsif ($valgrind) {
649	$valopts = '--tool=memcheck';
650    }
651    if ($gdb_attach) {
652	$valopts .= ' --db-attach=yes';
653    }
654    if ($valgrind == 1) {
655	$valgrind_command = "valgrind $valopts -q --num-callers=50 --error-limit=no --leak-check=yes";
656    } elsif ($valgrind == 2) {
657	$valgrind_command = "valgrind $valopts --num-callers=50 --error-limit=no --leak-resolution=high --leak-check=yes";
658    } elsif ($valgrind == 3) {
659	$valgrind_command = "valgrind $valopts --error-limit=no --num-callers=50 --show-reachable=yes --leak-resolution=high --leak-check=yes";
660    } elsif ($valgrind == 4) {
661	$valgrind_command = "valgrind $valopts";
662    }
663
664    if (defined $output && $output ne "" && ! -d $output) {
665	mkdir $output || die "Can't create directory $output: $!\n";
666    }
667
668    if (defined $calc_md5_dir && $calc_md5_dir ne "" && ! -d $calc_md5_dir) {
669	mkdir $calc_md5_dir || die "Can't create directory $calc_md5_dir: $!\n";
670    }
671
672    if (defined $calc_md5_file) {
673	unlink $calc_md5_file;
674	system "touch $calc_md5_file";
675    }
676
677    my $status = 1;
678    my ($suppress);
679    if (! defined $output && ! defined $calc_md5_dir &&
680	! defined $calc_md5_file) {
681	$suppress = '-n';
682    }
683    my ($qopt) = $quiet ? "-q" : "";
684    $testpattern_command = "$valgrind_command ./testpattern -y $suppress $qopt";
685    if ($single) {
686	foreach my $printer (@printer_list) {
687	    open TESTPATTERN, "|$testpattern_command" or
688		die "Can't run $testpattern_command: $!\n";
689	    do_printer($printer, \*TESTPATTERN);
690	    $status |= close TESTPATTERN;
691	    $status |= ($? & 255);
692	    last if ($? & 255);
693	}
694    } else {
695	open TESTPATTERN, "|$testpattern_command" or
696	    die "Can't run $testpattern_command: $!\n";
697	map { do_printer($_, \*TESTPATTERN) } @printer_list;
698	$status = close TESTPATTERN;
699    }
700    if ($status) {
701	exit 0;
702    } else {
703	exit 1;
704    }
705}
706