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