This commit was manufactured by cvs2svn to create tag
[opensuse:hwinfo.git] / src / ids / convert_hd
1 #! /usr/bin/perl
2
3 use Getopt::Long;
4 use XML::Writer;
5 use XML::Parser;
6 use IO;
7 use Dumpvalue;
8
9 sub help;
10
11 sub read_name_file;
12 sub read_driver_file;
13 sub read_id_file;
14 sub read_pcimap_file;
15 sub read_usbmap_file;
16 sub read_alias_file;
17 sub eisa_id;
18 sub eisa_str;
19
20 sub remove_nops;
21 sub remove_duplicates;
22 sub fix_driver_info;
23
24 sub cmp_id;
25 sub cmp_skey;
26 sub cmp_item;
27
28 sub match_id;
29 sub match_skey;
30 sub match_item;
31
32 sub join_skey;
33
34 sub split_item;
35
36 sub get_xml_data;
37 sub parse_xml_item;
38 sub parse_xml_key;
39 sub parse_xml_id;
40 sub parse_xml_id_id;
41 sub parse_xml_id_range;
42 sub parse_xml_id_mask;
43 sub parse_xml_driver;
44 sub parse_xml_driver_display;
45 sub parse_xml_driver_module;
46 sub parse_xml_driver_mouse;
47 sub parse_xml_driver_xfree;
48 sub parse_xml_pair;
49 sub parse_xml_cdata;
50 sub idstr2value;
51
52 sub dump2ids;
53 sub dump2xml;
54 sub dump_xml_item;
55 sub dump_xml_names;
56 sub dump_xml_drivers;
57 sub id2xml;
58
59 sub hd_dtd;
60 sub hd_dtd_internal;
61
62 $dump = new Dumpvalue();
63
64 (
65   $he_other, $he_bus_id, $he_baseclass_id, $he_subclass_id, $he_progif_id,
66   $he_vendor_id, $he_device_id, $he_subvendor_id, $he_subdevice_id, $he_rev_id,
67   $he_bus_name, $he_baseclass_name, $he_subclass_name, $he_progif_name,
68   $he_vendor_name, $he_device_name, $he_subvendor_name, $he_subdevice_name,
69   $he_rev_name, $he_serial, $he_driver, $he_requires,
70   $he_nomask,
71   $he_driver_module_insmod, $he_driver_module_modprobe,
72   $he_driver_module_config, $he_driver_xfree, $he_driver_xfree_config,
73   $he_driver_mouse, $he_driver_display, $he_driver_any
74 ) = ( 0 .. 100 );
75 $he_class_id = $he_nomask;
76
77 @ent_names = (
78   "other", "bus.id", "baseclass.id", "subclass.id", "progif.id",
79   "vendor.id", "device.id", "subvendor.id", "subdevice.id", "rev.id",
80   "bus.name", "baseclass.name", "subclass.name", "progif.name",
81   "vendor.name", "device.name", "subvendor.name", "subdevice.name",
82   "rev.name", "serial", "driver", "requires",
83   "class.id", "driver.module.insmod", "driver.module.modprobe",
84   "driver.module.config", "driver.xfree", "driver.xfree.config",
85   "driver.mouse", "driver.display", "driver.any"
86 );
87 @ent_values{@ent_names} = ( 0 .. 100 );
88
89 @xml_names = (
90   "other", "bus", "baseclass", "subclass", "progif",
91   "vendor", "device", "subvendor", "subdevice", "revision",
92   "bus", "baseclass", "subclass", "progif",
93   "vendor", "device", "subvendor", "subdevice",
94   "revision", "serial", "driver", "requires"
95 );
96 @xml_values{@xml_names} = ( 0 .. 100 );
97
98 ( $tag_none, $tag_pci, $tag_eisa, $tag_usb, $tag_special, $tag_pcmcia ) = ( 0 .. 5 );
99
100 @tag_name = ( "", "pci", "eisa", "usb", "special", "pcmcia" );
101 @tag_values{@tag_name} = ( 0 .. 5 );
102 $tag_values{none} = 0;
103
104 ( $flag_id, $flag_range, $flag_mask, $flag_string, $flag_regexp ) = ( 0 .. 4 );
105 $flag_cont = 8;
106
107 # map usb modules to device classes
108 %usbmod2class = (
109   'ov511'     => [ 0x10f, 0 ],
110   'pwc'       => [ 0x10f, 0 ],
111   'hpusbscsi' => [ 0x10c, 0 ],
112   'microtek'  => [ 0x10c, 0 ],
113   'scanner'   => [ 0x10c, 0 ]
114 );
115
116
117 # options
118 $opt_write_ids = 1;
119 $opt_write_xml = 0;
120 $opt_sort_ids = 0;
121 $opt_sort_reverse = 0;
122 $opt_sort_random = 0;           # for testing
123 $opt_split = 0;
124 $opt_with_source = 0;
125 $opt_fix_driver = 1;
126 $opt_help = 0;
127 $opt_internal_dtd = 0;
128
129 $opt_ok = GetOptions(
130   'ids'           => \$opt_write_ids,
131   'no-ids'        => sub { $opt_write_ids = 0 },
132   'xml'           => \$opt_write_xml,
133   'no-xml'        => sub { $opt_write_xml = 0 },
134   'sort'          => \$opt_sort,
135   'reverse'       => \$opt_sort_reverse,
136   'random'        => \$opt_sort_random,
137   'split'         => \$opt_split,
138   'with-source'   => \$opt_with_source,
139   'fix-driver'    => \$opt_fix_driver,
140   'no-fix-driver' => sub { $opt_fix_driver = 0 },
141   'internal-dtd'  => \$opt_internal_dtd,
142   'help'          => \&help
143 ) ;
144
145 for $f (@ARGV) {
146   if(open F, $f) {
147     @f = (<F>);
148     close F;
149
150     # file format check
151
152     undef $format;
153
154     for (@f) {
155       if(/^\s*\<\?xml\s/) {
156         $format = 'xml';
157         last;
158       }
159
160       if(/^#\s+pci\s+module\s+vendor\s+device\s+subvendor\s+subdevice\s+class\s+class_mask\s+driver_data\s*$/) {
161         $format = 'pcimap';
162         last;
163       }
164
165       if(/^#\s+usb\s+module\s+match_flags\s+idVendor\s+idProduct\s+/) {
166         $format = 'usbmap';
167         last;
168       }
169
170       if(/^\s*alias\s+(pci|usb):v\S+\s+\S+$/) {
171         $format = 'alias';
172         last;
173       }
174
175     }
176
177     if(!$format) {
178       $i = join "|", map "\Q$_", @ent_names;
179       for (@f) {
180         if(/^\s*[+&|]?($i)\s/) {
181           $format = 'ids';
182           last;
183         }
184       }
185     }
186
187     if(!$format) {
188       for (@f) {
189         if(/^\t[a-z]\s/) {
190           $format = 'drivers';
191           last;
192         }
193       }
194     }
195
196     $format = 'names' if !$format;
197
198     if($format eq 'names') {
199
200       print STDERR "======  \"$f\": name info  ======\n";
201       read_name_file $f, \@f;
202
203     }
204     elsif($format eq 'drivers') {
205
206       print STDERR "======  \"$f\": driver info  ======\n";
207       read_driver_file $f, \@f;
208
209     }
210     elsif($format eq 'xml') {
211
212       print STDERR "======  \"$f\": xml info  ======\n";
213       $xmlp = new XML::Parser(Style => 'Tree', ParseParamEnt => 1);
214       get_xml_data $xmlp->parsefile($f);
215
216     }
217     elsif($format eq 'ids') {
218
219       print STDERR "======  \"$f\": id info  ======\n";
220       read_id_file $f, \@f;
221
222     }
223     elsif($format eq 'pcimap') {
224
225       print STDERR "======  \"$f\": pcimap info  ======\n";
226       read_pcimap_file $f, \@f;
227
228     }
229     elsif($format eq 'usbmap') {
230
231       print STDERR "======  \"$f\": usbmap info  ======\n";
232       read_usbmap_file $f, \@f;
233
234     }
235     elsif($format eq 'alias') {
236
237       print STDERR "======  \"$f\": alias info  ======\n";
238       read_alias_file $f, \@f;
239
240     }
241   }
242   else {
243     die "$f: $!\n"
244   }
245 }
246
247 print STDERR "removing unnecessary items\n";
248 remove_nops;
249
250 print STDERR "got ${\scalar @hd} items\n";
251
252 if($opt_fix_driver) {
253   fix_driver_info;
254 }
255
256 if($opt_split) {
257   print STDERR "splitting items\n";
258   for (@hd) {
259     push @hd_new, split_item($_);
260   }
261   @hd = @hd_new;
262   undef @hd_new;
263 }
264
265 if($opt_sort_ids) {
266   print STDERR "sorting\n";
267   if($opt_sort_random) {
268     @hd = sort { $cmp_item_cnt++, rand() <=> rand() } @hd;
269   }
270   elsif($opt_sort_reverse) {
271     @hd = sort { cmp_item $b, $a } @hd;
272   }
273   else {
274     @hd = sort { cmp_item $a, $b } @hd;
275   }
276 }
277
278 if($opt_write_ids) {
279   print STDERR "writing \"hd.ids\"\n";
280   dump2ids;
281 }
282
283 if($opt_write_xml) {
284   print STDERR "writing \"hd.xml\"\n";
285   dump2xml;
286 }
287
288 print STDERR "cmps: $cmp_item_cnt\n" if $cmp_item_cnt;
289
290 # $dump->dumpValue( \@hd );
291
292
293 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
294
295 sub help
296 {
297   print STDERR
298   "Usage: convert_hd [options] files\n" .
299   "Convert various hardware info to libhd/hwinfo internal format or to XML.\n" .
300   "  --ids           write internal format (default) to \"hd.ids\"\n" .
301   "  --no-ids        do not write internal format\n" .
302   "  --xml           write XML to \"hd.xml\", DTD to \"hd.dtd\"\n" .
303   "  --no-xml        do not write XML (default)\n" .
304   "  --with-source   add comment to each item indicating info source\n" .
305   "  --internal-dtd  generate internal dtd\n\n" .
306   "  Note: for more sophisticated operations on hardware data use check_hd.\n";
307
308   exit 0;
309 }
310
311
312 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
313 sub num
314 {
315   return $_[0] =~ /^0/ ? oct $_[0] : return $_[0] + 0;
316 }
317
318
319 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
320 #
321 # read file with name/class info
322 #
323 # (either pciutils or SaX/SaX2 format)
324 #
325
326 sub read_name_file
327 {
328   my ( $file_name, $file, $line, $sax_version, $tag, $id, $val, $ent );
329   my ( @id0, @id1, @id2, @id3, @id4, $raw, $opt, $ext, $srv, $str );
330   local $_;
331
332   my $rnf_add_id0 = sub
333   {
334     my ( $id0, $name0, $ent_id0, $ent_name0, $id, $val );
335
336     # note: $tag belongs to read_name_file()
337     ( $ent_id0, $ent_name0, $tag, $id0, $name0 ) = @_;
338
339     $ent = $ent_id0;
340
341     @id0 = ( $flag_id, $tag, $id0 );
342     undef @id1; undef @id2; undef @id3;
343
344     $id->[$ent_id0] = [ @id0 ];
345     $val->[$ent_name0] = [ $flag_string, $name0 ];
346
347     push @hd, [ "$file_name($line)", [ $id ], $val ];
348   };
349
350   my $rnf_add_bus = sub
351   {
352     $rnf_add_id0->($he_bus_id, $he_bus_name, 0, @_);
353   };
354
355   my $rnf_add_baseclass = sub
356   {
357     $rnf_add_id0->($he_baseclass_id, $he_baseclass_name, 0, @_);
358   };
359
360   my $rnf_add_vendor = sub
361   {
362     $rnf_add_id0->($he_vendor_id, $he_vendor_name, @_);
363   };
364
365   my $rnf_add_subdevice = sub
366   {
367     my ( $id2, $id3, $range, $name, $class, $id, $val );
368
369     ( $id2, $id3, $range, $name, $class ) = @_;
370
371     @id2 = ( $flag_id, $tag, $id2 );
372     @id3 = ( $flag_id, $tag, $id3 );
373     $id3[3] = $range if defined $range;
374
375     if($ent == $he_device_id || $ent == $he_subdevice_id) {
376       $ent = $he_subdevice_id;
377
378       $id->[$he_vendor_id] = [ @id0 ];
379       $id->[$he_device_id] = [ @id1 ];
380       $id->[$he_subvendor_id] = [ @id2 ];
381       $id->[$he_subdevice_id] = [ @id3 ];
382       $val->[$he_subdevice_name] = [ $flag_string, $name ];
383       if(defined $class) {
384         $val->[$he_baseclass_id] = [ $flag_id, $tag_none, $class >> 8 ];
385         $val->[$he_subclass_id] = [ $flag_id, $tag_none, $class & 0xff ];
386       }
387     }
388     else {
389       die "oops $file_name($line): subdevice id expected\n";
390     }
391
392     push @hd, [ "$file_name($line)", [ $id ], $val ];
393   };
394
395   ( $file_name, $file ) = @_;
396
397   $line = 0;
398   undef $sax_version;
399
400   for (@$file) {
401     $line++;
402     chomp;
403     s/\s*$//;
404     next if /^\s*[#;]/;
405     next if /^$/;
406
407     # SaX Identity file
408     if(/^NAME=(.+?)§DEVICE=(.+?)§VID=0x([0-9a-fA-F]+?)§DID=0x([0-9a-fA-F]+?)§SERVER=([^§]+)(§EXT=([^§]*))?(§OPT=([^§]*))?(§RAW=([^§]*))?$/) {
409       #       1            2           3                     4                      5      6     7        8     9        10    11
410
411       $rnf_add_vendor->($tag_pci, hex($3), $1);
412
413       @id0 = ( $flag_id, $tag, hex($3) );
414       @id1 = ( $flag_id, $tag, hex($4) );
415       @id3 = ( $flag_string, $2 );
416
417       $id = [];
418       $val = [];
419
420       $id->[$he_vendor_id] = [ @id0 ];
421       $id->[$he_device_id] = [ @id1 ];
422       $val->[$he_device_name] = [ @id3 ];
423
424       push @hd, [ "$file_name($line)", [ $id ], $val ];
425
426       ( $srv, $ext, $opt, $raw ) = ( $5, $7, $9, $11 );
427       $sax_tmp = $srv =~ /^3DLABS|MACH64|P9000|RUSH|S3|SVGA|TGA$/ ? 1 : 2;
428       $sax_version = $sax_tmp unless defined $sax_version;
429       die "line has SaX$sax_tmp format (expected SaX$sax_version): $file_name($line)\n" if $sax_tmp != $sax_version;
430
431       $id = [];
432       $val = [];
433
434       $id->[$he_vendor_id] = [ @id0 ];
435       $id->[$he_device_id] = [ @id1 ];
436
437       if($opt) {
438         $str = join "|", ( $sax_version == 1 ? 3 : 4, $srv, undef, undef, $ext, $opt );
439       }
440       elsif($ext) {
441         $str = join "|", ( $sax_version == 1 ? 3 : 4, $srv, undef, undef, $ext );
442       }
443       else {
444         $str = join "|", ( $sax_version == 1 ? 3 : 4, $srv );
445       }
446
447       @id4 = ( "x\t$str" );
448       if($raw) {
449         for $str (split /,/, $raw) { $id4[0] .= "\x00X\t$str" }
450       }
451
452       $val->[$he_driver] = [ $flag_string, @id4 ];
453
454       push @hd, [ "$file_name($line)", [ $id ], $val ];
455     }
456
457     elsif(/^B\s+([0-9a-fA-F]+)\s+(.*?)\s*$/) {
458
459       $rnf_add_bus->(hex($1), $2);
460
461     }
462
463     elsif(/^C\s+([0-9a-fA-F]+)\s+(.*?)\s*$/) {
464
465       $rnf_add_baseclass->(hex($1), $2);
466
467     }
468
469     elsif(/^([0-9a-fA-F]{4})(\s+(.*?))?\s*$/) {
470
471       $rnf_add_vendor->($tag_pci, hex($1), $3);
472
473     }
474
475     elsif(/^u([0-9a-fA-F]{4})(\s+(.*?))?\s*$/) {
476
477       $rnf_add_vendor->($tag_usb, hex($1), $3);
478
479     }
480
481     elsif(/^s([0-9a-fA-F]{4})(\s+(.*?))?\s*$/) {
482
483       $rnf_add_vendor->($tag_special, hex($1), $3);
484
485     }
486
487     elsif(/^([A-Z_@]{3})(\s+(.*?))?\s*$/) {
488
489       $rnf_add_vendor->($tag_eisa, eisa_id($1), $3);
490
491     }
492
493     elsif(/^\t([0-9a-fA-F]{1,4})(\+([0-9a-fA-F]+))?(\.([0-9a-fA-F]+))?(\s+(.*?))?\s*$/) {
494
495       $range = $3 ? hex($3) : undef;
496       $class = $5 ? hex($5) : undef;
497
498       @id1 = ( $flag_id, $tag, hex($1) );
499       $id1[3] = $range if defined $range;
500       undef @id2; undef @id3;
501
502       $id = [];
503       $val = [];
504
505       if($ent == $he_baseclass_id || $ent == $he_subclass_id) {
506         $ent = $he_subclass_id;
507
508         $id->[$he_baseclass_id] = [ @id0 ];
509         $id->[$he_subclass_id] = [ @id1 ];
510         $val->[$he_subclass_name] = [ $flag_string, $7 ];
511       }
512       elsif($ent == $he_vendor_id || $ent == $he_device_id || $ent == $he_subdevice_id) {
513         $ent = $he_device_id;
514
515         $id->[$he_vendor_id] = [ @id0 ];
516         $id->[$he_device_id] = [ @id1 ];
517         $val->[$he_device_name] = [ $flag_string, $7 ];
518         if(defined $class) {
519           $val->[$he_baseclass_id] = [ $flag_id, $tag_none, $class >> 8 ];
520           $val->[$he_subclass_id] = [ $flag_id, $tag_none, $class & 0xff ];
521         }
522       }
523       else {
524         die "oops $file_name($line): device id expected\n";
525       }
526
527       push @hd, [ "$file_name($line)", [ $id ], $val ];
528
529     }
530
531     elsif($ent == $he_subclass_id && /^\t\t([0-9a-fA-F]+)\s+(.*?)\s*$/) {
532
533       @id2 = ( $flag_id, $tag, hex($1) );
534       undef @id3;
535
536       $id = [];
537       $val = [];
538
539       $id->[$he_baseclass_id] = [ @id0 ];
540       $id->[$he_subclass_id] = [ @id1 ];
541       $id->[$he_progif_id] = [ @id2 ];
542       $val->[$he_progif_name] = [ $flag_string, $2 ];
543
544       push @hd, [ "$file_name($line)", [ $id ], $val ];
545
546     }
547
548     elsif(/^\t\t([0-9a-fA-F]{4})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?(\.([0-9a-fA-F]+))?(\s+(.*?))?\s*$/) {
549
550       $rnf_add_subdevice->(hex($1), hex($2), $4 ? hex($4) : undef, $8, $6 ? hex($6) : undef);
551
552     }
553
554     elsif(/^\t\t([A-Z_@]{3})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?(\.([0-9a-fA-F]+))?(\s+(.*?))?\s*$/) {
555
556       $rnf_add_subdevice->(eisa_id($1), hex($2), $4 ? hex($4) : undef, $8, $6 ? hex($6) : undef);
557
558     }
559
560     elsif(/^\t\t([0-9a-fA-F]{4})([0-9a-fA-F]{4})\s+(.*?)\s*$/) {
561
562       # NOTE: subvendor & subdevice ids are reversed!
563       $rnf_add_subdevice->(hex($2), hex($1), undef, $3);
564
565     }
566
567     else {
568       die "invalid line: $file_name($line)\n";
569     }
570   }
571 }
572
573
574 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
575 #
576 # read file with driver info
577 #
578
579 sub read_driver_file
580 {
581   my ( $line, @drv, $file, $file_name, $drv_type, $tag );
582   local $_;
583
584   my $rdf_save_drv = sub
585   {
586     if($drv_type) {
587       push @hd, [ @drv ] if defined @drv;
588       @drv = ( "$file_name($line)" );
589       $drv[2][$he_driver] = [ $flag_string ];
590       $drv_type = undef;
591     }
592   };
593
594   my $rdf_add_id = sub
595   {
596     my ( $tag, $id0, $id1, $range1, $id2, $id3, $range3, $id );
597
598     ( $tag, $id0, $id1, $range1, $id2, $id3, $range3 ) = @_;
599
600     $rdf_save_drv->();
601
602     $id = [];
603
604     @id0 = ( $flag_id, $tag, $id0 );
605     @id1 = ( $flag_id, $tag, $id1 );
606     $id1[3] = $range1 if defined $range1;
607
608     $id->[$he_vendor_id] = [ @id0 ];
609     $id->[$he_device_id] = [ @id1 ];
610
611     if(defined $id2) {
612       @id2 = ( $flag_id, $tag, $id2 );
613       @id3 = ( $flag_id, $tag, $id3 );
614       $id3[3] = $range3 if defined $range3;
615
616       $id->[$he_subvendor_id] = [ @id2 ];
617       $id->[$he_subdevice_id] = [ @id3 ];
618     }
619     push @{$drv[1]}, $id;
620   };
621
622   ( $file_name, $file ) = @_;
623
624   $drv_type = 1;
625
626   for (@$file) {
627     $line++;
628     chomp;
629     s/\s*$//;
630     next if /^[#;]/;
631     next if /^$/;
632
633     if(/^([us]?)([0-9a-fA-F]{4})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?\s*$/) {
634
635       $tag = $tag_pci;
636       $tag = $tag_usb if $1 eq 'u';
637       $tag = $tag_special if $1 eq 's';
638
639       $rdf_add_id->($tag, hex($2), hex($3), $5 ? hex($5) : undef);
640
641     }
642
643     elsif(/^([A-Z_@]{3})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?\s*$/) {
644
645       $rdf_add_id->($tag_eisa, eisa_id($1), hex($2), $4 ? hex($4) : undef);
646
647     }
648
649     elsif(/^([us]?)([0-9a-fA-F]{4})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?\s+([us]?)([0-9a-fA-F]{4})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?\s*$/) {
650
651       $tag = $tag_pci;
652       $tag = $tag_usb if $1 eq 'u';
653       $tag = $tag_special if $1 eq 's';
654
655       $rdf_add_id->($tag, hex($2), hex($3), $5 ? hex($5) : undef, hex($7), hex($8), $10 ? hex($10) : undef);
656
657     }
658
659     elsif(/^([A-Z_@]{3})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?\s+([A-Z_@]{3})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?\s*$/) {
660
661       $rdf_add_id->($tag_eisa, eisa_id($1), hex($2), $4 ? hex($4) : undef, eisa_id($5), hex($6), $8 ? hex($8) : undef);
662
663     }
664
665     elsif(/^\t([a-z])\s+(.*?)\s*$/) {
666
667       push @{$drv[2][$he_driver]}, "$1\t$2";
668       $drv_type = $1;
669
670     }
671
672     elsif($drv_type && /^\t\t\s*(.*)$/) {
673
674       $drv_type = "X" if $drv_type eq "x";
675       $drv_type = "M" if $drv_type eq "m";
676       $drv[2][$he_driver][-1] .= "\x00$drv_type\t$1";
677
678     }
679
680     else {
681       die "invalid line: $file_name($line)\n";
682     }
683   }
684
685   $rdf_save_drv->();
686 }
687
688
689 sub num
690 {
691   return $_[0] =~ /^0/ ? oct $_[0] : return $_[0] + 0;
692 }
693
694 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
695 #
696 # read file with id info
697 #
698
699 sub read_id_file
700 {
701   my ( $line, $file, $file_name, $tag, $pre, $fields, @item, @id, $state, $keyid );
702   my ( $is_id, $i );
703   local $_;
704
705   my $rif_save_item = sub
706   {
707     if(@item > 1) {
708       push @hd, [ @item ];
709     }
710     @item = ( "$file_name($line)" );
711   };
712
713   # parse id field
714   my $str2id = sub
715   {
716     my ($val, $id, $tag, $mask, $range, @id);
717
718     $val = shift;
719
720     if($val =~ s/^(${\join '|', @tag_name})\s+//o) {
721       die "internal oops: $file_name($line)\n" unless exists $tag_values{$1};
722       $tag = $tag_values{$1};
723     }
724     else {
725       $tag = 0;
726     }
727
728     if($val =~ /^\s*(\S+)\s*([&+])\s*(\S+)\s*$/) {
729       $id = $1;
730       if($2 eq "+") {
731         $range = $3;
732       }
733       else {
734         $mask = $3;
735       }
736     }
737     else {
738       $id = $val;
739     }
740
741     if(defined $range) {
742       if($range =~ /^(0x[0-9a-zA-Z]+|\d+)$/) {
743         $range = num $range;
744       }
745       else {
746         die "$file_name($line): invalid range\n"
747       }
748     }
749
750     if(defined $mask) {
751       if($mask =~ /^(0x[0-9a-zA-Z]+|\d+)$/) {
752         $mask = num $mask;
753       }
754       else {
755         die "$file_name($line): invalid mask\n"
756       }
757     }
758
759     if($id =~ /^(0x[0-9a-zA-Z]+|\d+)$/) {
760       $id = num $id;
761     }
762     elsif(($tag == $tag_none || $tag == $tag_eisa) && $id =~ /^[A-Z_@]{3}$/) {
763       $id = eisa_id $id;
764       $tag = $tag_eisa;
765     }
766     else {
767       die "$file_name($line): invalid id\n"
768     }
769
770     @id = ( $flag_id, $tag, $id );
771     $id[3] = $range if defined $range;
772     $id[4] = $mask if defined $mask;
773
774     return \@id;
775   };
776
777   ( $file_name, $file ) = @_;
778
779   $fields = join "|", map "\Q$_", @ent_names;
780
781   $state = 0;
782
783   $rif_save_item->();
784
785   for (@$file) {
786     $line++;
787     chomp;
788     s/\s*$//;
789     next if /^\s*[#;]/;
790     next if /^$/;
791
792     if(/^\s*([+&|]?)($fields)\s+(.+)/) {
793       ($pre, $key, $val) = ($1, $2, $3);
794       # print ">$pre< $is_id>$key< >$val<\n";
795       die "internal oops: $file_name($line)\n" unless exists $ent_values{$key};
796       $keyid = $ent_values{$key};
797       $is_id = $keyid < $he_nomask && $key =~ /\.id$/ ? 1 : 0;
798     }
799     else {
800       die "invalid line: $file_name($line)\n";
801     }
802
803     if($pre eq "") {
804       die "invalid line: $file_name($line)\n" unless $state == 0 || $state == 2;
805       if($state == 2) {
806         $item[2] = [ @id ];
807         undef @id;
808       }
809       $rif_save_item->();
810       $state = 1;
811     }
812     elsif($pre eq "|") {
813       die "invalid line: $file_name($line)\n" unless $state == 1;
814       push @{$item[1]}, [ @id ];
815       undef @id;
816     }
817     elsif($pre eq "&") {
818       die "invalid line: $file_name($line)\n" unless $state == 1;
819     }
820     elsif($pre eq "+") {
821       die "invalid line: $file_name($line)\n" unless $state == 1 || $state == 2;
822       if($state == 1) {
823         push @{$item[1]}, [ @id ];
824         undef @id;
825       }
826       $state = 2;
827     }
828     else {
829       die "internal oops: $file_name($line)\n";
830     }
831
832     if($is_id) {
833       $id[$keyid] = $str2id->($val);
834     }
835     elsif($keyid < $he_nomask) {
836       $id[$keyid] = [ $flag_string, $val ];
837     }
838     elsif($keyid == $he_class_id) {
839       $i = ${$str2id->($val)}[2];
840       $id[$he_baseclass_id] = [ $flag_id, $tag_none, $i >> 8 ];
841       $id[$he_subclass_id] = [ $flag_id, $tag_none, $i & 0xff ];
842     }
843     else {
844       undef $i;
845       if($keyid == $he_driver_module_insmod) {
846         $i = "i";
847       }
848       elsif($keyid == $he_driver_module_modprobe) {
849         $i = "m";
850       }
851       elsif($keyid == $he_driver_module_config) {
852         $i = "M";
853       }
854       elsif($keyid == $he_driver_xfree) {
855         $i = "x";
856       }
857       elsif($keyid == $he_driver_xfree_config) {
858         $i = "X";
859       }
860       elsif($keyid == $he_driver_mouse) {
861         $i = "p";
862       }
863       elsif($keyid == $he_driver_display) {
864         $i = "d";
865       }
866       elsif($keyid == $he_driver_any) {
867         $i = "a";
868       }
869       else {
870         die "unhandled entry: $file_name($line)\n"
871       }
872       $val = "$i\t$val";
873       if(!defined $id[$he_driver]) {
874         $id[$he_driver] = [ $flag_string ];
875       }
876       if($i eq "X" || $i eq "M") {
877         $id[$he_driver]->[-1] .= "\x00$val"
878       }
879       else {
880         push @{$id[$he_driver]}, $val;
881       }
882     }
883   }
884
885   if($state == 2) {
886     $item[2] = [ @id ];
887     undef @id;
888   }
889
890   $rif_save_item->();
891 }
892
893
894 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
895 #
896 # read pcimap file
897 #
898
899 sub read_pcimap_file
900 {
901   my (@l, $id, $n, $key, $val, $mask);
902   local $_;
903
904   ( $file_name, $file ) = @_;
905
906   for (@$file) {
907     $line++;
908     chomp;
909     s/\s*$//;
910     next if /^\s*#/;
911     next if /^$/;
912
913     @l = split;
914
915     die "invalid line: $file_name($line)\n" unless @l == 8;
916
917     $val = [];
918
919     $val->[$he_driver] = [ $flag_string, "m\t$l[0]" ];
920
921     $key = [];
922
923     $key->[$he_vendor_id] = [ $flag_id, $tag_pci, $n ] if ($n = num $l[1]) != 0xffffffff;
924     $key->[$he_device_id] = [ $flag_id, $tag_pci, $n ] if ($n = num $l[2]) != 0xffffffff;
925     $key->[$he_subvendor_id] = [ $flag_id, $tag_pci, $n ] if ($n = num $l[3]) != 0xffffffff;
926     $key->[$he_subdevice_id] = [ $flag_id, $tag_pci, $n ] if ($n = num $l[4]) != 0xffffffff;
927
928     $n = num $l[6];
929
930     if($mask = ($n >> 16) & 0xff) {
931       $key->[$he_baseclass_id] = [ $flag_id, $tag_none, (num($l[5]) >> 16) & 0xff ];
932       if($mask != 0xff) {
933         $key->[$he_baseclass_id][4] = (~$mask & 0xff);
934       }
935     }
936
937     if($mask = ($n >> 8) & 0xff) {
938       $key->[$he_subclass_id] = [ $flag_id, $tag_none, (num($l[5]) >> 8) & 0xff ];
939       if($mask != 0xff) {
940         $key->[$he_subclass_id][4] = (~$mask & 0xff);
941       }
942     }
943
944     if($mask = $n & 0xff) {
945       $key->[$he_progif_id] = [ $flag_id, $tag_none, num($l[5]) & 0xff ];
946       if($mask != 0xff) {
947         $key->[$he_progif_id][4] = (~$mask & 0xff);
948       }
949     }
950
951     push @hd, [ "$file_name($line)", [ $key ], $val ];
952   }
953 }
954
955
956 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
957 #
958 # read usbmap file
959 #
960
961 sub read_usbmap_file
962 {
963   my (@l, $id, $n, $key, $val, $mask);
964   local $_;
965
966   ( $file_name, $file ) = @_;
967
968   for (@$file) {
969     $line++;
970     chomp;
971     s/\s*$//;
972     next if /^\s*#/;
973     next if /^$/;
974
975     @l = split;
976
977     die "invalid line: $file_name($line)\n" unless @l == 13;
978
979     next if num($l[1]) != 3;    # match_flags != 3
980
981     $val = [];
982
983     $key = [];
984
985     $key->[$he_vendor_id] = [ $flag_id, $tag_usb, num($l[2]) ];
986     $key->[$he_device_id] = [ $flag_id, $tag_usb, num($l[3]) ];
987
988     $val->[$he_driver] = [ $flag_string, "m\t$l[0]" ];
989
990     if($usbmod2class{$l[0]}) {
991       $val->[$he_baseclass_id] = [ $flag_id, $tag_none, $usbmod2class{$l[0]}[0] ] if defined $usbmod2class{$l[0]}[0];
992       $val->[$he_subclass_id] = [ $flag_id, $tag_none, $usbmod2class{$l[0]}[1] ] if defined $usbmod2class{$l[0]}[1];
993     }
994
995     push @hd, [ "$file_name($line)", [ $key ], $val ];
996   }
997 }
998
999
1000 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1001 #
1002 # read alias file
1003 #
1004
1005 sub read_alias_file
1006 {
1007   my ($f, $id, $n, $key, $val, $mask, $tag, $module, $spec);
1008   local $_;
1009
1010   $f = '[0-9A-F*]+';
1011
1012   ( $file_name, $file ) = @_;
1013
1014   for (@$file) {
1015     $line++;
1016     chomp;
1017     s/\s*$//;
1018     next if /^\s*#/;
1019     next if /^$/;
1020
1021     next unless /^\s*alias\s+(pci|usb):(\S+)\s+(\S+)/;
1022
1023     $tag = $1 eq 'pci' ? $tag_pci : $tag_usb;
1024     $spec = $2;
1025     $module = $3;
1026
1027     $val = [];
1028
1029     $val->[$he_driver] = [ $flag_string, "m\t$module" ];
1030
1031     $key = [];
1032
1033     next if $tag ne $tag_pci;
1034
1035     die "invalid line: $file_name($line)\n" unless
1036       $spec =~ /^v($f)d($f)sv($f)sd($f)bc($f)sc($f)i($f)$/;
1037
1038     $key->[$he_vendor_id] = [ $flag_id, $tag_pci, hex $1 ] if $1 ne '*';
1039     $key->[$he_device_id] = [ $flag_id, $tag_pci, hex $2 ] if $2 ne '*';
1040     $key->[$he_subvendor_id] = [ $flag_id, $tag_pci, hex $3 ] if $3 ne '*';
1041     $key->[$he_subdevice_id] = [ $flag_id, $tag_pci, hex $4 ] if $4 ne '*';
1042     $key->[$he_baseclass_id] = [ $flag_id, $tag_none, hex $5 ] if $5 ne '*';
1043     $key->[$he_subclass_id] = [ $flag_id, $tag_none, hex $6 ] if $6 ne '*';
1044     $key->[$he_progif_id] = [ $flag_id, $tag_none, hex $7 ] if $7 ne '*';
1045
1046     push @hd, [ "$file_name($line)", [ $key ], $val ];
1047   }
1048 }
1049
1050
1051 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1052 #
1053 # convert 3-letter eisa id to number
1054 #
1055
1056 sub eisa_id
1057 {
1058   my ( $str, $id, $i, $j );
1059
1060   $str = shift;
1061   $id = 0;
1062
1063   die "internal oops" unless length($str) == 3;
1064   for($i = 0; $i < 3; $i++) {
1065     $id <<= 5;
1066     $j = ord substr $str, $i, 1;
1067     $j -= ord('A') - 1;
1068     die "internal oops" unless $j >= 0 && $j <= 0x1f;
1069     $id += $j;
1070   }
1071   
1072   return $id;
1073 }
1074
1075
1076 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1077 #
1078 # convert numerical eisa id to 3-letter string
1079 #
1080
1081 sub eisa_str
1082 {
1083   my ( $id, $str );
1084
1085   $id = shift;
1086
1087   die "internal oops: eisa id \"$id\"" unless $id >= 0 && $id <= 0x7fff;
1088
1089   $str  = chr((($id >> 10) & 0x1f) + ord('A') - 1);
1090   $str .= chr((($id >>  5) & 0x1f) + ord('A') - 1);
1091   $str .= chr(( $id        & 0x1f) + ord('A') - 1);
1092
1093   return $str;
1094 }
1095
1096
1097 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1098 #
1099 # remove entries that have no effect
1100 #
1101
1102 sub remove_nops
1103 {
1104   my ($hd, $id, $f, $i, $cf);
1105   local $_;
1106
1107   for $hd (@hd) {
1108     if(!defined($hd->[1]) || !@{$hd->[1]} || !defined($hd->[2]) || !@{$hd->[2]}) {
1109       undef $hd;
1110       next;
1111     }
1112     for $id (@{$hd->[1]}, $hd->[2]) {
1113       if(defined($id)) {
1114         $cf = 0;
1115         for $f (@$id) {
1116           if(defined $f) {
1117             $cf++;
1118             if(@$f == 2 && $f->[0] == $flag_string && $f->[1] eq "") {
1119               undef $f;
1120               $cf--;
1121             }
1122           }
1123         }
1124         undef $id if !$cf;
1125       }
1126     }
1127     if(!defined($hd->[1]) || !@{$hd->[1]} || !defined($hd->[2]) || !@{$hd->[2]}) {
1128       print STDERR "$hd->[0] has no info, dropped\n";
1129       undef $hd;
1130       next;
1131     }
1132   }
1133
1134   @hd = grep { defined } @hd;
1135 }
1136
1137
1138 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1139 #
1140 # remove duplicate entries
1141 #
1142
1143 sub remove_duplicates
1144 {
1145   my ($hd, $hd0, $hd1, $len, $i, $j, $m, $v, $buf, $errors, $drop);
1146   local $_;
1147
1148   $len = @hd;
1149
1150   for($j = 0; $j < $len; $j++) {
1151     print STDERR ">> $j\r";
1152     $hd0 = \$hd[$j];
1153     for($i = $j + 1; $i < $len; $i++) {
1154       $hd1 = \$hd[$i];
1155       $m = match_item $$hd0, $$hd1;
1156       # print "$$hd0->[0] -- $$hd1->[0]: $m\n";
1157       if($m) {
1158         $drop = cmp_item $$hd0, $$hd1;
1159         $drop = !$drop || abs($drop) == 2 ? ", dropped" : undef;
1160         undef $buf;
1161         # print STDERR "j: $$hd0->[0], $$hd1->[0]\n";
1162         $v = join_skey $$hd0->[2], $$hd1->[2], \$buf, \$errors;
1163         if($errors) {
1164           print STDERR "$$hd1->[0] conflicts with $$hd0->[0]$drop:\n$buf\n";
1165           $$hd1 = undef if $drop;
1166         }
1167         else {
1168           if($drop) {
1169             print STDERR "$$hd1->[0] added to $$hd0->[0] and dropped\n";
1170             $$hd0->[2] = $v;
1171 #            $$hd1 = undef;
1172           }
1173           else {
1174             print STDERR "$$hd1->[0] shadowed by $$hd0->[0]\n";
1175             $$hd0->[2] = $v;
1176           }
1177         }
1178       }
1179     }
1180   }
1181
1182   @hd = grep { defined } @hd;
1183
1184   for $hd (@hd) {
1185     if(
1186       !defined($hd->[2]) ||
1187       !defined($hd->[2][$he_driver]) ||
1188       !(defined($hd->[2][$he_device_name]) || defined($hd->[2][$he_subdevice_name]))
1189     ) {
1190       undef $hd;
1191       next;
1192     }
1193   }
1194
1195   @hd = grep { defined } @hd;
1196
1197 }
1198
1199
1200 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1201 #
1202 # remove duplicate entries
1203 #
1204
1205 sub remove_duplicatesx
1206 {
1207   my ($hd0, $hd1, $len, $i, $j, $m, $v, $buf, $errors, $drop);
1208   local $_;
1209
1210   $len = @hd;
1211
1212   for($j = 0; $j < $len; $j++) {
1213     print STDERR ">> $j\r";
1214     $hd0 = \$hd[$j];
1215     for($i = $j + 1; $i < $len; $i++) {
1216       $hd1 = \$hd[$i];
1217       $m = match_item $$hd0, $$hd1;
1218       # print "$$hd0->[0] -- $$hd1->[0]: $m\n";
1219       if($m) {
1220         $drop = cmp_item $$hd0, $$hd1;
1221         $drop = !$drop || abs($drop) == 2 ? ", dropped" : undef;
1222         undef $buf;
1223         $v = join_skey $$hd0->[2], $$hd1->[2], \$buf, \$errors;
1224         if($errors) {
1225           print STDERR "$$hd1->[0] conflicts with $$hd0->[0]$drop:\n$buf\n";
1226           $$hd1 = undef if $drop;
1227         }
1228         else {
1229           if($drop) {
1230             print STDERR "$$hd1->[0] added to $$hd0->[0] and dropped\n";
1231             $$hd0->[2] = $v;
1232             $$hd1 = undef;
1233           }
1234           else {
1235             print STDERR "$$hd1->[0] shadowed by $$hd0->[0]\n";
1236           }
1237         }
1238       }
1239     }
1240   }
1241
1242   @hd = grep { defined } @hd;
1243 }
1244
1245
1246 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1247 #
1248 # fix drive info
1249 #
1250
1251 sub fix_driver_info
1252 {
1253   my ($hd, $hid, $drv, $i, @i, @info, @req, %req);
1254
1255   for $hd (@hd) {
1256     if(
1257       !defined($hd->[2]) ||
1258       !defined($hd->[2][$he_driver])
1259     ) {
1260       next;
1261     }
1262     $hid = $hd->[2][$he_driver];
1263     next unless $hid->[0] == $flag_string;
1264
1265     undef @req;
1266
1267     for $drv (@$hid[1 .. @$hid - 1]) {
1268       @i = split /\x00/, $drv;
1269       for $i (@i) {
1270         next if $i =~ /^[MX]\t/;
1271         $i =~ s/\|+$//;
1272         next unless $i =~ /^x\t/;
1273         @info = split /\|/, $i;
1274         # remove leasding 'XF86_' from server name
1275         $info[1] =~ s/^XF86_// if $info[1];
1276         # sort package, extension and option lists
1277         push @req, split /,/, $info[3] if $info[3];
1278         # $info[3] = join ',', sort split /,/, $info[3] if $info[3];
1279         $info[3] = undef if $info[3];
1280         $info[4] = join ',', sort split /,/, $info[4] if $info[4];
1281         $info[5] = join ',', sort split /,/, $info[5] if $info[5];
1282         $info[6] = join ',', sort { $a <=> $b } split /,/, $info[6] if $info[6];
1283         $i = join '|', @info;  
1284       }
1285       $drv = join "\x00", @i;
1286       # print ">$drv<\n"
1287     }
1288
1289     if(@req) {
1290       $hid = $hd->[2][$he_requires];
1291       if($hid) {
1292         if($hid->[0] != $flag_string) {
1293           die "oops, invalid data"
1294         }
1295         push @req, split /\|/, $hid->[1];
1296         $hid->[1] = join '|', @req;
1297       }
1298       else {
1299         $hd->[2][$he_requires] = [ $flag_string, join('|', @req) ];
1300       }
1301     }
1302   }
1303
1304   for $hd (@hd) {
1305     if(
1306       !defined($hd->[2]) ||
1307       !defined($hd->[2][$he_requires])
1308     ) {
1309       next;
1310     }
1311     $hid = $hd->[2][$he_requires];
1312     next unless $hid->[0] == $flag_string;
1313
1314     undef @req;
1315     undef %req;
1316
1317     @req = split /\|/, $hid->[1];
1318     @req{@req} = @req;
1319
1320     $hid->[1] = join '|', sort keys %req;
1321   }
1322 }
1323
1324
1325 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1326 #
1327 # hd: [ "source", [ skey, skey, ... ], [ val ] ]
1328 # skey/val: [ ... , id, ..., id, ... ]
1329 # id: [ $flag_id, $tag, $value, $range, $mask ]
1330 # id: [ $flag_string, "str", "str", ... ]
1331
1332 sub cmp_id
1333 {
1334   my ($id0, $id1, $len0, $len1, $len, $i, $k);
1335
1336   ($id0, $id1) = @_;
1337
1338   return 0 if !defined($id0) && !defined($id1);
1339   return -1 if !defined($id0);
1340   return 1 if !defined($id1);
1341
1342   if($id0->[0] != $id1->[0]) {
1343     return $id0->[0] <=> $id1->[0];
1344   }
1345
1346   $len0 = @$id0;
1347   $len1 = @$id1;
1348   $len = $len0 < $len1 ? $len0 : $len1;
1349
1350   if($id0->[0] == $flag_string) {
1351     for($i = 1; $i < $len; $i++) {
1352       $k = $id0->[$i] cmp $id1->[$i];
1353       return $k if $k;
1354     }
1355     return $len0 <=> $len1;
1356   }
1357
1358   if($id0->[0] == $flag_id) {
1359     $k = $id0->[1] <=> $id1->[1];
1360     return $k if $k;
1361     $k = $id0->[2] <=> $id1->[2];
1362     return $k if $k;
1363     $k = $len0 <=> $len1;
1364     return $k if $k || $len <= 3;
1365     # print "-\n";
1366     # $dump->dumpValue( $id0 );
1367     # $dump->dumpValue( $id1 );
1368     # die "internal oops: strange id" if $len < 4;
1369     $i = $len - 1;
1370     return -1 if !defined($id0->[$i]);
1371     return 1 if !defined($id1->[$i]);
1372     return $id0->[$i] <=> $id1->[$i];
1373   }
1374
1375   die "internal oops: can't compare that!";
1376 }
1377
1378
1379 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1380 #
1381 sub cmp_skey
1382 {
1383   my ($skey0, $skey1, $len0, $len1, $len, $i, $k);
1384
1385   ($skey0, $skey1) = @_;
1386
1387   return 0 if !defined($skey0) && !defined($skey1);
1388   return -1 if !defined($skey0);
1389   return 1 if !defined($skey1);
1390
1391   $len0 = @$skey0;
1392   $len1 = @$skey1;
1393   $len = $len0 < $len1 ? $len0 : $len1;
1394
1395   # $dump->dumpValue( $skey0 );
1396   # $dump->dumpValue( $skey1 );
1397
1398   for($i = 0; $i < $len; $i++) {
1399     next unless defined($skey0->[$i]) || defined($skey1->[$i]);
1400
1401     # note: this looks reversed, but is intentional!
1402     return 1 if !defined($skey0->[$i]);
1403     return -1 if !defined($skey1->[$i]);
1404
1405     $k = cmp_id $skey0->[$i], $skey1->[$i];
1406
1407     return $k if $k;
1408   }
1409
1410   return $len0 <=> $len1;
1411 }
1412
1413
1414 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1415 #
1416 #   0: equal
1417 # +-1: differing keys
1418 # +-2: differing values
1419 #
1420 sub cmp_item
1421 {
1422   my ($item0, $item1, $len0, $len1, $len, $i, $k);
1423
1424   ($item0, $item1) = @_;
1425
1426   $cmp_item_cnt++;
1427
1428   return 0 if !defined($item0) && !defined($item1);
1429   return -1 if !defined($item0);
1430   return 1 if !defined($item1);
1431
1432   $len0 = @{$item0->[1]};
1433   $len1 = @{$item1->[1]};
1434   $len = $len0 < $len1 ? $len0 : $len1;
1435
1436 #  $dump->dumpValue( $item0 );
1437
1438   for($i = 0; $i < $len; $i++) {
1439     return -1 if !defined($item0->[1][$i]);
1440     return 1 if !defined($item1->[1][$i]);
1441     $k = cmp_skey $item0->[1][$i], $item1->[1][$i];
1442     # print "  skey: $k\n";
1443     return $k if $k;
1444   }
1445   $k = $len0 <=> $len1;
1446   return $k if $k;
1447
1448   return 0 if !defined($item0->[2]) && !defined($item1->[2]);
1449   return -2 if !defined($item0->[2]);
1450   return 2 if !defined($item1->[2]);
1451
1452   $k = cmp_skey $item0->[2], $item1->[2];
1453   # print "  val: $k\n";
1454   return 2 * $k;
1455 }
1456
1457
1458 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1459 #
1460 # check if id1 is part of id0
1461 #
1462 # return:
1463 #   1: yes
1464 #   0: no
1465 #   undef: don't know
1466 #
1467 # hd: [ "source", [ skey, skey, ... ], [ val ] ]
1468 # skey/val: [ ... , id, ..., id, ... ]
1469 # id: [ $flag_id, $tag, $value, $range, $mask ]
1470 # id: [ $flag_string, "str", "str", ... ]
1471
1472 sub match_id
1473 {
1474   my ($id0, $id1, $len0, $len1, $len, $i, $k);
1475
1476   ($id0, $id1) = @_;
1477
1478   return 0 if !defined($id0) || !defined($id1);
1479
1480   return 0 if $id0->[0] != $id1->[0];
1481
1482   $len0 = @$id0;
1483   $len1 = @$id1;
1484   $len = $len0 < $len1 ? $len0 : $len1;
1485
1486   if($id0->[0] == $flag_string) {
1487     for($i = 1; $i < $len; $i++) {
1488       return 0 if $id0->[$i] cmp $id1->[$i];
1489     }
1490     return $len0 != $len1 ? 0 : 1;
1491   }
1492
1493   if($id0->[0] == $flag_id) {
1494     return 0 if $id0->[1] != $id1->[1];
1495     if($len1 == 3) {
1496       if($len0 == 3) {
1497         return $id0->[2] != $id1->[2] ? 0 : 1;
1498       }
1499       elsif($len0 == 4) {
1500         return $id1->[2] >= $id0->[2] && $id1->[2] < $id0->[2] + $id0->[3] ? 1 : 0;
1501       }
1502       elsif($len0 == 5) {
1503         return ($id1->[2] & ~$id0->[4]) == $id0->[2] ? 1 : 0;
1504       }
1505       else {
1506         die "invalid id";
1507       }
1508     }
1509     elsif($len1 == 4) {
1510       return undef;
1511     }
1512     elsif($len1 == 5) {
1513       return undef;
1514     }
1515     else {
1516       die "invalid id";
1517     }
1518   }
1519
1520   die "internal oops: can't match that!";
1521 }
1522
1523
1524 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1525 #
1526 # skey1 part of skey0?
1527 #
1528 sub match_skey
1529 {
1530   my ($skey0, $skey1, $len0, $len1, $len, $i, $k);
1531
1532   ($skey0, $skey1) = @_;
1533
1534   return 0 if !defined($skey0) || !defined($skey1);
1535
1536   $len0 = @$skey0;
1537   $len1 = @$skey1;
1538
1539   $len = $len0 > $len1 ? $len0 : $len1;
1540
1541   # $dump->dumpValue( $skey0 );
1542   # $dump->dumpValue( $skey1 );
1543
1544   for($i = 0; $i < $len; $i++) {
1545     next unless defined($skey1->[$i]);
1546
1547     return 0 if !defined($skey0->[$i]) && defined($skey1->[$i]);
1548
1549     $k = match_id $skey0->[$i], $skey1->[$i];
1550
1551     return $k if !$k;
1552   }
1553
1554   return 1;
1555 }
1556
1557
1558 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1559 #
1560 # item1 part of item0?
1561 #
1562 sub match_item
1563 {
1564   my ($item0, $item1, $len0, $len1, $i, $j, $k, $m);
1565
1566   ($item0, $item1) = @_;
1567
1568   $match_item_cnt++;
1569
1570   return 0 if !defined($item0) || !defined($item1);
1571
1572   $len0 = @{$item0->[1]};
1573   $len1 = @{$item1->[1]};
1574
1575   for($j = 0; $j < $len1; $j++) {
1576     for($i = 0; $i < $len0; $i++) {
1577       $k = match_skey $item0->[1][$i], $item1->[1][$j];
1578       $m = $k if defined $k;
1579       return $k if $k;
1580     }
1581   }
1582
1583   return $m
1584 }
1585
1586
1587 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1588 #
1589 # add skey1 to skey0
1590 #
1591 sub join_skey
1592 {
1593   my ($skey0, $skey1, $len, $i, $k, $n, $buf, $err);
1594
1595   ($skey0, $skey1, $buf, $errors) = @_;
1596
1597   $$errors = 0;
1598
1599   return undef if !defined($skey0) && !defined($skey1);
1600   return [ @$skey0 ] if !defined($skey1);
1601   return [ @$skey1 ] if !defined($skey0);
1602
1603   $n = [ @$skey0 ];
1604
1605   $len = @$skey1;
1606
1607   for($i = 0; $i < $len; $i++) {
1608     next unless defined $skey1->[$i];
1609
1610     $n->[$i] = $skey1->[$i];
1611
1612     next unless defined $skey0->[$i];
1613
1614     $k = cmp_id $skey0->[$i], $skey1->[$i];
1615
1616     if($k) {
1617       if(defined $buf) {
1618         if($i != $he_driver) {
1619           $$buf .= ent_name_pr("  0:", $ent_names[$i]);
1620           $$buf .= id_dump($i, $skey0->[$i]) . "\n";
1621           $$buf .= ent_name_pr("  1:", $ent_names[$i]);
1622           $$buf .= id_dump($i, $skey1->[$i]) . "\n";
1623         }
1624         else {
1625           $$buf .= drv_dump("  0:", $skey0->[$i]);
1626           $$buf =~ s/\n&/\n  0:/;
1627           $$buf .= drv_dump("  1:", $skey1->[$i]);
1628           $$buf =~ s/\n&/\n  1:/;
1629         }
1630       }
1631       $$errors++ if defined $errors;
1632     }
1633   }
1634
1635   return $n;
1636 }
1637
1638
1639
1640 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1641 #
1642 # split key fields
1643 #
1644 sub split_item
1645 {
1646   my ($item, @items, $tmp);
1647   local $_;
1648
1649   $item = shift;
1650
1651   return $item if !defined($item) || !defined($item->[1]);
1652
1653   for (@{$item->[1]}) {
1654     $tmp = [ @$item ];
1655     $tmp->[1] = [ $_ ];
1656     push @items, $tmp;
1657   }
1658
1659   return @items;
1660 }
1661
1662
1663 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1664
1665 sub get_xml_data
1666 {
1667   my ($xml, $i, $j);
1668
1669   $xml = shift;
1670
1671   if($xml->[0] ne 'hwdata') {
1672     die "invalid XML root element (expected 'hwdata')\n"
1673   }
1674
1675   for($i = 1; $i < @{$xml->[1]}; $i += 2) {
1676     if($xml->[1][$i] eq 'item') {
1677       push @hd, parse_xml_item($xml->[1][$i + 1]);
1678     }
1679   }
1680 }
1681
1682
1683 sub parse_xml_item
1684 {
1685   my (@xml, %attr, $i, $item);
1686
1687   @xml = @{$_[0]};
1688   %attr = %{shift @xml};
1689
1690   for($i = 0; $i < @xml; $i += 2) {
1691     if($xml[$i] eq 'key') {
1692       push @{$item->[1]}, parse_xml_key($xml[$i + 1]);
1693     }
1694     else {
1695       $item->[2] = parse_xml_key($_[0]);
1696     }
1697   }
1698
1699   return $item;
1700 }
1701
1702
1703 sub parse_xml_key
1704 {
1705   my (@xml, %attr, $i, @key, $val, $id, $is_id, $keyid, $keyid2, $tmp);
1706
1707   @xml = @{$_[0]};
1708   %attr = %{shift @xml};
1709
1710   for($i = 0; $i < @xml; $i += 2) {
1711     next if $xml[$i] eq '0' || $xml[$i] eq 'key';
1712     
1713     $keyid = $xml_values{$xml[$i]};
1714     $is_id = $keyid < $he_nomask && $ent_names[$keyid] =~ /\.(id|name)$/ ? 1 : 0;
1715
1716     if(!defined($keyid)) {
1717       die "invalid key element \"$xml[$i]\"\n";
1718     }
1719
1720     if($keyid == $he_driver) {
1721       $id = parse_xml_driver($xml[$i + 1]);
1722       if(!defined($key[$keyid])) {
1723         $key[$keyid] = $id;
1724       }
1725       else {
1726         push @{$key[$keyid]}, $id->[1];
1727       }
1728     }
1729     elsif($is_id) {
1730       $id = parse_xml_id($xml[$i + 1]);
1731       if($id->[0] == $flag_id) {
1732         $tmp = $ent_names[$keyid];
1733         $tmp =~ s/\.name$/.id/;
1734         $keyid2 = $ent_values{$tmp};
1735         if(!defined($keyid2)) {
1736           die "oops, no .id for $xml[$i]?";
1737         }
1738       }
1739       else {
1740         $tmp = $ent_names[$keyid];
1741         $tmp =~ s/\.id$/.name/;
1742         $keyid2 = $ent_values{$tmp};
1743         if(!defined($keyid2)) {
1744           die "oops, no .name for $xml[$i]?";
1745         }
1746       }
1747       $key[$keyid2] = $id;
1748     }
1749     else {
1750       $val = parse_xml_cdata($xml[$i + 1]);
1751       if(defined($key[$keyid]) && $keyid == $he_requires) {
1752         $key[$keyid][1] .= "|$val";
1753       }
1754       else {
1755         $key[$keyid] = [ $flag_string, $val ];
1756       }
1757     }
1758   }
1759
1760   return [ @key ];
1761 }
1762
1763
1764 sub parse_xml_id
1765 {
1766   my (@xml, %attr, $i, $id, $val);
1767
1768   @xml = @{$_[0]};
1769   %attr = %{shift @xml};
1770
1771   for($i = 0; $i < @xml; $i += 2) {
1772     next if $xml[$i] eq '0';
1773
1774     if($xml[$i] eq 'id') {
1775       $id = parse_xml_id_id($xml[$i + 1]);
1776     }
1777     elsif($xml[$i] eq 'idrange') {
1778       $id = parse_xml_id_range($xml[$i + 1]);
1779     }
1780     elsif($xml[$i] eq 'idmask') {
1781       $id = parse_xml_id_mask($xml[$i + 1]);
1782     }
1783     elsif($xml[$i] eq 'name') {
1784       $val = parse_xml_cdata($xml[$i + 1]);
1785       $id = [ $flag_string, $val ];
1786     }
1787     else {
1788       die "invalid id element \"$xml[$i]\"\n";
1789     }
1790   }
1791
1792   return $id;
1793 }
1794
1795
1796 sub parse_xml_id_id
1797 {
1798   my (@xml, %attr, $i, $tag, $value);
1799
1800   @xml = @{$_[0]};
1801   %attr = %{shift @xml};
1802
1803   $tag = $tag_values{$attr{type}};
1804
1805   if(!defined($tag)) {
1806     die "missing/unsupported id attribute \"$attr{type}\"\n";
1807   }
1808
1809   for($i = 0; $i < @xml; $i += 2) {
1810     if($xml[$i] eq '0') {
1811       $value = idstr2value $tag, $xml[$i + 1];
1812     }
1813     else {
1814       die "cdata expected, got \"$xml[$i]\"\n";
1815     }
1816   }
1817
1818   return [ $flag_id, $tag, $value ];
1819 }
1820
1821
1822 sub parse_xml_id_range
1823 {
1824   my (@xml, %attr, $i, $tag, $value, $range);
1825
1826   @xml = @{$_[0]};
1827   %attr = %{shift @xml};
1828
1829   $tag = $tag_values{$attr{type}};
1830
1831   if(!defined($tag)) {
1832     die "missing/unsupported id attribute \"$attr{type}\"\n";
1833   }
1834
1835   for($i = 0; $i < @xml; $i += 2) {
1836     next if $xml[$i] eq '0';
1837     if($xml[$i] eq 'first') {
1838       $value = idstr2value $tag, parse_xml_cdata($xml[$i + 1]);
1839     }
1840     elsif($xml[$i] eq 'last') {
1841       $range = idstr2value $tag, parse_xml_cdata($xml[$i + 1]);
1842     }
1843     else {
1844       die "invalid idrange element \"$xml[$i]\"\n";
1845     }
1846   }
1847
1848   if(!defined($value) || !defined($range)) {
1849     die "invalid idrange\n";
1850   }
1851
1852   return [ $flag_id, $tag, $value, $range - $value + 1 ];
1853 }
1854
1855
1856 sub parse_xml_id_mask
1857 {
1858   my (@xml, %attr, $i, $tag, $value, $mask);
1859
1860   @xml = @{$_[0]};
1861   %attr = %{shift @xml};
1862
1863   $tag = $tag_values{$attr{type}};
1864
1865   if(!defined($tag)) {
1866     die "missing/unsupported id attribute \"$attr{type}\"\n";
1867   }
1868
1869   for($i = 0; $i < @xml; $i += 2) {
1870     next if $xml[$i] eq '0';
1871     if($xml[$i] eq 'value') {
1872       $value = idstr2value $tag, parse_xml_cdata($xml[$i + 1]);
1873     }
1874     elsif($xml[$i] eq 'mask') {
1875       $mask = idstr2value $tag, parse_xml_cdata($xml[$i + 1]);
1876     }
1877     else {
1878       die "invalid idmask element \"$xml[$i]\"\n";
1879     }
1880   }
1881
1882   if(!defined($value) || !defined($mask)) {
1883     die "invalid idmask\n";
1884   }
1885
1886   return [ $flag_id, $tag, $value, undef, $mask ];
1887 }
1888
1889
1890 sub parse_xml_driver
1891 {
1892   my (@xml, %attr, $i, $val);
1893
1894   @xml = @{$_[0]};
1895   %attr = %{shift @xml};
1896
1897   for($i = 0; $i < @xml; $i += 2) {
1898     next if $xml[$i] eq '0';
1899
1900     if($xml[$i] eq 'any') {
1901       $val = "a\t" . parse_xml_cdata($xml[$i + 1]);
1902     }
1903     elsif($xml[$i] eq 'display') {
1904       $val = parse_xml_driver_display($xml[$i + 1]);
1905     }
1906     elsif($xml[$i] eq 'module') {
1907       $val = parse_xml_driver_module($xml[$i + 1]);
1908     }
1909     elsif($xml[$i] eq 'mouse') {
1910       $val = parse_xml_driver_mouse($xml[$i + 1]);
1911     }
1912     elsif($xml[$i] eq 'xfree') {
1913       $val = parse_xml_driver_xfree($xml[$i + 1]);
1914     }
1915     else {
1916       die "invalid driver element \"$xml[$i]\"\n";
1917     }
1918   }
1919
1920   return [ $flag_string, $val ];
1921 }
1922
1923
1924 sub parse_xml_driver_display
1925 {
1926   my (@xml, %attr, $i, @val);
1927
1928   @xml = @{$_[0]};
1929   %attr = %{shift @xml};
1930
1931   for($i = 0; $i < @xml; $i += 2) {
1932     next if $xml[$i] eq '0';
1933
1934     if($xml[$i] eq 'resolution') {
1935       $val[0] = join('x', parse_xml_pair($xml[$i + 1], 'width', 'height'));
1936     }
1937     elsif($xml[$i] eq 'vsync') {
1938       $val[1] = join('-', parse_xml_pair($xml[$i + 1], 'min', 'max'));
1939     }
1940     elsif($xml[$i] eq 'hsync') {
1941       $val[2] = join('-', parse_xml_pair($xml[$i + 1], 'min', 'max'));
1942     }
1943     elsif($xml[$i] eq 'bandwidth') {
1944       $val[3] = parse_xml_cdata($xml[$i + 1]);
1945     }
1946     else {
1947       die "invalid display element \"$xml[$i]\"\n";
1948     }
1949   }
1950
1951   if(!@val) {
1952     die "invalid display info\n";
1953   }
1954
1955   return "d\t" . join('|', @val);
1956 }
1957
1958
1959 sub parse_xml_driver_module
1960 {
1961   my (@xml, %attr, $i, $val, $type, @conf, @mods);
1962
1963   @xml = @{$_[0]};
1964   %attr = %{shift @xml};
1965
1966   for($i = 0; $i < @xml; $i += 2) {
1967     next if $xml[$i] eq '0';
1968
1969     $val = parse_xml_cdata($xml[$i + 1]);
1970
1971     if($xml[$i] eq 'modprobe') {
1972       if($type && $type ne 'm') {
1973         die "invalid module info: \"$xml[$i]\"\n";
1974       }
1975       $type = 'm';
1976       push @mods, $val;
1977     }
1978     elsif($xml[$i] eq 'insmod') {
1979       if($type && $type ne 'i') {
1980         die "invalid module info: \"$xml[$i]\"\n";
1981       }
1982       $type = 'i';
1983       push @mods, $val;
1984     }
1985     elsif($xml[$i] eq 'modconf') {
1986       if($type && $type ne 'm') {
1987         die "invalid module info: \"$xml[$i]\"\n";
1988       }
1989       push @conf, "\x00M\t$val";
1990     }
1991     else {
1992       die "invalid module element \"$xml[$i]\"\n";
1993     }
1994   }
1995
1996   if(!$type && !@mods) {
1997     die "invalid module info\n";
1998   }
1999
2000   $val = "$type\t" . join('|', @mods);
2001
2002   if(@conf) {
2003     $val .= join('', @conf);
2004   }
2005
2006   return $val;
2007 }
2008
2009
2010 sub parse_xml_driver_mouse
2011 {
2012   my (@xml, %attr, $i, $val, @val);
2013
2014   @xml = @{$_[0]};
2015   %attr = %{shift @xml};
2016
2017   for($i = 0; $i < @xml; $i += 2) {
2018     next if $xml[$i] eq '0';
2019
2020     $val = parse_xml_cdata($xml[$i + 1]);
2021
2022     if($xml[$i] eq 'xf86') {
2023       $val[0] = $val;
2024     }
2025     elsif($xml[$i] eq 'gpm') {
2026       $val[1] = $val;
2027     }
2028     elsif($xml[$i] eq 'buttons') {
2029       $val[2] = $val;
2030     }
2031     elsif($xml[$i] eq 'wheels') {
2032       $val[3] = $val;
2033     }
2034     else {
2035       die "invalid mouse element \"$xml[$i]\"\n";
2036     }
2037   }
2038
2039   if(!@val) {
2040     die "invalid mouse info\n";
2041   }
2042
2043   return "p\t" . join('|', @val);
2044 }
2045
2046
2047 sub parse_xml_driver_xfree
2048 {
2049   my (@xml, %attr, $i, $val, @val, @conf);
2050
2051   @xml = @{$_[0]};
2052   %attr = %{shift @xml};
2053
2054   for($i = 0; $i < @xml; $i += 2) {
2055     next if $xml[$i] eq '0';
2056
2057     if($xml[$i] eq 'has3d') {
2058       $val[2] = '3d';
2059     }
2060     else {
2061       $val = parse_xml_cdata($xml[$i + 1]);
2062
2063       if($xml[$i] eq 'version') {
2064         $val[0] = $val;
2065       }
2066       elsif($xml[$i] eq 'server') {
2067         $val[1] = $val;
2068       }
2069       elsif($xml[$i] eq 'extension') {
2070         $val[4] .= "," if defined $val[4];
2071         $val[4] .= $val;
2072       }
2073       elsif($xml[$i] eq 'option') {
2074         $val[5] .= "," if defined $val[5];
2075         $val[5] .= $val;
2076       }
2077       elsif($xml[$i] eq 'bpp') {
2078         $val[6] .= "," if defined $val[6];
2079         $val[6] .= $val;
2080       }
2081       elsif($xml[$i] eq 'dacspeed') {
2082         $val[7] = $val;
2083       }
2084       elsif($xml[$i] eq 'script') {
2085         $val[8] = $val;
2086       }
2087       elsif($xml[$i] eq 'xf86conf') {
2088         push @conf, "\x00X\t$val";
2089       }
2090       else {
2091         die "invalid xfree element \"$xml[$i]\"\n";
2092       }
2093     }
2094   }
2095
2096   if(!@val) {
2097     die "invalid xfree info\n";
2098   }
2099
2100   $val = "x\t" . join('|', @val);
2101
2102   if(@conf) {
2103     $val .= join('', @conf);
2104   }
2105
2106   return $val;
2107 }
2108
2109
2110 sub parse_xml_pair
2111 {
2112   my (@xml, %attr, $i, $val0, $val1, $elem0, $elem1);
2113
2114   $elem0 = $_[1];
2115   $elem1 = $_[2];
2116
2117   @xml = @{$_[0]};
2118   %attr = %{shift @xml};
2119
2120   for($i = 0; $i < @xml; $i += 2) {
2121     next if $xml[$i] eq '0';
2122     if($xml[$i] eq $elem0) {
2123       $val0 = parse_xml_cdata($xml[$i + 1]);
2124     }
2125     elsif($xml[$i] eq $elem1) {
2126       $val1 = parse_xml_cdata($xml[$i + 1]);
2127     }
2128     else {
2129       die "invalid element \"$xml[$i]\"\n";
2130     }
2131   }
2132
2133   if(!defined($val0) || !defined($val1)) {
2134     die "invalid element\n";
2135   }
2136
2137   return ($val0, $val1);
2138 }
2139
2140
2141 sub parse_xml_cdata
2142 {
2143   my (@xml, %attr, $i);
2144
2145   @xml = @{$_[0]};
2146   %attr = %{shift @xml};
2147
2148   for($i = 0; $i < @xml; $i += 2) {
2149     if($xml[$i] eq '0') {
2150       return $xml[$i + 1]
2151     }
2152   }
2153 }
2154
2155
2156 sub idstr2value
2157 {
2158   my ($tag, $value);
2159
2160   ($tag, $value) = @_;
2161
2162   if($tag == $tag_eisa && length($value) == 3 && $value !~ /^[0-9]/) {
2163     $value = eisa_id $value;
2164   }
2165   else {
2166     $value = num $value;
2167   }
2168
2169   return $value;
2170 }
2171
2172 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2173
2174 sub ent_name_pr
2175 {
2176   my ($str, $len);
2177
2178   $str = $_[0] . $_[1];
2179
2180   $len = length $str;
2181
2182   $str .= "\t";
2183   $len = ($len & ~7) + 8;
2184   $str .= "\t" x ((24 - $len)/8) if $len < 24;
2185   
2186   return $str;
2187 }
2188
2189
2190 sub id_dump
2191 {
2192   my ($id, $ent, $str, $tag, $format);
2193
2194   ($ent, $id) = @_;
2195
2196   if($id->[0] == $flag_id) {
2197     $tag = $id->[1];
2198     if($tag == $tag_eisa && ($ent == $he_vendor_id || $ent == $he_subvendor_id)) {
2199       $str = eisa_str $id->[2];
2200     }
2201     else {
2202       $str .= $tag_name[$tag];
2203       $str .= " " if $tag;
2204       $format = "0x%04x";
2205       $format = "0x%02x" if $ent == $he_bus_id || $ent == $he_subclass_id || $ent == $he_progif_id;
2206       $format = "0x%03x" if $ent == $he_baseclass_id;
2207       $str .= sprintf $format, $id->[2];
2208     }
2209     if(defined $id->[3]) {
2210       $str .= sprintf "+0x%04x", $id->[3];
2211     }
2212     elsif(defined $id->[4]) {
2213       $str .= sprintf "&0x%04x", $id->[4];
2214     }
2215   }
2216   elsif($id->[0] == $flag_string) {
2217     if(defined($id->[2])) {
2218       die "oops: strage string data\n";
2219     }
2220     $str = $id->[1];
2221   }
2222   else {
2223     die "oops: unknown id flag\n"
2224   }
2225   
2226   return $str;
2227 }
2228
2229
2230 sub drv_dump
2231 {
2232   my ($id, $str, $i, $pre, $type, $drv, $buf);
2233
2234   ($pre, $id) = @_;
2235
2236   die "oops: invalid driver data\n" if $id->[0] != $flag_string;
2237
2238   for($i = 1; $i < @{$id}; $i++) {
2239     for $drv (split /\x00/, $id->[$i]) {
2240       $type = substr $drv, 0, 2;
2241
2242       if($type eq "x\t") {
2243         $buf .= ent_name_pr($pre, $ent_names[$he_driver_xfree]);
2244         $buf .= substr($drv, 2) . "\n";
2245       }
2246       elsif($type eq "X\t") {
2247         $buf .= ent_name_pr($pre, $ent_names[$he_driver_xfree_config]);
2248         $buf .= substr($drv, 2) . "\n";
2249       }
2250       elsif($type eq "i\t") {
2251         $buf .= ent_name_pr($pre, $ent_names[$he_driver_module_insmod]);
2252         $buf .= substr($drv, 2) . "\n";
2253       }
2254       elsif($type eq "m\t") {
2255         $buf .= ent_name_pr($pre, $ent_names[$he_driver_module_modprobe]);
2256         $buf .= substr($drv, 2) . "\n";
2257       }
2258       elsif($type eq "M\t") {
2259         $buf .= ent_name_pr($pre, $ent_names[$he_driver_module_config]);
2260         $buf .= substr($drv, 2) . "\n";
2261       }
2262       elsif($type eq "p\t") {
2263         $buf .= ent_name_pr($pre, $ent_names[$he_driver_mouse]);
2264         $buf .= substr($drv, 2) . "\n";
2265       }
2266       elsif($type eq "d\t") {
2267         $buf .= ent_name_pr($pre, $ent_names[$he_driver_display]);
2268         $buf .= substr($drv, 2) . "\n";
2269       }
2270       elsif($type eq "a\t") {
2271         $buf .= ent_name_pr($pre, $ent_names[$he_driver_any]);
2272         $buf .= substr($drv, 2) . "\n";
2273       }
2274       else {
2275         die "oops: unhandled driver info type: $drv\n";
2276       }
2277
2278       $pre = "&" if $pre ne "+";
2279     }
2280   }
2281
2282   return $buf;
2283 }
2284
2285
2286 sub ent_dump
2287 {
2288   my ($pre, $id, $ent, $buf);
2289
2290   ($buf, $pre, $id) = @_;
2291
2292   $pre = defined($pre) ? "|" : " " if $pre ne "+";
2293   for($ent = 0; $ent < @{$id}; $ent++) {
2294     if(defined $id->[$ent]) {
2295       if($ent != $he_driver) {
2296         $$buf .= ent_name_pr($pre, $ent_names[$ent]);
2297         $$buf .= id_dump($ent, $id->[$ent]);
2298         $$buf .= "\n";
2299       }
2300       else {
2301         $$buf .= drv_dump($pre, $id->[$ent]);
2302       }
2303       $pre = "&" if $pre ne "+";
2304     }
2305   }
2306
2307   return $pre;
2308 }
2309
2310
2311 sub dump2ids
2312 {
2313   my ($item, $id, $ent, $pre, $buf);
2314
2315   # $dump->dumpValue( \@hd );
2316
2317   open F, ">hd.ids";
2318
2319   for $item (@hd) {
2320     undef $buf;
2321     undef $pre;
2322     print F "# $item->[0]\n" if $opt_with_source;
2323     for $id (@{$item->[1]}) {
2324       $pre = ent_dump \$buf, $pre, $id;
2325     }
2326     $pre = "+";
2327     ent_dump \$buf, $pre, $item->[2];
2328     $buf .= "\n";
2329
2330     print F $buf;
2331   }
2332
2333   close F;
2334 }
2335
2336
2337 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2338
2339 sub dump2xml
2340 {
2341   my ($item, $dtd);
2342
2343   if($opt_internal_dtd) {
2344     $dtd = hd_dtd_internal;
2345   }
2346   else {
2347     $dtd = "<!DOCTYPE hwdata SYSTEM \"hd.dtd\">\n";
2348   }
2349
2350   $xml_file = new IO::File(">hd.xml");
2351   $xml = new XML::Writer(OUTPUT => $xml_file, DATA_MODE => 1, DATA_INDENT => 2);
2352
2353   $xml->xmlDecl("utf-8");
2354
2355   print $xml_file "\n$dtd";
2356
2357   $xml->startTag("hwdata");
2358
2359   print $xml_file "\n";
2360
2361   for $item (@hd) {
2362     dump_xml_item $item;
2363   }
2364
2365   $xml->endTag("hwdata");
2366   $xml->end();
2367
2368   if(!$opt_internal_dtd) {
2369     print STDERR "writing \"hd.dtd\"\n";
2370     open DTD, ">hd.dtd";
2371     print DTD hd_dtd;
2372     close DTD;
2373   }
2374 }
2375
2376
2377 sub dump_xml_id
2378 {
2379   my ($ent, $id, $i, $tag, $str, $format, $range, $mask);
2380
2381   ($ent, $id) = @_;
2382
2383   $i = $xml_names[$ent];
2384
2385   die "oops: entry $ent not allowed here\n" unless $i;
2386
2387   if($ent == $he_requires) {
2388     if($id->[0] == $flag_string) {
2389       die "oops: strange string data\n" if defined $id->[2];
2390       for $str (split /\|/, $id->[1]) {
2391         $xml->dataElement("requires", $str);
2392       }
2393     }
2394     else {
2395       die "oops: requires _id_???\n"
2396     }
2397   }
2398   else {
2399     $xml->startTag($i);
2400
2401     if($ent == $he_serial) {
2402       if($id->[0] == $flag_string) {
2403         die "oops: strange string data\n" if defined $id->[2];
2404         $xml->characters($id->[1]);
2405       }
2406       else {
2407         die "oops: serial _id_???\n"
2408       }
2409     }
2410     else {
2411       if($id->[0] == $flag_id) {
2412         $tag = $id->[1];
2413         if($tag == $tag_eisa && ($ent == $he_vendor_id || $ent == $he_subvendor_id)) {
2414           $str = eisa_str $id->[2];
2415         }
2416         else {
2417           $format = "0x%04x";
2418           $format = "0x%02x" if $ent == $he_bus_id || $ent == $he_subclass_id || $ent == $he_progif_id;
2419           $format = "0x%03x" if $ent == $he_baseclass_id;
2420           $str = sprintf $format, $id->[2];
2421         }
2422         if(defined $id->[3]) {
2423           if($tag == $tag_eisa && ($ent == $he_vendor_id || $ent == $he_subvendor_id)) {
2424             $range = eisa_str $id->[2] + $id->[3] - 1;
2425           }
2426           else {
2427             $range = sprintf "0x%04x", $id->[2] + $id->[3] - 1;
2428           }
2429         }
2430         elsif(defined $id->[4]) {
2431           $mask = sprintf "0x%04x", $id->[4];
2432         }
2433         $tag = $tag_name[$tag];
2434
2435         if(defined $range) {
2436           if($tag) {
2437             $xml->startTag("idrange", "type" => $tag);
2438           }
2439           else {
2440             $xml->startTag("idrange");
2441           }
2442           $xml->dataElement("first", $str);
2443           $xml->dataElement("last", $range);
2444           $xml->endTag();
2445         }
2446         elsif(defined $mask) {
2447           if($tag) {
2448             $xml->startTag("idmask", "type" => $tag);
2449           }
2450           else {
2451             $xml->startTag("idmask");
2452           }
2453           $xml->dataElement("value", $str);
2454           $xml->dataElement("mask", $mask);
2455           $xml->endTag();
2456         }
2457         else {
2458           if($tag) {
2459             $xml->dataElement("id", $str, "type" => $tag);
2460           }
2461           else {
2462             $xml->dataElement("id", $str);
2463           }
2464         }
2465       }
2466       elsif($id->[0] == $flag_string) {
2467         die "oops: strage string data\n" if defined $id->[2];
2468         $xml->dataElement("name", $id->[1]);
2469       }
2470       else {
2471         die "oops: unknown id flag\n"
2472       }
2473     }
2474
2475     $xml->endTag();
2476   }
2477 }
2478
2479
2480 sub dump_xml_drv
2481 {
2482   my ($id, $str, $i, $j, $k, $type, $drv, $info, @info, $current);
2483
2484   $id = shift;
2485
2486   die "oops: invalid driver data\n" if $id->[0] != $flag_string;
2487
2488   for($i = 1; $i < @{$id}; $i++) {
2489
2490     $xml->startTag('driver');
2491
2492     undef $current;
2493
2494     for $drv (split /\x00/, $id->[$i]) {
2495       $type = substr $drv, 0, 2;
2496       $info = substr $drv, 2;
2497       @info = split /\|/, $info;
2498
2499       if($type eq "i\t") {
2500         $xml->endTag() if $current; $current = $type;
2501         $xml->startTag('module');
2502         for $j (@info) {
2503           $xml->dataElement('insmod', $j);
2504         }
2505       }
2506       elsif($type eq "m\t") {
2507         $xml->endTag() if $current; $current = $type;
2508         $xml->startTag('module');
2509         for $j (@info) {
2510           $xml->dataElement('modprobe', $j);
2511         }
2512       }
2513       elsif($type eq "M\t") {
2514         die "oops: incorrect driver info: $drv\n" unless $current eq "m\t";
2515         $xml->dataElement('modconf', $info);
2516       }
2517       elsif($type eq "a\t") {
2518         $xml->endTag() if $current; $current = undef;;
2519         $xml->dataElement('any', $info);
2520       }
2521       elsif($type eq "d\t") {
2522         $xml->endTag() if $current; $current = undef;
2523         $xml->startTag('display');
2524         if($info[0] =~ /^(\d+)x(\d+)$/) {
2525           ($j, $k) = ($1, $2);
2526           $xml->startTag('resolution');
2527           $xml->dataElement('width', $j);
2528           $xml->dataElement('height', $k);
2529           $xml->endTag('resolution');
2530         }
2531         if($info[1] =~ /^(\d+)-(\d+)$/) {
2532           ($j, $k) = ($1, $2);
2533           $xml->startTag('vsync');
2534           $xml->dataElement('min', $j);
2535           $xml->dataElement('max', $k);
2536           $xml->endTag('vsync');
2537         }
2538         if($info[2] =~ /^(\d+)-(\d+)$/) {
2539           ($j, $k) = ($1, $2);
2540           $xml->startTag('hsync');
2541           $xml->dataElement('min', $j);
2542           $xml->dataElement('max', $k);
2543           $xml->endTag('hsync');
2544         }
2545         if($info[3] =~ /^\d+$/) {
2546           $xml->dataElement('bandwidth', $info[3]);
2547         }
2548         $xml->endTag('display');
2549       }
2550       elsif($type eq "x\t") {
2551         $xml->endTag() if $current; $current = $type;
2552         $xml->startTag('xfree');
2553         if(defined $info[0]) {
2554           $xml->dataElement('version', $info[0]);
2555         }
2556         if($info[1]) {
2557           $xml->dataElement('server', $info[1]);
2558         }
2559         if($info[2]) {
2560           $xml->emptyTag('has3d');
2561         }
2562 #        if($info[3]) {
2563 #          for $j (split /,/, $info[3]) {
2564 #            $xml->dataElement('package', $j);
2565 #          }
2566 #        }
2567         if($info[4]) {
2568           for $j (split /,/, $info[4]) {
2569             $xml->dataElement('extension', $j);
2570           }
2571         }
2572         if($info[5]) {
2573           for $j (split /,/, $info[5]) {
2574             $xml->dataElement('option', $j);
2575           }
2576         }
2577         if($info[6]) {
2578           for $j (split /,/, $info[6]) {
2579             $xml->dataElement('bpp', $j);
2580           }
2581         }
2582         if($info[7] =~ /^\d+$/) {
2583           $xml->dataElement('dacspeed', $info[7]);
2584         }
2585         if($info[8]) {
2586           $xml->dataElement('script', $info[8]);
2587         }
2588       }
2589       elsif($type eq "X\t") {
2590         die "oops: incorrect driver info: $drv\n" unless $current eq "x\t";
2591         $xml->dataElement('xf86conf', $info);
2592       }
2593       elsif($type eq "p\t") {
2594         $xml->endTag() if $current; $current = undef;
2595         $xml->startTag('mouse');
2596         if($info[0]) {
2597           $xml->dataElement('xf86', $info[0]);
2598         }
2599         if($info[1]) {
2600           $xml->dataElement('gpm', $info[1]);
2601         }
2602         if($info[2] ne "") {
2603           $xml->dataElement('buttons', $info[2]);
2604         }
2605         if($info[3] ne "") {
2606           $xml->dataElement('wheels', $info[3]);
2607         }
2608         $xml->endTag('mouse');
2609       }
2610       else {
2611         $xml->endTag() if $current; $current = undef;
2612         # die "oops: unhandled driver info type: $drv\n";
2613       }
2614     }
2615
2616     $xml->endTag() if $current;
2617
2618     $xml->endTag('driver');
2619
2620   }
2621 }
2622
2623
2624 sub dump_xml_ent
2625 {
2626   my ($id, $ent);
2627
2628   $id = shift;
2629
2630   for($ent = 0; $ent < @{$id}; $ent++) {
2631     if(defined $id->[$ent]) {
2632       if($ent != $he_driver) {
2633         dump_xml_id $ent, $id->[$ent];
2634       }
2635       else {
2636         dump_xml_drv $id->[$ent];
2637       }
2638     }
2639   }
2640
2641 }
2642
2643
2644 sub dump_xml_item
2645 {
2646   my ($item, $id);
2647
2648   $item = shift;
2649
2650   $xml->startTag('item');
2651
2652   for $id (@{$item->[1]}) {
2653     $xml->startTag('key');
2654     dump_xml_ent $id;
2655     $xml->endTag('key');
2656   }
2657
2658   dump_xml_ent $item->[2];
2659
2660   $xml->endTag('item');
2661   print $xml_file "\n";
2662 }
2663
2664
2665 sub hd_dtd
2666 {
2667   my $dtd = <<'EOF'
2668 <!-- libhd DTD V0.2 -->
2669
2670 <!ENTITY % keyfields "bus|baseclass|subclass|progif|vendor|device|subvendor|subdevice|revision|serial|driver|requires">
2671 <!ENTITY % idelements "id|idrange|idmask|name">
2672 <!ENTITY % idtypes "none|pci|eisa|usb|pcmcia|special">
2673
2674 <!ELEMENT hwdata (item*)>
2675
2676 <!ELEMENT item (key+,(%keyfields;)*)>
2677
2678 <!ELEMENT key (%keyfields;)+>
2679
2680   <!ELEMENT bus (%idelements;)>
2681   <!ELEMENT baseclass (%idelements;)>
2682   <!ELEMENT subclass (%idelements;)>
2683   <!ELEMENT progif (%idelements;)>
2684   <!ELEMENT vendor (%idelements;)>
2685   <!ELEMENT device (%idelements;)>
2686   <!ELEMENT subvendor (%idelements;)>
2687   <!ELEMENT subdevice (%idelements;)>
2688   <!ELEMENT revision (%idelements;)>
2689   <!ELEMENT serial (#PCDATA)>
2690   <!ELEMENT requires (#PCDATA)>
2691   <!ELEMENT id (#PCDATA)>
2692   <!ELEMENT idrange (first,last)>
2693     <!ELEMENT first (#PCDATA)>
2694     <!ELEMENT last (#PCDATA)>
2695   <!ELEMENT idmask (value,mask)>
2696     <!ELEMENT value (#PCDATA)>
2697     <!ELEMENT mask (#PCDATA)>
2698   <!ATTLIST id type (%idtypes;) "none">
2699   <!ATTLIST idrange type (%idtypes;) "none">
2700   <!ATTLIST idmask type (%idtypes;) "none">
2701   <!ELEMENT name (#PCDATA)>
2702
2703 <!ELEMENT driver (any|display|module|mouse|xfree)?>
2704
2705   <!ELEMENT any (#PCDATA)>
2706
2707   <!ELEMENT display (resolution?,vsync?,hsync?,bandwidth?)>
2708     <!ELEMENT resolution (width,height)>
2709       <!ELEMENT width (#PCDATA)>
2710       <!ELEMENT height (#PCDATA)>
2711     <!ELEMENT vsync (min,max)>
2712     <!ELEMENT hsync (min,max)>
2713       <!ELEMENT min (#PCDATA)>
2714       <!ELEMENT max (#PCDATA)>
2715     <!ELEMENT bandwidth (#PCDATA)>
2716
2717   <!ELEMENT module (insmod+|(modprobe+,modconf*))>
2718     <!ELEMENT insmod (#PCDATA)>
2719     <!ELEMENT modprobe (#PCDATA)>
2720     <!ELEMENT modconf (#PCDATA)>
2721
2722   <!ELEMENT mouse (xf86?,gpm?,buttons?,wheels?)>
2723     <!ELEMENT xf86 (#PCDATA)>
2724     <!ELEMENT gpm (#PCDATA)>
2725     <!ELEMENT buttons (#PCDATA)>
2726     <!ELEMENT wheels (#PCDATA)>
2727
2728   <!ELEMENT xfree (version,server?,has3d?,extension*,option*,bpp*,dacspeed?,script?,xf86conf*)>
2729     <!ELEMENT version (#PCDATA)>
2730     <!ELEMENT server (#PCDATA)>
2731     <!ELEMENT has3d EMPTY>
2732     <!ELEMENT extension (#PCDATA)>
2733     <!ELEMENT option (#PCDATA)>
2734     <!ELEMENT bpp (#PCDATA)>
2735     <!ELEMENT dacspeed (#PCDATA)>
2736     <!ELEMENT script (#PCDATA)>
2737     <!ELEMENT xf86conf (#PCDATA)>
2738 EOF
2739 ;
2740
2741   return $dtd;
2742 }
2743
2744
2745 sub hd_dtd_internal
2746 {
2747   my $dtd = <<'EOF'
2748 <!DOCTYPE hwdata [
2749   <!ELEMENT hwdata (item*)>
2750   <!ELEMENT item (key+,(bus|baseclass|subclass|progif|vendor|device|subvendor|subdevice|revision|serial|driver|requires)*)>
2751     <!ELEMENT key (bus|baseclass|subclass|progif|vendor|device|subvendor|subdevice|revision|serial|driver|requires)+>
2752       <!ELEMENT bus (id|idrange|idmask|name)>
2753       <!ELEMENT baseclass (id|idrange|idmask|name)>
2754       <!ELEMENT subclass (id|idrange|idmask|name)>
2755       <!ELEMENT progif (id|idrange|idmask|name)>
2756       <!ELEMENT vendor (id|idrange|idmask|name)>
2757       <!ELEMENT device (id|idrange|idmask|name)>
2758       <!ELEMENT subvendor (id|idrange|idmask|name)>
2759       <!ELEMENT subdevice (id|idrange|idmask|name)>
2760       <!ELEMENT revision (id|idrange|idmask|name)>
2761       <!ELEMENT serial (#PCDATA)>
2762       <!ELEMENT requires (#PCDATA)>
2763       <!ELEMENT id (#PCDATA)>
2764       <!ELEMENT idrange (first,last)>
2765         <!ELEMENT first (#PCDATA)>
2766         <!ELEMENT last (#PCDATA)>
2767       <!ELEMENT idmask (value,mask)>
2768         <!ELEMENT value (#PCDATA)>
2769         <!ELEMENT mask (#PCDATA)>
2770       <!ATTLIST id type (none|pci|eisa|usb|pcmcia|special) "none">
2771       <!ATTLIST idrange type (none|pci|eisa|usb|special) "none">
2772       <!ATTLIST idmask type (none|pci|eisa|usb|special) "none">
2773     <!ELEMENT name (#PCDATA)>
2774     <!ELEMENT driver (any|display|module|mouse|xfree)?>
2775       <!ELEMENT any (#PCDATA)>
2776       <!ELEMENT display (resolution?,vsync?,hsync?,bandwidth?)>
2777         <!ELEMENT resolution (width,height)>
2778           <!ELEMENT width (#PCDATA)>
2779           <!ELEMENT height (#PCDATA)>
2780         <!ELEMENT vsync (min,max)>
2781         <!ELEMENT hsync (min,max)>
2782           <!ELEMENT min (#PCDATA)>
2783           <!ELEMENT max (#PCDATA)>
2784         <!ELEMENT bandwidth (#PCDATA)>
2785       <!ELEMENT module (insmod+|(modprobe+,modconf*))>
2786         <!ELEMENT insmod (#PCDATA)>
2787         <!ELEMENT modprobe (#PCDATA)>
2788         <!ELEMENT modconf (#PCDATA)>
2789       <!ELEMENT mouse (xf86?,gpm?,buttons?,wheels?)>
2790         <!ELEMENT xf86 (#PCDATA)>
2791         <!ELEMENT gpm (#PCDATA)>
2792         <!ELEMENT buttons (#PCDATA)>
2793         <!ELEMENT wheels (#PCDATA)>
2794       <!ELEMENT xfree (version,server?,has3d?,extension*,option*,bpp*,dacspeed?,script?,xf86conf*)>
2795         <!ELEMENT version (#PCDATA)>
2796         <!ELEMENT server (#PCDATA)>
2797         <!ELEMENT has3d EMPTY>
2798         <!ELEMENT extension (#PCDATA)>
2799         <!ELEMENT option (#PCDATA)>
2800         <!ELEMENT bpp (#PCDATA)>
2801         <!ELEMENT dacspeed (#PCDATA)>
2802         <!ELEMENT script (#PCDATA)>
2803         <!ELEMENT xf86conf (#PCDATA)>
2804 ]> 
2805 EOF
2806 ;
2807
2808   return $dtd;
2809 }
2810