This file is indexed.

/usr/bin/colplot is in collectl-utils 4.7.1-1.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
#!/usr/bin/perl -w

# Copyright 2004-2010 Hewlett-Packard Development Company, L.P.
#
# colplot may be copied only under the terms of either the Artistic License
# or the GNU General Public License, which may be found in the source kit

# Debug Values:
#   1 - print interesting stuff
#   2 - more intersting stuff including many colplotib processing STEPs
#   4 - don't delete ctl files
#   8 - remote processing details
#  16 - show some library subroutine calls
#  32 - file selection
#  64 - in web ui, display resultant hrefs and exit
# 128 - api stuff...

# GOOD GNUPLOT REF: http://t16web.lanl.gov/Kawano/gnuplot/plot1-e.html

use Cwd;
use Config;
use Getopt::Long;
use File::Basename;
use Time::Local;
use strict;
use Compress::Zlib;

#    C o m m o n    I n i t i a l i z a t i o n

my $LibDir='/usr/share/collectl';

my $Version="4.7.1";
my $Copyright='Copyright 2004-2010 Hewlett-Packard Development Company, L.P.';
my $License=  "colplot may be copied only under the terms of either the Artistic License\n";
$License.= "or the GNU General Public License, which may be found in the source kit";

# We might have been invoked via a link (at least on Linux) and we need to know
# where our physical location is.  Note we have to do this before we call initConfig()
# because we don't know where the 'require' file is located yet!
my $exeName=(!defined(readlink($0))) ? $0 : readlink($0);
my $BinDir=dirname($exeName);
my $pcFlag=($Config{'osname'}=~/MSWin32/) ? 1 : 0;
my $sep=($pcFlag) ? "\\" : "/";

# If the plot library exists in the same directory as colplot, we load it
# instead of the standard one, which MUST be the case on a PC since we're no
# longed supporting a lib/
$LibDir=$BinDir    if -e "$BinDir${sep}colplotlib.ph";
require "$LibDir${sep}colplotlib.ph";

# set up $mycfg data structure, required by colplotlib and initparams
my $mycfg={ exename=>$exeName, bindir=>$BinDir, libdir=>$LibDir, pcflag=>$pcFlag };
initConfig($mycfg);

# These are globals
my ($debug, $config, $form, $mode, $chgdir, $dir);
my ($fromDate, $thruDate, $fromTime, $thruTime, $refresh, $winsize, $timeframe);
my ($contains, $anyall, $width, $height, $thick, $legend, $adjust, $xaxis, $pagbrk, $xtics);
my ($ylog, $plots, $filters, $instance, $email, $filetype, $incctlFlag, $subject);
my ($plotType, $yminwidth, $tabcol, $uniqueFlag, $oneperdayFlag, $address, $plotfile);

# some defaults
$uniqueFlag=-1;
$debug=$oneperdayFlag=$incctlFlag=$xtics=0;
$mode=$timeframe=$plots=$form=$filetype=$contains=$anyall=$email=$subject=$filters=$address='';
$dir=$mycfg->{PlotDir};

# If not running gnuplot 4 on a pc (which always has x11 and png support AND won't
# respond to a query), before we begin we want to know what kind of graphics gnuplot
# supports both for the -v command as well as to warn use via the web.
my ($x11Flag, $pngFlag)=(1,1);
if (!$pcFlag || $mycfg->{GnuVersion}<4)
{
  my $command="echo 'set terminal x11'|$mycfg->{GnuPlot} 2>&1";
  my $result=`$command`;
  $x11Flag=($result=~/unknown or ambiguous/) ? 0 : 1;
  $command="echo 'set terminal png'|$mycfg->{GnuPlot} 2>&1";
  $result=`$command`;
  $pngFlag=($result=~/unknown or ambiguous/) ? 0 : 1;
}

#####################################################
#	C o m m a n d    L i n e    I n t e r f a c e
#####################################################

# If we didn't come in via the web...
my $cliFlag=0;
if (!$mycfg->{htmlflag})
{
  error("Type -help for help")    if scalar(@ARGV)==0;
  cli();
  exit;
}

#####################################################
#       W e b    I n t e r f a c e
#####################################################

# Initialization
my $thisScript=basename($0);

# make sure buffers flushed or we may deliver partial plots
select STDOUT;
$|=1;

# Note that now all browsers set QUERY_STRING
my ($qs, $vars, @vars);
$qs=$vars=$ENV{"QUERY_STRING"};
$qs=$vars=''    if !defined($qs);
@vars=split(/&/, $vars);

# Need to uncomment lines to see stuff outside loop
#htmlHeader($mycfg, 'debug');
#print "<BR>Query: $vars\n";

# Defaults
$fromTime="00:00";
$thruTime="24:00";

foreach my $element (@vars)
{
  # Note - convert parameters AFTER splitting on the & and = incase these
  # are embedded in data itself...
  (my $name, my $value)=split(/=/, $element);
  $value=~s/%([\dA-Fa-f][\dA-Fa-f])/pack('C', hex($1))/eg;
  $value=~s/\+/ /g    if $name ne 'directory';
  printText("Name: $name Value: $value")    if $debug & 1;

  showplots($mycfg)    if $name=~/^showplots/i;

  # Debug flag is handled slightly differently
  $debug=defined($value) ? $value : 1    if ($name=~/debug/i && $value=~/\d+/);
  $config=$value      if $name=~/^config/;
  $form=$value        if $name=~/^form/i;
  $mode=$value        if $name=~/^mode/i;
  $chgdir=$value      if $name=~/^chgdir/i;
  $dir=$value         if $name=~/^dir/i;
  $fromDate=$value    if $name=~/^fdate/i;
  $thruDate=$value    if $name=~/^tdate/i;
  $fromTime=$value    if $name=~/^ftime/i;
  $thruTime=$value    if $name=~/^ttime/i;
  $refresh=$value     if $name=~/^refresh/i;
  $winsize=$value     if $name=~/^winsize/i;
  $timeframe=$value   if $name=~/^timeframe/i;
  $contains=$value    if $name=~/^contains/;
  $anyall=$value      if $name=~/^anyall/;
  $width=$value       if $name=~/^width/i;
  $height=$value      if $name=~/^height/i;
  $thick=$value       if $name=~/^thick/i;
  $legend=$value      if $name=~/^legend/i;
  $adjust=$value      if $name=~/^adjust/i;
  $xaxis=$value       if $name=~/^xaxis/i;
  $pagbrk=$value      if $name=~/^pagbrk/i;
  $xtics=$value       if $name=~/^xtics/i && $value ne '';
  $ylog=$value        if $name=~/^ylog/i;
  $plots.="$value "   if $name=~/^plots|plotnames/i && $plots!~/$value/;
  $filters=$value     if $name=~/^filters/;
  $instance=$value    if $name=~/^instance/;
  $email=$value       if $name=~/^email/;
  $filetype=$value    if $name=~/^filetype/;
  $incctlFlag=1       if $name=~/^incctl/i && $value=~/on/i;
  $subject=$value     if $name=~/^subj/i   && $value ne '';
  $plotType=$value    if $name=~/^type/i;
  $yminwidth=$value   if $name=~/^yminwidth/i;
  $tabcol=$value      if $name=~/^tabcol/i;
  $uniqueFlag=1       if $name=~/^unique/i;
  $oneperdayFlag=1    if $name=~/^oneperday/i;
  $address=$value     if $name=~/^addr/i;
  $plotfile=$value    if $name=~/^filename/i;    # amazing this was never used before
}
$plots=~s/\s+$//;    # trim tailing space
$mycfg->{debug}=$debug;

