Added regedit unit test, a couple minor changes to regedit.
[wine] / programs / winetest / include / wine.pm
1 # --------------------------------------------------------------------
2 # Module:      wine.pm
3 #
4 # Purpose: Module to supply wrapper around and support for gateway to
5 #          Windows API functions
6 #
7 # Copyright 2001 John F Sturtz for Codeweavers
8 #
9 # This library is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public
11 # License as published by the Free Software Foundation; either
12 # version 2.1 of the License, or (at your option) any later version.
13 #
14 # This library is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 # Lesser General Public License for more details.
18 #
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with this library; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 # --------------------------------------------------------------------
23
24 package wine;
25
26 use strict;
27 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $AUTOLOAD $todo_level
28             $successes $failures $todo_successes $todo_failures
29             $winetest_report_success
30             %return_types %prototypes %loaded_modules);
31
32 require Exporter;
33
34 @ISA = qw(Exporter);
35
36 # Items to export into callers namespace by default. Note: do not export
37 # names by default without a very good reason. Use EXPORT_OK instead.
38 # Do not simply export all your public functions/methods/constants.
39 @EXPORT = qw(
40              AUTOLOAD
41              alloc_callback
42              assert
43              hd
44              ok
45              todo
46              todo_wine
47              trace
48              wc
49              wclen
50             );
51
52 $VERSION = '0.01';
53 bootstrap wine $VERSION;
54
55 # Global variables
56 $wine::err = 0;
57 $wine::exit_status = 0;
58 $wine::debug = defined($ENV{WINETEST_DEBUG}) ? $ENV{WINETEST_DEBUG} : 1;
59 $wine::platform = defined($ENV{WINETEST_PLATFORM}) ? $ENV{WINETEST_PLATFORM} : "windows";
60
61 $todo_level = 0;
62 $successes = 0;
63 $failures = 0;
64 $todo_successes = 0;
65 $todo_failures = 0;
66 %loaded_modules = ();
67 $winetest_report_success = defined($ENV{WINETEST_REPORT_SUCCESS}) ? $ENV{WINETEST_REPORT_SUCCESS} : 0;
68
69
70 # --------------------------------------------------------------
71 # | Return-type constants                                      |
72 # |                                                            |
73 # | [todo]  I think there's a way to define these in a C       |
74 # |         header file, so that both the C functions in the   |
75 # |         XS module and the Perl routines in the .pm have    |
76 # |         access to them.  But I haven't worked it out       |
77 # |         yet ...                                            |
78 # --------------------------------------------------------------
79 %return_types = ( 
80     "void" => 0,
81     "int" => 1, "long" => 1,
82     "word" => 2,
83     "ptr" => 3,
84     "str" => 4, "wstr" => 4
85 );
86
87
88 # ------------------------------------------------------------------------
89 # | Sub:       AUTOLOAD                                                  |
90 # | -------------------------------------------------------------------- |
91 # | Purpose:   Used to catch calls to undefined routines                 |
92 # |                                                                      |
93 # |     Any routine which is called and not defined is assumed to be     |
94 # |     a call to the Wine API function of the same name.  We trans-     |
95 # |     late it into a call to the call() subroutine, with FUNCTION      |
96 # |     set to the function invoked and all other args passed thru.      |
97 # ------------------------------------------------------------------------
98 sub AUTOLOAD
99 {
100     # --------------------------------------------------------------
101     # | Figure out who we are                                      |
102     # --------------------------------------------------------------
103     my ($pkg, $func) = (split /::/, $AUTOLOAD)[0,1];
104
105     # --------------------------------------------------------------
106     # | Any function that is in the @EXPORT array is passed thru   |
107     # | to AutoLoader to pick up the appropriate XS extension      |
108     # --------------------------------------------------------------
109     if (grep ($_ eq $func, @EXPORT))
110     {
111         $AutoLoader::AUTOLOAD = $AUTOLOAD;
112         goto &AutoLoader::AUTOLOAD;
113     }
114
115     # --------------------------------------------------------------
116     # | Ignore this                                                |
117     # --------------------------------------------------------------
118     return
119         if ($func eq 'DESTROY');
120
121     # --------------------------------------------------------------
122     # | Otherwise, assume any undefined method is the name of a    |
123     # | wine API call, and all the args are to be passed through   |
124     # --------------------------------------------------------------
125     if (defined($prototypes{$func}))
126     {
127         return call( $func, @_ );
128     }
129     die "Function '$func' not declared";
130 } # End AUTOLOAD
131
132
133
134 # ------------------------------------------------------------------------
135 # | Sub:         call                                                    |
136 # | -------------------------------------------------------------------- |
137 # | Purpose:     Call a wine API function                                |
138 # |                                                                      |
139 # | Usage:       call FUNCTION, [ARGS ...]
140 # |                                                                      |
141 # | Returns:     value returned by API function called                   |
142 # ------------------------------------------------------------------------
143 sub call($@)
144 {
145     my ($function,@args) = @_;
146     my ($module,$funcptr,$ret_type,$arg_types) = @{$prototypes{$function}};
147
148     unless ($funcptr)
149     {
150         my $handle = $loaded_modules{$module};
151         $funcptr = get_proc_address( $handle, $function ) or die "Could not get address for $module.$function";
152         ${$prototypes{$function}}[1] = $funcptr;
153     }
154
155     if ($wine::debug > 1)
156     {
157         print STDERR "==== Call $function(";
158         for (@args)
159         {
160             print STDERR " ", +(ref () ? ("(" . ${$_} . ")") : "$_");
161         }
162         print STDERR " " if (scalar @args);
163         print STDERR ")\n";
164     }
165
166     # Check and translate args before call
167     my @args2;
168     if (defined($arg_types)) {
169         my @arg_types = @$arg_types;
170
171         if($#args != $#arg_types) {
172             die "$function: Wrong number of arguments, expected " .
173                 ($#arg_types + 1) . ", got " . ($#args + 1) . "\n";
174         }
175
176         while (defined(my $arg = shift @args) &&
177                defined(my $arg_type = shift @arg_types))
178         {
179             if($arg_type == 1 || $arg_type == 2) { # int || word
180                 $arg = int($arg);
181             }
182             push @args2, $arg;
183         }
184     } else {
185         @args2 = @args;
186     }
187
188     # Now call call_wine_API(), which will turn around and call
189     # the appropriate wine API function.
190     my ($err,$r) = call_wine_API( $funcptr, $ret_type, $wine::debug-1, @args2 );
191
192     if ($wine::debug > 1)
193     {
194         print STDERR "==== Ret  $function()";
195         if (defined($r)) { printf STDERR " ret=0x%x", $r; }
196         if (defined($err)) { printf STDERR " err=%d", $err; }
197         print STDERR "\n";
198     }
199
200     # Pass the return value back
201     $wine::err = $err;
202     return ($r);
203 }
204
205
206 # ----------------------------------------------------------------------
207 # | Subroutine:  declare
208 # ----------------------------------------------------------------------
209 sub declare($%)
210 {
211     my ($module, %list) = @_;
212     my ($handle, $func);
213
214     if (defined($loaded_modules{$module}))
215     {
216         $handle = $loaded_modules{$module};
217     }
218     else
219     {
220         $handle = load_library($module) or die "Could not load '$module'";
221         $loaded_modules{$module} = $handle;
222     }
223
224     foreach $func (keys %list)
225     {
226         if(ref($list{$func}) eq "ARRAY") {
227             my ($return_type, $argument_types) = @{$list{$func}};
228
229             my $ret_type = $return_types{$return_type};
230             my $arg_types = [map { $return_types{$_} } @$argument_types];
231
232             $prototypes{$func} = [ $module, 0, $ret_type, $arg_types ];
233         } else {
234             my $ret_type = $return_types{$list{$func}};
235
236             $prototypes{$func} = [ $module, 0, $ret_type ];
237         }
238     }
239 }
240
241
242 # ------------------------------------------------------------------------
243 # | Sub:         alloc_callback                                          |
244 # | -------------------------------------------------------------------- |
245 # | Purpose:     Allocate a thunk for a Wine API callback function.      |
246 # |                                                                      |
247 # |     Basically a thin wrapper over alloc_thunk(); see wine.xs for     |
248 # |     details ...                                                      |
249 # |                                                                      |
250 # | Usage:       alloc_callback SUB_REF, [ ARGS_TYPES ... ]              |
251 # |                                                                      |
252 # | Returns:     Pointer to thunk allocated (as an integer value)        |
253 # |                                                                      |
254 # |     The returned value is just a raw pointer to a block of memory    |
255 # |     allocated by the C code (cast into a Perl integer).  It isn't    |
256 # |     really suitable for anything but to be passed to a wine API      |
257 # |     function ...                                                     |
258 # ------------------------------------------------------------------------
259 sub alloc_callback($@)
260 {
261     # ----------------------------------------------
262     # | Locals                                     |
263     # |                                            |
264     # | [todo]  Check arg types                    |
265     # ----------------------------------------------
266     my  $sub_ref            = shift;
267     my  @callback_arg_types = @_;
268  
269     # [todo]  Check args
270     # [todo]  Some way of specifying args passed to callback
271
272     # --------------------------------------------------------------
273     # | Convert arg types to integers                              |
274     # --------------------------------------------------------------
275     map { $_ = $return_types{$_} } @callback_arg_types;
276
277     # --------------------------------------------------------------
278     # | Pass thru to alloc_thunk()                                 |
279     # --------------------------------------------------------------
280     return alloc_thunk ($sub_ref, @callback_arg_types);
281 }
282
283
284 # ----------------------------------------------------------------------
285 # | Subroutine:  hd                                                    |
286 # |                                                                    |
287 # | Purpose:     Display a hex dump of a string                        |
288 # |                                                                    |
289 # | Usage:       hd STR                                                |
290 # | Usage:       hd STR, LENGTH                                        |
291 # |                                                                    |
292 # | Returns:     (none)                                                |
293 # ----------------------------------------------------------------------
294 sub hd($;$)
295 {
296     # Locals
297     my  ($buf, $length);
298     my  $first;
299     my  ($str1, $str2, $str, $t);
300     my  ($c, $x);
301
302 # Begin sub hd
303
304     # --------------------------------------------------------------
305     # | Get args; if no BUF specified, blow                        |
306     # --------------------------------------------------------------
307     $buf = shift;
308     $length = (shift or length ($buf));
309     return
310         if ((not defined ($buf)) || ($length <= 0));
311
312     # --------------------------------------------------------------
313     # | Initialize                                                 |
314     # --------------------------------------------------------------
315     $first = 1;
316     $str1 = "00000:";
317     $str2 = "";
318
319     # --------------------------------------------------------------
320     # | For each character                                         |
321     # --------------------------------------------------------------
322     for (0 .. ($length - 1))
323     {
324         $c = substr ($buf, $_, 1);
325         $x = sprintf ("%02x", ord ($c));
326         $str1 .= (" " . $x);
327         $str2 .= (((ord ($c) >= 33) && (ord ($c) <= 126)) ? $c : ".");
328
329         # --------------------------------------------------------------
330         # | Every group of 4, add an extra space                       |
331         # --------------------------------------------------------------
332         if
333         (
334             ((($_ + 1) % 16) == 4)  ||
335             ((($_ + 1) % 16) == 12)
336         )
337         {
338             $str1 .= " ";
339             $str2 .= " ";
340         }
341
342         # --------------------------------------------------------------
343         # | Every group of 8, add a '-'                                |
344         # --------------------------------------------------------------
345         elsif
346         (
347             ((($_ + 1) % 16) == 8)
348         )
349         {
350             $str1 .= " -";
351             $str2 .= " ";
352         }
353
354         # --------------------------------------------------------------
355         # | Every group of 16, dump                                    |
356         # --------------------------------------------------------------
357         if
358         (
359             ((($_ + 1) % 16) == 0)      ||
360             ($_ == ($length - 1))
361         )
362         {
363             $str = sprintf ("%-64s%s", $str1, $str2);
364             if ($first)
365             {
366                 $t = ("-" x length ($str));
367                 print "  $t\n";
368                 print "  | $length bytes\n";
369                 print "  $t\n";
370                 $first = 0;
371             }
372             print "  $str\n";
373             $str1 = sprintf ("%05d:", ($_ + 1));
374             $str2 = "";
375             if ($_ == ($length - 1))
376             {
377                 print "  $t\n";
378             }
379         }
380
381     }  # end for
382
383
384     # --------------------------------------------------------------
385     # | Exit point                                                 |
386     # --------------------------------------------------------------
387     return;
388
389 } # End sub hd
390
391
392
393 # ----------------------------------------------------------------------
394 # | Subroutine:  wc                                                    |
395 # |                                                                    |
396 # | Purpose:     Generate unicode string                               |
397 # |                                                                    |
398 # | Usage:       wc ASCII_STRING                                       |
399 # |                                                                    |
400 # | Returns:     string generated                                      |
401 # ----------------------------------------------------------------------
402 sub wc($)
403 {
404     return pack("S*",unpack("C*",shift));
405 } # End sub wc
406
407
408
409 # ----------------------------------------------------------------------
410 # | Subroutine:  wclen                                                 |
411 # |                                                                    |
412 # | Purpose:     Return length of unicode string                       |
413 # |                                                                    |
414 # | Usage:       wclen UNICODE_STRING                                  |
415 # |                                                                    |
416 # | Returns:     string generated                                      |
417 # ----------------------------------------------------------------------
418 sub wclen($)
419 {
420     # Locals
421     my  $str = shift;
422     my  ($c1, $c2, $n);
423
424 # Begin sub wclen
425
426     $n = 0;
427     while (length ($str) > 0)
428     {
429         $c1 = substr ($str, 0, 1, "");
430         $c2 = substr ($str, 0, 1, "");
431         (($c1 eq "\x00") && ($c2 eq "\x00")) ? last : $n++;
432     }
433
434     return ($n);
435
436 } # End sub wclen
437
438
439
440 # ----------------------------------------------------------------------
441 # Subroutine:  ok
442 #
443 # Purpose:     Print warning if something fails
444 #
445 # Usage:       ok CONDITION [DESCRIPTION]
446 #
447 # Returns:     (none)
448 # ----------------------------------------------------------------------
449 sub ok($;$)
450 {
451     my $assertion = shift;
452     my $description = shift;
453     my ($filename, $line) = (caller (0))[1,2];
454     if ($todo_level)
455     {
456         if ($assertion)
457         {
458             print STDERR ("$filename:$line: Test succeeded inside todo block" .
459                           ($description ? ": $description" : "") . "\n");
460             $todo_failures++;
461         }
462         else { $todo_successes++; }
463     }
464     else
465     {
466         if (!$assertion)
467         {
468             print STDERR ("$filename:$line: Test failed" .
469                           ($description ? ": $description" : "") . "\n");
470             $failures++;
471         }
472         else 
473         {
474             print STDERR ("$filename:$line: Test succeeded\n") if ($winetest_report_success);
475             $successes++;
476         }
477     }
478 }
479
480
481 # ----------------------------------------------------------------------
482 # Subroutine:  assert
483 #
484 # Purpose:     Print error and die if something fails
485 #
486 # Usage:       assert CONDITION [DESCRIPTION]
487 #
488 # Returns:     (none)
489 # ----------------------------------------------------------------------
490 sub assert($;$)
491 {
492     my $assertion = shift;
493     my $description = shift;
494     my ($filename, $line) = (caller (0))[1,2];
495     unless ($assertion)
496     {
497         die ("$filename:$line: Assertion failed" . ($description ? ": $description" : "") . "\n");
498     }
499 }
500
501
502 # ----------------------------------------------------------------------
503 # Subroutine:  trace
504 #
505 # Purpose:     Print debugging traces
506 #
507 # Usage:       trace format [arguments]
508 # ----------------------------------------------------------------------
509 sub trace($@)
510 {
511     return unless ($wine::debug > 0);
512     my $format = shift;
513     my $filename = (caller(0))[1];
514     $filename =~ s!.*/!!;
515     printf "trace:$filename $format", @_;
516 }
517
518 # ----------------------------------------------------------------------
519 # Subroutine:  todo
520 #
521 # Purpose:     Specify a block of code as todo for a given platform
522 #
523 # Usage:       todo name coderef
524 # ----------------------------------------------------------------------
525 sub todo($$)
526 {
527     my ($platform,$code) = @_;
528     if ($wine::platform eq $platform)
529     {
530         $todo_level++;
531         eval &$code;
532         $todo_level--;
533     }
534     else
535     {
536         eval &$code;
537     }
538 }
539
540
541 # ----------------------------------------------------------------------
542 # Subroutine:  todo_wine
543 #
544 # Purpose:     Specify a block of test as todo for the Wine platform
545 #
546 # Usage:       todo_wine { code }
547 # ----------------------------------------------------------------------
548 sub todo_wine(&)
549 {
550     my $code = shift;
551     todo( "wine", $code );
552 }
553
554
555 # ----------------------------------------------------------------------
556 # Subroutine:  END
557 #
558 # Purpose:     Called at the end of execution, print results summary
559 # ----------------------------------------------------------------------
560 END
561 {
562     return if $?;  # got some other error already
563     if ($wine::debug > 0)
564     {
565         my $filename = (caller(0))[1];
566         printf STDERR ("%s: %d tests executed, %d marked as todo, %d %s.\n",
567                        $filename, $successes + $failures + $todo_successes + $todo_failures,
568                        $todo_successes, $failures + $todo_failures,
569                        ($failures + $todo_failures != 1) ? "failures" : "failure" );
570     }
571     $? = ($failures + $todo_failures < 255) ? $failures + $todo_failures : 255;
572 }
573
574
575 # Autoload methods go after =cut, and are processed by the autosplit program.
576 1;
577 __END__
578
579
580
581 # ------------------------------------------------------------------------
582 # | pod documentation                                                    |
583 # |                                                                      |
584 # |                                                                      |
585 # ------------------------------------------------------------------------
586
587 =head1 NAME
588
589 wine - Perl extension for calling wine API functions
590
591 =head1 SYNOPSIS
592
593     use wine;
594
595     wine::declare( "kernel32",
596                    SetLastError => ["void", ["int"]],
597                    GetLastError => ["int", []] );
598     SetLastError( 1234 );
599     printf "%d\n", GetLastError();
600
601
602 =head1 DESCRIPTION
603
604 This module provides a gateway for calling Win32 API functions from
605 a Perl script.
606
607 =head1 CALLING WIN32 API FUNCTIONS
608
609 The functions you want to call must first be declared by calling
610 the wine::declare method. The first argument is the name of the
611 module containing the APIs, and the next argument is a list of
612 function names and their return and argument types. For instance:
613
614     wine::declare( "kernel32",
615                    SetLastError => ["void", ["int"]],
616                    GetLastError => ["int", []] );
617
618 declares that the functions SetLastError and GetLastError are
619 contained in the kernel32 dll.
620
621 Once you have done that you can call the functions directly just
622 like native Perl functions:
623
624   SetLastError( $some_error );
625
626 The supported return types are:
627
628 =over 4
629
630 =item void
631
632 =item word
633
634 =item long
635
636 =item ptr
637
638 =item str
639
640 =item wstr
641
642 =back
643
644 =head1 $wine::err VARIABLE
645
646 In the Win32 API, an integer error code is maintained which always
647 contains the status of the last API function called.  In C code,
648 it is accessed via the GetLastError() function.  From a Perl script,
649 it can be accessed via the package global $wine::err.  For example:
650
651     GlobalGetAtomNameA ($atom, \$buf, -1);
652     if ($wine::err == 234)
653     {
654         ...
655     }
656
657 Wine returns 234 (ERROR_MORE_DATA) from the GlobalGetAtomNameA()
658 API function in this case because the buffer length passed is -1
659 (hardly enough room to store anything in ...)
660
661 If the called API didn't set the last error code, $wine:;err is
662 undefined.
663
664 =head1 $wine::debug VARIABLE
665
666 This variable can be set to 1 to enable debugging of the API calls,
667 which will print a lot of information about what's going on inside the
668 wine package while calling an API function.
669
670 =head1 OTHER USEFUL FUNCTIONS
671
672 The bundle that includes the wine extension also includes a module of
673 plain ol' Perl subroutines which are useful for interacting with wine
674 API functions. Currently supported functions are:
675
676 =over 4
677
678 =item hd BUF [, LENGTH]
679
680 Dump a formatted hex dump to STDOUT.  BUF is a string containing
681 the buffer to dump; LENGTH is the length to dump (length (BUF) if
682 omitted).  This is handy because wine often writes a null character
683 into the middle of a buffer, thinking that the next piece of code to
684 look at the buffer will be a piece of C code that will regard it as
685 a string terminator.  Little does it know that the buffer is going
686 to be returned to a Perl script, which may not ...
687
688 =item wc STR
689
690 Generate and return a wide-character (Unicode) string from the given
691 ASCII string
692
693 =item wclen WSTR
694
695 Return the length of the given wide-character string
696
697 =item assert CONDITION
698
699 Print a message if the assertion fails (i.e., CONDITION is false),
700 or do nothing quietly if it is true.  The message includes the script
701 name and line number of the assertion that failed.
702
703 =back
704
705
706
707 =head1 AUTHOR
708
709 John F Sturtz, jsturtz@codeweavers.com
710
711 =head1 SEE ALSO
712
713 wine documentation
714
715 =cut