| [7] | 1 | #!perl -w | 
|---|
| [67] | 2 | # mapsymw - mapsym wrapper form OpenWatcom map files | 
|---|
|  | 3 | # Generate IBM format map file and run mapsym | 
|---|
|  | 4 | # Writes IBM format map file to OpenWatcom map file directory | 
|---|
| [7] | 5 |  | 
|---|
| [63] | 6 | # Copyright (c) 2007-2019 Steven Levine and Associates, Inc. | 
|---|
| [7] | 7 | # All rights reserved. | 
|---|
|  | 8 |  | 
|---|
|  | 9 | # This program is free software licensed under the terms of the GNU | 
|---|
|  | 10 | # General Public License.  The GPL Software License can be found in | 
|---|
|  | 11 | # gnugpl2.txt or at http://www.gnu.org/licenses/licenses.html#GPL | 
|---|
|  | 12 |  | 
|---|
|  | 13 | # 2007-07-02 SHL Baseline | 
|---|
|  | 14 | # 2008-12-14 SHL Ensure symbols sorted by value - some apps care | 
|---|
|  | 15 | # 2012-03-19 SHL Segment names must be uppercase for pmdf | 
|---|
|  | 16 | # 2014-06-13 SHL Correct typos | 
|---|
|  | 17 | # 2014-11-01 SHL Generate ibm map files in watcom map file directory | 
|---|
|  | 18 | # 2015-01-31 SHL Use unix slashes to keep kLIBC sh.exe happy | 
|---|
| [63] | 19 | # 2019-03-02 SHL More C++ logic | 
|---|
|  | 20 | # 2019-03-03 SHL Rework dup logic and sym length limiting | 
|---|
| [67] | 21 | # 2019-03-22 SHL Disable debug code | 
|---|
|  | 22 | # 2019-04-09 SHL Comments | 
|---|
| [7] | 23 |  | 
|---|
|  | 24 | # mapsym requires each segment to have at least 1 symbol | 
|---|
|  | 25 | # mapsym requires 32 bit segments to have at least 1 symbol with offset > 65K | 
|---|
|  | 26 | # we generate dummy symbols to enforce this | 
|---|
|  | 27 | # mapsym does not understand segment 0 | 
|---|
|  | 28 | # we generate Imp flags to support this | 
|---|
|  | 29 |  | 
|---|
|  | 30 | use strict; | 
|---|
|  | 31 | use warnings; | 
|---|
|  | 32 |  | 
|---|
|  | 33 | # use Package::Subpackage Options; | 
|---|
|  | 34 | use POSIX qw(strftime); | 
|---|
|  | 35 | use Getopt::Std; | 
|---|
|  | 36 | use File::Basename; | 
|---|
|  | 37 |  | 
|---|
| [63] | 38 | our $g_version = '0.4'; | 
|---|
| [7] | 39 |  | 
|---|
|  | 40 | our $g_cmdname; | 
|---|
|  | 41 | our @g_mapfiles;                        # All map files | 
|---|
|  | 42 | our $g_mapfile;                         # Current .map file name | 
|---|
|  | 43 |  | 
|---|
|  | 44 | &initialize; | 
|---|
|  | 45 |  | 
|---|
|  | 46 | our %g_opts; | 
|---|
|  | 47 |  | 
|---|
|  | 48 | &scan_args; | 
|---|
|  | 49 |  | 
|---|
|  | 50 | print "\n"; | 
|---|
|  | 51 |  | 
|---|
|  | 52 | foreach $g_mapfile (@g_mapfiles) { | 
|---|
|  | 53 | $g_mapfile =~ tr|\\|/|;       # 2015-01-31 SHL Unix slashes for sh.exe | 
|---|
|  | 54 | &mapsym; | 
|---|
|  | 55 | } | 
|---|
|  | 56 |  | 
|---|
|  | 57 | exit; | 
|---|
|  | 58 |  | 
|---|
|  | 59 | # end main | 
|---|
|  | 60 |  | 
|---|
|  | 61 | #=== initialize() Initialize globals === | 
|---|
|  | 62 |  | 
|---|
|  | 63 | sub initialize { | 
|---|
|  | 64 |  | 
|---|
|  | 65 | &set_cmd_name; | 
|---|
|  | 66 |  | 
|---|
|  | 67 | } # initialize | 
|---|
|  | 68 |  | 
|---|
|  | 69 | #=== mapsym() Generate work file, run mapsym on work file === | 
|---|
|  | 70 |  | 
|---|
|  | 71 | sub mapsym { | 
|---|
|  | 72 |  | 
|---|
|  | 73 | # Isolate map file basename | 
|---|
|  | 74 | my $mapid = basename($g_mapfile); | 
|---|
|  | 75 | $mapid =~ s/\.[^.]*$//;               # Strip ext | 
|---|
|  | 76 | verbose_msg("\nProcessing $mapid"); | 
|---|
|  | 77 |  | 
|---|
|  | 78 | fatal("$g_mapfile does not exist.") if ! -f $g_mapfile; | 
|---|
|  | 79 |  | 
|---|
|  | 80 | open MAPFILE, $g_mapfile or die "open $g_mapfile $!"; | 
|---|
|  | 81 |  | 
|---|
|  | 82 | my $g_wrkfile = $g_mapfile . '-ibm'; | 
|---|
| [63] | 83 | $g_wrkfile =~ s/-watcom-ibm$/-ibm/;   # In case renamed from .map | 
|---|
| [7] | 84 | unlink $g_wrkfile || die "unlink $g_wrkfile $!" if -f $g_wrkfile; | 
|---|
|  | 85 | open WRKFILE, ">$g_wrkfile" or die "open $g_wrkfile $!"; | 
|---|
|  | 86 |  | 
|---|
|  | 87 | my $modname; | 
|---|
|  | 88 | my $state = ''; | 
|---|
|  | 89 | my $segcnt = 0; | 
|---|
|  | 90 | my $symcnt = 0; | 
|---|
|  | 91 | my $is32bit; | 
|---|
|  | 92 | my %segsinfo; | 
|---|
|  | 93 | my %syms; | 
|---|
|  | 94 | my $segnum; | 
|---|
|  | 95 | my $offset; | 
|---|
|  | 96 | my $segaddr; | 
|---|
|  | 97 |  | 
|---|
|  | 98 | my $segfmt; | 
|---|
|  | 99 | my $symfmt; | 
|---|
|  | 100 |  | 
|---|
|  | 101 | while (<MAPFILE>) { | 
|---|
|  | 102 |  | 
|---|
| [63] | 103 | chomp;                              # EOL | 
|---|
| [7] | 104 |  | 
|---|
|  | 105 | if (/Executable Image: (\S+)\.\w+$/) { | 
|---|
|  | 106 | $modname = $1; | 
|---|
|  | 107 | print WRKFILE "Generated by $g_cmdname from $g_mapfile on ", | 
|---|
|  | 108 | strftime('%A, %B %d, %Y at %I:%M %p', localtime), "\n\n"; | 
|---|
|  | 109 | print WRKFILE " $modname\n"; | 
|---|
|  | 110 | } | 
|---|
|  | 111 |  | 
|---|
|  | 112 | $state = 'segments' | 
|---|
|  | 113 | if /Segment                Class          Group          Address         Size/; | 
|---|
|  | 114 |  | 
|---|
|  | 115 | $state = 'addresses' if /Address        Symbol/; | 
|---|
|  | 116 |  | 
|---|
|  | 117 | # Skip don't cares | 
|---|
|  | 118 | next if /^=/; | 
|---|
|  | 119 | next if /^ /; | 
|---|
|  | 120 | next if /^$/; | 
|---|
|  | 121 |  | 
|---|
|  | 122 | if ($state eq 'segments') { | 
|---|
|  | 123 | # In | 
|---|
|  | 124 | # Segment                Class          Group          Address         Size | 
|---|
|  | 125 | # _TEXT16                CODE           AUTO           0001:00000000   00000068 | 
|---|
|  | 126 | # Out | 
|---|
|  | 127 | # 0        1         2         3         4         5         6 | 
|---|
|  | 128 | # 123456789012345678901234567890123456789012345678901234567890 | 
|---|
|  | 129 | #  Start         Length     Name                   Class | 
|---|
|  | 130 | #  0001:00000000 000000030H _MSGSEG32              CODE 32-bit | 
|---|
|  | 131 |  | 
|---|
|  | 132 | if (/^(\w+)\s+(\w+)\s+\w+\s+([[:xdigit:]]+):([[:xdigit:]]+)\s+([[:xdigit:]]+)$/) { | 
|---|
|  | 133 | my $segname = $1; | 
|---|
|  | 134 | my $class = $2; | 
|---|
|  | 135 | $segnum = $3;                   # Has leading 0's | 
|---|
|  | 136 | $offset = $4; | 
|---|
|  | 137 | my $seglen = $5; | 
|---|
|  | 138 |  | 
|---|
|  | 139 | $segaddr = "$segnum:$offset"; | 
|---|
|  | 140 |  | 
|---|
|  | 141 | if (!$segcnt) { | 
|---|
|  | 142 | # First segment - determine address size (16/32 bit) | 
|---|
|  | 143 | $is32bit = length($offset) == 8; | 
|---|
|  | 144 | # Output title | 
|---|
|  | 145 | print WRKFILE "\n"; | 
|---|
|  | 146 | if ($is32bit) { | 
|---|
|  | 147 | print WRKFILE " Start         Length     Name                   Class\n"; | 
|---|
|  | 148 | $segfmt = " %13s 0%8sH %-22s %s\n"; | 
|---|
|  | 149 | $symfmt = " %13s  %3s  %s\n"; | 
|---|
|  | 150 | } else { | 
|---|
|  | 151 | print WRKFILE " Start     Length Name                   Class\n"; | 
|---|
|  | 152 | $segfmt = " %9s 0%4sH %-22s %s\n"; | 
|---|
|  | 153 | $symfmt = " %9s  %3s  %s\n"; | 
|---|
|  | 154 | } | 
|---|
|  | 155 | } | 
|---|
|  | 156 |  | 
|---|
|  | 157 | $seglen = substr($5, -4) if !$is32bit; | 
|---|
|  | 158 |  | 
|---|
|  | 159 | printf WRKFILE $segfmt, $segaddr, $seglen, $segname, $class; | 
|---|
|  | 160 | $segcnt++; | 
|---|
|  | 161 | } | 
|---|
|  | 162 | } # if segments | 
|---|
|  | 163 |  | 
|---|
|  | 164 | if ($state eq 'addresses') { | 
|---|
|  | 165 | # In | 
|---|
|  | 166 | # Address        Symbol | 
|---|
|  | 167 | # 0002:0004ae46+ ArcTextProc | 
|---|
|  | 168 | # 0002:0d11+     void near IoctlAudioCapability( __2bd9g9REQPACKET far *, short unsigned ) | 
|---|
|  | 169 | # Out | 
|---|
|  | 170 | # 0        1         2         3         4         5         6 | 
|---|
|  | 171 | # 123456789012345678901234567890123456789012345678901234567890 | 
|---|
|  | 172 | #   Address         Publics by Value | 
|---|
|  | 173 | #  0000:00000000  Imp  WinEmptyClipbrd      (PMWIN.733) | 
|---|
|  | 174 | #  0002:0001ED40       __towlower_dummy | 
|---|
|  | 175 | if (/^([[:xdigit:]]+):([[:xdigit:]]+)[+*]?\s+(.+)$/) { | 
|---|
|  | 176 | $segnum = $1; | 
|---|
|  | 177 | $offset = $2; | 
|---|
|  | 178 | my $sym = $3; | 
|---|
|  | 179 |  | 
|---|
|  | 180 | my $seginfo; | 
|---|
| [63] | 181 | if (defined($segsinfo{$segnum})) { | 
|---|
|  | 182 | $seginfo = $segsinfo{$segnum}; | 
|---|
|  | 183 | } else { | 
|---|
| [7] | 184 | $seginfo = {max_offset => 0, | 
|---|
|  | 185 | symcnt => 0}; | 
|---|
|  | 186 | } | 
|---|
|  | 187 |  | 
|---|
|  | 188 | my $n = hex $offset; | 
|---|
|  | 189 | # Remember max symbol offset | 
|---|
|  | 190 | $seginfo->{max_offset} = $n if $n > $seginfo->{max_offset}; | 
|---|
|  | 191 | $seginfo->{symcnt}++; | 
|---|
|  | 192 |  | 
|---|
| [63] | 193 | $segsinfo{$segnum} = $seginfo; | 
|---|
| [7] | 194 |  | 
|---|
|  | 195 | $segaddr = "$segnum:$offset"; | 
|---|
|  | 196 |  | 
|---|
|  | 197 | # Convert C++ symbols to something mapsym will accept | 
|---|
| [67] | 198 | # warn "\n$sym\n"; | 
|---|
| [7] | 199 |  | 
|---|
|  | 200 | $_ = $sym; | 
|---|
|  | 201 |  | 
|---|
| [63] | 202 | s/\s+\(.*\)//;                  # Drop (...) comments preceeded by whitespace | 
|---|
| [7] | 203 |  | 
|---|
| [63] | 204 | # Make C++ compatible with mapsym | 
|---|
| [7] | 205 |  | 
|---|
| [63] | 206 | # Drop keywords and decorations | 
|---|
|  | 207 | s/\b(near|int|short|unsigned|long|void|char|const|wchar_t)\b\s*[*&]?\s*//g; | 
|---|
| [7] | 208 |  | 
|---|
| [63] | 209 | # s/<[^<>]+>//g;                # Replace < > templates with nothing | 
|---|
|  | 210 |  | 
|---|
|  | 211 | s/std::\s*/std/g;               # Replace std:: with std | 
|---|
|  | 212 | s/::~/__x/g;                    # Replace destructor ::~ with __x | 
|---|
|  | 213 | s/::/__/g;                      # Replace :: with __ | 
|---|
|  | 214 |  | 
|---|
|  | 215 | s/[\[\]()<>,]/ /g;              # Replace [ ] < > and , with spaces | 
|---|
|  | 216 | s/\s+/_/g;                      # Replace whitespace with single _ | 
|---|
|  | 217 | s/__+/_/g;                      # Replace multiple _ with single _ | 
|---|
|  | 218 |  | 
|---|
| [7] | 219 | # Drop leading and trailing _ to match source code | 
|---|
|  | 220 |  | 
|---|
| [63] | 221 | s/^_//;                         # Drop leading _ (cdecl) | 
|---|
|  | 222 | s/_$//;                         # Drop trailing _ (watcall) | 
|---|
| [7] | 223 |  | 
|---|
| [63] | 224 | $_ = substr($_, 0, 63) if length($_) > 63;              # limit | 
|---|
| [7] | 225 |  | 
|---|
| [67] | 226 | # warn "\n$_\n"; | 
|---|
| [63] | 227 |  | 
|---|
| [7] | 228 | # Prune some libc symbols to avoid mapsym overflows | 
|---|
|  | 229 | if ($mapid =~ /libc06/) { | 
|---|
|  | 230 | # 0001:000b73e0  __ZNSt7codecvtIcc11__mbstate_tEC2Ej | 
|---|
|  | 231 | # next if / [0-9A-F]{4}:[0-9A-F]{8} {7}S/; | 
|---|
|  | 232 | next if /\b__Z/;              # Prune libstdc++ | 
|---|
|  | 233 | } | 
|---|
|  | 234 |  | 
|---|
|  | 235 | if (!$symcnt) { | 
|---|
|  | 236 | # First symbol - output title | 
|---|
|  | 237 | print WRKFILE "\n"; | 
|---|
|  | 238 | if ($is32bit) { | 
|---|
|  | 239 | print WRKFILE "  Address         Publics by Value\n"; | 
|---|
|  | 240 | } else { | 
|---|
|  | 241 | print WRKFILE "  Address     Publics by Value\n"; | 
|---|
|  | 242 | } | 
|---|
|  | 243 | } | 
|---|
|  | 244 |  | 
|---|
|  | 245 | $syms{$segaddr} = $_; | 
|---|
|  | 246 |  | 
|---|
|  | 247 | $symcnt++; | 
|---|
|  | 248 | } | 
|---|
|  | 249 | } # if addresses | 
|---|
|  | 250 |  | 
|---|
|  | 251 | } # while lines | 
|---|
|  | 252 |  | 
|---|
|  | 253 | close MAPFILE; | 
|---|
|  | 254 |  | 
|---|
|  | 255 | # Sort segments | 
|---|
|  | 256 |  | 
|---|
|  | 257 | my @keys = sort keys %segsinfo; | 
|---|
|  | 258 | if (@keys) { | 
|---|
|  | 259 | my $maxseg = pop @keys; | 
|---|
|  | 260 | @keys = '0000'..$maxseg; | 
|---|
|  | 261 | } | 
|---|
|  | 262 |  | 
|---|
|  | 263 | # Generate dummy symbols for 32-bit segments smaller than 64KB | 
|---|
|  | 264 |  | 
|---|
|  | 265 | foreach $segnum (@keys) { | 
|---|
|  | 266 | if ($segnum != 0) { | 
|---|
|  | 267 | my $seginfo; | 
|---|
|  | 268 | if (defined($segsinfo{$segnum})) { | 
|---|
|  | 269 | $seginfo = $segsinfo{$segnum}; | 
|---|
| [63] | 270 | } else { | 
|---|
| [7] | 271 | $seginfo = {max_offset => 0, | 
|---|
|  | 272 | symcnt => 0}; | 
|---|
|  | 273 | } | 
|---|
|  | 274 | if ($seginfo->{symcnt} == 0) { | 
|---|
|  | 275 | warn "Segment $segnum has no symbols - generating dummy symbol\n"; | 
|---|
|  | 276 | $_ = "SEG${segnum}_dummy"; | 
|---|
|  | 277 | if ($is32bit) { | 
|---|
|  | 278 | $segaddr = "$segnum:00010000"; | 
|---|
|  | 279 | } else { | 
|---|
|  | 280 | $segaddr = "$segnum:0000"; | 
|---|
|  | 281 | } | 
|---|
|  | 282 | $syms{$segaddr} = $_; | 
|---|
|  | 283 | $symcnt++; | 
|---|
|  | 284 | } elsif ($is32bit && $seginfo->{max_offset} < 0x10000) { | 
|---|
|  | 285 | warn "32 bit segment $segnum is smaller than 64K - generating dummy symbol\n"; | 
|---|
|  | 286 | $_ = "SEG${segnum}_dummy"; | 
|---|
|  | 287 | $segaddr = "$segnum:00010000"; | 
|---|
|  | 288 | $syms{$segaddr} = $_; | 
|---|
|  | 289 | $symcnt++; | 
|---|
|  | 290 | } | 
|---|
|  | 291 | } | 
|---|
| [63] | 292 | } # foreach $segnum | 
|---|
| [7] | 293 |  | 
|---|
|  | 294 | # Generate symbols by value listing | 
|---|
| [63] | 295 | # FIXME to warn if may overflow 64K limit | 
|---|
| [7] | 296 |  | 
|---|
|  | 297 | @keys = sort keys %syms; | 
|---|
| [63] | 298 | my %used; | 
|---|
| [7] | 299 | foreach $segaddr (@keys) { | 
|---|
|  | 300 | my $sym = $syms{$segaddr}; | 
|---|
|  | 301 | my $imp = substr($segaddr, 0, 4) eq '0000' ? 'Imp' : ''; | 
|---|
| [63] | 302 | # Avoid dups | 
|---|
|  | 303 | if (defined $used{$sym}) { | 
|---|
|  | 304 | my $seq = $used{$sym}; | 
|---|
| [7] | 305 | $seq++; | 
|---|
| [63] | 306 | $used{$sym} = $seq; | 
|---|
| [7] | 307 | $sym = "${sym}_$seq"; | 
|---|
| [63] | 308 | } else { | 
|---|
|  | 309 | $used{$sym} = 0; | 
|---|
| [7] | 310 | } | 
|---|
|  | 311 | printf WRKFILE $symfmt, $segaddr, $imp, $sym; | 
|---|
| [63] | 312 | } # foreach | 
|---|
| [7] | 313 |  | 
|---|
|  | 314 | close WRKFILE; | 
|---|
|  | 315 |  | 
|---|
|  | 316 | die "Can not locate module name.  $g_mapfile is probably not a Watcom map file\n" if !defined($modname); | 
|---|
|  | 317 |  | 
|---|
|  | 318 | my $symfile = "$mapid.sym"; | 
|---|
|  | 319 | unlink $symfile || die "unlink $symfile $!" if -f $symfile; | 
|---|
|  | 320 |  | 
|---|
|  | 321 | warn "Processed $segcnt segments and $symcnt symbols for $modname\n"; | 
|---|
|  | 322 |  | 
|---|
|  | 323 | system("mapsym $g_wrkfile"); | 
|---|
|  | 324 |  | 
|---|
|  | 325 | } # mapsym | 
|---|
|  | 326 |  | 
|---|
|  | 327 | #=== scan_args(cmdLine) Scan command line === | 
|---|
|  | 328 |  | 
|---|
|  | 329 | sub scan_args { | 
|---|
|  | 330 |  | 
|---|
|  | 331 | getopts('dhtvV', \%g_opts) || &usage; | 
|---|
|  | 332 |  | 
|---|
|  | 333 | &help if $g_opts{h}; | 
|---|
|  | 334 |  | 
|---|
|  | 335 | if ($g_opts{V}) { | 
|---|
|  | 336 | print "$g_cmdname v$g_version"; | 
|---|
|  | 337 | exit; | 
|---|
|  | 338 | } | 
|---|
|  | 339 |  | 
|---|
|  | 340 | my $arg; | 
|---|
|  | 341 |  | 
|---|
|  | 342 | for $arg (@ARGV) { | 
|---|
|  | 343 | my @maps = glob($arg); | 
|---|
|  | 344 | usage("File $arg not found") if @maps == 0; | 
|---|
|  | 345 | push @g_mapfiles, @maps; | 
|---|
|  | 346 | } # for arg | 
|---|
|  | 347 |  | 
|---|
|  | 348 | } # scan_args | 
|---|
|  | 349 |  | 
|---|
|  | 350 | #=== help() Display scan_args usage help exit routine === | 
|---|
|  | 351 |  | 
|---|
|  | 352 | sub help { | 
|---|
|  | 353 |  | 
|---|
|  | 354 | print <<EOD; | 
|---|
|  | 355 | Generate .sym file for Watcom map files. | 
|---|
|  | 356 | Generates temporary map file reformatted for mapsym and | 
|---|
|  | 357 | invokes mapsym to process this map file. | 
|---|
|  | 358 |  | 
|---|
|  | 359 | Usage: $g_cmdname [-d] [-h] [-v] [-V] mapfile... | 
|---|
|  | 360 | -d      Display debug messages | 
|---|
|  | 361 | -h      Display this message | 
|---|
|  | 362 | -v      Display progress messages | 
|---|
|  | 363 | -V      Display version | 
|---|
|  | 364 |  | 
|---|
|  | 365 | mapfile List of map files to process | 
|---|
|  | 366 | EOD | 
|---|
|  | 367 |  | 
|---|
|  | 368 | exit 255; | 
|---|
|  | 369 |  | 
|---|
|  | 370 | } # help | 
|---|
|  | 371 |  | 
|---|
|  | 372 | #=== usage(message) Report Scanargs usage error exit routine === | 
|---|
|  | 373 |  | 
|---|
|  | 374 | sub usage { | 
|---|
|  | 375 |  | 
|---|
|  | 376 | my $msg = shift; | 
|---|
|  | 377 | print "\n$msg\n" if $msg; | 
|---|
|  | 378 | print <<EOD; | 
|---|
|  | 379 |  | 
|---|
|  | 380 | Usage: $g_cmdname [-d] [-h] [-v] [-V] mapfile... | 
|---|
|  | 381 | EOD | 
|---|
|  | 382 | exit 255; | 
|---|
|  | 383 |  | 
|---|
|  | 384 | } # usage | 
|---|
|  | 385 |  | 
|---|
|  | 386 | #========================================================================== | 
|---|
|  | 387 | #=== SkelFunc standards - Delete unused - Move modified above this mark === | 
|---|
|  | 388 | #========================================================================== | 
|---|
|  | 389 |  | 
|---|
|  | 390 | #=== verbose_msg(message) Display message if verbose === | 
|---|
|  | 391 |  | 
|---|
|  | 392 | sub verbose_msg { | 
|---|
|  | 393 | if ($g_opts{v}) { | 
|---|
|  | 394 | my $msg = shift; | 
|---|
|  | 395 | if (defined $msg) { | 
|---|
|  | 396 | print STDOUT "$msg\n"; | 
|---|
|  | 397 | } else { | 
|---|
|  | 398 | print STDOUT "\n"; | 
|---|
|  | 399 | } | 
|---|
|  | 400 | } | 
|---|
|  | 401 | } # verbose_msg | 
|---|
|  | 402 |  | 
|---|
|  | 403 | #========================================================================== | 
|---|
|  | 404 | #=== SkelPerl standards - Delete unused - Move modified above this mark === | 
|---|
|  | 405 | #========================================================================== | 
|---|
|  | 406 |  | 
|---|
|  | 407 | #=== fatal(message) Report fatal error and exit === | 
|---|
|  | 408 |  | 
|---|
|  | 409 | sub fatal { | 
|---|
|  | 410 | my $msg = shift; | 
|---|
|  | 411 | print "\n"; | 
|---|
|  | 412 | print STDERR "$g_cmdname: $msg\a\n"; | 
|---|
|  | 413 | exit 254; | 
|---|
|  | 414 |  | 
|---|
|  | 415 | } # fatal | 
|---|
|  | 416 |  | 
|---|
|  | 417 | #=== set_cmd_name() Set $g_cmdname to script name less path and extension === | 
|---|
|  | 418 |  | 
|---|
|  | 419 | sub set_cmd_name { | 
|---|
|  | 420 | $g_cmdname = $0; | 
|---|
|  | 421 | $g_cmdname = basename($g_cmdname); | 
|---|
|  | 422 | $g_cmdname =~ s/\.[^.]*$//;           # Chop ext | 
|---|
|  | 423 |  | 
|---|
|  | 424 | } # set_cmd_name | 
|---|
|  | 425 |  | 
|---|
|  | 426 | # The end | 
|---|