if ($mycfg->{deftimeframe}=~/float/)
{
  $winsize=($mycfg->{deftimeframe}=~/float=(\d+)/) ? $1 : 60;
  $timeframe='float';
}

#    S e t    D e f a u l t s

$mode='live'     if $mode=~/on/;
if ($mycfg->{htmlflag})
{
  $mode='normal'    if $mode eq '';

  $width= defined($mycfg->{htmlWidth})  ? $mycfg->{termWidth}  : 1.2
      if !defined($width);
  $height=defined($mycfg->{htmlHeight}) ? $mycfg->{termHeight} : 0.3
      if !defined($height);
}
else
{
  $width= defined($mycfg->{termWidth})  ? $mycfg->{termWidth}  : 1
      if !defined($width);
  $height=defined($mycfg->{termHeight}) ? $mycfg->{termHeight} : 0.2
      if !defined($height);
}
$thick=1    if !defined($thick);

$fromTime="0$fromTime"    if length($fromTime)<5;
$thruTime="0$thruTime"    if length($thruTime)<5;

####################################
#   M a i n    Q u e r y    F o r m
####################################

if ($form eq '')
{
  htmlHeader($mycfg, "ColPlot");
  error("$dir does not exist!")    if !-e $dir;

  error("$mycfg->{GnuPlot} does not support png graphics.  See FAQ for details.")
      if !$pngFlag;

  # defaults
  $refresh=60        if !defined($refresh);
  $winsize='60'      if !defined($winsize);
  $legend='on'       if !defined($legend);
  $adjust='off'      if !defined($adjust);
  $xaxis='on'        if !defined($xaxis);
  $pagbrk='off'      if !defined($pagbrk);
  $ylog='off'        if !defined($ylog);

  # get starting/ending dates for all files in directory (type3 doesn't use 5th param)
  my $pparams= {fdate=>20010101, tdate=>20380101, contains=>'', unique=>$uniqueFlag };
  findFiles(3, $mycfg, $pparams, "$dir$sep*", undef) || 
	error("No plottable files match your selection criteria.  Are your dir and/or file protections right?")
		if $mycfg->{deftimeframe} eq 'auto';
  $fromDate=$pparams->{fdate};
  $thruDate=$pparams->{tdate};

  # Note the 'magic' by setting the next form via 'genpage'
  print "<body>\n";
  print "<FORM NAME=main ACTION=$thisScript METHOD=GET>\n";
  print "<input type=hidden name=form value=genpage>\n";
  print "<input type=hidden  name=debug value=$debug>\n";
  print "<input type=hidden  name=directory value=$dir>\n";
  print "<input type=hidden  name=mode value=$mode>\n";
  print "<input type=hidden  name=addr value=$address>\n";

  my $helpFile="$mycfg->{HelpDir}colplot-help.html";
  print  "<center>\n";
  printf "<b><font size=6>ColPlot %sV$Version</b>\n", $mode=~/live/ ? '<i>Live</i> ' : '';
  print  "<font size=4><br><b>For Directory: $dir</b>\n";
  print  "<font size=3>\n";
  print  "<a href=FAQ-colplot.html>view faq</a>\n";
  print  "<br>\n";
  print  "<input type=submit VALUE='Generate Plot'>\n";
  print  "<input type=submit NAME=chgdir VALUE='Change Dir'>\n";
  print  "<input type=submit NAME=chgdir VALUE='List Dir'>\n";
  print  "<input type=button VALUE='Help' onclick=window.open('$helpFile')>\n";

  print "<br><input type=checkbox name=mode id=enable><label for='enable'>\n";
  print "<b>Enable Refresh Every <input type=text name=refresh size=1 value=$refresh id=refresh><label for='refresh'> seconds</b>\n";
  print "</center>\n";

  my $tfixed=($mycfg->{deftimeframe}!~/float/) ? 'checked' : '';
  my $tfloat=($mycfg->{deftimeframe}=~/float/) ? 'checked' : '';
  print "<p>\n";
  print "<b>From: <input type=text name=fdate size=8 value=$fromDate>\n";
  print "<input type=text name=ftime size=5 value=$fromTime>\n";
  print "<b>Thru: <input type=text name=tdate size=8 value=$thruDate>\n";
  print "<input type=text name=ttime size=5 value=$thruTime>\n";
  print "<input type=radio name=timeframe value=fixed checked>\n";
  print "<font=5><i>OR</i>\n";
  print "<b>Last: <input type=text name=winsize size=1 value=$winsize> Minutes\n";
  print "<input type=radio name=timeframe value=float>\n";

  # If anyall passed as 'all', use that as default, otherwise go with 'any'
  my  $allSel=($anyall=~/all/) ? 'selected="selected"' : '';
  my  $anySel=($allSel eq '')  ? 'selected="selected"' : '';
print <<EOF;
<p>
<b>Filenames Containing</b>
  <select name="anyall" size="1">
    <option $allSel>All</option>
    <option $anySel>Any</option>
  </select></td>
<b>of: </b><INPUT type=text size=5 name=contains value=$contains>
<b>Plot Type:</b>
  <select name="type" size="1">
    <option selected="selected">Default</option>
    <option value='l'>line</option>
    <option value='p'>point</option>
    <option value='ls'>stackedline</option>
    <option value='ps'>stackedpoint</option>
  </select></td>
<b>Display:</b>
    <select name=tabcol size=1>
    <option value=vert>Vertical</option>
    <option value=vertday>VerticalDay</option>
    <option value=sp selected>SysPlot</option>
    <option value=ps>PlotSys</option>
    <option value=dsp>DaySysPlot</option>
    <option value=dps>DayPlotSys</option>
    <option value=spd>SysPlotDay</option>
    <option value=sdp>SysDayPlot</option>
    <option value=pds>PlotDaySys</option>
    <option value=psd>PlotSysDay</option>
  </select>

<p><b>Summary Plots</b>
<table>
<tr>
  <td><input type=checkbox name=plots id=sumall value=sumall><label for='sumall'>All Plots</label></td>
  <td><input type=checkbox name=plots value=cpu id=cpu>      <label for='cpu'>CPU</td>
  <td><input type=checkbox name=plots value=ctxint id=ctxint><label for='ctxint'>Ctx/Int</td>
  <td><input type=checkbox name=plots value=disk id=disk>    <label for='disk'>Disk</td>
  <td><input type=checkbox name=plots value=inode id=inode>  <label for='inode'>I-Node</td>
  <td><input type=checkbox name=plots value=inter id=inter>  <label for='inter'>IConnect</td>
  <td><input type=checkbox name=plots value=sumlus id=sumlus><label for='sumlus'>Lustre</td>
</tr>
<tr>
  <td><input type=checkbox name=plots value=mem id=mem>      <label for='mem'>Memory</td>
  <td><input type=checkbox name=plots value=net id=net>      <label for='net'>Network</td>
  <td><input type=checkbox name=plots value=sumnfs id=sumnfs><label for='sumnfs'>NFS</td>
  <td><input type=checkbox name=plots value=proc id=proc>    <label for='proc'>Proc</td>
  <td><input type=checkbox name=plots value=sock id=sock>    <label for='sock'>Socket</td>
  <td><input type=checkbox name=plots value=swap id=swap>    <label for='swap'>Swap</td>
  <td><input type=checkbox name=plots value=tcp id=tcp>      <label for='tcp'>TCP</td>
</tr>
<tr><td colspan=5><b>Detail Plots</b></td></tr>
<tr>
  <td><input type=checkbox name=plots value=detall id=detail>    <label for='detail'>All Plots</td>
  <td><input type=checkbox name=plots value=cpudet id=cpudet>    <label for='cpudet'>CPU</td>
  <td><input type=checkbox name=plots value=diskdet id=diskdet>  <label for='diskdet'>Disk</td>
  <td><input type=checkbox name=plots value=detnfs id=detnfs>    <label for='detnfs'>NFS</td>
  <td><input type=checkbox name=plots value=interdet id=interdet><label for='interdet'>IConnect</td>
  <td><input type=checkbox name=plots value=netdet id=netdet>    <label for='netdet'>Network</td>
  <td><input type=checkbox name=plots value=detlus id=detlus>    <label for='detlus'>Lustre</td>
  <td><input type=checkbox name=plots value=fans id=fans>        <label for='fans'>Fans</td>
  <td><input type=checkbox name=plots value=temps id=temps>      <label for='temps'>Temps</td>
  <td><input type=checkbox name=plots value=power id=power>      <label for='power'>Power</td>
<tr>
  <td colspan=2><b>Detail Filters:</b></td>
  <td colspan=3><input type=input name=filters size=40></td>
</tr>
<tr>
  <td colspan=2><b>Plots by Name(s):</b></td>
  <td colspan=3><input type=input name=plotnames size=40></td>
  <td><input type=button VALUE='Help' onclick=window.open('?showplots')></td>
 </tr>
</table>
EOF

  # GS is optional, but if not there we can't do pdfs
  my $gsFlag=(defined($mycfg->{GS}) && -e $mycfg->{GS}) ? 1 : 0;
  my $selectPdf=($gsFlag) ? '<option value=pdf>pdf</option>' : '';

  print "<p>Email or Directory: <INPUT type=text size=20 name=email>\n";
  print "Subj: <INPUT type=text size=20 name=subject>\n";
  print "Type <select name=filetype>\n";
  print " $selectPdf\n";
  print " <option value=png>png</option>\n";
  print " <option value=none>none</option>\n";
  print "</select>\n";
  print "&nbsp; <label for='incctl'>Include Ctl File: <INPUT type=checkbox name=incctl id=incctl>\n";

  print "<p><b>Format Control/Destination</b></b><br>\n";
  print "Width: <INPUT type=text name=width size=1 value=$width>\n";
  print "Height: <INPUT type=text name=height size=1 value=$height>\n";
  print "Thick: <INPUT type=text name=thick size=1 value=$thick>\n";
  print "X-Increment: <INPUT type=text name=xtics size=1 value=$xtics>\n";

  printf "<label for='legend'>Legend<input type=checkbox name=legend %s id=legend>\n", $legend=~/on/ ? 'checked' : '';
  printf "<label for='adjust'>AdjustHeight<input type=checkbox name=adjust %s id=adjust>\n", $adjust=~/on/ ? 'checked' : '';
  printf "<label for='pagbrk'>PagBrk<input type=checkbox name=pagbrk %s id=pagbrk>\n", $pagbrk=~/on/ ? 'checked' : '';
  print  "<label for='ylog'>YLog<input type=checkbox name=Ylog id=ylog>\n";
  printf "<label for='xaxis'>X-Axis<input type=checkbox name=xaxis %s id=xaxis>\n", $xaxis=~/on/ ? 'checked' : '';

  print "<p>\n";
  print "<input type=submit VALUE='Generate Plot'>\n";
  print "<input type=reset  VALUE=Reset>\n";
  print "</form>\n";
}

