278
|
|
|
|
|
|
|
$section = lc($1); |
279
|
|
|
|
|
|
|
$self->{'correct_case'}{$section} = $1; |
280
|
|
|
|
|
|
|
# remember which sections are tables |
281
|
|
|
|
|
|
|
$self->{'tables'}{$1} = 1; |
282
|
|
|
|
|
|
|
} elsif(/^<\/msi>/) { |
283
|
|
|
|
|
|
|
$section = 'trailer'; |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
if($section ne $lastsection) { |
287
|
|
|
|
|
|
|
$lastsection = $section; |
288
|
|
|
|
|
|
|
push(@{$self->{'order'}}, $section); |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# remember what tables each foreign key appears in |
292
|
|
|
|
|
|
|
if(/^\s*]+>([^<]+)) { |
293
|
|
|
|
|
|
|
my $colname = $1; |
294
|
|
|
|
|
|
|
if($colname =~ /_$/) { |
295
|
|
|
|
|
|
|
unless(exists($self->{'foreign_keys'}{$colname})) { |
296
|
|
|
|
|
|
|
$self->{'foreign_keys'}{$colname} = []; |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
push(@{$self->{'foreign_keys'}{$colname}}, $section); |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
push(@{$self->{'sections'}{$section}}, $_); |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# find the file encoding |
306
|
|
|
|
|
|
|
foreach my $line (@{$self->{sections}->{header}}) { |
307
|
|
|
|
|
|
|
if($line =~ /\sencoding="([^"]+)"/) { |
308
|
|
|
|
|
|
|
$self->{'encoding'} = $1; |
309
|
|
|
|
|
|
|
last; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
return 1; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=item I |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
$is->savefile( ); |
319
|
|
|
|
|
|
|
$is->savefile( $filename ); |
320
|
|
|
|
|
|
|
$is->savefile( $io_file_handle ); |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
Stores the ism data in a file. Can be called |
323
|
|
|
|
|
|
|
with either a filename or an IO::File object that is |
324
|
|
|
|
|
|
|
opened in write ("w") mode. If no argument is passed, |
325
|
|
|
|
|
|
|
and the last load was via a filename, savefile will |
326
|
|
|
|
|
|
|
default to the filename previously supplied. |
327
|
|
|
|
|
|
|
Returns 1 on success, 0 on failure. |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=cut |
330
|
|
|
|
|
|
|
sub savefile { |
331
|
|
|
|
|
|
|
my ($self, $file) = @_; |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
unless(defined($file)) { |
334
|
|
|
|
|
|
|
if(defined($self->{'filename'})) { |
335
|
|
|
|
|
|
|
$file = $self->{'filename'}; |
336
|
|
|
|
|
|
|
} else { |
337
|
|
|
|
|
|
|
carp("You must provide a filename to save to"); |
338
|
|
|
|
|
|
|
return 0; |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
my ($fh, $i_opened_file) = _openfile($file, "w"); |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
unless(defined($fh)) { |
345
|
|
|
|
|
|
|
return 0; |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
print $fh $self->save(); |
349
|
|
|
|
|
|
|
$fh->close() if($i_opened_file); |
350
|
|
|
|
|
|
|
return 1; |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=item I |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
$is->save(); |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Returns the ism data as a string. |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
=cut |
360
|
|
|
|
|
|
|
sub save { |
361
|
|
|
|
|
|
|
my ($self) = @_; |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
my $encoding = $self->{'encoding'}; |
364
|
|
|
|
|
|
|
my $has_encoding = defined($encoding); |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
my $text = ''; |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
foreach my $section (@{$self->{'order'}}) { |
369
|
|
|
|
|
|
|
if($self->{'parsed'}{$section}) { |
370
|
|
|
|
|
|
|
# the table has been (possibly) modified, so rebuild it |
371
|
|
|
|
|
|
|
if($section eq 'summary') { |
372
|
|
|
|
|
|
|
$text .= ($has_encoding) ? |
373
|
|
|
|
|
|
|
encode($encoding, $self->_save_summary) : |
374
|
|
|
|
|
|
|
$self->_save_summary; |
375
|
|
|
|
|
|
|
} else { |
376
|
|
|
|
|
|
|
$text .= ($has_encoding) ? |
377
|
|
|
|
|
|
|
encode($encoding, $self->_save_table($section)) : |
378
|
|
|
|
|
|
|
$self->_save_table($section); |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
} else { |
382
|
|
|
|
|
|
|
# when the last table gets modified, we end up with an |
383
|
|
|
|
|
|
|
# extra newline |
384
|
|
|
|
|
|
|
if($section eq 'trailer') { |
385
|
|
|
|
|
|
|
$text =~ s/\n\n$/\n/; |
386
|
|
|
|
|
|
|
} |
387
|
|
|
|
|
|
|
# section wasn't touched, just spit out the stored text |
388
|
|
|
|
|
|
|
$text .= join("\n", @{$self->{'sections'}{$section}}) . "\n"; |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
return $text; |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
sub _save_summary { |
395
|
|
|
|
|
|
|
my ($self) = @_; |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
my $count = 0; |
398
|
|
|
|
|
|
|
my %order = |
399
|
|
|
|
|
|
|
map { $_ => $count++ } |
400
|
|
|
|
|
|
|
$self->summary_fields; |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
my $summary = $self->{'parsed'}{'summary'}; |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
my $text = "\t\n"; |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
foreach my $field (sort { $order{$a} <=> $order{$b} } keys %order) { |
407
|
|
|
|
|
|
|
if(exists($summary->{$field})) { |
408
|
|
|
|
|
|
|
if(!defined($summary->{$field}) || $summary->{$field} eq '') { |
409
|
|
|
|
|
|
|
$text .= "\t\t<$field/>\n"; |
410
|
|
|
|
|
|
|
} else { |
411
|
|
|
|
|
|
|
$text .= "\t\t<$field>" . $summary->{$field} . "$field>\n"; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
$text .= "\t\n\t\n"; |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
return $text; |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# internal function. formats the data in a table that has |
422
|
|
|
|
|
|
|
# been modified back to the appropriate output format |
423
|
|
|
|
|
|
|
sub _save_table { |
424
|
|
|
|
|
|
|
my ($self, $table) = @_; |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
427
|
|
|
|
|
|
|
my $text = "\t
428
|
|
|
|
|
|
|
foreach my $key (sort keys %{$p->{'attributes'}}) { |
429
|
|
|
|
|
|
|
$text .= " $key=\"$p->{'attributes'}{$key}\""; |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
$text .= ">\n"; |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
foreach my $col (@{$p->{'columns'}}) { |
434
|
|
|
|
|
|
|
$text .= "\t\t
|
435
|
|
|
|
|
|
|
if($col->{'is_key'}) { |
436
|
|
|
|
|
|
|
$text .= ' key="yes"'; |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
$text .= ' def="' . $col->{'type'} . $col->{'width'} . '"'; |
439
|
|
|
|
|
|
|
$text .= ">$col->{'name'}\n"; |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
foreach my $key (sort keys %{$p->{'data'}}) { |
442
|
|
|
|
|
|
|
my $row = $p->{'data'}{$key}; |
443
|
|
|
|
|
|
|
$text .= "\t\t"; |
444
|
|
|
|
|
|
|
foreach my $col (@$row) { |
445
|
|
|
|
|
|
|
if(defined($col) and length($col) > 0) { |
446
|
|
|
|
|
|
|
$text .= " | " . _xml_escape($col) . " | ";
447
|
|
|
|
|
|
|
} else { |
448
|
|
|
|
|
|
|
$text .= " | | ";
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
$text .= "\n"; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
$text .= "\t | \n\n"; |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
return $text; |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# internal function. parses the text of an IS table |
460
|
|
|
|
|
|
|
# so that it can be easily manipulated |
461
|
|
|
|
|
|
|
sub _parse_table { |
462
|
|
|
|
|
|
|
my ($self, $table) = @_; |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
$table = lc($table); |
465
|
|
|
|
|
|
|
return if($self->{'parsed'}{$table}); |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
my $text = $self->{'sections'}{$table}; |
468
|
|
|
|
|
|
|
unless(defined($text)) { |
469
|
|
|
|
|
|
|
carp("No such table $table"); |
470
|
|
|
|
|
|
|
return; |
471
|
|
|
|
|
|
|
} |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
my @cols; |
474
|
|
|
|
|
|
|
my %data; |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
my $xml = join("\n", @$text); |
477
|
|
|
|
|
|
|
my @parsed = @{$self->{'parser'}->parse($xml)->[1]}; |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
my $attributes = shift @parsed; |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
while(@parsed) { |
482
|
|
|
|
|
|
|
my $type = shift @parsed; |
483
|
|
|
|
|
|
|
if($type eq 'col') { |
484
|
|
|
|
|
|
|
my $columns = shift @parsed; |
485
|
|
|
|
|
|
|
my $column_name = $columns->[2]; |
486
|
|
|
|
|
|
|
my $is_key = ( defined($columns->[0]{'key'}) and $columns->[0]{'key'} eq 'yes' ); |
487
|
|
|
|
|
|
|
my ($type, $width) = ($columns->[0]{'def'} =~ /(\w)(\d+)/); |
488
|
|
|
|
|
|
|
push(@cols, { |
489
|
|
|
|
|
|
|
name => $column_name, |
490
|
|
|
|
|
|
|
is_key => $is_key, |
491
|
|
|
|
|
|
|
type => $type, |
492
|
|
|
|
|
|
|
width => $width, |
493
|
|
|
|
|
|
|
}); |
494
|
|
|
|
|
|
|
} elsif($type eq 'row') { |
495
|
|
|
|
|
|
|
my $columns = shift @parsed; |
496
|
|
|
|
|
|
|
my @row; |
497
|
|
|
|
|
|
|
my $lookup_key = ''; |
498
|
|
|
|
|
|
|
foreach my $i (0..$#cols) { |
499
|
|
|
|
|
|
|
my $value = $columns->[ ($i+1)*2 ][2]; |
500
|
|
|
|
|
|
|
$row[$i] = $value; |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
if($cols[$i]{'is_key'}) { |
503
|
|
|
|
|
|
|
my $key_value = $value; |
504
|
|
|
|
|
|
|
unless(defined($key_value)) { $key_value = ''; } |
505
|
|
|
|
|
|
|
$lookup_key .= sprintf("%-" . $cols[$i]{'width'} . "s", $key_value) |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
$data{ $lookup_key } = \@row; |
509
|
|
|
|
|
|
|
} else { |
510
|
|
|
|
|
|
|
# ignore text |
511
|
|
|
|
|
|
|
shift @parsed; |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
$self->{'parsed'}{$table} = { |
516
|
|
|
|
|
|
|
attributes => $attributes, |
517
|
|
|
|
|
|
|
columns => \@cols, |
518
|
|
|
|
|
|
|
data => \%data, |
519
|
|
|
|
|
|
|
}; |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
sub _parsed { |
523
|
|
|
|
|
|
|
my ($self, $table) = @_; |
524
|
|
|
|
|
|
|
$table = lc($table); |
525
|
|
|
|
|
|
|
unless(exists($self->{'parsed'}{$table})) { |
526
|
|
|
|
|
|
|
$self->_parse_table($table); |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
return $self->{'parsed'}{$table}; |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
=item I |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
my $tables = $is->tables(); |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
Returns an arrayref containing a list of all the tables |
536
|
|
|
|
|
|
|
that were found in the ISM file. |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=cut |
539
|
|
|
|
|
|
|
sub tables { |
540
|
|
|
|
|
|
|
my ($self) = @_; |
541
|
|
|
|
|
|
|
return [ sort keys %{$self->{'tables'}} ]; |
542
|
|
|
|
|
|
|
} |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
=item I |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
if($is->has_table( 'ModuleSignature' ) { |
547
|
|
|
|
|
|
|
print "This is a merge module\n"; |
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
Returns true if a table exists with the supplied name, false otherwise. |
551
|
|
|
|
|
|
|
Table names are case-insensitive. |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=cut |
554
|
|
|
|
|
|
|
sub has_table { |
555
|
|
|
|
|
|
|
my ($self, $table) = @_; |
556
|
|
|
|
|
|
|
return exists($self->{'sections'}{lc($table)}); |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
=item I |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
my $is_key = $is->column_is_key( $table, $column_name ); |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
Returns true if the column is a key column, false |
564
|
|
|
|
|
|
|
other wise. Returns undef if the column doesn't exist. |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=cut |
567
|
|
|
|
|
|
|
sub column_is_key { |
568
|
|
|
|
|
|
|
my ($self, $table, $column) = @_; |
569
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
570
|
|
|
|
|
|
|
foreach my $col (@{$p->{'columns'}}) { |
571
|
|
|
|
|
|
|
if($col->{'name'} eq $column) { |
572
|
|
|
|
|
|
|
return $col->{'is_key'}; |
573
|
|
|
|
|
|
|
} |
574
|
|
|
|
|
|
|
} |
575
|
|
|
|
|
|
|
return; |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=item I |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
my $width = $is->column_width( $table, $column_name ); |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
Returns the width of the named column. Returns undef if |
583
|
|
|
|
|
|
|
the column doesn't exist. |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=cut |
586
|
|
|
|
|
|
|
sub column_width { |
587
|
|
|
|
|
|
|
my ($self, $table, $column) = @_; |
588
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
589
|
|
|
|
|
|
|
return $p->{'columns'}{$column}{'width'}; |
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=item I |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
my $type = $is->column_type( $table, $column_name ); |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
Returns the type of the named column. Returns undef if the |
597
|
|
|
|
|
|
|
column doesn't exist. |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=cut |
600
|
|
|
|
|
|
|
sub column_type { |
601
|
|
|
|
|
|
|
my ($self, $table, $column) = @_; |
602
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
603
|
|
|
|
|
|
|
return $p->{'columns'}{$column}{'type'}; |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
=item I |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
my $columns = $is->columns( $table ); |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
Returns an array ref containing the names of the columns |
611
|
|
|
|
|
|
|
in the given table. |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=cut |
614
|
|
|
|
|
|
|
sub columns { |
615
|
|
|
|
|
|
|
my ($self, $table) = @_; |
616
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
617
|
|
|
|
|
|
|
my @cols; |
618
|
|
|
|
|
|
|
foreach my $col (@{$p->{'columns'}}) { |
619
|
|
|
|
|
|
|
push(@cols, $col->{'name'}); |
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
return \@cols; |
622
|
|
|
|
|
|
|
} |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
=item I |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
my $key_columns = $is->key_columns( $table ); |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
Returns an array ref containing the names of the |
629
|
|
|
|
|
|
|
key columns in the given table. |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
=cut |
632
|
|
|
|
|
|
|
sub key_columns { |
633
|
|
|
|
|
|
|
my ($self, $table) = @_; |
634
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
635
|
|
|
|
|
|
|
my @keys; |
636
|
|
|
|
|
|
|
foreach my $col (@{$p->{'columns'}}) { |
637
|
|
|
|
|
|
|
if($col->{'is_key'}) { |
638
|
|
|
|
|
|
|
push(@keys, $col->{'name'}); |
639
|
|
|
|
|
|
|
} |
640
|
|
|
|
|
|
|
} |
641
|
|
|
|
|
|
|
return \@keys; |
642
|
|
|
|
|
|
|
} |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
sub _find_row { |
645
|
|
|
|
|
|
|
my ($self, $table, $rowdata) = @_; |
646
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
my $lookup_key = $self->_build_key( $table, $rowdata ); |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
if(exists($p->{'data'}{$lookup_key})) { |
651
|
|
|
|
|
|
|
return $lookup_key; |
652
|
|
|
|
|
|
|
} else { |
653
|
|
|
|
|
|
|
return; |
654
|
|
|
|
|
|
|
} |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
sub _search_row { |
658
|
|
|
|
|
|
|
my ($self, $table, $rowdata) = @_; |
659
|
|
|
|
|
|
|
my @results; |
660
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
661
|
|
|
|
|
|
|
foreach my $row (values %{$p->{'data'}}) { |
662
|
|
|
|
|
|
|
my $match = 1; |
663
|
|
|
|
|
|
|
foreach my $i (0..$#{$rowdata}) { |
664
|
|
|
|
|
|
|
# undef means they don't care about this column |
665
|
|
|
|
|
|
|
if(defined($rowdata->[$i])) { |
666
|
|
|
|
|
|
|
# empty string from the user matches undef in the data |
667
|
|
|
|
|
|
|
if(defined($row->[$i])) { |
668
|
|
|
|
|
|
|
if(ref($rowdata->[$i]) eq 'Regexp') { |
669
|
|
|
|
|
|
|
if($row->[$i] !~ /$rowdata->[$i]/) { |
670
|
|
|
|
|
|
|
$match = 0; |
671
|
|
|
|
|
|
|
last; |
672
|
|
|
|
|
|
|
} |
673
|
|
|
|
|
|
|
} elsif($rowdata->[$i] ne $row->[$i]) { |
674
|
|
|
|
|
|
|
$match = 0; |
675
|
|
|
|
|
|
|
last; |
676
|
|
|
|
|
|
|
} |
677
|
|
|
|
|
|
|
} elsif($rowdata->[$i] ne '') { |
678
|
|
|
|
|
|
|
$match = 0; |
679
|
|
|
|
|
|
|
last; |
680
|
|
|
|
|
|
|
} |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
} |
683
|
|
|
|
|
|
|
if($match) { |
684
|
|
|
|
|
|
|
push(@results, $row); |
685
|
|
|
|
|
|
|
} |
686
|
|
|
|
|
|
|
} |
687
|
|
|
|
|
|
|
return \@results; |
688
|
|
|
|
|
|
|
} |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
# the lookup key is just the primary key columns concatenated together, |
691
|
|
|
|
|
|
|
# with padding to the full column length. this function builds the key |
692
|
|
|
|
|
|
|
# given the column values |
693
|
|
|
|
|
|
|
sub _build_key { |
694
|
|
|
|
|
|
|
my ($self, $table, $values) = @_; |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
697
|
|
|
|
|
|
|
my $lookup_key = ''; |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
# build the lookup key by concatenating the key columns |
700
|
|
|
|
|
|
|
foreach my $i (0..$#{$p->{'columns'}}) { |
701
|
|
|
|
|
|
|
if($p->{'columns'}[$i]{'is_key'}) { |
702
|
|
|
|
|
|
|
my $width = $p->{'columns'}[$i]{'width'}; |
703
|
|
|
|
|
|
|
$lookup_key .= sprintf("%-${width}s", |
704
|
|
|
|
|
|
|
(defined($values->[$i])) ? $values->[$i] : ''); |
705
|
|
|
|
|
|
|
} |
706
|
|
|
|
|
|
|
} |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
return $lookup_key; |
709
|
|
|
|
|
|
|
} |
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
# takes the various formats allowed for specifying row data, |
712
|
|
|
|
|
|
|
# and returns a consistent structure to be used by other methods. |
713
|
|
|
|
|
|
|
# also fills in any missing columns with undef |
714
|
|
|
|
|
|
|
sub _reformat_args { |
715
|
|
|
|
|
|
|
my ($self, $table, @args) = @_; |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
718
|
|
|
|
|
|
|
my $row = []; |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
if(ref($args[0]) eq 'ARRAY') { |
721
|
|
|
|
|
|
|
$row = $args[0]; |
722
|
|
|
|
|
|
|
} elsif(ref($args[0]) eq 'HASH') { |
723
|
|
|
|
|
|
|
my $h = $args[0]; |
724
|
|
|
|
|
|
|
foreach my $col (@{$p->{'columns'}}) { |
725
|
|
|
|
|
|
|
push(@$row, $h->{ $col->{'name'} }); |
726
|
|
|
|
|
|
|
} |
727
|
|
|
|
|
|
|
} else { |
728
|
|
|
|
|
|
|
$row = \@args; |
729
|
|
|
|
|
|
|
} |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
# if the user left columns off the end, fill them |
732
|
|
|
|
|
|
|
# with undef |
733
|
|
|
|
|
|
|
my $missing_columns = $#{$p->{'columns'}} - $#{$row}; |
734
|
|
|
|
|
|
|
if($missing_columns > 0) { |
735
|
|
|
|
|
|
|
for( 1..$missing_columns ) { |
736
|
|
|
|
|
|
|
push(@{$row}, undef); |
737
|
|
|
|
|
|
|
} |
738
|
|
|
|
|
|
|
} |
739
|
|
|
|
|
|
|
return $row; |
740
|
|
|
|
|
|
|
} |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
sub _check_args { |
743
|
|
|
|
|
|
|
my ($self, $table, @args) = @_; |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
746
|
|
|
|
|
|
|
my $row = $self->_reformat_args($table, @args); |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
unless( $#{$row} eq $#{$p->{'columns'}} ) { |
749
|
|
|
|
|
|
|
carp("Wrong number of columns supplied for table $table"); |
750
|
|
|
|
|
|
|
return; |
751
|
|
|
|
|
|
|
} |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
foreach my $i (0..$#{$row}) { |
754
|
|
|
|
|
|
|
next unless(defined($row->[$i])); |
755
|
|
|
|
|
|
|
my $type = $p->{'columns'}[$i]{'type'}; |
756
|
|
|
|
|
|
|
if($type =~ /^i$/i) { |
757
|
|
|
|
|
|
|
if($row->[$i] =~ /[^\d-]/) { |
758
|
|
|
|
|
|
|
croak("Value in $p->{'columns'}[$i]{'name'} column must be numeric"); |
759
|
|
|
|
|
|
|
return; |
760
|
|
|
|
|
|
|
} |
761
|
|
|
|
|
|
|
} else { |
762
|
|
|
|
|
|
|
my $width = $p->{'columns'}[$i]{'width'}; |
763
|
|
|
|
|
|
|
if($width > 0 and length($row->[$i]) > $width) { |
764
|
|
|
|
|
|
|
croak("Value in $p->{'columns'}[$i]{'name'} column is too long"); |
765
|
|
|
|
|
|
|
return; |
766
|
|
|
|
|
|
|
} |
767
|
|
|
|
|
|
|
} |
768
|
|
|
|
|
|
|
} |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
return $row; |
771
|
|
|
|
|
|
|
} |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
=item I |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
my $version = $is->property('ProductVersion'); |
776
|
|
|
|
|
|
|
my $success = $is->property('ProductVersion', $version); |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
Gets or sets the value associated with a property. If a value is |
779
|
|
|
|
|
|
|
supplied, it will attempt to update the property and return 1 |
780
|
|
|
|
|
|
|
on success and 0 on failure. undef is returned if the property does not exist. |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
=cut |
783
|
|
|
|
|
|
|
sub property { |
784
|
|
|
|
|
|
|
my ($self, $property, $value) = @_; |
785
|
|
|
|
|
|
|
unless(defined($self->getHash_Property({ Property=>$property }))) { |
786
|
|
|
|
|
|
|
return; |
787
|
|
|
|
|
|
|
} |
788
|
|
|
|
|
|
|
if(defined($value)) { |
789
|
|
|
|
|
|
|
$self->update_Property({ Property=>$property, Value=>$value }); |
790
|
|
|
|
|
|
|
} |
791
|
|
|
|
|
|
|
return $self->getHash_Property({ Property=>$property }); |
792
|
|
|
|
|
|
|
} |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
=item I |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
my $summary_value = $is->summary( $summary_field ); |
797
|
|
|
|
|
|
|
my $success = $is->summary( $summary_field, $value ); |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
my $summary_table = $is->summary; |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
Gets or sets the value associated with a field in the summary table. |
802
|
|
|
|
|
|
|
If no field name is provided, a reference to a hash containing all |
803
|
|
|
|
|
|
|
of the summary field/value pairs. |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
=cut |
806
|
|
|
|
|
|
|
sub summary { |
807
|
|
|
|
|
|
|
my ($self, $field, $value) = @_; |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
unless(exists($self->{'parsed'}{'summary'})) { |
810
|
|
|
|
|
|
|
$self->_parse_summary; |
811
|
|
|
|
|
|
|
} |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
my $data = $self->{'parsed'}{'summary'}; |
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
return $data unless(defined($field)); |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
if(defined($value)) { |
818
|
|
|
|
|
|
|
# make sure this summary field is allowed by the DTD |
819
|
|
|
|
|
|
|
return 0 unless($self->valid_summary_field($field)); |
820
|
|
|
|
|
|
|
$data->{$field} = $value; |
821
|
|
|
|
|
|
|
return 1; |
822
|
|
|
|
|
|
|
} else { |
823
|
|
|
|
|
|
|
return $data->{$field}; |
824
|
|
|
|
|
|
|
} |
825
|
|
|
|
|
|
|
} |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
=item I |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
my @field_names = $is->summary_fields; |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
Returns a list of the valid fields for the summary table, as they appear |
832
|
|
|
|
|
|
|
in the DTD embedded in the ISM file. |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
=cut |
835
|
|
|
|
|
|
|
sub summary_fields { |
836
|
|
|
|
|
|
|
my ($self) = @_; |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
$self->_parse_summary unless(defined($self->{'parsed'}{'summary'})); |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
return @{$self->{'summary_fields'}}; |
841
|
|
|
|
|
|
|
} |
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
=item I |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
my $is_valid = $is->valid_summary_field( $field_name ); |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
Returns 1 if the field $field_name is valid according to the DTD |
848
|
|
|
|
|
|
|
in the ISM file, 0 otherwise. |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
=cut |
851
|
|
|
|
|
|
|
sub valid_summary_field { |
852
|
|
|
|
|
|
|
my ($self, $field) = @_; |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
return 0 unless(defined($field)); |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
foreach my $valid_field ($self->summary_fields) { |
857
|
|
|
|
|
|
|
return 1 if($field eq $valid_field); |
858
|
|
|
|
|
|
|
} |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
return 0; |
861
|
|
|
|
|
|
|
} |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
# get the list of valid summary fields from the DTD |
864
|
|
|
|
|
|
|
sub _parse_summary_fields { |
865
|
|
|
|
|
|
|
my ($self) = @_; |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
my $text = join('', @{$self->{'sections'}{'dtd'}}); |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
my ($summary_fields_text) = $text =~ /
|
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
$summary_fields_text =~ s/[\?\s]//g; |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
my @summary_fields = split(',', $summary_fields_text); |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
$self->{'summary_fields'} = \@summary_fields; |
876
|
|
|
|
|
|
|
} |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
# turn the XML for the summary table into something we can manipulate easily |
879
|
|
|
|
|
|
|
sub _parse_summary { |
880
|
|
|
|
|
|
|
my ($self) = @_; |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
$self->_parse_summary_fields; |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
my $text = $self->{'sections'}{'summary'}; |
885
|
|
|
|
|
|
|
unless(defined($text)) { |
886
|
|
|
|
|
|
|
carp("No summary found"); |
887
|
|
|
|
|
|
|
return; |
888
|
|
|
|
|
|
|
} |
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
my %data; |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
my $xml = join("\n", @$text); |
893
|
|
|
|
|
|
|
my @parsed = @{$self->{'parser'}->parse($xml)->[1]}; |
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
my $attributes = shift @parsed; |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
while(@parsed) { |
898
|
|
|
|
|
|
|
my $type = shift @parsed; |
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
# ignore text |
901
|
|
|
|
|
|
|
if($type eq '0') { |
902
|
|
|
|
|
|
|
shift @parsed; |
903
|
|
|
|
|
|
|
next; |
904
|
|
|
|
|
|
|
} |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
my $value = shift @parsed; |
907
|
|
|
|
|
|
|
$data{ $type } = $value->[2]; |
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
} |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
$self->{'parsed'}{'summary'} = \%data; |
912
|
|
|
|
|
|
|
} |
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
=item I |
915
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
my $components = $is->featureComponents( $feature ); |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
Returns an arrayref of the components in the named feature. Returns |
919
|
|
|
|
|
|
|
undef if the feature does not exist. |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
=cut |
922
|
|
|
|
|
|
|
sub featureComponents { |
923
|
|
|
|
|
|
|
my ($self, $feature) = @_; |
924
|
|
|
|
|
|
|
my $list = $self->searchHash_FeatureComponents({ Feature_=>$feature }); |
925
|
|
|
|
|
|
|
unless(@{$list}) { |
926
|
|
|
|
|
|
|
return; |
927
|
|
|
|
|
|
|
} |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
my @components = sort map { $_->{'Component_'} } @{$list}; |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
return \@components; |
932
|
|
|
|
|
|
|
} |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
=back |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
=head1 COMPONENT ATTRIBUTES |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
All of the attribute methods can accept an attribute as either |
940
|
|
|
|
|
|
|
a name or a value. The name can be prefixed with msidbComponentAttributes |
941
|
|
|
|
|
|
|
as it is in the MSI documentation, but it is not required. |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
Valid attributes: |
944
|
|
|
|
|
|
|
LocalOnly 0 |
945
|
|
|
|
|
|
|
SourceOnly 1 |
946
|
|
|
|
|
|
|
Optional 2 |
947
|
|
|
|
|
|
|
RegistryKeyPath 4 |
948
|
|
|
|
|
|
|
SharedDllRefCount 8 |
949
|
|
|
|
|
|
|
Permanent 16 |
950
|
|
|
|
|
|
|
ODBCDataSource 32 |
951
|
|
|
|
|
|
|
Transitive 64 |
952
|
|
|
|
|
|
|
NeverOverwrite 128 |
953
|
|
|
|
|
|
|
64bit 256 |
954
|
|
|
|
|
|
|
DisableRegistryReflection 512 |
955
|
|
|
|
|
|
|
UninstallOnSupersedence 1024 |
956
|
|
|
|
|
|
|
AttributesShared 2048 |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
=over 4 |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
=item I |
961
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
my $success = $is->set_component_attribute( $component_name, '64bit', 1 ); |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
Update the value of a single component attribute flag. Returns 1 on success, |
965
|
|
|
|
|
|
|
0 on failure. |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
=cut |
968
|
|
|
|
|
|
|
sub set_component_attribute { |
969
|
|
|
|
|
|
|
my ($self, $component_name, $attribute, $bit_on) = @_; |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
my $attr_num = $self->get_component_attribute_value( $attribute ); |
972
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
return 0 unless(defined($attr_num)); |
974
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
if($attr_num == 0) { |
976
|
|
|
|
|
|
|
$attr_num = 1; |
977
|
|
|
|
|
|
|
$bit_on = !$bit_on; |
978
|
|
|
|
|
|
|
} |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
my $component = $self->getHash_Component($component_name); |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
return 0 unless(defined($component)); |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
if($bit_on) { |
985
|
|
|
|
|
|
|
$component->{'Attributes'} |= $attr_num; |
986
|
|
|
|
|
|
|
} else { |
987
|
|
|
|
|
|
|
my $inverted_attr_num = ~$attr_num; |
988
|
|
|
|
|
|
|
$component->{'Attributes'} &= $inverted_attr_num; |
989
|
|
|
|
|
|
|
} |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
return $self->update_component($component); |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
} |
994
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
=item I |
996
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
my $is_64bit = $is->get_component_attribute( $component_name, '64bit' ); |
998
|
|
|
|
|
|
|
my $is_shared = $is->get_component_attribute( $component_name, 8 ); |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
Returns 1 if the named component has the given attribute set, 0 otherwise. |
1001
|
|
|
|
|
|
|
Returns undef if the component does not exist, or the attribute is invalid. |
1002
|
|
|
|
|
|
|
The attribute name or value can be used. |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
=cut |
1005
|
|
|
|
|
|
|
sub get_component_attribute { |
1006
|
|
|
|
|
|
|
my ($self, $component_name, $attribute) = @_; |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
my $invert = 0; |
1009
|
|
|
|
|
|
|
my $attr_num = $self->get_component_attribute_value( $attribute ); |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
return unless(defined($attr_num)); |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
# for some reason, the docs have bit 1 listed twice, once for |
1014
|
|
|
|
|
|
|
# on and once for off (as hex value 0x0) |
1015
|
|
|
|
|
|
|
if($attr_num == 0) { |
1016
|
|
|
|
|
|
|
$attr_num = 1; |
1017
|
|
|
|
|
|
|
$invert = 1; |
1018
|
|
|
|
|
|
|
} |
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
my $component = $self->getHash_Component($component_name); |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
# must find exactly one component with this name |
1023
|
|
|
|
|
|
|
return unless(defined($component)); |
1024
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
my $set = ($component->{'Attributes'} & $attr_num) ? 1 : 0; |
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
$set = !$set if($invert); |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
return $set; |
1030
|
|
|
|
|
|
|
} |
1031
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
=item I |
1033
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
my $attr_number = $is->get_component_attribute_value( 'LocalOnly' ); |
1035
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
Given a component attribute name, returns the bit value associated |
1037
|
|
|
|
|
|
|
with the attribute. The msidbComponentAttributes prefix for attribute names |
1038
|
|
|
|
|
|
|
is accepted, but not required. Given a valid attribute value, simply returns |
1039
|
|
|
|
|
|
|
the value. Returns undef on invalid input. |
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
=cut |
1042
|
|
|
|
|
|
|
sub get_component_attribute_value { |
1043
|
|
|
|
|
|
|
my ($self, $attribute) = @_; |
1044
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
$attribute =~ s/^msidbComponentAttributes//; |
1046
|
|
|
|
|
|
|
if($attribute =~ /^\d+$/) { |
1047
|
|
|
|
|
|
|
if(exists($component_attr_names{$attribute})) { |
1048
|
|
|
|
|
|
|
return $attribute; |
1049
|
|
|
|
|
|
|
} |
1050
|
|
|
|
|
|
|
} elsif(exists($component_attr_values{$attribute})) { |
1051
|
|
|
|
|
|
|
return $component_attr_values{$attribute}; |
1052
|
|
|
|
|
|
|
} |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
return; |
1055
|
|
|
|
|
|
|
} |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
=item I |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
my $attr_name = $is->get_component_attribute_name( 512 ); |
1060
|
|
|
|
|
|
|
|
1061
|
|
|
|
|
|
|
Given a component attribute value, returns the name associated |
1062
|
|
|
|
|
|
|
with the value. Given a valid attribute name, simply returns |
1063
|
|
|
|
|
|
|
the name. The msidbComponentAttributes prefix for attribute names |
1064
|
|
|
|
|
|
|
is accepted, but not required. Returns undef on invalid input. |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
=cut |
1067
|
|
|
|
|
|
|
sub get_component_attribute_name { |
1068
|
|
|
|
|
|
|
my ($self, $attribute) = @_; |
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
$attribute =~ s/^msidbComponentAttributes//; |
1071
|
|
|
|
|
|
|
if($attribute =~ /^\d+$/) { |
1072
|
|
|
|
|
|
|
if(exists($component_attr_names{$attribute})) { |
1073
|
|
|
|
|
|
|
return $component_attr_values{$attribute}; |
1074
|
|
|
|
|
|
|
} |
1075
|
|
|
|
|
|
|
} elsif(exists($component_attr_values{$attribute})) { |
1076
|
|
|
|
|
|
|
return $component_attr_names{$attribute}; |
1077
|
|
|
|
|
|
|
} |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
return; |
1080
|
|
|
|
|
|
|
} |
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
=item I |
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
my @attr_names = $is->valid_component_attributes; |
1085
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
Returns a list of valid attribute names. |
1087
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
=cut |
1089
|
|
|
|
|
|
|
sub valid_component_attributes { |
1090
|
|
|
|
|
|
|
return map { $component_attr_names{$_} } sort { $a <=> $b } keys %component_attr_names; |
1091
|
|
|
|
|
|
|
} |
1092
|
|
|
|
|
|
|
|
1093
|
|
|
|
|
|
|
=back |
1094
|
|
|
|
|
|
|
|
1095
|
|
|
|
|
|
|
=head1 ROW MANIPULATION METHOD SYNTAX |
1096
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
Row manipulation methods can be called in different ways. |
1098
|
|
|
|
|
|
|
First, they are all case insensitve, and the '_' is |
1099
|
|
|
|
|
|
|
optional, so for the 'Property' table, these are equivilent: |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
$is->add_row( 'Property', $rowdata ); |
1102
|
|
|
|
|
|
|
$is->AddRow( 'Property', $rowdata ); |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
Also, you can call each method using the table name in |
1105
|
|
|
|
|
|
|
place of the word 'row', so these are equivilent to the |
1106
|
|
|
|
|
|
|
two above: |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
$is->add_property( $rowdata ); |
1109
|
|
|
|
|
|
|
$is->AddProperty( $rowdata ); |
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
All row manipulation methods are called with a set of data |
1112
|
|
|
|
|
|
|
describing a row. In the methods below, it is represented by |
1113
|
|
|
|
|
|
|
the variable $rowdata. It can be passed to the function in |
1114
|
|
|
|
|
|
|
one of three formats: a list, an array ref or a hash ref. |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
List |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
You can simply put the columns in an array in the correct |
1119
|
|
|
|
|
|
|
order (which you can get by looking at the ism or calling |
1120
|
|
|
|
|
|
|
the I method), and pass it to the method. |
1121
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
my @rowdata = ( 'Column_1_Value', 'Column_2_value' ); |
1123
|
|
|
|
|
|
|
$success = $is->update_row( $table, @rowdata ); |
1124
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
Array ref |
1126
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
You can do the same as above, but pass it as a single |
1128
|
|
|
|
|
|
|
array reference. |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
$success = $is->update_row( $table, \@rowdata ); |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
Hash ref |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
You can also pass a hash ref, using column names |
1135
|
|
|
|
|
|
|
as keys. |
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
my %rowdata = ( |
1138
|
|
|
|
|
|
|
Property => 'ProductVersion', |
1139
|
|
|
|
|
|
|
Value => '1.2.3.4', |
1140
|
|
|
|
|
|
|
ISComments => '', |
1141
|
|
|
|
|
|
|
); |
1142
|
|
|
|
|
|
|
$success = $is->update_row( $table, \%rowdata ); |
1143
|
|
|
|
|
|
|
|
1144
|
|
|
|
|
|
|
=head1 ROW MANIPULATION METHODS |
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
=over 4 |
1147
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
=item I |
1149
|
|
|
|
|
|
|
|
1150
|
|
|
|
|
|
|
my $row = $is->getHash_row( $table, $rowdata ); |
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
Returns a hash ref containing the data that matches the keys |
1153
|
|
|
|
|
|
|
supplied in $rowdata. Returns undef if the row is not found. |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
=cut |
1156
|
|
|
|
|
|
|
sub _get_row_hash { |
1157
|
|
|
|
|
|
|
my ($self, $table, @args) = @_; |
1158
|
|
|
|
|
|
|
my $args = $self->_reformat_args($table, @args); |
1159
|
|
|
|
|
|
|
my $rowkey = $self->_find_row($table, $args); |
1160
|
|
|
|
|
|
|
if(defined($rowkey)) { |
1161
|
|
|
|
|
|
|
my %rowdata; |
1162
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
foreach my $i (0..$#{$p->{'columns'}}) { |
1165
|
|
|
|
|
|
|
$rowdata{ $p->{'columns'}[$i]{'name'} } = $p->{'data'}{$rowkey}[$i]; |
1166
|
|
|
|
|
|
|
} |
1167
|
|
|
|
|
|
|
return \%rowdata; |
1168
|
|
|
|
|
|
|
} else { |
1169
|
|
|
|
|
|
|
return; |
1170
|
|
|
|
|
|
|
} |
1171
|
|
|
|
|
|
|
} |
1172
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
=item I |
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
my $row = $is->getArray_row( $table, $rowdata ); |
1176
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
Returns an array ref containing the data that matches the keys |
1178
|
|
|
|
|
|
|
supplied in $rowdata. Returns undef if the row is not found. |
1179
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
=cut |
1181
|
|
|
|
|
|
|
sub _get_row_array { |
1182
|
|
|
|
|
|
|
my ($self, $table, @args) = @_; |
1183
|
|
|
|
|
|
|
my $args = $self->_reformat_args($table, @args); |
1184
|
|
|
|
|
|
|
my $rowkey = $self->_find_row($table, $args); |
1185
|
|
|
|
|
|
|
if(defined($rowkey)) { |
1186
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
1187
|
|
|
|
|
|
|
return $p->{'data'}{$rowkey}; |
1188
|
|
|
|
|
|
|
} else { |
1189
|
|
|
|
|
|
|
return; |
1190
|
|
|
|
|
|
|
} |
1191
|
|
|
|
|
|
|
} |
1192
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
=item I |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
my $success = $is->update_row( $table, $rowdata ); |
1196
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
Updates the row that matches the keys supplied in |
1198
|
|
|
|
|
|
|
$rowdata. Any columns for which an undef is supplied |
1199
|
|
|
|
|
|
|
will remain unchanged. An empty string will force |
1200
|
|
|
|
|
|
|
the column to be empty. Returns 1 on success, 0 on |
1201
|
|
|
|
|
|
|
failure. |
1202
|
|
|
|
|
|
|
|
1203
|
|
|
|
|
|
|
=cut |
1204
|
|
|
|
|
|
|
sub _update_row { |
1205
|
|
|
|
|
|
|
my ($self, $table, @args) = @_; |
1206
|
|
|
|
|
|
|
my $rowdata = $self->_check_args($table, @args); |
1207
|
|
|
|
|
|
|
unless(defined($rowdata)) { |
1208
|
|
|
|
|
|
|
return 0; |
1209
|
|
|
|
|
|
|
} |
1210
|
|
|
|
|
|
|
my $rowkey = $self->_find_row($table, $rowdata); |
1211
|
|
|
|
|
|
|
unless(defined($rowkey)) { |
1212
|
|
|
|
|
|
|
carp("Failed to locate row in $table for update"); |
1213
|
|
|
|
|
|
|
return 0; |
1214
|
|
|
|
|
|
|
} |
1215
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
1216
|
|
|
|
|
|
|
foreach my $i (0..$#{$rowdata}) { |
1217
|
|
|
|
|
|
|
if(defined($rowdata->[$i])) { |
1218
|
|
|
|
|
|
|
$p->{'data'}{$rowkey}[$i] = $rowdata->[$i]; |
1219
|
|
|
|
|
|
|
} |
1220
|
|
|
|
|
|
|
} |
1221
|
|
|
|
|
|
|
return 1; |
1222
|
|
|
|
|
|
|
} |
1223
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
=item I |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
my $success = $is->add_row( $table, $rowdata ); |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
Adds a row containing the data in $rowdata. Returns |
1229
|
|
|
|
|
|
|
1 on success, 0 on failure. |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
=cut |
1232
|
|
|
|
|
|
|
sub _add_row { |
1233
|
|
|
|
|
|
|
my ($self, $table, @args) = @_; |
1234
|
|
|
|
|
|
|
my $rowdata = $self->_check_args($table, @args); |
1235
|
|
|
|
|
|
|
unless(defined($rowdata)) { |
1236
|
|
|
|
|
|
|
return 0; |
1237
|
|
|
|
|
|
|
} |
1238
|
|
|
|
|
|
|
my $rowkey = $self->_find_row($table, $rowdata); |
1239
|
|
|
|
|
|
|
if(defined($rowkey)) { |
1240
|
|
|
|
|
|
|
carp("Row to add in '$table' table already exists"); |
1241
|
|
|
|
|
|
|
return 0; |
1242
|
|
|
|
|
|
|
} |
1243
|
|
|
|
|
|
|
$rowkey = $self->_build_key($table, $rowdata); |
1244
|
|
|
|
|
|
|
unless(defined($rowkey)) { |
1245
|
|
|
|
|
|
|
return 0; |
1246
|
|
|
|
|
|
|
} |
1247
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
1248
|
|
|
|
|
|
|
$p->{'data'}{$rowkey} = $rowdata; |
1249
|
|
|
|
|
|
|
return 1; |
1250
|
|
|
|
|
|
|
} |
1251
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
=item I |
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
my $success = $is->del_row( $table, $rowdata ); |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
Deletes the row that matches the keys supplied in |
1257
|
|
|
|
|
|
|
$rowdata. Returns 1 on success, 0 on failure. |
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
=cut |
1260
|
|
|
|
|
|
|
sub _del_row { |
1261
|
|
|
|
|
|
|
my ($self, $table, @args) = @_; |
1262
|
|
|
|
|
|
|
my $args = $self->_reformat_args($table, @args); |
1263
|
|
|
|
|
|
|
my $rowkey = $self->_find_row($table, $args); |
1264
|
|
|
|
|
|
|
unless(defined($rowkey)) { |
1265
|
|
|
|
|
|
|
carp("Failed to locate row in $table for delete"); |
1266
|
|
|
|
|
|
|
return 0; |
1267
|
|
|
|
|
|
|
} |
1268
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
1269
|
|
|
|
|
|
|
delete($p->{'data'}{$rowkey}); |
1270
|
|
|
|
|
|
|
return 1; |
1271
|
|
|
|
|
|
|
} |
1272
|
|
|
|
|
|
|
|
1273
|
|
|
|
|
|
|
=item I |
1274
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
$is->purge_row( $table, $rowdata ); |
1276
|
|
|
|
|
|
|
$is->purge_row( 'Component', 'Awesome.dll' ); |
1277
|
|
|
|
|
|
|
$is->PurgeComponent( 'Awesome.dll' ); |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
Removes the row that matches the key in $rowdata from the given table, and any rows |
1280
|
|
|
|
|
|
|
in other tables with foreign keys that reference it. Key values are |
1281
|
|
|
|
|
|
|
case sensitive. This only works for tables with a key column that has |
1282
|
|
|
|
|
|
|
the same name as the table, which seems to be the only way you can use |
1283
|
|
|
|
|
|
|
foreign keys in an ISM in any case. Returns 1 on success, 0 on failure. |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
=cut |
1286
|
|
|
|
|
|
|
sub _purge_row { |
1287
|
|
|
|
|
|
|
my ($self, $table, $key_value) = @_; |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
# make sure the key exists in the table |
1290
|
|
|
|
|
|
|
my $rowkey = $self->_find_row($table, $self->_reformat_args($table, $key_value)); |
1291
|
|
|
|
|
|
|
unless(defined($rowkey)) { |
1292
|
|
|
|
|
|
|
return 0; |
1293
|
|
|
|
|
|
|
} |
1294
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
$self->_del_row($table, $rowkey); |
1296
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
my $foreign_key_col = $self->{'correct_case'}{$table} . '_'; |
1298
|
|
|
|
|
|
|
|
1299
|
|
|
|
|
|
|
foreach my $table (@{$self->{'foreign_keys'}{$foreign_key_col}}) { |
1300
|
|
|
|
|
|
|
my $rows_to_delete = $self->_search_row_array($table, { $foreign_key_col => $key_value }); |
1301
|
|
|
|
|
|
|
if(@{$rows_to_delete}) { |
1302
|
|
|
|
|
|
|
foreach my $row (@{$rows_to_delete}) { |
1303
|
|
|
|
|
|
|
$self->_del_row($table, $row) or return 0; |
1304
|
|
|
|
|
|
|
} |
1305
|
|
|
|
|
|
|
} |
1306
|
|
|
|
|
|
|
} |
1307
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
return 1; |
1309
|
|
|
|
|
|
|
} |
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
=item I |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
my $success = $is->add_or_update_row( $table, $rowdata ); |
1314
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
Adds a row if no row exists with the supplied keys, updates |
1316
|
|
|
|
|
|
|
the matching row otherwise. |
1317
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
=cut |
1319
|
|
|
|
|
|
|
sub _add_or_update_row { |
1320
|
|
|
|
|
|
|
my ($self, $table, @args) = @_; |
1321
|
|
|
|
|
|
|
my $args = $self->_reformat_args($table, @args); |
1322
|
|
|
|
|
|
|
my $rowkey = $self->_find_row($table, $args); |
1323
|
|
|
|
|
|
|
if(defined($rowkey)) { |
1324
|
|
|
|
|
|
|
return $self->_update_row($table, $args); |
1325
|
|
|
|
|
|
|
} else { |
1326
|
|
|
|
|
|
|
return $self->_add_row($table, $args); |
1327
|
|
|
|
|
|
|
} |
1328
|
|
|
|
|
|
|
} |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
=item I |
1331
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
my $rows = $is->searchHash_row( $table, $rowdata ); |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
Returns any rows in the given table that match the supplied |
1335
|
|
|
|
|
|
|
columns. The return value is an arrayref, where each entry is |
1336
|
|
|
|
|
|
|
a hash as would be returned by I. Returns an empty |
1337
|
|
|
|
|
|
|
arrayref if no matches are found. Returns the entire table if |
1338
|
|
|
|
|
|
|
no $rowdata argument is provided. |
1339
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
Columns with undefined values will be ignored for matching purposes. |
1341
|
|
|
|
|
|
|
Values used for matching can be either literal strings, in which |
1342
|
|
|
|
|
|
|
case an exact match is required, or quoted regular expressions such as: |
1343
|
|
|
|
|
|
|
|
1344
|
|
|
|
|
|
|
my $rows = $is->searchHash_row( 'Property', { Property=>qr/^_/ } ); |
1345
|
|
|
|
|
|
|
|
1346
|
|
|
|
|
|
|
This would search for all properties that begin with an underscore. |
1347
|
|
|
|
|
|
|
|
1348
|
|
|
|
|
|
|
=cut |
1349
|
|
|
|
|
|
|
sub _search_row_hash { |
1350
|
|
|
|
|
|
|
my ($self, $table, @args) = @_; |
1351
|
|
|
|
|
|
|
my $args = $self->_reformat_args($table, @args); |
1352
|
|
|
|
|
|
|
my $results = $self->_search_row($table, $args); |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
my @hash_results; |
1355
|
|
|
|
|
|
|
my $p = $self->_parsed($table); |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
foreach my $row (@{$results}) { |
1358
|
|
|
|
|
|
|
my %rowdata; |
1359
|
|
|
|
|
|
|
foreach my $i (0..$#{$p->{'columns'}}) { |
1360
|
|
|
|
|
|
|
$rowdata{ $p->{'columns'}[$i]{'name'} } = $row->[$i]; |
1361
|
|
|
|
|
|
|
} |
1362
|
|
|
|
|
|
|
push(@hash_results, \%rowdata); |
1363
|
|
|
|
|
|
|
} |
1364
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
return \@hash_results; |
1366
|
|
|
|
|
|
|
} |
1367
|
|
|
|
|
|
|
|
1368
|
|
|
|
|
|
|
=item I |
1369
|
|
|
|
|
|
|
|
1370
|
|
|
|
|
|
|
my $rows = $is->searchArray_row( $table, $rowdata ); |
1371
|
|
|
|
|
|
|
|
1372
|
|
|
|
|
|
|
Works the same as I, but returns an arrayref containing |
1373
|
|
|
|
|
|
|
arrayrefs, like I instead of hashrefs. |
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
=cut |
1376
|
|
|
|
|
|
|
sub _search_row_array { |
1377
|
|
|
|
|
|
|
my ($self, $table, @args) = @_; |
1378
|
|
|
|
|
|
|
my $args = $self->_reformat_args($table, @args); |
1379
|
|
|
|
|
|
|
return $self->_search_row($table, $args); |
1380
|
|
|
|
|
|
|
} |
1381
|
|
|
|
|
|
|
|
1382
|
|
|
|
|
|
|
# this is (almost) a copy of the xml_escape function in XML::Parser::Expat. |
1383
|
|
|
|
|
|
|
# The version there doesn't seem to work properly on data that was read |
1384
|
|
|
|
|
|
|
# in via XML::Parser, because a call to study causes subsequent matches to |
1385
|
|
|
|
|
|
|
# fail |
1386
|
|
|
|
|
|
|
sub _xml_escape { |
1387
|
|
|
|
|
|
|
my $text = shift @_; |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
$text =~ s/\&/\&/g; |
1390
|
|
|
|
|
|
|
$text =~ s/\</g; |
1391
|
|
|
|
|
|
|
$text =~ s/>/\>/g; |
1392
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
foreach (@_) { |
1394
|
|
|
|
|
|
|
die "xml_escape: '$_' isn't a single character" if length($_) > 1; |
1395
|
|
|
|
|
|
|
|
1396
|
|
|
|
|
|
|
if ($_ eq '"') { |
1397
|
|
|
|
|
|
|
$text =~ s/\"/\"/; |
1398
|
|
|
|
|
|
|
} |
1399
|
|
|
|
|
|
|
elsif ($_ eq "'") { |
1400
|
|
|
|
|
|
|
$text =~ s/\'/\'/; |
1401
|
|
|
|
|
|
|
} |
1402
|
|
|
|
|
|
|
else { |
1403
|
|
|
|
|
|
|
my $rep = '' . sprintf('x%X', ord($_)) . ';'; |
1404
|
|
|
|
|
|
|
if (/\W/) { |
1405
|
|
|
|
|
|
|
my $ptrn = "\\$_"; |
1406
|
|
|
|
|
|
|
$text =~ s/$ptrn/$rep/g; |
1407
|
|
|
|
|
|
|
} |
1408
|
|
|
|
|
|
|
else { |
1409
|
|
|
|
|
|
|
$text =~ s/$_/$rep/g; |
1410
|
|
|
|
|
|
|
} |
1411
|
|
|
|
|
|
|
} |
1412
|
|
|
|
|
|
|
} |
1413
|
|
|
|
|
|
|
$text; |
1414
|
|
|
|
|
|
|
} |
1415
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
=back |
1417
|
|
|
|
|
|
|
|
1418
|
|
|
|
|
|
|
=head1 AUTHOR |
1419
|
|
|
|
|
|
|
|
1420
|
|
|
|
|
|
|
Kirk Baucom, Ekbaucom@schizoid.comE |
1421
|
|
|
|
|
|
|
|
1422
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
1423
|
|
|
|
|
|
|
|
1424
|
|
|
|
|
|
|
Copyright 2003 by Kirk Baucom |
1425
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
1427
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
1428
|
|
|
|
|
|
|
|
1429
|
|
|
|
|
|
|
=cut |
1430
|
|
|
|
|
|
|
|
1431
|
|
|
|
|
|
|
1; |
1432
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
__DATA__ |