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