########################################
#    C H A N G E     D I R E C T O R Y
########################################

# This must preceed genpage 
elsif (defined($chgdir) && $chgdir=~/Change Dir/i)
{
  htmlHeader($mycfg, "ColPlot");
  print "<FORM NAME=main ACTION=$thisScript METHOD=GET>\n";
  print "<input type=hidden  name=debug value=$debug>\n";
  print "<p><b>New Directory Name: <INPUT type=text name=directory size=40 value=$dir>\n";
  print "<p>\n";

  print "<input type=submit VALUE='Change It!'>\n";
}

#################################
#    L I S T   D I R E C T O R Y
#################################

# This too must preceed genpage
elsif (defined($chgdir) && $chgdir=~/List Dir/)
{
  my $command=$pcFlag ? "dir $dir" : "ls -x $dir";
  htmlHeader($mycfg, $dir);
  print "<form>\n";

  print "<h3>Contents of: $dir</h3>\n";
  open(LS, "$command |") or die "<BR>Couldn't execute $command";

  print "<PRE>\n";
  while (my $line=<LS>)
  {
    print $line;
  }
  close LS;
  print "<PRE>\n";
 
  print "<p>\n";
  print "</form>\n";
}

#########################################
#   G e n e r a t e    P a g e   H T M L
#########################################

