Annotation of embedaddon/ntp/scripts/monitoring/ntploopwatch, revision 1.1

1.1     ! misho       1: #!/usr/bin/perl -w
        !             2: ;# --*-perl-*--
        !             3: ;#
        !             4: ;# /src/NTP/ntp4-dev/scripts/monitoring/ntploopwatch,v 4.7 2004/11/14 16:11:05 kardel RELEASE_20050508_A
        !             5: ;#
        !             6: ;# process loop filter statistics file and either
        !             7: ;#     - show statistics periodically using gnuplot
        !             8: ;#     - or print a single plot
        !             9: ;#
        !            10: ;#  Copyright (c) 1992-1998 
        !            11: ;#  Rainer Pruy, Friedrich-Alexander Universität Erlangen-Nürnberg
        !            12: ;#
        !            13: ;#
        !            14: ;#############################################################
        !            15: $0 =~ s!^.*/([^/]+)$!$1!;
        !            16: $F = ' ' x length($0);
        !            17: $|=1;
        !            18: 
        !            19: $ENV{'SHELL'} = '/bin/sh'; # use bourne shell
        !            20: 
        !            21: undef($config);
        !            22: undef($workdir);
        !            23: undef($PrintIt);
        !            24: undef($samples);
        !            25: undef($StartTime);
        !            26: undef($EndTime);
        !            27: ($a,$b) if 0;                  # keep -w happy
        !            28: $usage = <<"E-O-P";
        !            29: usage:
        !            30:   to watch statistics permanently:
        !            31:      $0 [-v[<level>]] [-c <config-file>] [-d <working-dir>]
        !            32:      $F [-h <hostname>]
        !            33: 
        !            34:   to get a single print out specify also
        !            35:      $F -P[<printer>] [-s<samples>]
        !            36:      $F               [-S <start-time>] [-E <end-time>]
        !            37:      $F               [-Y <MaxOffs>] [-y <MinOffs>]
        !            38: 
        !            39: If You like long option names, You can use:
        !            40:     -help
        !            41:     -c    +config
        !            42:     -d    +directory
        !            43:     -h    +host
        !            44:     -v    +verbose[=<level>]
        !            45:     -P    +printer[=<printer>]
        !            46:     -s    +samples[=<samples>]
        !            47:     -S    +starttime
        !            48:     -E    +endtime
        !            49:     -Y    +maxy
        !            50:     -y    +miny
        !            51: 
        !            52: If <printer> contains a '/' (slash character) output is directed to 
        !            53: a file of this name instead of delivered to a printer.
        !            54: E-O-P
        !            55: 
        !            56: ;# add directory to look for lr.pl and timelocal.pl (in front of current list)
        !            57: unshift(@INC,".");
        !            58: 
        !            59: require "lr.pl";       # linear regresion routines
        !            60: 
        !            61: $MJD_1970 = 40587;             # from ntp.h (V3)
        !            62: $RecordSize = 48;              # usually a line fits into 42 bytes
        !            63: $MinClip = 1;          # clip Y scales with greater range than this
        !            64: 
        !            65: ;# largest extension of Y scale from mean value, factor for standart deviation
        !            66: $FuzzLow = 2.2;                        # for side closer to zero
        !            67: $FuzzBig = 1.8;                        # for side farther from zero
        !            68: 
        !            69: require "ctime.pl";
        !            70: require "timelocal.pl";
        !            71: ;# early distributions of ctime.pl had a bug
        !            72: $ENV{'TZ'} = 'MET' unless defined $ENV{'TZ'} || $[ > 4.010;
        !            73: if (defined(@ctime'MoY))
        !            74: {
        !            75:   *Month=*ctime'MoY;
        !            76:   *Day=*ctime'DoW;
        !            77: }                                      # ' re-sync emacs fontification
        !            78: else
        !            79: {
        !            80:   @Month = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
        !            81:   @Day   = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
        !            82: }
        !            83: print @ctime'DoW if 0; # ' re-sync emacs fontification
        !            84: 
        !            85: ;# max number of days per month
        !            86: @MaxNumDaysPerMonth = (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
        !            87: 
        !            88: ;# config settable parameters
        !            89: $delay = 60;
        !            90: $srcprefix = "./var\@\$STATHOST/loopstats.";
        !            91: $showoffs = 1;
        !            92: $showfreq = 1;
        !            93: $showcmpl = 0;
        !            94: $showoreg = 0;
        !            95: $showfreg = 0;
        !            96: undef($timebase);
        !            97: undef($freqbase);
        !            98: undef($cmplscale);
        !            99: undef($MaxY);
        !           100: undef($MinY);
        !           101: $deltaT  = 512; # indicate sample data gaps greater than $deltaT seconds
        !           102: $verbose = 1;
        !           103: 
        !           104: while($_ = shift(@ARGV))
        !           105: {
        !           106:     (/^[+-]help$/) && die($usage);
        !           107:     
        !           108:     (/^-c$/ || /^\+config$/) &&
        !           109:        (@ARGV || die($usage), $config = shift(@ARGV), next);
        !           110: 
        !           111:     (/^-d$/ || /^\+directory$/) &&
        !           112:        (@ARGV || die($usage), $workdir = shift(@ARGV), next);
        !           113: 
        !           114:     (/^-h$/ || /^\+host$/) &&
        !           115:        (@ARGV || die($usage), $STATHOST = shift, next);
        !           116:     
        !           117:     (/^-v(\d*)$/ || /^\+verbose=?(\d*)$/) &&
        !           118:        ($verbose=($1 eq "") ? 1 : $1, next);
        !           119: 
        !           120:     (/^-P(\S*)$/ || /^\+[Pp]rinter=?(\S*)$/) &&
        !           121:        ($PrintIt = $1, $verbose==1 && ($verbose = 0), next);
        !           122: 
        !           123:     (/^-s(\d*)$/ || /^\+samples=?(\d*)$/) &&
        !           124:        (($samples = ($1 eq "") ? (shift || die($usage)): $1), next);
        !           125:     
        !           126:     (/^-S$/ || /^\+[Ss]tart[Tt]ime$/) &&
        !           127:        (@ARGV || die($usage), $StartTime=&date_time_spec2seconds(shift),next);
        !           128: 
        !           129:     (/^-E$/ || /^\+[Ee]nd[Tt]ime$/) &&
        !           130:        (@ARGV || die($usage), $EndTime = &date_time_spec2seconds(shift),next);
        !           131:     
        !           132:     (/^-Y$/ || /^\+[Mm]ax[Yy]$/) &&
        !           133:        (@ARGV || die($usage), $MaxY = shift, next);
        !           134:     
        !           135:     (/^-y$/ || /^\+[Mm]in[Yy]$/) &&
        !           136:        (@ARGV || die($usage), $MinY = shift, next);
        !           137:     
        !           138:     die("$0: unexpected argument \"$_\"\n$usage");
        !           139: }
        !           140: 
        !           141: if (defined($workdir))
        !           142: {
        !           143:   chdir($workdir) ||
        !           144:       die("$0: failed to change working dir to \"$workdir\": $!\n");
        !           145: }
        !           146: 
        !           147: $PrintIt = "ps" if defined($PrintIt) && $PrintIt eq "";
        !           148: 
        !           149: if (!defined($PrintIt))
        !           150: {
        !           151:     defined($samples) &&
        !           152:        print "WARNING: your samples value may be shadowed by config file settings\n";
        !           153:     defined($StartTime) &&
        !           154:        print "WARNING: your StartTime value may be shadowed by config file settings\n";
        !           155:     defined($EndTime) &&
        !           156:        print "WARNING: your EndTime value may be shadowed by config file settings\n";
        !           157:     defined($MaxY) &&
        !           158:        print "WARNING: your MaxY value may be shadowed by config file settings\n";
        !           159:     defined($MinY) &&
        !           160:        print "WARNING: your MinY value may be shadowed by config file settings\n";
        !           161:        
        !           162:     ;# check operating environment
        !           163:     ;# 
        !           164:     ;# gnuplot usually has X support
        !           165:     ;# I vaguely remember there was one with sunview support
        !           166:     ;#
        !           167:     ;# If Your plotcmd can display graphics using some other method
        !           168:     ;# (Tek window,..) fix the following test
        !           169:     ;# (or may be, just disable it)
        !           170:     ;#
        !           171:     !(defined($ENV{'DISPLAY'}) || defined($ENV{'WINDOW_PARENT'})) &&
        !           172:        die("Need window system to monitor statistics\n");
        !           173: }
        !           174: 
        !           175: ;# configuration file
        !           176: $config = "loopwatch.config" unless defined($config);
        !           177: ($STATHOST = $config) =~ s!.*loopwatch\.config.([^/\.]*)$!$1!
        !           178:     unless defined($STATHOST);
        !           179: ($STATTAG = $STATHOST) =~ s/^([^\.\*\s]+)\..*$/$1/;
        !           180: 
        !           181: $srcprefix =~ s/\$STATHOST/$STATHOST/g;
        !           182: 
        !           183: ;# plot command 
        !           184: @plotcmd=("gnuplot",
        !           185:          '-title', "Ntp loop filter statistics $STATHOST",
        !           186:          '-name', "NtpLoopWatch_$STATTAG");
        !           187: $tmpfile = "/tmp/ntpstat.$$";
        !           188: 
        !           189: ;# other variables
        !           190: $doplot = "";  # assembled command for @plotcmd to display plot
        !           191: undef($laststat);
        !           192: 
        !           193: ;# plot value ranges
        !           194: undef($mintime);
        !           195: undef($maxtime);
        !           196: undef($minoffs);
        !           197: undef($maxoffs);
        !           198: undef($minfreq);
        !           199: undef($maxfreq);
        !           200: undef($mincmpl);
        !           201: undef($maxcmpl);
        !           202: undef($miny);
        !           203: undef($maxy);
        !           204: 
        !           205: ;# stop operation if plot command dies
        !           206: sub sigchld
        !           207: {
        !           208:   local($pid) = wait;
        !           209:   unlink($tmpfile);
        !           210:   warn(sprintf("%s: %s died: exit status: %d signal %d\n",
        !           211:              $0,
        !           212:               (defined($Plotpid) && $Plotpid == $pid)
        !           213:               ? "plotcmd" : "unknown child $pid",
        !           214:               $?>>8,$? & 0xff)) if $?;
        !           215:   exit(1) if $? && defined($Plotpid) && $pid == $Plotpid;
        !           216: }
        !           217: &sigchld if 0;
        !           218: $SIG{'CHLD'} = "sigchld";
        !           219: $SIG{'CLD'} = "sigchld";
        !           220: 
        !           221: sub abort
        !           222: {
        !           223:   unlink($tmpfile);
        !           224:   defined($Plotpid) && kill('TERM',$Plotpid);
        !           225:   die("$0: received signal SIG$_[$[] - exiting\n");
        !           226: }
        !           227: &abort if 0;   # make -w happy - &abort IS used
        !           228: $SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = $SIG{'PIPE'} = "abort";
        !           229: 
        !           230: ;#
        !           231: sub abs
        !           232: {
        !           233:   ($_[$[] < 0) ? -($_[$[]) : $_[$[];
        !           234: }
        !           235: 
        !           236: sub boolval
        !           237: {
        !           238:   local($v) = ($_[$[]);
        !           239: 
        !           240:   return 1 if ($v eq 'yes') || ($v eq 'y');
        !           241:   return 1 if ($v =~ /^[0-9]*$/) && ($v != 0);
        !           242:   return 0;
        !           243: }
        !           244: 
        !           245: ;#####################
        !           246: ;# start of real work 
        !           247: 
        !           248: print "starting plot command (" . join(" ",@plotcmd) . ")\n" if $verbose > 1;
        !           249: 
        !           250: $Plotpid = open(PLOT,"|-");
        !           251: select((select(PLOT),$|=1)[$[]);       # make PLOT line bufferd
        !           252: 
        !           253: defined($Plotpid) ||
        !           254:     die("$0: failed to start plot command: $!\n");
        !           255: 
        !           256: unless ($Plotpid)
        !           257: {
        !           258:    ;# child == plot command
        !           259:    close(STDOUT);
        !           260:    open(STDOUT,">&STDERR") ||
        !           261:        die("$0: failed to redirect STDOUT of plot command: $!\n");
        !           262:    
        !           263:    print STDOUT "plot command running as $$\n";
        !           264: 
        !           265:    exec @plotcmd;
        !           266:    die("$0: failed to exec (@plotcmd): $!\n");
        !           267:    exit(1); # in case ...
        !           268: }
        !           269: 
        !           270: sub read_config
        !           271: {
        !           272:   local($at) = (stat($config))[$[+9];
        !           273:   local($_,$c,$v);
        !           274: 
        !           275:   (undef($laststat),(print("stat $config failed: $!\n")),return) if ! defined($at);
        !           276:   return if (defined($laststat) && ($laststat == $at));
        !           277:   $laststat = $at;
        !           278: 
        !           279:   print "reading configuration from \"$config\"\n" if $verbose;
        !           280: 
        !           281:   open(CF,"<$config") ||
        !           282:       (warn("$0: failed to read \"$config\" - using old settings ($!)\n"),
        !           283:        return);
        !           284:   while(<CF>)
        !           285:   {
        !           286:     chop;
        !           287:     s/^([^\#]*[^\#\s]?)\s*\#.*$//;
        !           288:     next if /^\s*$/;
        !           289: 
        !           290:     s/^\s*([^=\s]*)\s*=\s*(.*\S)\s*$/$1=$2/;
        !           291: 
        !           292:     ($c,$v) = split(/=/,$_,2);
        !           293:     print "processing \"$c=$v\"\n" if $verbose > 3;
        !           294:     ($c eq "delay") && ($delay = $v,1) && next;
        !           295:     ($c eq 'samples') && (!defined($PrintIt) || !defined($samples)) &&
        !           296:        ($samples = $v,1) && next;
        !           297:     ($c eq 'srcprefix') && (($srcprefix=$v)=~s/\$STATHOST/$STATHOST/g,1)
        !           298:        && next;
        !           299:     ($c eq 'showoffs') &&
        !           300:        ($showoffs = boolval($v),1) && next;
        !           301:     ($c eq 'showfreq') &&
        !           302:        ($showfreq = boolval($v),1) && next;
        !           303:     ($c eq 'showcmpl') &&
        !           304:        ($showcmpl = boolval($v),1) && next;
        !           305:     ($c eq 'showoreg') &&
        !           306:        ($showoreg = boolval($v),1) && next;
        !           307:     ($c eq 'showfreg') &&
        !           308:        ($showfreg = boolval($v),1) && next;
        !           309: 
        !           310:     ($c eq 'exit') && (unlink($tmpfile),die("$0: exit by config request\n"));
        !           311: 
        !           312:     ($c eq 'freqbase' ||
        !           313:      $c eq 'cmplscale') &&
        !           314:        do {
        !           315:            if (! defined($v) || $v eq "" || $v eq 'dynamic')
        !           316:            {
        !           317:              eval "undef(\$$c);";
        !           318:            }
        !           319:            else
        !           320:            {
        !           321:              eval "\$$c = \$v;";
        !           322:            }
        !           323:            next;
        !           324:        };
        !           325:     ($c eq 'timebase') &&
        !           326:        do {
        !           327:            if (! defined($v) || $v eq "" || $v eq "dynamic")
        !           328:            {
        !           329:              undef($timebase);
        !           330:            }
        !           331:            else
        !           332:            {
        !           333:              $timebase=&date_time_spec2seconds($v);
        !           334:            }
        !           335:        };
        !           336:     ($c eq 'EndTime') &&
        !           337:        do {
        !           338:            next if defined($EndTime) && defined($PrintIt);
        !           339:            if (! defined($v) || $v eq "" || $v eq "none")
        !           340:            {
        !           341:              undef($EndTime);
        !           342:            }
        !           343:            else
        !           344:            {
        !           345:              $EndTime=&date_time_spec2seconds($v);
        !           346:            }
        !           347:        };
        !           348:     ($c eq 'StartTime') &&
        !           349:        do {
        !           350:            next if defined($StartTime) && defined($PrintIt);
        !           351:            if (! defined($v) || $v eq "" || $v eq "none")
        !           352:            {
        !           353:              undef($StartTime);
        !           354:            }
        !           355:            else
        !           356:            {
        !           357:              $StartTime=&date_time_spec2seconds($v);
        !           358:            }
        !           359:        };
        !           360: 
        !           361:     ($c eq 'MaxY') &&
        !           362:        do {
        !           363:            next if defined($MaxY) && defined($PrintIt);
        !           364:            if (! defined($v) || $v eq "" || $v eq "none")
        !           365:            {
        !           366:              undef($MaxY);
        !           367:            }
        !           368:            else
        !           369:            {
        !           370:              $MaxY=$v;
        !           371:            }
        !           372:        };
        !           373: 
        !           374:     ($c eq 'MinY') &&
        !           375:        do {
        !           376:            next if defined($MinY) && defined($PrintIt);
        !           377:            if (! defined($v) || $v eq "" || $v eq "none")
        !           378:            {
        !           379:              undef($MinY);
        !           380:            }
        !           381:            else
        !           382:            {
        !           383:              $MinY=$v;
        !           384:            }
        !           385:        };
        !           386: 
        !           387:     ($c eq 'deltaT') &&
        !           388:        do {
        !           389:            if (!defined($v) || $v eq "")
        !           390:            {
        !           391:              undef($deltaT);
        !           392:            }
        !           393:            else
        !           394:            {
        !           395:              $deltaT = $v;
        !           396:            }
        !           397:            next;
        !           398:        };
        !           399:     ($c eq 'verbose') && ! defined($PrintIt) &&
        !           400:        do {
        !           401:             if (!defined($v) || $v == 0)
        !           402:             {
        !           403:               $verbose = 0;
        !           404:             }
        !           405:             else
        !           406:             {
        !           407:               $verbose = $v;
        !           408:             }
        !           409:             next;
        !           410:        };
        !           411:     ;# otherwise: silently ignore unrecognized config line
        !           412:   }
        !           413:   close(CF);
        !           414:   ;# set show defaults when nothing selected
        !           415:   $showoffs = $showfreq = $showcmpl = 1
        !           416:       unless $showoffs || $showfreq || $showcmpl;
        !           417:   if ($verbose > 3)
        !           418:   {
        !           419:     print  "new configuration:\n";
        !           420:     print  "   delay\t= $delay\n";
        !           421:     print  "   samples\t= $samples\n";
        !           422:     print  "   srcprefix\t= $srcprefix\n";
        !           423:     print  "   showoffs\t= $showoffs\n";
        !           424:     print  "   showfreq\t= $showfreq\n";
        !           425:     print  "   showcmpl\t= $showcmpl\n";
        !           426:     print  "   showoreg\t= $showoreg\n";
        !           427:     print  "   showfreg\t= $showfreg\n";
        !           428:     printf "   timebase\t= %s",defined($timebase)?&ctime($timebase):"dynamic\n";
        !           429:     printf "   freqbase\t= %s\n",defined($freqbase)  ?"$freqbase":"dynamic";
        !           430:     printf "   cmplscale\t= %s\n",defined($cmplscale)?"$cmplscale":"dynamic";
        !           431:     printf "   StartTime\t= %s",defined($StartTime)?&ctime($StartTime):"none\n";
        !           432:     printf "   EndTime\t= %s",  defined($EndTime) ?  &ctime($EndTime):"none\n";
        !           433:     printf "   MaxY\t= %s",defined($MaxY)? $MaxY      :"none\n";
        !           434:     printf "   MinY\t= %s",defined($MinY)? $MinY      :"none\n";
        !           435:     print  "   verbose\t= $verbose\n";
        !           436:   }
        !           437: print "configuration file read\n" if $verbose > 2;
        !           438: }
        !           439: 
        !           440: sub make_doplot($$)
        !           441: {
        !           442:     my($lo, $lf) = @_;
        !           443:     local($c) = ("");
        !           444:     local($fmt)
        !           445:        = ("%s \"%s\" using 1:%d title '%s <%lf %lf> %6s' with lines");
        !           446:     local($regfmt)
        !           447:        = ("%s ((%lf * x) + %lf) title 'lin. approx. %s (%f t[h]) %s %f <%f> %6s' with lines");
        !           448:     
        !           449:     $doplot = "    set title 'NTP loopfilter statistics for $STATHOST  " .
        !           450:        "(last $LastCnt samples from $srcprefix*)'\n";
        !           451:     
        !           452:     local($xts,$xte,$i,$t);
        !           453:     
        !           454:     local($s,$c) = ("");
        !           455: 
        !           456:     ;# number of integral seconds to get at least 12 tic marks on x axis
        !           457:     $t = int(($maxtime - $mintime) / 12 + 0.5);
        !           458:     $t = 1 unless $t;          # prevent $t to be zero
        !           459:     foreach $i (30,
        !           460:                60,5*60,15*60,30*60,
        !           461:                60*60,2*60*60,6*60*60,12*60*60,
        !           462:                24*60*60,48*60*60)
        !           463:     {
        !           464:        last if $t < $i;
        !           465:        $t = $t - ($t % $i);
        !           466:     }
        !           467:     print "time label resolution: $t seconds\n" if $verbose > 1;
        !           468:     
        !           469:     ;# make gnuplot use wall clock time labels instead of NTP seconds
        !           470:     for ($c="", $i = $mintime - ($mintime % $t);
        !           471:         $i <= $maxtime + $t;
        !           472:         $i += $t, $c=",")
        !           473:     {
        !           474:        $s .= $c;
        !           475:        ((int($i / $t) % 2) &&
        !           476:         ($s .= sprintf("'' %lf",($i - $LastTimeBase)/3600))) ||
        !           477:             (($t <= 60) &&
        !           478:              ($s .= sprintf("'%d:%02d:%02d' %lf",
        !           479:                             (localtime($i))[$[+2,$[+1,$[+0],
        !           480:                             ($i - $LastTimeBase)/3600))) 
        !           481:                 || (($t <= 2*60*60) &&
        !           482:                     ($s .= sprintf("'%d:%02d' %lf",
        !           483:                                    (localtime($i))[$[+2,$[+1],
        !           484:                                    ($i - $LastTimeBase)/3600)))
        !           485:                     || (($t <= 12*60*60) &&
        !           486:                         ($s .= sprintf("'%s %d:00' %lf",
        !           487:                                        $Day[(localtime($i))[$[+6]],
        !           488:                                        (localtime($i))[$[+2],
        !           489:                                        ($i - $LastTimeBase)/3600)))
        !           490:                         || ($s .= sprintf("'%d.%d-%d:00' %lf",
        !           491:                                           (localtime($i))[$[+3,$[+4,$[+2],
        !           492:                                           ($i - $LastTimeBase)/3600));
        !           493:     }
        !           494:     $doplot .= "set xtics ($s)\n";
        !           495:     
        !           496:     chop($xts = &ctime($mintime));
        !           497:     chop($xte = &ctime($maxtime));
        !           498:     $doplot .= "set xlabel 'Start:  $xts    --   Time Scale   --    End:  $xte'\n";
        !           499:     $doplot .= "set yrange [" ;
        !           500:     $doplot .= defined($MinY) ? sprintf("%lf", $MinY) : $miny;
        !           501:     $doplot .= ':';
        !           502:     $doplot .= defined($MaxY) ? sprintf("%lf", $MaxY) : $maxy;
        !           503:     $doplot .= "]\n";
        !           504:     
        !           505:     $doplot .= "   plot";
        !           506:     $c = "";
        !           507:     $showoffs &&
        !           508:        ($doplot .= sprintf($fmt,$c,$tmpfile,2,
        !           509:                            "offset",
        !           510:                            $minoffs,$maxoffs,
        !           511:                            "[ms]"),
        !           512:         $c = ",");
        !           513:     $LastCmplScale = 1 if ! defined($LastCmplScale);
        !           514:     $showcmpl &&
        !           515:        ($doplot .= sprintf($fmt,$c,$tmpfile,4,
        !           516:                            "compliance" .
        !           517:                            (&abs($LastCmplScale) > 1
        !           518:                             ? " / $LastCmplScale"
        !           519:                             : (&abs($LastCmplScale) == 1 ? "" : " * ".(1/$LastCmplScale))),
        !           520:                            $mincmpl/$LastCmplScale,$maxcmpl/$LastCmplScale,
        !           521:                            ""),
        !           522:         $c = ",");
        !           523:     $LastFreqBase = 0 if ! defined($LastFreqBase);
        !           524:     $LastFreqBaseString = "?" if ! defined($LastFreqBaseString);
        !           525:     $FreqScale = 1 if ! defined($FreqScale);
        !           526:     $FreqScaleInv = 1 if ! defined($FreqScaleInv);
        !           527:     $showfreq &&
        !           528:        ($doplot .= sprintf($fmt,$c,$tmpfile,3,
        !           529:                            "frequency" .
        !           530:                            ($LastFreqBase > 0
        !           531:                             ? " - $LastFreqBaseString" 
        !           532:                             : ($LastFreqBase == 0 ? "" : " + $LastFreqBaseString")),
        !           533:                            $minfreq * $FreqScale - $LastFreqBase,
        !           534:                            $maxfreq * $FreqScale - $LastFreqBase,
        !           535:                            "[${FreqScaleInv}ppm]"),
        !           536:         $c = ",");
        !           537:     $showoreg && $showoffs &&
        !           538:        ($doplot .= sprintf($regfmt, $c,
        !           539:                            $lo->B(),$lo->A(),
        !           540:                            "offset   ",
        !           541:                            $lo->B(),
        !           542:                            (($lo->A()) < 0 ? '-' : '+'),
        !           543:                            &abs($lo->A()), $lo->r(),
        !           544:                            "[ms]"),
        !           545:         $c = ",");
        !           546:     $showfreg && $showfreq &&
        !           547:        ($doplot .= sprintf($regfmt, $c,
        !           548:                            $lf->B() * $FreqScale,
        !           549:                            ($lf->A() + $minfreq) * $FreqScale - $LastFreqBase,
        !           550:                            "frequency",
        !           551:                            $lf->B() * $FreqScale,
        !           552:                            (($lf->A() + $minfreq) * $FreqScale - $LastFreqBase) < 0 ? '-' : '+',
        !           553:                            &abs(($lf->A() + $minfreq) * $FreqScale - $LastFreqBase),
        !           554:                            $lf->r(),
        !           555:                            "[${FreqScaleInv}ppm]"),
        !           556:         $c = ",");
        !           557:     $doplot .= "\n";
        !           558: }
        !           559: 
        !           560: %F_key   = ();
        !           561: %F_name  = ();
        !           562: %F_size  = ();
        !           563: %F_mtime = ();
        !           564: %F_first = ();
        !           565: %F_last  = ();
        !           566: 
        !           567: sub genfile
        !           568: {
        !           569:     local($cnt,$in,$out,$lo,$lf,@fpos) = @_;
        !           570:     
        !           571:     local(@F,@t,$t,$lastT) = ();
        !           572:     local(@break,@time,@offs,@freq,@cmpl,@loffset,@filekey) = ();
        !           573:     local($lm,$l,@f);
        !           574:     
        !           575:     local($sdir,$sname);
        !           576:     
        !           577:     ;# allocate some storage for the tables
        !           578:     ;# otherwise realloc may get into troubles
        !           579:     if (defined($StartTime) && defined($EndTime))
        !           580:     {
        !           581:        $l = ($EndTime-$StartTime) -$[+1 +1; # worst case: 1 sample per second
        !           582:     }
        !           583:     else
        !           584:     {
        !           585:        $l = $cnt + 10;
        !           586:     }
        !           587:     print "preextending arrays to $l entries\n" if $verbose > 2;
        !           588:     $#break =   $l; for ($i=$[; $i<=$l;$i++) { $break[$i] = 0; }
        !           589:     $#time =    $l; for ($i=$[; $i<=$l;$i++) { $time[$i] = 0; }
        !           590:     $#offs =    $l; for ($i=$[; $i<=$l;$i++) { $offs[$i] = 0; }
        !           591:     $#freq =    $l; for ($i=$[; $i<=$l;$i++) { $freq[$i] = 0; }
        !           592:     $#cmpl =    $l; for ($i=$[; $i<=$l;$i++) { $cmpl[$i] = 0; }
        !           593:     $#loffset = $l; for ($i=$[; $i<=$l;$i++) { $loffset[$i] = 0; }
        !           594:     $#filekey = $l; for ($i=$[; $i<=$l;$i++) { $filekey[$i] = 0; }
        !           595:     ;# now reduce size again
        !           596:     $#break =   $[ - 1;
        !           597:     $#time =    $[ - 1;
        !           598:     $#offs =    $[ - 1;
        !           599:     $#freq =    $[ - 1;
        !           600:     $#cmpl =    $[ - 1;
        !           601:     $#loffset = $[ - 1;
        !           602:     $#filekey = $[ - 1;
        !           603:     print "memory allocation ready\n" if $verbose > 2;
        !           604:     sleep(3) if $verbose > 1;
        !           605: 
        !           606:     $fpos[$[] = '' if !defined($fpos[$[]);
        !           607: 
        !           608:     if (index($in,"/") < $[)
        !           609:     {
        !           610:        $sdir = ".";
        !           611:        $sname = $in;
        !           612:     }
        !           613:     else
        !           614:     {
        !           615:        ($sdir,$sname) = ($in =~ m!^(.*)/([^/]*)!);
        !           616:        $sname = "" unless defined($sname);
        !           617:     }
        !           618:     
        !           619:     $Ltime = -1 if ! defined($Ltime);
        !           620:     if (!defined($Lsdir) || $Lsdir ne $sdir || $Ltime != (stat($sdir))[$[+9] ||
        !           621:        grep($F_mtime{$_} != (stat($F_name{$_}))[$[+9], @F_files))
        !           622:        
        !           623:     {
        !           624:        print "rescanning directory \"$sdir\" for files \"$sname*\"\n"
        !           625:            if $verbose > 1;
        !           626: 
        !           627:        ;# rescan directory on changes
        !           628:        $Lsdir = $sdir;
        !           629:        $Ltime = (stat($sdir))[$[+9];
        !           630:        </X{> if 0;             # dummy line - calm down my formatter
        !           631:        local(@newfiles) = < ${in}*[0-9] >;
        !           632:        local($st_dev,$st_ino,$st_mtime,$st_size,$name,$key,$modified);
        !           633: 
        !           634:        foreach $name (@newfiles)
        !           635:        {
        !           636:            ($st_dev,$st_ino,$st_size,$st_mtime) =
        !           637:                (stat($name))[$[,$[+1,$[+7,$[+9];
        !           638:            $modified = 0;
        !           639:            $key = sprintf("%lx|%lu", $st_dev, $st_ino);
        !           640:            
        !           641:            print "candidate file \"$name\"",
        !           642:                   (defined($st_dev) ? "" : " failed: $!"),"\n"
        !           643:                      if $verbose > 2;
        !           644:            
        !           645:            if (! defined($F_key{$name}) || $F_key{$name} ne $key)
        !           646:            {
        !           647:                $F_key{$name} = $key;
        !           648:                $modified++;
        !           649:            }
        !           650:            if (!defined($F_name{$key}) || $F_name{$key} ne $name)
        !           651:            {
        !           652:                $F_name{$key} = $name;
        !           653:                $modified++;
        !           654:            }
        !           655:            if (!defined($F_size{$key}) || $F_size{$key} != $st_size)
        !           656:            {
        !           657:                $F_size{$key} = $st_size;
        !           658:                $modified++;
        !           659:            }
        !           660:            if (!defined($F_mtime{$key}) || $F_mtime{$key} != $st_mtime)
        !           661:            {
        !           662:                $F_mtime{$key} = $st_mtime;
        !           663:                $modified++;
        !           664:            }
        !           665:            if ($modified)
        !           666:            {
        !           667:                print "new data \"$name\" key: $key;\n" if $verbose > 1;
        !           668:                print "             size: $st_size; mtime: $st_mtime;\n"
        !           669:                    if $verbose > 1;
        !           670:                $F_last{$key} = $F_first{$key} = $st_mtime;
        !           671:                $F_first{$key}--; # prevent zero divide later on
        !           672:                ;# now compute derivated attributes
        !           673:                open(IN, "<$name") ||
        !           674:                    do {
        !           675:                        warn "$0: failed to open \"$name\": $!";
        !           676:                        next;
        !           677:                    };
        !           678: 
        !           679:                while(<IN>)
        !           680:                {
        !           681:                    @F = split;
        !           682:                    next if @F < 5;
        !           683:                    next if $F[$[] eq "";
        !           684:                    $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
        !           685:                    $t += $F[$[+1];
        !           686:                    $F_first{$key} = $t;
        !           687:                    print "\tfound first entry: $t ",&ctime($t)
        !           688:                        if $verbose > 4;
        !           689:                    last;
        !           690:                }
        !           691:                seek(IN,
        !           692:                     ($st_size > 4*$RecordSize) ? $st_size - 4*$RecordSize : 0,
        !           693:                     0);
        !           694:                while(<IN>)
        !           695:                {
        !           696:                    @F = split;
        !           697:                    next if @F < 5;
        !           698:                    next if $F[$[] eq "";
        !           699:                    $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
        !           700:                    $t += $F[$[+1];
        !           701:                    $F_last{$key} = $t;
        !           702:                    $_ = <IN>;
        !           703:                    print "\tfound last entry: $t ", &ctime($t)
        !           704:                        if $verbose > 4 && ! defined($_);
        !           705:                    last unless defined($_);
        !           706:                    redo;
        !           707:                    ;# Ok, calm down...
        !           708:                    ;# using $_ = <IN> in conjunction with redo
        !           709:                    ;# is semantically equivalent to the while loop, but
        !           710:                    ;# I needed a one line look ahead and this solution
        !           711:                    ;# was what I thought of first
        !           712:                    ;# and.. If you do not like it dont look
        !           713:                }
        !           714:                close(IN);
        !           715:                print("             first: ",$F_first{$key},
        !           716:                      " last: ",$F_last{$key},"\n") if $verbose > 1;
        !           717:            }
        !           718:        }
        !           719:        ;# now reclaim memory used for files no longer referenced ...
        !           720:        local(%Names);
        !           721:        grep($Names{$_} = 1,@newfiles);
        !           722:        foreach (keys %F_key)
        !           723:        {
        !           724:            next if defined($Names{$_});
        !           725:            delete $F_key{$_};
        !           726:            $verbose > 2 && print "no longer referenced: \"$_\"\n";
        !           727:        }
        !           728:        %Names = ();
        !           729:        
        !           730:        grep($Names{$_} = 1,values(%F_key));
        !           731:        foreach (keys %F_name)
        !           732:        {
        !           733:            next if defined($Names{$_});
        !           734:            delete $F_name{$_};
        !           735:            $verbose > 2 && print "unref name($_)= $F_name{$_}\n";
        !           736:        }
        !           737:        foreach (keys %F_size)
        !           738:        {
        !           739:            next if defined($Names{$_});
        !           740:            delete $F_size{$_};
        !           741:            $verbose > 2 && print "unref size($_)\n";
        !           742:        }
        !           743:        foreach (keys %F_mtime)
        !           744:        {
        !           745:            next if defined($Names{$_});
        !           746:            delete $F_mtime{$_};
        !           747:            $verbose > 2 && print "unref mtime($_)\n";
        !           748:        }
        !           749:        foreach (keys %F_first)
        !           750:        {
        !           751:            next if defined($Names{$_});
        !           752:            delete $F_first{$_};
        !           753:            $verbose > 2 && print "unref first($_)\n";
        !           754:        }
        !           755:        foreach (keys %F_last)
        !           756:        {
        !           757:            next if defined($Names{$_});
        !           758:            delete $F_last{$_};
        !           759:            $verbose > 2 && print "unref last($_)\n";
        !           760:        }
        !           761:        ;# create list sorted by time
        !           762:        @F_files = sort {$F_first{$a} <=> $F_first{$b}; } keys(%F_name);
        !           763:        if ($verbose > 1)
        !           764:        {
        !           765:            print "Resulting file list:\n";
        !           766:            foreach (@F_files)
        !           767:            {
        !           768:                print "\t$_\t$F_name{$_}\n";
        !           769:            }
        !           770:        }
        !           771:     }
        !           772:     
        !           773:     printf("processing %s; output \"$out\" (%d input files)\n",
        !           774:           ((defined($StartTime) && defined($EndTime))
        !           775:            ? "time range"
        !           776:            : (defined($StartTime) ? "$cnt samples from StartTime" :
        !           777:              (defined($EndTime) ? "$cnt samples to EndTime" :
        !           778:                 "last $cnt samples"))),
        !           779:            scalar(@F_files))
        !           780:        if $verbose > 1;
        !           781:     
        !           782:     ;# open output file - will be input for plotcmd
        !           783:     open(OUT,">$out") || 
        !           784:        do {
        !           785:            warn("$0: cannot create \"$out\": $!\n");
        !           786:        };
        !           787:     
        !           788:     @f = @F_files;
        !           789:     if (defined($StartTime))
        !           790:     {
        !           791:        while (@f && ($F_last{$f[$[]} < $StartTime))
        !           792:        {
        !           793:            print("shifting ", $F_name{$f[$[]},
        !           794:                  " last: ", $F_last{$f[$[]},
        !           795:                  " < StartTime: $StartTime\n")
        !           796:                if $verbose > 3;
        !           797:            shift(@f);
        !           798:        }
        !           799: 
        !           800: 
        !           801:     }
        !           802:     if (defined($EndTime))
        !           803:     {
        !           804:        while (@f && ($F_first{$f[$#f]} > $EndTime))
        !           805:        {
        !           806:            print("popping  ", $F_name{$f[$#f]},
        !           807:                  " first: ", $F_first{$f[$#f]},
        !           808:                  " > EndTime: $EndTime\n")
        !           809:                if $verbose > 3;
        !           810:            pop(@f);
        !           811:        }
        !           812:     }
        !           813:     
        !           814:     if (@f)
        !           815:     {
        !           816:        if (defined($StartTime))
        !           817:        {
        !           818:            print "guess start according to StartTime ($StartTime)\n"
        !           819:                if $verbose > 3;
        !           820: 
        !           821:            if ($fpos[$[] eq 'start')
        !           822:            {
        !           823:                if (grep($_ eq $fpos[$[+1],@f))
        !           824:                {
        !           825:                    shift(@f) while @f && $f[$[] ne $fpos[$[+1];
        !           826:                }
        !           827:                else
        !           828:                {
        !           829:                    @fpos = ('start', $f[$[], undef);
        !           830:                }
        !           831:            }
        !           832:            else
        !           833:            {
        !           834:                @fpos = ('start' , $f[$[], undef);
        !           835:            }
        !           836:            
        !           837:            if (!defined($fpos[$[+2]))
        !           838:            {
        !           839:                if ($StartTime <= $F_first{$f[$[]})
        !           840:                {
        !           841:                    $fpos[$[+2] = 0;
        !           842:                }
        !           843:                else
        !           844:                {
        !           845:                    $fpos[$[+2] =
        !           846:                        int($F_size{$f[$[]} *
        !           847:                            (($StartTime - $F_first{$f[$[]})/
        !           848:                             ($F_last{$f[$[]} - $F_first{$f[$[]})));
        !           849:                    $fpos[$[+2] = ($fpos[$[+2] <= 2 * $RecordSize)
        !           850:                        ? 0 : $fpos[$[+2] - 2 * $RecordSize;
        !           851:                    ;# anyway  as the data may contain "time holes" 
        !           852:                    ;# our heuristics may baldly fail
        !           853:                    ;# so just start at 0
        !           854:                    $fpos[$[+2] = 0;
        !           855:                }
        !           856:            }
        !           857:        }
        !           858:        elsif (defined($EndTime))
        !           859:        {
        !           860:            print "guess starting point according to EndTime ($EndTime)\n"
        !           861:                if $verbose > 3;
        !           862:            
        !           863:            if ($fpos[$[] eq 'end')
        !           864:            {
        !           865:                if (grep($_ eq $fpos[$[+1],@f))
        !           866:                {
        !           867:                    shift(@f) while @f && $f[$[] ne $fpos[$[+1];
        !           868:                }
        !           869:                else
        !           870:                {
        !           871:                    @fpos = ('end', $f[$[], undef);
        !           872:                }
        !           873:            }
        !           874:            else
        !           875:            {
        !           876:                @fpos = ('end', $f[$[], undef);
        !           877:            }
        !           878:            
        !           879:            if (!defined($fpos[$[+2]))
        !           880:            {
        !           881:                local(@x) = reverse(@f);
        !           882:                local($s,$c) = (0,$cnt);
        !           883:                if ($EndTime < $F_last{$x[$[]})
        !           884:                {
        !           885:                    ;# last file will only be used partially
        !           886:                    $s = int($F_size{$x[$[]} *
        !           887:                             (($EndTime - $F_first{$x[$[]}) /
        !           888:                              ($F_last{$x[$[]} - $F_first{$x[$[]})));
        !           889:                    $s = int($s/$RecordSize);
        !           890:                    $c -= $s - 1;
        !           891:                    if ($c <= 0)
        !           892:                    {
        !           893:                        ;# start is in the same file
        !           894:                        $fpos[$[+1] = $x[$[];
        !           895:                        $fpos[$[+2] = ($c >=-2) ? 0 : (-$c - 2) * $RecordSize;
        !           896:                        shift(@f) while @f && ($f[$[] ne $x[$[]);
        !           897:                    }
        !           898:                    else
        !           899:                    {
        !           900:                        shift(@x);
        !           901:                    }
        !           902:                }
        !           903:                
        !           904:                if (!defined($fpos[$[+2]))
        !           905:                {
        !           906:                    local($_);
        !           907:                    while($_ = shift(@x))
        !           908:                    {
        !           909:                        $s = int($F_size{$_}/$RecordSize);
        !           910:                        $c -= $s - 1;
        !           911:                        if ($c <= 0)
        !           912:                        {
        !           913:                            $fpos[$[+1] = $_;
        !           914:                            $fpos[$[+2] = ($c>-2) ? 0 : (-$c - 2) * $RecordSize;
        !           915:                            shift(@f) while @f && ($f[$[] ne $_);
        !           916:                            last;
        !           917:                        }
        !           918:                    }
        !           919:                }
        !           920:            }
        !           921:        }
        !           922:        else
        !           923:        {
        !           924:            print "guessing starting point according to count ($cnt)\n"
        !           925:                if $verbose > 3;
        !           926:            ;# guess offset to get last available $cnt samples
        !           927:            if ($fpos[$[] eq 'cnt')
        !           928:            {
        !           929:                if (grep($_ eq $fpos[$[+1],@f))
        !           930:                {
        !           931:                    print "old positioning applies\n" if $verbose > 3;
        !           932:                    shift(@f) while @f && $f[$[] ne $fpos[$[+1];
        !           933:                }
        !           934:                else
        !           935:                {
        !           936:                    @fpos = ('cnt', $f[$[], undef);
        !           937:                }
        !           938:            }
        !           939:            else
        !           940:            {
        !           941:                @fpos = ('cnt', $f[$[], undef);
        !           942:            }
        !           943:            
        !           944:            if (!defined($fpos[$[+2]))
        !           945:            {
        !           946:                local(@x) = reverse(@f);
        !           947:                local($s,$c) = (0,$cnt);
        !           948:                
        !           949:                local($_);
        !           950:                while($_ = shift(@x))
        !           951:                {
        !           952:                    print "examing \"$_\" $c samples still needed\n"
        !           953:                        if $verbose > 4;
        !           954:                    $s = int($F_size{$_}/$RecordSize);
        !           955:                    $c -= $s - 1;
        !           956:                    if ($c <= 0)
        !           957:                    {
        !           958:                        $fpos[$[+1] = $_;
        !           959:                        $fpos[$[+2] = ($c>-2) ? 0 : (-$c - 2) * $RecordSize;
        !           960:                        shift(@f) while @f && ($f[$[] ne $_);
        !           961:                        last;
        !           962:                    }
        !           963:                }
        !           964:                if (!defined($fpos[$[+2]))
        !           965:                {
        !           966:                    print "no starting point yet - using start of data\n"
        !           967:                        if $verbose > 2;
        !           968:                    $fpos[$[+2] = 0;
        !           969:                }
        !           970:            }
        !           971:        }
        !           972:     }
        !           973:     print "Ooops, no suitable input file ??\n"
        !           974:        if $verbose > 1 && @f <= 0;
        !           975: 
        !           976:     printf("Starting at (%s) \"%s\" offset %ld using %d files\n",
        !           977:           $fpos[$[+1],
        !           978:           $F_name{$fpos[$[+1]},
        !           979:           $fpos[$[+2],
        !           980:           scalar(@f))
        !           981:        if $verbose > 2;
        !           982: 
        !           983:     $lm = 1;
        !           984:     $l = 0;    
        !           985:     foreach $key (@f)
        !           986:     {
        !           987:        $file = $F_name{$key};
        !           988:        print "processing file \"$file\"\n" if $verbose > 2;
        !           989:        
        !           990:        open(IN,"<$file") ||
        !           991:            (warn("$0: cannot read \"$file\": $!\n"), next);
        !           992:        
        !           993:        ;# try to seek to a position nearer to the start of the interesting lines
        !           994:        ;# should always affect only first item in @f
        !           995:        ($key eq $fpos[$[+1]) &&
        !           996:            (($verbose > 1) &&
        !           997:             print("Seeking to offset $fpos[$[+2]\n"),
        !           998:                seek(IN,$fpos[$[+2],0) ||
        !           999:                    warn("$0: seek(\"$F_name{$key}\" failed: $|\n"));
        !          1000:        
        !          1001:        while(<IN>)
        !          1002:        {
        !          1003:            $l++;
        !          1004:            ($verbose > 3) &&
        !          1005:                (($l % $lm) == 0 && print("\t$l lines read\n") &&
        !          1006:                 (($l ==     2) && ($lm =    10) ||
        !          1007:                  ($l ==   100) && ($lm =   100) ||
        !          1008:                  ($l ==   500) && ($lm =   500) ||
        !          1009:                  ($l ==  1000) && ($lm =  1000) ||
        !          1010:                  ($l ==  5000) && ($lm =  5000) ||
        !          1011:                  ($l == 10000) && ($lm = 10000)));
        !          1012:            
        !          1013:            @F = split;
        !          1014:            
        !          1015:            next if @F < 6;     # no valid input line is this short
        !          1016:            next if $F[$[] eq "";
        !          1017:            next if ($F[$[] !~ /^\d+$/);
        !          1018:            ($F[$[] !~ /^\d+$/) && # A 'never should have happend' error
        !          1019:                die("$0: unexpected input line: >$_<\n");
        !          1020:            
        !          1021:            ;# modified Julian to UNIX epoch
        !          1022:            $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
        !          1023:            $t += $F[$[+1];     # add seconds + fraction
        !          1024:            
        !          1025:            ;# multiply offset by 1000 to get ms - try to avoid float op
        !          1026:            (($F[$[+2] =~ s/(\d*)\.(\d{3})(\d*)/$1$2.$3/) &&
        !          1027:             $F[$[+2] =~ s/0+([\d\.])/($1 eq '.') ? '0.' : $1/e) # strip leading zeros
        !          1028:                || ($F[$[+2] *= 1000);
        !          1029: 
        !          1030:            
        !          1031:            ;# skip samples out of specified time range
        !          1032:            next if (defined($StartTime) && $StartTime > $t);
        !          1033:            next if (defined($EndTime) && $EndTime < $t);
        !          1034:            
        !          1035:            next if defined($lastT) && $t < $lastT; # backward in time ??
        !          1036:            
        !          1037:            push(@offs,$F[$[+2]);
        !          1038:            push(@freq,$F[$[+3] * (2**20/10**6));
        !          1039:            push(@cmpl,$F[$[+5]);
        !          1040:            
        !          1041:            push(@break, (defined($lastT) && ($t - $lastT > $deltaT))); 
        !          1042:            $lastT = $t;
        !          1043:            push(@time,$t);
        !          1044:            push(@loffset, tell(IN) - length($_));
        !          1045:            push(@filekey, $key);
        !          1046:            
        !          1047:            shift(@break),shift(@time),shift(@offs),
        !          1048:            shift(@freq), shift(@cmpl),shift(@loffset),
        !          1049:            shift(@filekey)
        !          1050:                if @time > $cnt &&
        !          1051:                    ! (defined($StartTime) && defined($EndTime));
        !          1052: 
        !          1053:            last if @time >= $cnt && defined($StartTime) && !defined($EndTime);
        !          1054:        }
        !          1055:        close(IN);
        !          1056:        last if @time >= $cnt && defined($StartTime) && !defined($EndTime);
        !          1057:     }
        !          1058:     print "input scanned ($l lines/",scalar(@time)," samples)\n"
        !          1059:        if $verbose > 1;
        !          1060:     
        !          1061:     if (@time)
        !          1062:     {
        !          1063:        local($_,@F);
        !          1064:        
        !          1065:        local($timebase) unless defined($timebase);
        !          1066:        local($freqbase) unless defined($freqbase);
        !          1067:        local($cmplscale) unless defined($cmplscale);
        !          1068:        
        !          1069:        undef $mintime;
        !          1070:        undef $maxtime;
        !          1071:        undef $minoffs;
        !          1072:        undef $maxoffs;
        !          1073:        undef $minfreq;
        !          1074:        undef $maxfreq;
        !          1075:        undef $mincmpl;
        !          1076:        undef $maxcmpl;
        !          1077:        undef $miny;
        !          1078:        undef $maxy ;
        !          1079:        
        !          1080:        print "computing ranges\n" if $verbose > 2;
        !          1081:        
        !          1082:        $LastCnt = @time;
        !          1083: 
        !          1084:        ;# @time is in ascending order (;-)
        !          1085:        $mintime = $time[$[];
        !          1086:        $maxtime = $time[$#time];
        !          1087:        unless (defined($timebase))
        !          1088:        {
        !          1089:            local($time,@X) = (time);
        !          1090:            @X = localtime($time);
        !          1091:            
        !          1092:            ;# compute today 00:00:00
        !          1093:            $timebase = $time - ((($X[$[+2]*60)+$X[$[+1])*60+$X[$[]);
        !          1094: 
        !          1095:        }
        !          1096:        $LastTimeBase = $timebase;
        !          1097: 
        !          1098:        if ($showoffs)
        !          1099:        {
        !          1100:            local($i,$m,$f);
        !          1101:            
        !          1102:            $minoffs = &min(@offs);
        !          1103:            $maxoffs = &max(@offs);
        !          1104:            
        !          1105:            ;# I know, it is not perl style using indices to access arrays,
        !          1106:            ;# but I have to proccess two arrays in sync, non-destructively
        !          1107:            ;# (otherwise a (shift(@a1),shift(a2)) would do),
        !          1108:            ;# I dont like to make copies of these arrays as they may be huge
        !          1109:            $i = $[;
        !          1110:            $lo->sample(($time[$i]-$timebase)/3600,$offs[$i]),$i++
        !          1111:                while $i <= $#time;
        !          1112: 
        !          1113:            ($minoffs == $maxoffs) && ($minoffs -= 0.1,$maxoffs += 0.1);
        !          1114: 
        !          1115:            $i = $lo->sigma();
        !          1116:            $m = $lo->mean();
        !          1117: 
        !          1118:            print "mean offset: $m sigma: $i\n" if $verbose > 2;
        !          1119: 
        !          1120:            if (($maxoffs - $minoffs) > $MinClip)
        !          1121:            {
        !          1122:                $f = (&abs($minoffs) < &abs($maxoffs)) ? $FuzzLow : $FuzzBig;
        !          1123:                $miny = (($m - $minoffs) <= ($f * $i))
        !          1124:                    ? $minoffs : ($m - $f * $i);
        !          1125:                $f = ($f == $FuzzLow) ? $FuzzBig : $FuzzLow;
        !          1126:                $maxy = (($maxoffs - $m) <= ($f * $i))
        !          1127:                    ? $maxoffs : ($m + $f * $i);
        !          1128:            }
        !          1129:            else
        !          1130:            {
        !          1131:                $miny = $minoffs;
        !          1132:                $maxy = $maxoffs;
        !          1133:            }
        !          1134:            ($maxy-$miny) == 0 &&
        !          1135:                (($maxy,$miny)
        !          1136:                 = (($maxoffs - $minoffs) > 0)
        !          1137:                 ? ($maxoffs,$minoffs) : ($MinClip,-$MinClip));
        !          1138: 
        !          1139:            $maxy = $MaxY if defined($MaxY) && $MaxY < $maxy;
        !          1140:            $miny = $MinY if defined($MinY) && $MinY > $miny;
        !          1141: 
        !          1142:            print  "offset min clipped from $minoffs to $miny\n"
        !          1143:                if $verbose > 2 && $minoffs != $miny;
        !          1144:            print  "offset max clipped from $maxoffs to $maxy\n"
        !          1145:                if $verbose > 2 && $maxoffs != $maxy;
        !          1146:        }
        !          1147:        
        !          1148:        if ($showfreq)
        !          1149:        {
        !          1150:            local($i,$m);
        !          1151:            
        !          1152:            $minfreq = &min(@freq);
        !          1153:            $maxfreq = &max(@freq);
        !          1154:            
        !          1155:            $i = $[;
        !          1156:            $lf->sample(($time[$i]-$timebase)/3600,$freq[$i]-$minfreq),
        !          1157:            $i++
        !          1158:                while $i <= $#time;
        !          1159:            
        !          1160:            $i = $lf->sigma();
        !          1161:            $m = $lf->mean() + $minfreq;
        !          1162: 
        !          1163:            print "mean frequency: $m sigma: $i\n" if $verbose > 2;
        !          1164: 
        !          1165:            if (defined($maxy))
        !          1166:            {
        !          1167:                local($s) =
        !          1168:                    ($maxfreq - $minfreq)
        !          1169:                        ? ($maxy - $miny) / ($maxfreq - $minfreq) : 1;
        !          1170: 
        !          1171:                if (defined($freqbase))
        !          1172:                {
        !          1173:                    $FreqScale = 1;
        !          1174:                    $FreqScaleInv = "";
        !          1175:                }
        !          1176:                else
        !          1177:                {
        !          1178:                    $FreqScale = 1;
        !          1179:                    $FreqScale = 10 ** int(log($s)/log(10) - 0.9999);
        !          1180:                    $FreqScaleInv =
        !          1181:                        ("$FreqScale" =~ /^10(0*)$/) ? "0.${1}1" : 
        !          1182:                         ($FreqScale == 1 ? "" : (1/$FreqScale));
        !          1183:                    
        !          1184:                    $freqbase = ($maxfreq + $minfreq)/ 2 * $FreqScale; #$m * $FreqScale;
        !          1185:                    $freqbase -= ($maxy + $miny) / 2; #$lf->mean();
        !          1186: 
        !          1187:                    ;# round resulting freqbase
        !          1188:                    ;# to precision of min max difference
        !          1189:                    $s = -12;
        !          1190:                    $s = int(log(($maxfreq-$minfreq)*$FreqScale)/log(10))-1
        !          1191:                        unless ($maxfreq-$minfreq) < 1e-12;
        !          1192:                    $s = 10 ** $s;
        !          1193:                    $freqbase = int($freqbase / $s) * $s;
        !          1194:                }
        !          1195:            }
        !          1196:            else
        !          1197:            {
        !          1198:                $FreqScale = 1;
        !          1199:                $FreqScaleInv = "";
        !          1200:                $freqbase = $m unless defined($freqbase);
        !          1201:                if (($maxfreq - $minfreq) > $MinClip)
        !          1202:                {
        !          1203:                    $f = (&abs($minfreq) < &abs($maxfreq))
        !          1204:                        ? $FuzzLow : $FuzzBig;
        !          1205:                    $miny = (($freqbase - $minfreq) <= ($f * $i))
        !          1206:                        ? ($minfreq-$freqbase) : (- $f * $i);
        !          1207:                    $f = ($f == $FuzzLow) ? $FuzzBig : $FuzzLow;
        !          1208:                    $maxy = (($maxfreq - $freqbase) <= ($f * $i))
        !          1209:                        ? ($maxfreq-$freqbase) : ($f * $i);
        !          1210:                }
        !          1211:                else
        !          1212:                {
        !          1213:                    $miny = $minfreq - $freqbase;
        !          1214:                    $maxy = $maxfreq - $freqbase;
        !          1215:                }
        !          1216:                ($maxy - $miny) == 0 &&
        !          1217:                    (($maxy,$miny) =
        !          1218:                     (($maxfreq - $minfreq) > 0)
        !          1219:                     ? ($maxfreq-$freqbase,$minfreq-$freqbase) : (0.5,-0.5));
        !          1220:                
        !          1221:                $maxy = $MaxY if defined($MaxY) && $MaxY < $maxy;
        !          1222:                $miny = $MinY if defined($MinY) && $MinY > $miny;
        !          1223: 
        !          1224:                print("frequency min clipped from ",$minfreq-$freqbase,
        !          1225:                      " to $miny\n")
        !          1226:                    if $verbose > 2 && $miny != ($minfreq - $freqbase);
        !          1227:                print("frequency max clipped from ",$maxfreq-$freqbase,
        !          1228:                      " to $maxy\n")
        !          1229:                    if $verbose > 2 && $maxy != ($maxfreq - $freqbase);
        !          1230:            }
        !          1231:            $LastFreqBaseString =
        !          1232:                sprintf("%g",$freqbase >= 0 ? $freqbase : -$freqbase);
        !          1233:            $LastFreqBase = $freqbase;
        !          1234:            print "LastFreqBaseString now \"$LastFreqBaseString\"\n"
        !          1235:                if $verbose > 5;
        !          1236:        }
        !          1237:        else
        !          1238:        {
        !          1239:            $FreqScale = 1;
        !          1240:            $FreqScaleInv = "";
        !          1241:            $LastFreqBase = 0;
        !          1242:            $LastFreqBaseString = "";
        !          1243:        }
        !          1244:                
        !          1245:        if ($showcmpl)
        !          1246:        {
        !          1247:            $mincmpl = &min(@cmpl);
        !          1248:            $maxcmpl = &max(@cmpl);
        !          1249: 
        !          1250:            if (!defined($cmplscale))
        !          1251:            {
        !          1252:                if (defined($maxy))
        !          1253:                {
        !          1254:                    local($cmp)
        !          1255:                        = (&abs($miny) > &abs($maxy)) ? &abs($miny) : $maxy;
        !          1256:                    $cmplscale = $cmp == $maxy ? 1 : -1;
        !          1257: 
        !          1258:                    foreach (0.01, 0.02, 0.05,
        !          1259:                             0.1, 0.2, 0.25, 0.4, 0.5,
        !          1260:                             1, 2, 4, 5,
        !          1261:                             10, 20, 25, 50,
        !          1262:                             100, 200, 250, 500, 1000)
        !          1263:                    {
        !          1264:                        $cmplscale *= $_, last if $maxcmpl/$_ <= $cmp;
        !          1265:                    }
        !          1266:                }
        !          1267:                else
        !          1268:                {
        !          1269:                    $cmplscale = 1;
        !          1270:                    $miny = $mincmpl ? 0 : -$MinClip;
        !          1271:                    $maxy = $maxcmpl+$MinClip;
        !          1272:                }
        !          1273:            }
        !          1274:            $LastCmplScale = $cmplscale;
        !          1275:        }
        !          1276:        else
        !          1277:        {
        !          1278:            $LastCmplScale = 1;
        !          1279:        }
        !          1280:        
        !          1281:        print "creating plot command input file\n" if $verbose > 2;
        !          1282:        
        !          1283:        
        !          1284:        print OUT ("# preprocessed NTP statistics file for $STATHOST\n");
        !          1285:        print OUT ("#    timebase is: ",&ctime($LastTimeBase))
        !          1286:            if defined($LastTimeBase);
        !          1287:        print OUT ("#    frequency is offset by  ",
        !          1288:                   ($LastFreqBase >= 0 ? "+" : "-"),
        !          1289:                   "$LastFreqBaseString [${FreqScaleInv}ppm]\n");
        !          1290:        print OUT ("#    compliance is scaled by $LastCmplScale\n");
        !          1291:        print OUT ("# time [h]\toffset [ms]\tfrequency [${FreqScaleInv}ppm]\tcompliance\n");
        !          1292:        
        !          1293:        printf OUT ("%s%lf\t%lf\t%lf\t%lf\n",
        !          1294:                    (shift(@break) ? "\n" : ""),
        !          1295:                    (shift(@time) - $LastTimeBase)/3600,
        !          1296:                    shift(@offs),
        !          1297:                    shift(@freq) * $FreqScale - $LastFreqBase,
        !          1298:                    shift(@cmpl) / $LastCmplScale)
        !          1299:            while(@time);
        !          1300:     }
        !          1301:     else
        !          1302:     {
        !          1303:        ;# prevent plotcmd from processing empty file
        !          1304:        print "Creating plot command dummy...\n" if $verbose > 2;
        !          1305:        print OUT "# dummy samples\n0 1 2 3\n1 1 2 3\n";
        !          1306:        $lo->sample(0,1);
        !          1307:        $lo->sample(1,1);
        !          1308:        $lf->sample(0,2);
        !          1309:        $lf->sample(1,2);
        !          1310:        @time = (0, 1); $maxtime = 1; $mintime = 0;
        !          1311:        @offs = (1, 1); $maxoffs = 1; $minoffs = 1;
        !          1312:        @freq = (2, 2); $maxfreq = 2; $minfreq = 2;
        !          1313:        @cmpl = (3, 3); $maxcmpl = 3; $mincmpl = 3;
        !          1314:        $LastCnt = 2;
        !          1315:        $LastFreqBase = 0;
        !          1316:        $LastCmplScale = 1;
        !          1317:        $LastTimeBase = 0;
        !          1318:        $miny = -$MinClip;
        !          1319:        $maxy = 3 + $MinClip;
        !          1320:     }
        !          1321:     close(OUT);
        !          1322:     
        !          1323:     print "plot command input file created\n"
        !          1324:        if $verbose > 2;
        !          1325:        
        !          1326:        
        !          1327:     if (($fpos[$[] eq 'cnt' && scalar(@loffset) >= $cnt) ||
        !          1328:        ($fpos[$[] eq 'start' && $mintime <= $StartTime) ||
        !          1329:        ($fpos[$[] eq 'end'))
        !          1330:     {
        !          1331:        return ($fpos[$[],$filekey[$[],$loffset[$[]);
        !          1332:     }
        !          1333:     else                       # found to few lines - next time start search earlier in file
        !          1334:     {
        !          1335:        if ($fpos[$[] eq 'start')
        !          1336:        {
        !          1337:            ;# the timestamps we got for F_first and F_last guaranteed
        !          1338:            ;# that no file is left out
        !          1339:            ;# the only thing that could happen is:
        !          1340:            ;# we guessed the starting point wrong
        !          1341:            ;# compute a new guess from the first record found
        !          1342:            ;# if this equals our last guess use data of first record
        !          1343:            ;# otherwise try new guess
        !          1344:            
        !          1345:            if ($fpos[$[+1] eq $filekey[$[] && $loffset[$[] > $fpos[$[+2])
        !          1346:            {
        !          1347:                local($noff);
        !          1348:                $noff = $loffset[$[] - ($cnt - @loffset + 1) * $RecordSize;
        !          1349:                $noff = 0 if $noff < 0;
        !          1350:                
        !          1351:                return (@fpos[$[,$[+1], ($noff == $fpos[$[+2]) ? $loffset[$[] : $noff);
        !          1352:            }
        !          1353:            return ($fpos[$[],$filekey[$[],$loffset[$[]);
        !          1354:        }
        !          1355:        elsif ($fpos[$[] eq 'end' || $fpos[$[] eq 'cnt')
        !          1356:        {
        !          1357:            ;# try to start earlier in file
        !          1358:            ;# if we already started at the beginning
        !          1359:            ;# try to use previous file
        !          1360:            ;# this assumes distance to better starting point is at most one file
        !          1361:            ;# the primary guess at top of genfile() should usually allow this
        !          1362:            ;# assumption
        !          1363:            ;# if the offset of the first sample used is within 
        !          1364:            ;# a different file than we guessed it must have occurred later
        !          1365:            ;# in the sequence of files
        !          1366:            ;# this only can happen if our starting file did not contain
        !          1367:            ;# a valid sample from the starting point we guessed
        !          1368:            ;# however this does not invalidate our assumption, no check needed
        !          1369:            local($noff,$key);
        !          1370:            if ($fpos[$[+2] > 0)
        !          1371:            {
        !          1372:                $noff = $fpos[$[+2] - $RecordSize * ($cnt - @loffset + 1);
        !          1373:                $noff = 0 if $noff < 0;
        !          1374:                return (@fpos[$[,$[+1],$noff);
        !          1375:            }
        !          1376:            else
        !          1377:            {
        !          1378:                if ($fpos[$[+1] eq $F_files[$[])
        !          1379:                {
        !          1380:                    ;# first file - and not enough samples
        !          1381:                    ;# use data of first sample
        !          1382:                    return ($fpos[$[], $filekey[$[], $loffset[$[]);
        !          1383:                }
        !          1384:                else
        !          1385:                {
        !          1386:                    ;# search key of previous file
        !          1387:                    $key = $F_files[$[];
        !          1388:                    @F = reverse(@F_files);
        !          1389:                    while ($_ = shift(@F))
        !          1390:                    {
        !          1391:                        if ($_ eq $fpos[$[+1])
        !          1392:                        {
        !          1393:                            $key = shift(@F) if @F;
        !          1394:                            last;
        !          1395:                        }
        !          1396:                    }
        !          1397:                    $noff = int($F_size{$key} / $RecordSize);
        !          1398:                    $noff -= $cnt - @loffset;
        !          1399:                    $noff = 0 if $noff < 0;
        !          1400:                    $noff *= $RecordSize;
        !          1401:                    return ($fpos[$[], $key, $noff);
        !          1402:                }
        !          1403:            }
        !          1404:        }
        !          1405:        else
        !          1406:        {
        !          1407:            return ();
        !          1408:        }
        !          1409:        
        !          1410:        return 0 if @loffset <= 1 || ($loffset[$#loffset] - $loffset[$[]) <= 1;
        !          1411:        
        !          1412:        ;# EOF - 1.1 * avg(line) * $cnt
        !          1413:        local($val) =  $loffset[$#loffset]
        !          1414:            - $cnt * 11 * (($loffset[$#loffset] - $loffset[$[]) / @loffset) / 10;
        !          1415:        return ($val < 0) ? 0 : $val;
        !          1416:     }
        !          1417: }
        !          1418: 
        !          1419: $Ltime = -1 if ! defined($Ltime);
        !          1420: $LastFreqBase = 0;
        !          1421: $LastFreqBaseString = "??";
        !          1422: 
        !          1423: ;# initial setup of plot
        !          1424: print "initialize plotting\n" if $verbose;
        !          1425: if (defined($PrintIt))
        !          1426: {
        !          1427:   if ($PrintIt =~ m,/,)
        !          1428:   {
        !          1429:     print "Saving plot to file $PrintIt\n";
        !          1430:     print PLOT "set output '$PrintIt'\n";
        !          1431:   }
        !          1432:   else
        !          1433:   {
        !          1434:     print "Printing plot on printer $PrintIt\n";
        !          1435:     print PLOT "set output '| lpr -P$PrintIt -h'\n";
        !          1436:   }
        !          1437:   print PLOT "set terminal postscript landscape color solid 'Helvetica' 10\n";
        !          1438: }
        !          1439: print PLOT "set grid\n";
        !          1440: print PLOT "set tics out\n";
        !          1441: print PLOT "set format y '%g '\n";
        !          1442: printf PLOT "set time 47\n" unless defined($PrintIt);
        !          1443: 
        !          1444: @filepos =();
        !          1445: while(1)
        !          1446: {
        !          1447:   print &ctime(time) if $verbose;
        !          1448: 
        !          1449:   ;# update diplay characteristics
        !          1450:   &read_config;# unless defined($PrintIt);
        !          1451: 
        !          1452:   unlink($tmpfile);
        !          1453:   my $lo = lr->new();
        !          1454:   my $lf = lr->new();
        !          1455:     
        !          1456:   @filepos = &genfile($samples,$srcprefix,$tmpfile,$lo,$lf,@filepos);
        !          1457: 
        !          1458:   ;# make plotcmd display samples
        !          1459:   make_doplot($lo, $lf);
        !          1460:   print "Displaying plot...\n" if $verbose > 1;
        !          1461:   print "command for plot sub process:\n$doplot----\n" if $verbose > 3;
        !          1462:   print PLOT $doplot;
        !          1463: }
        !          1464: continue
        !          1465: {
        !          1466:   if (defined($PrintIt))
        !          1467:   {
        !          1468:     delete $SIG{'CHLD'};
        !          1469:     print PLOT "quit\n";
        !          1470:     close(PLOT);
        !          1471:     if ($PrintIt =~ m,/,)
        !          1472:     {
        !          1473:       print "Plot saved to file $PrintIt\n";
        !          1474:     }
        !          1475:     else
        !          1476:     {
        !          1477:       print "Plot spooled to printer $PrintIt\n";
        !          1478:     }
        !          1479:     unlink($tmpfile);
        !          1480:     exit(0);
        !          1481:   }
        !          1482:   ;# wait $delay seconds
        !          1483:   print "waiting $delay seconds ..." if $verbose > 2;
        !          1484:   sleep($delay);
        !          1485:   print " continuing\n" if $verbose > 2;
        !          1486:   undef($LastFreqBaseString);
        !          1487: }
        !          1488: 
        !          1489: 
        !          1490: sub date_time_spec2seconds
        !          1491: {
        !          1492:     local($_) = @_;
        !          1493:     ;# a date_time_spec consistes of:
        !          1494:     ;#  YYYY-MM-DD_HH:MM:SS.ms
        !          1495:     ;# values can be omitted from the beginning and default than to
        !          1496:     ;# values of current date
        !          1497:     ;# values omitted from the end default to lowest possible values
        !          1498: 
        !          1499:     local($time) = time;
        !          1500:     local($sec,$min,$hour,$mday,$mon,$year)
        !          1501:        = localtime($time);
        !          1502: 
        !          1503:     local($last) = ();
        !          1504: 
        !          1505:     s/^\D*(.*\d)\D*/$1/;       # strip off garbage
        !          1506: 
        !          1507:   PARSE:
        !          1508:     {
        !          1509:        if (s/^(\d{4})(-|$)//)
        !          1510:        {
        !          1511:            if ($1 < 1970)
        !          1512:            {
        !          1513:                warn("$0: can not handle years before 1970 - year $1 ignored\n");
        !          1514:                return undef;
        !          1515:            }
        !          1516:            elsif ( $1 >= 2070)
        !          1517:            {
        !          1518:                warn("$0: can not handle years past 2070 - year $1 ignored\n");
        !          1519:                return undef;
        !          1520:            }
        !          1521:            else
        !          1522:            {
        !          1523:                $year = $1 % 100; # 0<= $year < 100
        !          1524:                                 ;# - interpreted 70 .. 99,00 .. 69
        !          1525:            }
        !          1526:            $last = $[ + 5;
        !          1527:            last PARSE if $_ eq '';
        !          1528:            warn("$0: bad date_time_spec: \"$_\" found after YEAR\n"),
        !          1529:            return(undef)
        !          1530:                if $2 eq '';
        !          1531:        }
        !          1532: 
        !          1533:        if (s/^(\d{1,2})(-|$)//)
        !          1534:        {
        !          1535:            warn("$0: implausible month $1\n"),return(undef)
        !          1536:                if $1 < 1 || $1 > 12;
        !          1537:            $mon = $1 - 1;
        !          1538:            $last = $[ + 4;
        !          1539:            last PARSE if $_ eq '';
        !          1540:            warn("$0: bad date_time_spec: \"$_\" found after MONTH\n"),
        !          1541:            return(undef)
        !          1542:                if $2 eq '';
        !          1543:        }
        !          1544:        else
        !          1545:        {
        !          1546:            warn("$0: bad date_time_spec \"$_\"\n"),return(undef)
        !          1547:                if defined($last);
        !          1548:            
        !          1549:        }
        !          1550: 
        !          1551:        if (s/^(\d{1,2})([_ ]|$)//)
        !          1552:        {
        !          1553:            warn("$0: implausible month day $1 for month ".($mon+1)." (".
        !          1554:                 $MaxNumDaysPerMonth[$mon].")$mon\n"),
        !          1555:            return(undef)
        !          1556:                if $1 < 1 || $1 > $MaxNumDaysPerMonth[$mon];
        !          1557:            $mday = $1;
        !          1558:            $last = $[ + 3;
        !          1559:            last PARSE if $_ eq '';
        !          1560:            warn("$0: bad date_time_spec \"$_\" found after MDAY\n"),
        !          1561:            return(undef)
        !          1562:                if $2 eq '';
        !          1563:        }
        !          1564:        else
        !          1565:        {
        !          1566:            warn("$0: bad date_time_spec \"$_\"\n"), return undef
        !          1567:                if defined($last);
        !          1568:        }
        !          1569: 
        !          1570:        ;# now we face a problem:
        !          1571:        ;# if ! defined($last) a prefix of "07:"
        !          1572:        ;# can be either 07:MM or 07:ss
        !          1573:        ;# to get the second interpretation make the user add
        !          1574:        ;# a msec fraction part and check for this special case
        !          1575:        if (! defined($last) && s/^(\d{1,2}):(\d{1,2}\.\d+)//)
        !          1576:        {
        !          1577:            warn("$0: implausible minute $1\n"), return undef
        !          1578:                if $1 < 0 || $1 >= 60;
        !          1579:            warn("$0: implausible second $1\n"), return undef
        !          1580:                if $2 < 0 || $2 >= 60;
        !          1581:            $min = $1;
        !          1582:            $sec = $2;
        !          1583:            $last = $[ + 1;
        !          1584:            last PARSE if $_ eq '';
        !          1585:            warn("$0: bad date_time_spec \"$_\" after SECONDS\n");
        !          1586:            return undef;
        !          1587:        }
        !          1588:        
        !          1589:        if (s/^(\d{1,2})(:|$)//)
        !          1590:        {
        !          1591:            warn("$0: implausible hour $1\n"), return undef
        !          1592:                if $1 < 0 || $1 > 24;
        !          1593:            $hour = $1;
        !          1594:            $last = $[ + 2;
        !          1595:            last PARSE if $_ eq '';
        !          1596:            warn("$0: bad date_time_spec found \"$_\" after HOUR\n"),
        !          1597:            return undef
        !          1598:                if $2 eq '';
        !          1599:        }
        !          1600:        else
        !          1601:        {
        !          1602:            warn("$0: bad date_time_spec \"$_\"\n"), return undef
        !          1603:                if defined($last);
        !          1604:        }
        !          1605: 
        !          1606:        if (s/^(\d{1,2})(:|$)//)
        !          1607:        {
        !          1608:            warn("$0: implausible minute $1\n"), return undef
        !          1609:                if $1 < 0 || $1 >=60;
        !          1610:            $min = $1;
        !          1611:            $last = $[ + 1;
        !          1612:            last PARSE if $_ eq '';
        !          1613:            warn("$0: bad date_time_spec found \"$_\" after MINUTE\n"),
        !          1614:            return undef
        !          1615:                if $2 eq '';
        !          1616:        }
        !          1617:        else
        !          1618:        {
        !          1619:            warn("$0: bad date_time_spec \"$_\"\n"), return undef
        !          1620:                if defined($last);
        !          1621:        }
        !          1622: 
        !          1623:        if (s/^(\d{1,2}(\.\d+)?)//)
        !          1624:        {
        !          1625:            warn("$0: implausible second $1\n"), return undef
        !          1626:                if $1 < 0 || $1 >=60;
        !          1627:            $sec = $1;
        !          1628:            $last = $[;
        !          1629:            last PARSE if $_ eq '';
        !          1630:            warn("$0: bad date_time_spec found \"$_\" after SECOND\n");
        !          1631:            return undef;
        !          1632:        }
        !          1633:     }
        !          1634: 
        !          1635:     return $time unless defined($last);
        !          1636: 
        !          1637:     $sec  = 0 if $last > $[;
        !          1638:     $min  = 0 if $last > $[ + 1;
        !          1639:     $hour = 0 if $last > $[ + 2;
        !          1640:     $mday = 1 if $last > $[ + 3;
        !          1641:     $mon  = 0 if $last > $[ + 4;
        !          1642:     local($rtime) = &timelocal($sec,$min,$hour,$mday,$mon,$year, 0,0, 0);
        !          1643: 
        !          1644:     ;# $rtime may be off if daylight savings time is in effect at given date
        !          1645:     return $rtime + ($sec - int($sec))
        !          1646:        if $hour == (localtime($rtime))[$[+2];
        !          1647:     return
        !          1648:        &timelocal($sec,$min,$hour,$mday,$mon,$year, 0,0, 1)
        !          1649:            + ($sec - int($sec));
        !          1650: }
        !          1651: 
        !          1652: 
        !          1653: sub min
        !          1654: {
        !          1655:   local($m) = shift;
        !          1656: 
        !          1657:   grep((($m > $_) && ($m = $_),0),@_);
        !          1658:   $m;
        !          1659: }
        !          1660: 
        !          1661: sub max
        !          1662: {
        !          1663:   local($m) = shift;
        !          1664: 
        !          1665:   grep((($m < $_) && ($m = $_),0),@_);
        !          1666:   $m;
        !          1667: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>