1#!/usr/bin/perl
2
3# Copyright (c) 2018, MIPI Alliance, Inc.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9#
10# * Redistributions of source code must retain the above copyright
11#   notice, this list of conditions and the following disclaimer.
12#
13# * Redistributions in binary form must reproduce the above copyright
14#   notice, this list of conditions and the following disclaimer in
15#   the documentation and/or other materials provided with the
16#   distribution.
17#
18# * Neither the name of the copyright holder nor the names of its
19#   contributors may be used to endorse or promote products derived
20#   from this software without specific prior written permission.
21#
22# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34#  Contributors:
35#   norbert.schulz@intel.com - initial API and implementation
36#
37use strict;
38use warnings;
39use File::Spec;
40use XML::Simple;
41use File::Find;
42use Getopt::Long;
43use Pod::Usage;
44use String::Escape;
45use bigint qw/hex/;
46
47use Data::Dumper;
48
49# =============================================================================
50# Globals
51# =============================================================================
52my ( undef, undef, $TOOL ) = File::Spec->splitpath( $0 );
53my $INDENTATION = 2;
54my $SRCLOCATION = 1;
55my $CONFIG = ();
56my $FILE_COUNTER = 0;
57my $MSG_STRUCT = ();
58my $FILE_STRUCT = ();
59my $USED_IDS = ();
60my %OPTIONS = ();
61
62
63# =============================================================================
64# Main functions.
65# =============================================================================
66
67sub main {
68    read_options ();
69
70    find({
71        wanted => \&file_handler,
72        preprocess => \&filter_file_list }, @{$OPTIONS{src}});
73
74    sub file_handler {
75        my $file = $File::Find::name;
76        if (-f $_ && !is_file_existing ($file)) {
77            _i ("Parsing: ".$file."\n");
78            add_file ($file);
79            my $count = parse_file ($_);
80            _i ("Parsing finished: ".$file.", found $count call(s)\n");
81        }
82    }
83
84    #
85    # Filters the file list before the file_handler functions will be
86    # called by File::find.
87    #
88    sub filter_file_list {
89        my @filtered_list = ();
90        # Filter files with wanted extensions.
91        foreach my $pattern (@{$CONFIG->{SrcFilePatterns}->{SrcFilePattern}}) {
92            push (@filtered_list, glob($pattern->{Pattern}));
93        }
94        # Add sub directories to scan whole trees.
95        foreach my $dir (grep { -d } @_) {
96            if ($dir ne '.' && $dir ne '..') {
97                push (@filtered_list, $dir);
98            }
99        }
100        return @filtered_list;
101    }
102
103    if (!defined ($FILE_STRUCT)) {
104        _e ("No files found, which match the provided patterns!\n");
105    }
106
107    if (!defined ($MSG_STRUCT)) {
108        _e ("No messages found!\n");
109    }
110
111    my $xml = generate_xml ($MSG_STRUCT, $FILE_STRUCT);
112
113    _i ("Writing XML file: ".$OPTIONS{catalog}."\n");
114    open (CATALOG, ">".$OPTIONS{catalog})
115        or _e ("Could not open file (w): $!\n");
116    print CATALOG $xml;
117    close (CATALOG);
118    _i ("Writing XML file finished\n");
119}
120
121#
122# generate_xml (<msg-data-structure>, <file-data-structure>)
123#
124# Description:
125# Generate SyS-T catalog XML structure based on read message
126# calls and scanned files.
127#
128sub generate_xml {
129    my $msg_struct = $_[0];
130    my $file_struct = $_[1];
131
132    _i ("Generating XML structure\n");
133
134    my $output =  load_template($OPTIONS{template});
135
136    # build sources section
137    #
138    if (defined $file_struct) {
139        if ($SRCLOCATION) {
140            my $source_list = '<syst:SourceFiles>'."\n";
141            foreach my $file_id (sort {$a <=> $b} keys %{$file_struct}) {
142                $source_list.= ' ' x ($INDENTATION*2);
143                $source_list.= '<syst:File ID="'.$file_id.'">';
144                $source_list.= '<![CDATA['.$file_struct->{$file_id}.']]>';
145                $source_list.= '</syst:File>'."\n"
146            }
147            $source_list.= ' ' x $INDENTATION;
148            $source_list.= '</syst:SourceFiles>'."\n";
149
150            $output  =~ s/<syst:SourceFiles\/>/$source_list/sm;
151        }
152
153        if (defined $msg_struct->{"32"}) {
154            if (index($output, '<syst:Catalog32/>') != -1) {
155                my $section = generate_catalog_section($msg_struct, 32);
156                $output =~ s/<syst:Catalog32\/>/$section/sm;
157            } else {
158                _w('32 bit catalog messages found, but template file '.
159                   'is missing the insertion pattern "<syst:Catalog32/>"'."\n");
160            }
161        }
162        if (defined $msg_struct->{"64"}) {
163            if (index($output, '<syst:Catalog64/>') != -1) {
164                my $section = generate_catalog_section($msg_struct, 64);
165                $output =~ s/<syst:Catalog64\/>/$section/sm;
166            } else {
167                _w('64 bit catalog messages found, but template file '.
168                   'is missing the insertion pattern "<Messages IdSize="64Bit"/>"'."\n");
169            }
170        }
171    } else {
172        _w( "no source files to parse found." );
173    }
174
175    _i ("Generating XML structure finished\n");
176
177    return $output;
178}
179
180#
181# generate_catalog_section (<found hits>, 32|64)
182#
183# Description
184sub generate_catalog_section {
185    my $hits = $_[0];
186    my $size = $_[1]+0;
187
188    my $data = $hits->{$size};
189    my $output ="";
190
191    $output.= '<syst:Catalog'.$size.'>'."\n";
192
193    foreach my $file_id (sort keys %{$data}) {
194        foreach my $line_id (sort {$a <=> $b} keys %{$data->{$file_id}}) {
195            foreach my $msg_id (sort {$a <=> $b} keys %{$data->{$file_id}->{$line_id}}) {
196                my $msg = $data->{$file_id}->{$line_id}->{$msg_id};
197                my $idval= "";
198
199                if ($size == 32) {
200                    $idval = sprintf("0x%08x", $msg_id);
201                } else {
202                    $idval = sprintf("0x%016x", $msg_id);
203                }
204                $output.= ' ' x ($INDENTATION*2);
205                $output.= '<syst:Format ID="'.$idval.'"';
206                if ($SRCLOCATION) {
207                    $output.= ' File="'.$file_id.'" Line="'.$line_id .'"';
208                }
209                $output.= '><![CDATA['.String::Escape::unbackslash($msg).']]>';
210                $output.= '</syst:Format>'."\n";
211            }
212        }
213    }
214    $output.= ' ' x $INDENTATION;
215    $output.= '</syst:Catalog'.$size.'>'."\n";
216
217    return $output;
218}
219
220#
221# add_file (<file>)
222#
223# Description:
224# Add a file to the file data structure and increment global
225# file counter.
226#
227sub add_file {
228    my $file     = $_[0];
229
230    $FILE_COUNTER++;
231
232    if ($SRCLOCATION) {
233        _i ("Add $file with file id $FILE_COUNTER to file catalog\n");
234    }
235
236    if (defined($FILE_STRUCT->{$FILE_COUNTER})) {
237        _e ("File with file id $FILE_COUNTER already exists!\n");
238    }
239    $FILE_STRUCT->{$FILE_COUNTER} = $file;
240}
241
242#
243# is_file_existing (<file>)
244#
245# Description:
246# Check if file is already existing in the file data structure
247#
248sub is_file_existing {
249    return (grep {$FILE_STRUCT->{$_} eq $_[0]} keys %{$FILE_STRUCT}) > 0;
250}
251
252#
253# load_template (<file>)
254#
255# Description:
256# Parse the given file as the catalog template file where the catalog
257# messages get added into.
258#
259sub load_template {
260    my $file         = $_[0];
261    local $/ = undef;
262    open (FILE, $file) or _e ("Could not open input template file ".$file.": $!\n");
263    my $content = <FILE>;
264    close FILE;
265
266    _i("Loaded template collateral file ".$file."\n");
267
268    return $content;
269}
270
271#
272# parse_file (<file>)
273#
274# Description:
275# Parse the given file to extract SyS-T catalog trace calls. Results will be
276# stored into global catalog data structure.
277#
278sub parse_file {
279    my $file         = $_[0];
280
281    open (FILE, $file) or _e ("Could not open file ".$file.": $!\n");
282    my @content = <FILE>;
283    close FILE;
284
285    my $add_count = 0;
286
287    # loop over the input lines
288    #
289    for (my $i = 0; $i < $#content+1; $i++) {
290        my $calltype= undef;
291
292        for my $idsize ( "32", "64" ) {
293
294            my $calltype ="Catalog".$idsize;
295            my $callset = $CONFIG->{CatalogCalls}->{$calltype}->{CatalogCall};
296
297             foreach my $function_name (keys %{$callset})
298             {
299                if (!defined ($callset->{$function_name}->{IdParamIdx})) {
300                    _e ("Configuration: 'IdParamIdx' is not defined for function ".
301                            "call: $function_name\n");
302                }
303                if (!defined ($callset->{$function_name}->{StringParamIdx})) {
304                    _e ("Configuration: 'StringParamIdx' is not defined for ".
305                            "function call: $function_name\n");
306                }
307
308                # find the current function call
309                if ($content[$i] =~ /\b${function_name}\b/) {
310                    my $call = strip_comments ($content[$i]);
311                    my $line_no_start = $i+1;
312                    my $line_no_end   = $i+1;
313
314                    if (!strip_whitespaces ($call)) {
315                        _i ("Catalog instrumentation call \@ ".
316                                "$file:$line_no_start will be skipped.\n");
317                        next;
318                    }
319                    _vi ("function call start: $function_name at line ".
320                        $line_no_start."\n");
321
322                    # try to find the end of the function call, which is might
323                    # not be at the same line.
324                    while ($content[$i] !~ /\)(\s*|\t*);/) {
325                        my $tmp = strip_comments ($content[++$i]);
326                        $call.=$tmp;
327                    }
328                    $line_no_end = $i+1;
329
330                    _vi ("functon call end: $function_name at line ".
331                        $line_no_end."\n");
332
333                    # remove new line and extract arguments
334                    $call =~ s/\n//g;
335                    $call =~ m/${function_name}\b(\s*|\t*)\((.*)\)(\s*|\t*);/;
336
337                    my $arguments = $2;
338
339                    # now split the arguments by character
340                    my @array = split(//, $arguments);
341                    my $current_arg_no = 0;
342                    my $inside_string = 0;
343                    my $possible_end_of_call = 0;
344                    my $bracket_count = 0;
345                    my $found_quotes = 0;
346                    my @function_args = ();
347                    my $opening_quote = "";
348
349                    # Iterate argument list character by character
350                    for (my $i = 0; $i < $#array+1; $i++) {
351                        # if a closing bracket will be found not inside the format
352                        # string it may be the end of the call.
353                        if ($array[$i] eq ")" && !$inside_string) {
354                            $possible_end_of_call ^= 1;
355                            if ($bracket_count == 0) {
356                                last;
357                            }
358                            $bracket_count--;
359                        }
360                        # if opening bracket was found, reset possible end marker
361                        if ($array[$i] eq "(" && !$inside_string) {
362                            $bracket_count++;
363                            $possible_end_of_call = 0;
364                        }
365
366                        # if a semicolon was found, not inside the format string
367                        # and one of the previous characters was a closing bracket,
368                        # it's the end of the call.
369                        if ($array[$i] eq ";" && !$inside_string
370                                && $possible_end_of_call) {
371                            _d ("End of call detected at line $line_no_start\n");
372                            last;
373                        }
374
375                        # Find the argument separators
376                        if ($array[$i] eq "," && !$inside_string) {
377                            $current_arg_no++;
378                            next;
379                        }
380
381                        # If a format string was found, do not search
382                        # for argument separators.
383                        if ($array[$i] eq "\"" || $array[$i] eq "'") {
384                            $found_quotes = 1;
385                            # check if the previous character escaped the current
386                            # one.
387                            if ($i >= 0 && $array[$i-1] ne "\\") {
388                                # Within a string, quotes match, so reset control variables.
389                                if ($inside_string && $opening_quote eq $array[$i]) {
390                                    $opening_quote = "";
391                                    $inside_string ^= 1;
392                                    next;
393                                # Not within a string, start quote empty. Begin of a string.
394                                } elsif (!$inside_string && $opening_quote eq "") {
395                                    $opening_quote = $array[$i];
396                                    $inside_string ^= 1;
397                                    next;
398                                }
399                            }
400                        }
401
402                        # Skip spaces outside of a string
403                        if (!$inside_string && $found_quotes && ($array[$i] eq " " || $array[$i] eq "\t")) {
404                            next;
405                        }
406
407                        # Add argument to array.
408                        $function_args[$current_arg_no].=$array[$i];
409                    }
410
411                    # remove leading and trailing whitespaces from the arguments
412                    map {
413                        $_ = strip_whitespaces ($_);
414                    } @function_args;
415
416                    my $str_idx = $callset->{$function_name}->{StringParamIdx};
417                    my $file = (defined ($FILE_STRUCT->{$FILE_COUNTER}) ?
418                                    $FILE_STRUCT->{$FILE_COUNTER} : $FILE_COUNTER);
419
420                    my $message_id = undef;
421                    my $message_str = $function_args[$str_idx-1];
422
423                    my $algorithm = 'fromIdParam';
424                    if (defined( $callset->{$function_name}->{Algorithm})) {
425                        $algorithm =  $callset->{$function_name}->{Algorithm};
426                    }
427                    _d("Algorithm = $algorithm\n");
428
429                    my $id_idx  =  $callset->{$function_name}->{IdParamIdx};
430                    _d("IdParamIdx = $id_idx\n");
431                    if ($algorithm eq 'hash65599') {
432                        $message_id = hash_x65599 ($message_str, $function_args[$id_idx-1]);
433                    } else {
434                        $message_id = to_value($function_args[$id_idx-1]);
435                    }
436
437                    _d ("StringParamIdx = $str_idx\n");
438                    _d ("IdSize = $idsize\n");
439                    _d ("Found call: ".strip_whitespaces ($call)."\n");
440                    _d ("Arguments: ".strip_whitespaces ($arguments)."\n");
441
442                    if (!$found_quotes) {
443                        _e ("Catalog instrumentation call \@ ".
444                                "$file:$line_no_start ".
445                                "could not be parsed. ".
446                                "No valid format string found.\n");
447                    }
448                    if (!is_dec($message_id) && !is_hex($message_id)) {
449                        _e ("Catalog instrumentation call \@ ".
450                                "$file:$line_no_start '$message_id' ".
451                                "is not decimal or hexadecimal.\n");
452                    }
453
454                    if (exists($USED_IDS->{$message_id})) {
455                        if ($algorithm eq 'hash65599') {
456                            _e ("Catalog instrumentation call \@ ".
457                                    "$file:$line_no_start hash based ID '$message_id' ".
458                                    "already exists. Change the offset value.\n");
459                        } else {
460                            _e ("Catalog instrumentation call \@ ".
461                                    "$file:$line_no_start with ID '$message_id' ".
462                                    "also exists \@ ".
463                                    $FILE_STRUCT->{$USED_IDS->{$message_id}->{file}}.
464                                    ":".
465                                    $USED_IDS->{$message_id}->{line}.".\n");
466                        }
467                    }
468                    $MSG_STRUCT->{$idsize}->{$FILE_COUNTER}->{$line_no_start}->{$message_id} = $message_str;
469
470                    $USED_IDS->{$message_id} ={
471                        file => $FILE_COUNTER,
472                        line => $line_no_start
473                    };
474                    $add_count++;
475
476                    _d ("-" x 79 . "\n");
477                }
478            }
479        }
480    }
481    close (FILE);
482
483    return $add_count;
484}
485
486
487# =============================================================================
488# Helper functions.
489# =============================================================================
490
491
492#
493# read_options_from_config_file ()
494#
495# Description:
496# Read options from config file and store them into global data structure.
497# The function also checks if the resp. option was overruled by a command
498# line switch.
499#
500sub read_options_from_config_file {
501    $CONFIG = read_config ($OPTIONS{config});
502
503    if (!defined ($CONFIG->{CatalogConfigs})
504            || !defined ($CONFIG->{CatalogConfigs}->{CatalogConfig})) {
505        return;
506    }
507
508    my %new_options = ();
509    foreach my $config_record (@{$CONFIG->{CatalogConfigs}->{CatalogConfig}}) {
510        my $option = $config_record->{option};
511        my $value = $config_record->{value};
512        if (!defined($OPTIONS{$option})) {
513            if ($option eq "guid" || $option eq "src") {
514                if (!defined($new_options{$option})) {
515                    @{$new_options{$option}} = ();
516                }
517                push (@{$new_options{$option}}, $value);
518            } else {
519                $new_options{$option} = $value;
520            }
521        } else {
522            _vi ("Config file option '$option' overruled by ".
523                    "command line option\n");
524        }
525    }
526
527    # Merge the two hashes.
528    @OPTIONS{keys %new_options} = values %new_options;
529}
530
531#
532# read_options ()
533#
534# Description:
535# Read options from command line an store into global data structure.
536#
537sub read_options {
538    GetOptions (\%OPTIONS,
539        'verbose|v',
540        'debug|d',
541        'src=s@',
542        'catalog|cf=s',
543        'config|c=s',
544        'template|tpl=s',
545        'nolocation|nl',
546        'help|h'
547    ) or pod2usage(-exitval => 0, -verbose => 2, -noperldoc => 1);
548
549    if (defined($OPTIONS{help})) {
550        pod2usage(-exitval => 0, -verbose => 2, -noperldoc => 1);
551    }
552
553    if (!defined($OPTIONS{config})) {
554        _e ("-config option is missing\n");
555    } elsif (!-f $OPTIONS{config}) {
556        _e ("Specified config is not a file\n");
557    }
558
559    read_options_from_config_file ();
560
561    if (defined($OPTIONS{src})) {
562        map {
563            _e ("Not existing or not a directory: $_\n") if !-e $_ || !-d $_;
564        } @{$OPTIONS{src}};
565    } else {
566        _e ("-src option is missing\n");
567    }
568
569    if (!defined($OPTIONS{catalog})) {
570        _e ("-catalog option is missing\n");
571    }
572
573    if (!defined($OPTIONS{template})) {
574        _e ("-template option is missing\n");
575    }
576
577    if (defined($OPTIONS{indentation})) {
578        $INDENTATION = $OPTIONS{indentation};
579    }
580
581    if (defined($OPTIONS{nolocation}) and
582        (uc($OPTIONS{nolocation}) eq "TRUE") or ($OPTIONS{nolocation} == 1)
583        )
584    {
585        $SRCLOCATION = 0;
586    }
587}
588
589sub parse_guid {
590    my $guid_expr = $_[0];
591    my $ret = ();
592
593    if ($guid_expr =~ /^(.*);(.*)$/) {
594        $ret->{guid} = $1;
595        $ret->{mask} = $2;
596    } else {
597        $ret->{guid} = $guid_expr;
598    }
599
600    return $ret;
601}
602
603#
604# strip_comments (<string>)
605#
606# Description:
607# Strip C-style comments.
608#
609sub strip_comments {
610    my $str = $_[0];
611    $str =~ s/\/\/.*//;
612    $str =~ s/\/\*.*\*\///g;
613
614    return $str;
615}
616
617#
618# strip_whitespaces (<string>)
619#
620# Description:
621# Strip leading and trailing whitespaces/tabs.
622#
623sub strip_whitespaces {
624    my $str = $_[0];
625    $str =~ s/^\s+//;
626    $str =~ s/\s+$//;
627    $str =~ s/^\t+//;
628    $str =~ s/\t+$//;
629
630    return $str;
631}
632
633#
634# read_config (<configuration-file>)
635#
636# Description:
637# Read configuration and return data structure. An error
638# message will be printed for every missing required section.
639#
640sub read_config {
641    my $config_file = $_[0];
642
643    my $xml_ref = XMLin($config_file,
644        KeyAttr => {
645            'CatalogCall' => 'Name'
646        },
647        ForceArray => [
648            'CatalogCall',
649            'SrcFilePattern',
650            'CatalogConfig'
651        ]
652    );
653
654    if (!defined ($xml_ref->{SrcFilePatterns})
655            || !defined ($xml_ref->{SrcFilePatterns}->{SrcFilePattern})) {
656        _e ("The specified config file does not contain a correct ".
657                "SrcFilePatterns/SrcFilePattern structure\n");
658    }
659
660    if (!defined ($xml_ref->{CatalogCalls}) ||
661        (!defined ($xml_ref->{CatalogCalls}->{Catalog32}->{CatalogCall}) &&
662         !defined ($xml_ref->{CatalogCalls}->{Catalog64}->{CatalogCall}))
663        )
664    {
665        _e ("The specified config file does not contain a correct ".
666                "CatalogCalls/CatalogCall structure\n");
667    }
668
669    return $xml_ref;
670}
671
672#
673# hash_x65599 (<string>, <offset>)
674#
675# Description:
676# Calculates a x65599 hash for the provided string and offset value.
677#
678sub hash_x65599 {
679    my $string  = String::Escape::unbackslash($_[0]);
680    my $offset    = int($_[1]);
681
682    my $hash = 0;
683    my $tail256 = substr($string, -256);
684
685    foreach my $char (split(//, $tail256 )) {
686        $hash = uint ($hash * 65599 + ord($char));
687    }
688
689    return $hash + $offset;
690}
691
692#
693# uint (<number>)
694#
695# Description:
696# Mimics the unsigned int data type in Perl. Required for the x65599 hash
697# calculation.
698#
699sub uint {
700    return unpack('I', pack('I', $_[0]));
701}
702
703#
704# to_value (<hex or decimal number>)
705#
706# Description:
707# convert C-Language value to perl hex value
708#
709sub to_value {
710    my $input = $_[0];
711    my $val=0;
712
713    # Remove possible LL or ULL from the hex number in the C file.
714    $input =~ s/u?ll$//gi;
715    if (is_hex($input)) {
716        $val = hex($input);
717    } else {
718        $val = $input+0;
719    }
720
721    return $val;
722}
723
724
725#
726# is_hex (<number>)
727#
728# Description:
729# Checks if a number is a hex value.
730#
731sub is_hex {
732    return ($_[0] =~ /^0x[0-9a-f]+$/i);
733}
734
735#
736# is_dec (<number>)
737#
738# Description:
739# Checks if a number is a decimal value.
740#
741sub is_dec {
742    return ($_[0] =~ /^\d+$/);
743}
744
745#
746# _e (<msg>)
747#
748# Description:
749# Print error message on STDERR and exit with exist status 1.
750#
751sub _e {
752    _print (*STDERR, "[ERROR]", $_[0]);
753    exit (1);
754}
755
756#
757# _w (<msg>)
758#
759# Description:
760# Print warning message on STDOUT.
761#
762sub _w {
763    _print (*STDOUT, "[WARNING]", $_[0]);
764}
765
766#
767# _i (<msg>)
768#
769# Description:
770# Print general message on STDOUT.
771#
772sub _i {
773    _print (*STDOUT, "", $_[0]);
774}
775
776#
777# _vi (<msg>)
778#
779# Description:
780# Print verbose message on STDOUT.
781#
782sub _vi {
783    _i ($_[0]) if $OPTIONS{verbose};
784}
785
786#
787# _d (<msg>)
788#
789# Description:
790# Print debug message on STDOUT.
791#
792sub _d {
793    _print (*STDOUT, "[DEBUG]", $_[0]) if $OPTIONS{debug};
794}
795
796#
797# _print (<msg>)
798#
799# Description:
800# General print handler.
801#
802sub _print {
803    my $handle  = $_[0];
804    my $prefix  = $_[1];
805    my $msg     = $_[2];
806
807    print $handle "$TOOL: ".($prefix ne "" ? $prefix.": " : ""). $msg;
808}
809
810main ();
811exit (0);
812
813__END__
814=head1 NAME
815
816B<syst_cgen.pl> -- A SyS-T collateral generation script.
817
818=head1 DESCRIPTION
819
820Generate a SyS-T colleteral file with catalog call information from source
821files. The script scans for pre-configured C-style macro calls and extracts
822the message, its ID and the line number and puts it into a SyS-T collateral
823XML structure.
824
825=head1 USAGE
826
827syst_cgen.pl
828
829           -config <config file>
830           [-template <catalog template file>]
831           [-src <src-dir-1>] [-src <src-dir-2> [...]] [-o <xml-file>]
832           [-verbose] [-debug]
833
834  -src <src-dir>                Search path for the sources.
835  -catalog|-cf <dest-file>      Destination catalog XML file.
836  -template|-tpl <catalog-file> Catalog template to be extended with messages.
837  -config|c <config-file>       Catalog generation config file.
838  -nolocation|-nl               Supress source location generation
839  -verbose|v                    Switch on verbose messages.
840  -debug|d                      Switch on debug messages.
841
842
843=head1 CONFIGURATION
844
845The catalog configuration file specifies the parameters of the script, e.g.
846the macro call names, the argument structure of the calls, and file extensions,
847which should be scanned.
848
849=head2 EXAMPLE CATALOG CONFIGURATION
850   <CatalogGenerator>
851       <CatalogConfigs>
852           <CatalogConfig option="catalog" value="generated_catalog.xml" />
853           <CatalogConfig option="template" value="template.xml" />
854           <CatalogConfig option="indentation" value="4" />
855           <CatalogConfig option="nolocation" value="true|false" />
856           <CatalogConfig option="src" value="." />
857       </CatalogConfigs>
858       <SrcFilePatterns>
859           <SrcFilePattern Pattern="*.{cpp,c,h}" />
860       </SrcFilePatterns>
861       <CatalogCalls>
862           <Catalog32>
863               <CatalogCall Name="MIPI_SYST_HASH" Algorithm="hash65599" IdParamIdx="2" StringParamIdx="1" />
864
865               <CatalogCall Name="MIPI_SYST_CATPRINTF32" IdParamIdx="3" StringParamIdx="4"    />
866               <CatalogCall Name="MIPI_SYST_CATPRINTF32_0" IdParamIdx="3" StringParamIdx="4"  />
867               <CatalogCall Name="MIPI_SYST_CATPRINTF32_1" IdParamIdx="3" StringParamIdx="4"  />
868               <CatalogCall Name="MIPI_SYST_CATPRINTF32_2" IdParamIdx="3" StringParamIdx="4"  />
869               <CatalogCall Name="MIPI_SYST_CATPRINTF32_3" IdParamIdx="3" StringParamIdx="4"  />
870               <CatalogCall Name="MIPI_SYST_CATPRINTF32_4" IdParamIdx="3" StringParamIdx="4"  />
871               <CatalogCall Name="MIPI_SYST_CATPRINTF32_5" IdParamIdx="3" StringParamIdx="4"  />
872               <CatalogCall Name="MIPI_SYST_CATPRINTF32_6" IdParamIdx="3" StringParamIdx="4"  />
873           </Catalog32>
874           <Catalog64>
875               <CatalogCall Name="MIPI_SYST_CATPRINTF64" IdParamIdx="3" StringParamIdx="4" />
876               <CatalogCall Name="MIPI_SYST_CATPRINTF64_0" IdParamIdx="3" StringParamIdx="4"  />
877               <CatalogCall Name="MIPI_SYST_CATPRINTF64_1" IdParamIdx="3" StringParamIdx="4"  />
878               <CatalogCall Name="MIPI_SYST_CATPRINTF64_2" IdParamIdx="3" StringParamIdx="4"  />
879               <CatalogCall Name="MIPI_SYST_CATPRINTF64_3" IdParamIdx="3" StringParamIdx="4"  />
880               <CatalogCall Name="MIPI_SYST_CATPRINTF64_4" IdParamIdx="3" StringParamIdx="4"  />
881               <CatalogCall Name="MIPI_SYST_CATPRINTF64_5" IdParamIdx="3" StringParamIdx="4"  />
882               <CatalogCall Name="MIPI_SYST_CATPRINTF64_6" IdParamIdx="3" StringParamIdx="4"  />
883           </Catalog64>
884       </CatalogCalls>
885   </CatalogGenerator>
886
887=head2 SECTION SrcFileExtensions
888
889   <SrcFilePatterns>
890      <SrcFilePattern Pattern="<pattern>" />
891   </SrcFilePatterns>
892
893This sections defines the file extensions, which will be scanned by the script.
894
895=head2 SECTION CatalogCalls
896
897   <CatalogCalls>
898      <Catalog64 or Catalog32>
899         <CatalogCall Name="<macro-name>" IdParamIdx="<id-argument-index>"
900            StringParamIdx="<format-string-index>" IdSize="<id-size>" />
901      </Catalog64 or /Catalog32>
902   </CatalogCalls>
903
904This section describes the used macro calls. It describes how the macro is
905named and where the arguments of intereset can be found by the script. There
906are different sections for 32 bit and 64 bit wide catalog calls.
907
908=head2 SECTION CatalogConfigs
909
910    <CatalogConfigs>
911        <CatalogConfig option="<option-name>" value="<value>" />
912    </CatalogConfigs>
913
914This section specifies pre-defined options for the catalog generation script.
915The options will be overruled by the respective command line options.
916
917=cut
918