elsif ($form=~/genpage/i)
{
  error("You must specify at least 1 plot")        if $plots eq '';

  $legend='off'    if !defined($legend);
  $adjust='off'    if !defined($adjust);
  $xaxis='off'     if !defined($xaxis);
  $pagbrk='off'    if !defined($pagbrk);
  $ylog='off'      if !defined($ylog);

  my $pparams={
        uitype=>    1,
	dir=>       $dir,
	contains=>  $contains,
	anyall=>    $anyall,
	plottype=>  $plotType,
	plots=>     $plots,
	fdate=>     $fromDate,
	tdate=>     $thruDate,
	ftime=>     $fromTime,
	ttime=>     $thruTime,
	width=>     $width,
        height=>    $height,
        thick=>     $thick,
	any=>       $anyall,
	legend=>    $legend,
        adjust=>    $adjust,
	xaxis=>     $xaxis,
        pagbrk=>    $pagbrk,
        xtics=>     $xtics,
        ylog=>      $ylog,
        email=>     $email,
	filetype=>  $filetype,
	incctl=>    $incctlFlag,
	subject=>   $subject,
        filters=>   $filters,
        mode=>      $mode,
        timeframe=> $timeframe,
        yminwidth=> $yminwidth,
        unique=>    -1    # use type of first file found
	};

  # Unless the user has explicitly chosen a 3-way table for display, we 
  # do multi-day plots.
  $pparams->{oneperday}=($tabcol!~/vertday|dps|dsp|pds|psd|sdp|spd/) ? 0 : 1;

  # Allow single digit hours in the time field
  $fromTime="0$fromTime"    if length($fromTime)==4;
  $thruTime="0$thruTime"    if length($thruTime)==4;

  validate($pparams);

  my @returnArray;
  # For local plots, which if typical, get a list of URLs for generating each individual plot.
  if ($address eq '')
  {
    @returnArray=buildPage($mycfg, $pparams);
    if (scalar(@returnArray==0))
    {
      printText("No files containing data that match your selections were found", 3);
      printText("Are your file protections correct?  Did you forget to uncompress them?", 3);
      exit;
    }
  }
  else
  {
    # For remote plots, possibly in pdsh format, convert to an array of addresses
    my @addresses=pdshFormat($address);

    foreach my $addr (@addresses)
    {
      $plots=~s/ /,/g;    # make sure plot names comma separated
      my $command="ssh $addr $exeName -dir $dir -plot $plots -href ";
      $command.="-date $fromDate-$thruDate -time $fromTime-$thruTime -width $width -height $height -thick $thick ";
      $command.=  "-contains $contains "         if $contains ne '';
      $command.=  "-any "                        if $anyall eq 'any';
      $command.=  "-nolegend "                   if $legend eq 'off';
      $command.=  "-adjust "                     if $adjust eq 'on';
      $command.=  "-noxaxis "                    if $xaxis eq 'off';
      $command.=  "-xtics $xtics "               if $xtics ne '';
      $command.=  "-ylog "                       if $ylog eq 'on';
      displayText($mycfg, "REMOTE: $command")    if $debug & 8;
      my @hrefs=`$command`;
      foreach my $href (@hrefs)
      {
        $href=~s/filetype=png/filetype=tty/;
        $href=~s/\s+$//;    # not really sure why trailing space
	$href.="\&addr=$addr";
	displayText($mycfg, "HREF: $href")    if $debug & 8;
        if ($href!~/^\&filename/)
        {
          displayText($mycfg, "Error: $href")    if $href!~/^No files matching/;
	  last;
        }
        push @returnArray, $href;
      }
    }
  }

  #    D i s p l a y    P l o t s    O R    D i s p o s e    O f    A    F i l e

  if ($email eq '')
  {
    # We need to store the hrefs in a hash, indexed by our display parameters so we can
    # load them into a table in the right order.
    my (%hrefs, %allhosts, %allplots, %alldates);
    my ($numhosts, $numplots, $numdates, $vertFlag)=(0,0,0,0);
    foreach my $params (@returnArray)
    {
      $params=~/filename=(.*)&plots=(.*?)&instance=(.*?)&/;
      my $filename=basename($1);
      my $plotname="$2$3";
      my $instance=$3;

      # Pull hostname and date out of filename (possibly including a differentiating prefix)
      $filename=~/(.*)-(\d{8}).*\.\w+$/;
      my $hostname=$1;
      my $fromdate=$2;

      # If hostname ends in a numeric, we may have multiple hosts of a cluster
      # and if single digits they won't collate correctly.  By forcing to 6
      # digits we can handle >1M hosts!
      $hostname=~s/(\d+)$/sprintf("%06d-$hostname", $1)/e;

      # Convert the returned parameter string until a full href
      my $href="<img src=\"./$thisScript?form=genplot&debug=$debug$params\">";
      displayText($mycfg, $href)    if $debug & 64;

      # We need unique entries for $href so we don't get any duplicate entries which
      # would result in losing an entry.  In the case of 'sp' and 'ps', there is never
      # more then one entry per system/plot combo and so we don't need (or want) a date.
      # if we know we'll only have a 1 column table (keeps the output more compressed)
      # as long as unique and '$oneColFlag' is set.  Including the instance name with 
      # the plot also assures uniqueness for both summary as well as detail plots.
      $vertFlag=1    if $tabcol=~/vert/;
      $hrefs{"$plotname:$hostname"}=$href              if $tabcol eq 'sp';
      $hrefs{"$hostname:$plotname"}=$href              if $tabcol eq 'ps';
      $hrefs{"$hostname:$plotname:$fromdate"}=$href    if $tabcol eq 'vert' || $tabcol eq 'vertday';
      $hrefs{"$hostname:$plotname:$fromdate"}=$href    if $tabcol eq 'dsp';
      $hrefs{"$plotname:$hostname:$fromdate"}=$href    if $tabcol eq 'dps';
      $hrefs{"$fromdate:$hostname:$plotname"}=$href    if $tabcol eq 'pds';
      $hrefs{"$hostname:$fromdate:$plotname"}=$href    if $tabcol eq 'psd';
      $hrefs{"$plotname:$fromdate:$hostname"}=$href    if $tabcol eq 'spd';
      $hrefs{"$fromdate:$plotname:$hostname"}=$href    if $tabcol eq 'sdp';

      # Save a set of all unique names for sort later on
      $numhosts++    if !defined($allhosts{$hostname});
      $numplots++    if !defined($allplots{$plotname});
      $numdates++    if !defined($alldates{$fromdate});
      $allhosts{$hostname}='';
      $allplots{$plotname}='';
      $alldates{$fromdate}='';
      #printText("TABCOL: $tabcol HOST: $hostname  PLOT: $plotname  DATE: $fromdate");
    }
    exit    if $debug & 64;

    # If the main sort (column name) only has a single instance, we DON'T want to
    # display as a table.  NOTE - it's possible to display 3-way plots that have a 
    # single row as a single column but I'm not sure if I should.  For that matter
    # I'm not even sure if the following should be done either.
    if (length($tabcol)==2)
    {
      $vertFlag=1    if ($numhosts==1 && $tabcol eq 'ps') ||
                        ($numplots==1 && $tabcol eq 'sp');
    }

    #    G e n e r a t e    H T M L    H e r e

    htmlHeader($mycfg, "plots");

    # last minute error check
    error("You cannot select multiple plots when using the 'Subject' API")    if !$vertFlag && defined($pparams->{links}) && $pparams->{links}==1;

    # API that allows links to be added to each plot
    my $subtitle='';
    my ($apiBase, $apiParams);
    if (defined($pparams->{links}) && $pparams->{links}==1)
    {
      ($apiBase,$apiParams)=loadAPI($pparams);
      my $apiInit=    "${apiBase}InitParams";
      my $apiSubtitle="${apiBase}Subtitle";

      no strict 'refs';
      &$apiInit($mycfg, $pparams, '', $apiParams);
      $subtitle=&$apiSubtitle();
    }

    # turn from date into yyyy/mm/dd and if different do the same for thru date
    my $temp=$pparams->{fdate};
    my $fDate=sprintf("%s\/%s\/%s", substr($temp,0,4),  substr($temp,4,2),  substr($temp,6,2));
    $temp=$pparams->{tdate};
    my $tDate=sprintf("%s\/%s\/%s", substr($temp,0,4),  substr($temp,4,2),  substr($temp,6,2));
    $tDate=''    if $tDate eq $fDate;

    print  "<center><font size=4>\n";
    printf "<b>ColPlot$subtitle ";
    printf "<i>Live!</i> Refresh: $refresh seconds</b>"    if $mode=~/live/;
    print  "<br>From: $fDate $pparams->{ftime} Thru: $tDate $pparams->{ttime}</b></font><br>\n";

    # The default is to report on multiple days/plot!  However, if one also specifies
    # a timeframe that doesn't go midnite to midnite, warn them that these times only
    # apply to starting/ending records.  However if plots are only for a single day,
    # no need to warn them about anything.
    print  "<br><font size=3><b><i>Warning - the from/thru times only apply to first/last date respectively</i></b>\n"
	if !$pparams->{oneperday} && ($pparams->{fdate} ne $pparams->{tdate}) && ($fromTime ne '00:00' || $thruTime ne '24:00');

    # Force refresh if we're in 'live' mode
    print "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"$refresh\">\n"    if $mode=~/live/;
 
    # This is really an optimization - if we want to do things vertically, we use
    # less screen real-estate by NOT putting things into a table...
    my (%hash1, %hash2, %hash3);

    if ($vertFlag)
    {
      my $firstKey=1;
      foreach my $key (sort keys %hrefs)
      {
        # API that allows links to be added to each plot
	if (defined($pparams->{links}) && $pparams->{links}==1)
        {
          no strict 'refs';
          my $apiLinks="${apiBase}Links";
          my $href=&$apiLinks($mycfg, $hrefs{$key});

          print "<a href=$href>$hrefs{$key}</a>\n";
          $firstKey=0;
	}
        else
        {
          # By leaving off the <br> we can play games with multiple entries/line
          print "$hrefs{$key}\n";
        }
      }
      print "</html>\n";
    }
    elsif ($tabcol eq 'ps' || $tabcol eq 'sp')
    {
      %hash1=%allplots    if $tabcol eq 'sp';
      %hash2=%allhosts    if $tabcol eq 'sp';
      %hash1=%allhosts    if $tabcol eq 'ps';
      %hash2=%allplots    if $tabcol eq 'ps';

      print "<table>\n";
      foreach my $name1 (sort keys %hash1)
      {
        my $row='';
        foreach my $name2 (sort keys %hash2)
        {
          my $href=$hrefs{"$name1:$name2"};
          $row.=sprintf("<td valign=top>%s</td>", defined($href) ? $href : '');
        }
        print "<tr>$row</tr>\n";
      }
      print "</table>\n";
      print "</html>\n";
    }
    else
    {
      # We want to set up looping parameter such that we go through all
      # possible combinations of host-plot-date in the selected order.
      # column header is always the inner most sort while the
      # the other 2 are 1st and 2nd in reverse order
      %hash1=%alldates    if $tabcol=~/^.{1}d.{1}/;
      %hash1=%allplots    if $tabcol=~/^.{1}p.{1}/;
      %hash1=%allhosts    if $tabcol=~/^.{1}s.{1}/;
      %hash2=%alldates    if $tabcol=~/d$/;
      %hash2=%allplots    if $tabcol=~/p$/;
      %hash2=%allhosts    if $tabcol=~/s$/;
      %hash3=%alldates    if $tabcol=~/^d/;
      %hash3=%allplots    if $tabcol=~/^p/;
      %hash3=%allhosts    if $tabcol=~/^s/;

      print "<BR><table>\n";
      foreach my $name1 (sort keys %hash1)
      {
        foreach my $name2 (sort keys %hash2)
        {
          my $row='';
          my $rowHasData=0;
          foreach my $name3 (sort keys %hash3)
          {
            my $href=$hrefs{"$name1:$name2:$name3"};
            #printText("1: $name1 2: $name2 3: $name3");
  
            $rowHasData=1    if  defined($href);
            $href=''         if !defined($href);
            $row.="<td>$href</td>";
          }
          print "<tr>$row</tr>\n"    if $rowHasData;
        }
      }
      print "<br></table>\n";
      print "</html>\n";
    }
  }
  else
  {
    fileDispose($pparams, \@returnArray);
  }
}

#####################################
#    R E N D E R    O N E    P L O T
#####################################

# This renders a single plot
elsif ($form=~/genplot/)
{
  # typical operation is to run locally
  if ($address eq '')
  {
    my $pparams;
    my $pngFile=buildPngPlot($mycfg, $pparams, $qs);
    displayText($mycfg, "Created: $pngFile")    if $debug & 1;

    # print file contents and remove file.
    if (!$pcFlag)
    {
      my $contents=`cat $pngFile`;
      print "Content-type: image/png\n\n";
      print $contents;
    }
    else
    {
      # Note we have to do it differently on DOS because there's no equivalent of 'cat file'
      # and a 'type' command doesn't work on binary data.
      open PNG, "<$pngFile" or die "<BR>Couldn't open '$pngFile'";
      binmode PNG;
      my $contents="";
      while (my $line=<PNG>)
      {
        $contents.=$line;
      }
      close PNG;

      open OUT, ">-" or die "<BR>Couldn't open STDOUT";
      binmode OUT;
      $|=1;

      print OUT "Content-type: image/png\n\n";
      print OUT $contents;
      close OUT;
    }
    unlink $pngFile        unless $debug & 4;
  }
  else
  {
    # Note that '$plots' should always be a singleton
    displayText($mycfg, "Generate REMOTE plot '$plots' on: $address for $plotfile")    if $debug & 8;

    # by separating the filename from the directory and specifying both to colplot
    # we can assure ourselves of requesting a single plot
    my $dirname=dirname($plotfile);
    my $filename=basename($plotfile);
    $filename=~/(.*)-\d*\./;
    my $prefix=$1;
    my $command="ssh $address $exeName -dir $dirname -contains $prefix -plot $plots -filetype tty ";
    $command.="-date $fromDate-$thruDate -time $fromTime-$thruTime -width $width -height $height -thick $thick ";
    $command.=  "-nolegend "                if $legend eq 'off';
    $command.=  "-adjust "                  if $adjust eq 'on';
    $command.=  "-noxaxis "                 if $xaxis eq 'off';
    $command.=  "-xtics $xtics "            if $xtics ne '';
    $command.=  "-ylog "                    if $ylog eq 'on';
    $command.=  "-yminwidth $yminwidth "    if $yminwidth!=0;
    $command.=  "2>/dev/null"               unless $debug & 8;
    displayText($mycfg, "Command: $command<br>")    if $debug & 1;

    my $contents=`$command`;
    print "Content-type: image/png\n\n";
    print $contents;
  }
}

else
{
  print "<h1>Invalid form: $form</h1>\n";
}
htmlFooter();

####################################################
#    C o m m a n d    L i n e    I n t e r f a c e
####################################################

sub cli
{
  $cliFlag=1;

  # simple defaults
  $width= (defined($mycfg->{width}))      ? $mycfg->{width} : 1;
  $height=(defined($mycfg->{htmlHeight})) ? $mycfg->{htmlHeight} : .2;
  $thick= (defined($mycfg->{thick}))      ? $mycfg->{thick} : 1;

  # These are unique to CLI
  my ($showParamsFlag, $date, $time, $nolegendFlag, $adjustFlag, $noxaxisFlag);
  my $filedir='';
  my $hrefFlag=0;

  # Defaults
  $pagbrk=0;
  $yminwidth=0;
  GetOptions (
    "debug=i"     => \$debug,
    "help!"       => sub {usage()},
    "showplots!"  => sub {showplots($mycfg)},
    "version!"    => sub {showversion()},
    "showparams!" => \$showParamsFlag,
    "dir=s"       => \$dir,
    "date=s"      => \$date,
    "time=s"      => \$time,
    "plots=s"     => \$plots,
    "filters=s"   => \$filters,
    "contains=s"  => \$contains,
    "all!"        => \$anyall,
    "type=s"      => \$plotType,
    "height=s"    => \$height,
    "width=s"     => \$width,
    "thick=i"     => \$thick,
    "adjust!"     => \$adjustFlag,
    "nolegend!"   => \$nolegendFlag,
    "noxaxis!"    => \$noxaxisFlag,
    "xtics=i"     => \$xtics,
    "ylog!"       => \$ylog,
    "email=s"     => \$email,
    "filedir=s"   => \$filedir,
    "filetype=s"  => \$filetype,
    "incctl!"     => \$incctlFlag,
    "pagbrk!"     => \$pagbrk,
    "subject=s"   => \$subject,
    "oneperday!"  => \$oneperdayFlag,
    "unique!"     => \$uniqueFlag,
    "lastmins=i"  => \$winsize,
    "href!"       => \$hrefFlag,
    "yminwidth=i" => \$yminwidth
    ) or error("type -help for help");

  # when writing to an x11 terminal, make sure gnuplot supports it!
  error("$mycfg->{GnuPlot} does not support x11 graphics. Consider --filedir. See FAQ for details.")
      if $filedir eq '' && !$x11Flag;

  # Be sure to stash in our configuration for library routines to get to.
  $mycfg->{debug}=$debug;
  $mycfg->{htmlflag}=0;

  if (defined($plotType))
  {
    error("invalid plottype")    if $plotType!~/^l$|^ls$|^sl$|^p$|^ps$|^sp$/;
    $plotType='ls'    if $plotType eq 'sl';
    $plotType='ps'    if $plotType eq 'sp';
  }

  if (defined($time))
  {
    ($fromTime, $thruTime)=split(/-/, $time);
    error("specifying a 'from' time not allowed with -lastmins")    if defined($winsize) && $fromTime ne '';

    $thruTime='24:00'         if !defined($thruTime);
    $fromTime="0$fromTime"    if length($fromTime)==4;    # this will be ignored if --lastmins
    $thruTime="0$thruTime"    if length($thruTime)==4;
  }
  else
  {
    $fromTime='00:00';
    $thruTime=(!defined($winsize)) ? '24:00' : '';       # thru time will be last file access time for --lastmins
  }

  # main logic uses '$email' for both mail AND file output
  error("invalid file type")                            if $filetype ne '' && $filetype!~/pdf|png|tty/;
  error("you cannot specify -email AND -file")          if $email ne '' && $filedir ne '';
  error("you cannot specify -email AND -filetype tty")  if $email ne '' && $filetype eq 'tty';
  $email=$filedir     if $filedir ne '';
  $filetype='term'    if $email eq '' && $filetype ne 'tty';
  $filetype='pdf'     if $filetype eq '' && $email ne '';

  error("you cannot specify -filedir with a file type of 'tty'")
      if $filetype=~/tty/i && $email ne '';

  error("specify a file to send your pdf or png output to")
      if $filetype=~/pdf/i && $email eq '';

  error("pdf output requires 'ghostscript' and it can't be found (see '.conf' file)")
        if $filetype=~/pdf/ && !-e $mycfg->{GS};

  error("-all requires -contains")    if $anyall ne '' && $contains eq '';
  $anyall=($anyall eq '') ? 'any' : 'all';

  # Convert spaces to commas
  $plots   =~s/[ ]/,/g;
  $contains=~s/[ ]/,/g;

  $ylog=$ylog                             ? 'on' : 'off';
  $pagbrk=$pagbrk                         ? 'on' : 'off';
  $legend=(!defined($nolegendFlag))       ? 'on' : 'off';
  $adjust=(defined($adjustFlag))          ? 'on' : 'off';
  $noxaxisFlag= (!defined($noxaxisFlag))  ? 'on' : 'off';
  $filetype='pdf'    if $pagbrk eq 'on';

  my $pparams={
        uitype=>    2,
        mode=>      '',
        showparams=>$showParamsFlag,
	dir=>       $dir,
	contains=>  $contains,
	anyall=>    $anyall,
	plottype=>  $plotType,
	plots=>     $plots,
	fdate=>     $fromDate,
	tdate=>     $thruDate,
	ftime=>     $fromTime,
	ttime=>     $thruTime,
	width=>     $width,
        height=>    $height,
        thick=>     $thick,
	any=>       $anyall,
	legend=>    $legend,
        adjust=>    $adjust,
	xaxis=>     $noxaxisFlag,
        pagbrk=>    $pagbrk,
        xtics=>     $xtics,
        ylog=>      $ylog,
        email=>     $email,
	filetype=>  $filetype,
	incctl=>    $incctlFlag,
	subject=>   $subject,
        filters=>   $filters,
        oneperday=> $oneperdayFlag,
        unique=>    $uniqueFlag,
        href=>      $hrefFlag,
        yminwidth=> $yminwidth
        };
  $timeframe='float'      if defined($winsize);
  $pparams->{uitype}=1    if $hrefFlag;

  if (defined($date))
  {
    ($fromDate, $thruDate)=split(/-/, $date);
    $thruDate=$fromDate    if !defined($thruDate);
    $pparams->{fdate}=$fromDate;
    $pparams->{tdate}=$thruDate;
  }
  else
  {
    my $selected={};
    $pparams->{fdate}=20000101;
    $pparams->{tdate}=29991231;
    findFiles(3, $mycfg, $pparams, "$dir$sep*", $selected) ||
      	error("No plottable files match your selection criteria.  Are your dir and/or file protections right?");
    $fromDate=$pparams->{fdate};
    $thruDate=$pparams->{tdate};
    #print "Found Files - MinDate: $pparams->{fdate} MaxDate: $pparams->{tdate}\n";
  }

  validate($pparams);

  #    G e n e r a t e    P l o t s    H e r e

  # If use ^Cs after here we need to delete ctl file
  $SIG{"INT"}=\&sigInt;

  my @returnArray=buildPage($mycfg, $pparams);
  if (scalar(@returnArray==0))
  {
    printText("No files matching selections were found");
  }

  #    D i s p o s e    O f    A n y    F i l e s

  fileDispose($pparams, \@returnArray)    if $pparams->{filetype}!~/term|tty/;
}

# File disposition should be the same whether on linux or a pc and from a web
# page or a command line interface (since the display routines are already smart
# enough to deal with html when necessary)
sub fileDispose
{
  my $pparams=    shift;
  my $returnArray=shift;

  my $attach=@$returnArray[0];
  if ($email!~/@/)
  {
    # Strip the trailing '*' from the attach name
    $attach=~s/\*//;
    if ($pparams->{filetype}=~/pdf/)
    {
      printText("Your Plot(s) have been written to ${attach}colplot.pdf", 3);
    }
    else
    {
      my $prefix=$$;
      if (!-d $email)
      {
        $prefix=basename($email);
	$email=dirname($email);
      }
      printText("Your Plot(s) have been copied to $email with a prefix of '$prefix-'", 3);
    }
  }
  else
  {
    # Ship it...
    my $subject=$pparams->{subject};
    $subject="Your plot file(s) are attached"    if $subject eq '';
    my $basename=basename($attach);
    my $command="$mycfg->{UUENCODE} $attach $basename | $mycfg->{MAIL} -s \"$subject\" $email";
    printText("Command: $command")    if $debug & 1;
    `$command`;

    printText("Your Plot(s) have been mailed to $email with a subject of \"$subject\"", 3);
    `$mycfg->{del} $mycfg->{tempdir}$$-*`    unless $debug & 4;
  }
}

########################################
#    S u p p o r t    F u n c t i o n s
########################################

sub validate
{
  my $pparams=shift;  

  #    A P I    C h e c k

  my $uiType= $pparams->{uitype};
  my $email=  $pparams->{email};
  my $subject=$pparams->{subject};

  if ( ($uiType==1 && (!defined($email) || $email eq '')  && $subject ne '') ||
       ($uiType==2 && $email eq '' && $subject ne '') )
  {
    my ($apiBase,$apiParams)=loadAPI($pparams);

    no strict 'refs';
    my $isValid="${apiBase}Valid";
    my $reason=&$isValid($pparams, $apiParams);
    error($reason)    if $reason ne '';
  }

  # we're about to generate a bunch of plots and while we can certainly get
  # errors the first time through in 'live' mode the rest of the time we won't.
  # At least for now, a fixed sized window always applies to the ending date
  $pparams->{fdate}=$pparams->{tdate}    if $mode=~/live/ && $timeframe=~/fixed/;

  # In 'live' mode the ending date should already be today but what about
  # date changes?  Let's be safe...
  $pparams->{tdate}=29991231 if $mode=~/live/;

  # dates not normally defined for cli output but if '--lastmins' the thru time may be
  if (!defined($winsize))
  {
    datecheck("From", $pparams->{fdate});
    datecheck("Thru", $pparams->{tdate});
    timecheck("From", $pparams->{ftime});
    error("Thru date must >= From date")    if $pparams->{fdate}>$pparams->{tdate};
  }
  timecheck("Thru", $pparams->{ttime})      if !defined($winsize) || $pparams->{ttime} ne '';

  # In 'float' mode, which usually only makes sense in 'live' mode, we need 
  # to reset the from/thru times (and occasionally the dates too!) to the 
  # size of that window.
  if ($timeframe=~/float/)
  {
    # get highest ending time of all the files we're plotting and reset
    # thru time to it.  NOTE - since colplot does not deal with time in
    # seconds, we need to add 1 minute to make sure we're past the end of
    # the time period by rounding up to the next whole minute.
    findFiles(3, $mycfg, $pparams, "$dir$sep*", undef) || 
	error("No plottable files match your selection criteria.  Are your file protections right?");

    # Web-based floating data is always based on the last access time of the file, which findFiles()
    # returns, but cli-based floating data may be overriden by --time.  If not specified it will also
    # be based on last access time.
    print "params->{ttime}: $pparams->{ttime}\n";
    if (!$cliFlag || $pparams->{ttime} eq '')
    {
      my $hour=$pparams->{lasthour};
      my $mins=$pparams->{lastmins}+1;
      print "HOUR: $hour  MINS: $mins\n";
      if ($mins==60)
      {
        $mins=0;
        $hour++;
      }
      $pparams->{ttime}=sprintf("%02d:%02d", $hour, $mins);
    }
    my ($hour, $mins)=split(/:/, $pparams->{ttime});

    # now convert latest date/time to seconds so we can subtract the window
    my $year=substr($pparams->{tdate}, 0, 4);
    my $mon= substr($pparams->{tdate}, 4, 2);
    my $day= substr($pparams->{tdate}, 6, 2);

    # Be sure to bump day when we cross midnight
    my ($secs, $lastSecs);
    if ($hour==24)
    {
      $lastSecs=timelocal(0, $mins, 0, $day, $mon-1, $year-1900);
      $lastSecs+=(3600*24);
      ($secs, $mins, $hour, $day, $mon, $year)=localtime($lastSecs);
      $pparams->{tdate}=sprintf("%d%02d%02d", $year+1900, $mon+1, $day);
    }
    else
    {
      $lastSecs=timelocal(0, $mins, $hour, $day, $mon-1, $year-1900);
    }

    ($secs, $mins, $hour, $day, $mon, $year)=localtime($lastSecs-$winsize*60);
    $pparams->{fdate}=sprintf("%d%02d%02d", $year+1900, $mon+1, $day);
    $pparams->{ftime}=sprintf("%02d:%02d", $hour, $mins);
  }

  my $width=$pparams->{width};
  error("Width may not be set to blank")           if $width eq "";
  $width.=".0"        if $width!~/\./;
  $width="0$width"    if $width=~/^\./;   
  error("Width must be in numeric format")         if $width!~/\d{1}\.\d{1}/;

  my $height=$pparams->{height};
  error("Height may not be set to blank")          if $height eq "";
  $height.=".0"         if $height!~/\./;
  $height="0$height"    if $height=~/^\./;   
  error("Height must be in numeric format")        if $height!~/\d{1}\.\d{1}/;

  my $thick=$pparams->{thick};

  my $xtics=$pparams->{xtics};
  error("X Increment must be in numeric format")   if $xtics ne '' && $xtics!~/^\d+$/;
  error("You must specify at least 1 plot")        if $pparams->{plots} eq '';

  my $filetype=$pparams->{filetype};
  my $incctlFlag=$pparams->{incctlflag};
  error("Email or Directory output requires a plot type or control file")
      if ($email ne '') && $filetype eq 'none' && !$incctlFlag;
  error("'Page Break' requires 'pdf' plot type")
      if ($email ne '') && $filetype ne 'pdf' && $pagbrk=~/on/;
  error("'Include control file' requires 'Email or Local Directory'")
      if ($email ne '') && $incctlFlag;

  if ( $email ne '' && $email=~/@/)
  {
    error("email only allowed on UNIX boxes")
	if $pcFlag;
    my @temp=split(/@/, $email);
    error("too many '\@'s in email address")
        if scalar(@temp)!=2;
    error("'$temp[1]' doesn't look like a domain name")
	if $temp[1]!~/^[a-zA-Z0-9.]+$/|| $temp[1]=~/\.$/;
  }  
  elsif ($email ne '')
  {
    # if we can't find $email, we take it to be of the form /dir/prefix,
    # but of so the directory better exist.  In other words it can't be
    # of the form /dir/prefix/prefix
    my $htmlFlag=$mycfg->{htmlflag};
    my $dirname=(-d $email) ? $email : dirname($email);  
    my $basename=basename($email);
    error("Directory '$dirname' does not exist")
        if !-e $dirname;
    error("Directory '$dirname' requires write access for all (for web server)")
	if $htmlFlag && ((stat($dirname))[2] & 7) != 7;
  }
  return;
}

sub loadAPI
{
  my $pparams=shift;

  my ($apiFile, $apiParams)=split(/,/, $pparams->{subject}, 2);
  my $apiBase=basename($apiFile);
  my $apiDir= dirname($apiFile);
  $apiBase=~s/\.ph//;    # remove extension if there

  # look for api file in current direcory first, then libdir
  my $found='';
  foreach my $dir ('.', $mycfg->{libdir})
  {
    my $fullpath="$dir$sep${apiBase}.ph";
    next    if !-e $fullpath;

    $found=$fullpath;
    last;
  }
  error("cannot find $apiBase in default directory OR '$mycfg->{libdir}'")    if $found eq '';

  my $params=(defined($apiParams)) ? ",$apiParams" : '';
  $pparams->{subject}="API:$found$params";     # put back together
  printText("Found api file: $pparams->{subject}")    if $debug & 1;

  # is this api module valid in the context it's being used?
  require $found;
  my $isValid="${apiBase}Valid";

  # need to disable 'strict' for access
  no strict 'refs';
  my $reason=&$isValid($pparams, $apiParams);
  error($reason)    if $reason ne '';
  return(($apiBase,$apiParams));
}

sub datecheck
{
  my $type=shift;
  my $date=shift;

  error("$type date must be in 'yyyymmdd' format")
	if $date!~/^\d{8}$/;
}

sub timecheck
{
  my $type=shift;
  my $time=shift;

  error("$type time must be in hh:mm[:ss] format")           if $time!~/^\d+:\d{2}$|^\d+:\d{2}:\d{2}$/;
  my ($hh, $mm,$ss)=split(/:/, $time);
  error("$type time cannot be greater than 24:00:00")        if $hh>23 && $time gt "24:00";
  error("$type minutes must be less than 60")                if $mm>59;
  error("$type seconds must be less than 60")                if defined($ss) && $ss>59;
}

sub sigInt
{
  print "Ouch...\n";
  my $tempDir=$mycfg->{tempdir};
  my $ctlFile="$tempDir$$-colplot.ctl";
  unlink $ctlFile;
  exit;
}


# The whole purpose of this is to allow us to call liberror w/o having to
# pass '$mycfg' on each call.
sub error
{
  displayText($mycfg, $_[0], 3);
  exit;
}

# Like 'error', this allows us to use 'displayText' w/o passing '$mycfg' each call
sub printText
{
  my $text= shift;
  my $level=shift;

  displayText($mycfg, $text, $level);
}

sub showplots
{
  my $mycfg=shift;

  $config={};
  exit(0)    if !initParamsInit($mycfg, $config);

  # Being lazy, let's sort the descriptions by category, modifier and type this way.
  my %sorted;
  my $numPlots=0;
  foreach my $plotname (keys %{$config->{PlotDesc}})
  {
    my $cat= $config->{PlotDesc}->{$plotname}->{cat};
    my $type=$config->{PlotDesc}->{$plotname}->{type};
    my $mod= (defined($config->{PlotDesc}->{$plotname}->{mod})) ?
		$config->{PlotDesc}->{$plotname}->{mod} : '';

    # temporarily change the type of detail and macros to force their sort 
    $type='y'    if $type eq 'd';
    $type='z'    if $type eq 'm';
    $sorted{"$type:$cat:$mod:$plotname"}='';
    $numPlots++;
  }

  if ($mycfg->{htmlflag})
  {
    htmlHeader($mycfg, "Show Plots");

    # With a browers, we're going to do this in 2 columns and so need a second
    # hash which sorts such that they can be processed one row at a time.
    my $num=0;
    my $numCol=2;
    my $numPerCol=int(($numPlots+1)/$numCol);
    my %byRow;
    foreach my $key (sort keys %sorted)
    {
      my $col=int($num/$numPerCol);
      my $key2=sprintf("%02d:$key", int($num/$numPerCol)+($num % $numPerCol)*$numCol);
      $byRow{$key2}='';
      $num++;
    }

    print "<body>\n";
    print "<center>\n";
    print "<h2>Plot Descriptions\n";
    print "<table>\n";
    print "<tr>\n";
    print "  <td><b>Plot</b></td><td><b>Subsys</b></td><td><b>Type</b></td><td><b>Description</b></td>\n";
    print "  <td><b>Plot</b></td><td><b>Subsys</b></td><td><b>Type</b></td><td><b>Description</b></td>\n";
    print "</tr>\n";

    $num=0;
    foreach my $key (sort keys %byRow)
    {
      my ($num, $type, $cat, $mod, $plotname)=(split(/:/, $key));
      my $desc=$config->{PlotDesc}->{$plotname}->{desc};
      $type='d'    if $type eq 'y';
      $type=' '    if $type eq 'z';

      print "<tr>"       if ($num % $numCol)==0;
      print "<td>$plotname</td><td>$cat</td><td>$type</td><td>$desc</td>";
      print "</tr>\n"    if ($num % $numCol)==$numCol;
      $num++
    }
    print "</table>\n";

    print "<p><input type=button value='Close Window' onclick='window.close()'>\n";
    print "</center>\n";
    htmlFooter($mycfg);
    exit;
  }

  # This is for terminal-based output
  foreach my $key (sort keys %sorted)
  {
    my ($type, $cat, $mod, $plotname)=split(/:/, $key);

    $type='d'    if $type eq 'y';   # details
    $type=''     if $type eq 'z';   # macros

    printf "%-11s %-6s  %1s  %s\n", 
  	  $plotname, $cat, $type, $config->{PlotDesc}->{$plotname}->{desc};
  }
  exit;
}

sub showversion
{ 
  my $libs=($pngFlag) ? '[png,' : '[nopng,';
  $libs.=($x11Flag) ? 'x11]' : 'nox11]';
  printText("colplot: V$Version, gnuplot: V:$mycfg->{GnuVersion}$libs\n\n$Copyright\n$License");
  exit;
}

sub usage
{
  my $help=<<EOF;
usage: colplot.pl [-switches]
Data, Date and Time
  -dir        dir      directory containing plot files
  -contains   string   plot files containing any strings
  -all                 plot files containing all strings
  -date       date     date range in yyyymmdd[-yyyymmdd] format
  -time       time     time range in hh:mm[-hh:mm] format

Plot types and filters
  -plots      plots    list of plots
  -filters    list     list of detail device filters
  -type       type     display ALL plots using this format
                         l - lines     sl - stacked lines
                         p - points    sp - stacked points
  -unique              only plot files generated with -ou
  -nounique            only plot files generated without -ou

Plot formats
  -adjust              adjust height of plot if not enough room for legend
  -filetype   type     type of output/email file
                         pdf    single pdf file (default)
                         png    each plot should be an individual png file
                         tty    write single png plot to directly to tty
  -height     value    plot height (1 = full page)
  -nolegend            do not print a legend
  -noxaxis             do not print X-axis
  -thick      value    plot line thickness
  -width      value    plot width (1 = full page)
  -xtics      value    specify tic marks every 'value' seconds
  -ylog                use logarithmic Y-axis

Remote plotting
  -href                display hrefs associated with this request

Plot Destination
  -email      address  address to email results to
  -filedir    dir      local location to copy results to
                       CAUTION - webservers need write access to all
  -incctl              include gnu control file with file/email
  -pagbrk              break pdf pages for each system
  -subject    subj     subject for email

Help
  -help                print this text
  -showplots           show list of valid plot types
  -showparams          show plotting parameters to be used for each file
                       as an aid for helping to debug custom plots
  -version             show version information

$Copyright

EOF
print $help;
exit;
}