#!C:\\perl\\bin\\perl.exe

# proc_files_win.pl
# Processes files on Windows PCs for the Collective.
# Additional processing is required once the files are moved to the Linux 
# box (see proc_files_lin.pl). This app only handles the Windows processing. 
# The app consists of a GUI (using tk) and processing functions (using perl).

# Various functions use private $statusFlag variables for error tracking; they 
# return a positive number on success and 0 on failure.

# Note: Functions have multiple returns. When a significant error is encountered,
# a function displays an error message and then returns at that point. This makes the
# code simpler, but when reading the code keep in mind that errors can cause early 
# returns from functions.

# Windows 95/98/ME Note: Perl's rename() function works on Win NT/2000/XP but not on 
# Win 95/98/ME. When the script detects a rename() failure, it makes a system call to 
# Window's ren command as a workaround.
# WARNING: The system call to ren has not been fully tested!


# Contents
# PART 1: The Premilinaries
# PART 1.1: Modules
# PART 1.2: Global Variables
# PART 2: The GUI
# PART 2.1: Widgets (Windows, Boxes, and their Contents)
            # Input Window
            # Output Window
            # Help Window
# PART 2.2: The Main Loop
# PART 2.3: GUI-Management Functions
            # OpenHelpWin()
            # OpenOutWin()
            # ShowError()
            # ShowQuery()
            # ScanWkgDir()
            # CheckStartDirState()
            # CheckExtsState()
            # CheckZipState()
            # CheckDestDirState()
# PART 3: Input Error Checking Functions
            # CheckForErrors()
            # CheckDirName()
            # CheckStartDir()
            # CheckDirContents()
            # CheckFixIncludes()
            # BuildDestPath()
            # CheckZipName()
            # CheckExts()
            # CheckDestDir()
# PART 4: File Processing Functions
            # ProcessFiles()
            # EchoInput()
            # ClearROA()
            # ZipSource()
            # FixSpaces()
            # FixCase()
            # FixIncludes()
            # CopyToLinux()
# PART 5: Help Data
            # GetHelpContents()


# PART 1: The Premilinaries
# PART 1.1: Modules
use strict;              # Catches errors during code.
use File::Find;          # For file operations.
use File::Path;          # For removal of non-empty directories.
use Tk;                  # For the GUI.
use Tk::DialogBox;       # For the GUI.


# PART 1.2: Global Variables
# Global input variables. These hold the user input and important values
# derived from the input.
my $wkgDir  = 'C:\Astell-THQ\wkg';     # Working directory (where dirs to be processed typicall are).
my $initDir = "???";                   # Initial directory; i.e., dir to be processed.
my $userDir = "";                      # For user to enter a project dir.
my $startDirFlag  = 1;                 # Flags whether (1) a dir from $wkgDir or (2) a user-input dir is to be used.
my $startDirState = 1;                 # Tracks whether or not to reset input dir items.
my @foundDirs     = ();                # Dirs found in $wkgDir; possible dirs to be processed.
my $numDirs       = 0;                 # Number of dirs found in $wkgDir.
my $clearROA      = 1;                 # Clear read-only attributes flag.
my $zipSource     = 1;                 # Zip source code flag.
my $zipBase       = "XXX";             # Base name for the possible zip file.
my $zipName       = "";                # Name and ext for zip file.
my $fixSpaces     = 1;                 # Change spaces to underscores flag.
my $fixCase       = 1;                 # Change upper case to lower.
my $fixIncludes   = 1;                 # Fix #include format in C/C++ source code files.
my $useCpp        = 1;                 # Fix #includes in .cpp files.
my $useC          = 1;                 # Fix #includes in .c files.
my $useH          = 1;                 # Fix #includes in .h files.
my $useHpp        = 1;                 # Fix #includes in .hpp files.
my $moreExts      = "";                # More extension to fix #includes.
my $destDirFlag   = 1;                 # Directory (on a Linux box) to copy processed dir to.
                                       # 0 - No copying.
                                       # 1 - Copy to THQ Collective.
                                       # 2 - Copy to THQ DevNet.
my $destDirName   = "XXX";             # Name of dest subdir (final subdir of $intiDir).
my $destDir       = "";                # Destination directory.
                                       # ($destDir = path . $destDirName.)
my $userDestDir   = "";                # For user to enter a destination dir.
my $overOld       = 0;                 # Overwrite dest dir flag.
my $echoInput     = 1;                 # Echo input in output flag.

# Predeclared variables (widget references) so that code can use them before they are defined.
my $inWin;
my $inTitleFr;
my $inBody;
my $inDirFr;
my $dirList;
my $rescanBtn;
my $initDirEn;
my $zipBaseEn;
my $fixCpp;
my $fixC;
my $fixH;
my $fixHpp;
my $moreExtsIntro;
my $moreExtsEn;
my $moreExtsExtro;
my $destDirEn;
my $noOverRbn;
my $yesOverRbn;
my $showOutWinBn;
my $outWin;
my $outTitleFr;
my $outBody;
my $helpWin;
my $helpTitleFr;
my $helpBody;

# Other global variables.
my $temp;                # Used to refer to widgets when permanent reference is not needed.
my $tempStr = "";        # Temporary string.
my $width;               # Used in sizing windows.
my $height;              # Used in sizing windows.
my $widthEntry;          # Width of an entry box.
my @msgItem;             # Holds text-and-tag entries for error and query messages.
my $text;                # Holds the text to insert into a Text widget.
my $tag;                 # Holds the tag for the text being inserted into a Text widget.
my $scanFlag = 0;        # Flags whether or $wkgDir has been scanned for dirs.


# PART 2: The GUI
# App can display these windows:
#   Input window (the Main Window) on left; is called up initially.
#   Output window (a Top Level) on right; is called up initially.
#   Help window (a Top Level) on right; user can call up.
# and these dialog boxes (Tk::DialogBox):
#   Error box; pops up when bad input is detected.
#   Query box; pops up to check that user actually wants to do some odd set of specifications.

# Canvases are used to create visual spacers (highlightthickness is set to 0 
# to turn off "border" outside canvas).

# $_toplevel_->protocol() is used to prevent inadvertent destruction of top level windows
# that are not the main window (e.g., the user clicks the X, which destroys
# the window. The protocol causes the window to be withdrawn instead. Cannot use:
#   $_toplevel_->bind( "", sub { $outWin->withdraw; } );
# as bind() detects the event but does not prevent it (so the window is 
# destroyed despite your best intentions.

# PART 2.1: Widgets (Windows, Boxes, and their Contents)

# Input Window
# Create the main window (the Input Window).
$inWin = new MainWindow;
$inWin->title( "Windows File Processing: Input" );
$inWin->configure( -background => "#EEEEEE" );
$inWin->focusFollowsMouse();

# When app loads, checks that processing will occur using Windows (Win32).
$inWin->bind( "", sub 
{
   if ( $^O ne "MSWin32" )
   {
      @msgItem = ( @msgItem, "ERROR: Wrong operating system!\n12b" );
      @msgItem = ( @msgItem, 
         "Processing must occur on a Windows PC, but your OS says it is $^O.\n\n" .
         "The program will shut down.\n10n" );
      ShowError();
      exit;
   }
} );

# Ctrl-W closes the app (defined in main window but affects all windows).
# 'all' forces all widgets to recognize event, else widgets with focus
# would ignore this.
$inWin->bind( 'all', "", sub 
{
   $temp = $inWin->focusCurrent();
   if ( $temp->toplevel() == $inWin->toplevel() )
   {
      exit;                 # Ctrl-W was pressed in Input window; exit app.
   } elsif ( $temp->toplevel() == $outWin->toplevel() )
   {
      $outWin->withdraw;    # Ctrl-W was pressed in Output window; withdraw that window.
   } elsif ( $temp->toplevel() == $helpWin->toplevel() )
   {
      $helpWin->withdraw;   # Ctrl-W was pressed in Help window; withdraw that window.
   } else
   {
      return;               # Ctrl-W was pressed elsewhere (dialog box?); ignore.
   }
} );


# Adjust window per screen resolution.
$width  = int( .5 * .9 * $inWin->screenwidth() );
$height = int( .8 * $inWin->screenheight() );
$inWin->geometry( $width . "x" . $height . "+20+15" );

# Base width of entry widgets on screen size.
if ( $inWin->screenwidth() < 800 )
{
   $widthEntry = 30;
} elsif ( $inWin->screenwidth() < 1000 )
{
   $widthEntry = 40;
} else
{
   $widthEntry = 60;
}


# Overall Title
$inWin->Label(
   -anchor     => 'w',
   -background => "#BBBBBB",
   -font       => "{Tahoma} 16 {bold}",
   -text       => "Windows File Processing for the Collective" )
->pack(
   -fill => 'x',
   -side => 'top' );

# Input Title Frame
$inTitleFr = $inWin->Frame( -background  => "#EEEEEE" )
->pack(
   -fill => 'x',
   -pady => 2,
   -side => 'top' );

# Input Title
$inTitleFr->Label(
   -anchor     => 'w',
   -background => "#EEEEEE", 
   -font       => "{Tahoma} 12 {bold}",
   -text       => "Input" )
->pack( -side => 'left' );

# Help Button
$inTitleFr->Button(
   -activebackground => "#F7DF8F",
   -background       => "#CCBB99",
   -command          => \&OpenHelpWin,
   -font             => "{Tahoma} 8 {bold}",
   -text             => "Help" )
->pack(
   -padx => 10,
   -side => 'right' );

# Visual Separator
$inWin->Canvas(
   -background         => "#888888",
   -height             => 2,
   -highlightthickness => 0,
   -takefocus          => 0 )
->pack(
   -fill => 'x',
   -pady => 2,
   -side => 'top' );

# Input Area
$inBody = $inWin->Scrolled( "Text",
   -background => "#EEEEEE",
   -cursor     => 'arrow',
   -font       => "{Tahoma} 10 {normal}",
   -relief     => 'flat',
   -scrollbars => 'osoe',
   -wrap       => 'word' )
->pack(
   -expand => 1,
   -fill   => 'both',
   -side   => 'top' );

$inBody->tagConfigure( '8n',  -font => "{Tahoma} 8 {normal}" );
$inBody->tagConfigure( '8b',  -font => "{Tahoma} 8 {bold}" );
$inBody->tagConfigure( '10n', -font => "{Tahoma} 10 {normal}" );
$inBody->tagConfigure( '10b', -font => "{Tahoma} 10 {bold}" );
$inBody->tagConfigure( '10i', -font => "{Tahoma} 10 {italic}" );
$inBody->tagConfigure( '12n', -font => "{Tahoma} 12 {normal}" );
$inBody->tagConfigure( '12b', -font => "{Tahoma} 12 {bold}" );

# Directory to be Processed
$inBody->insert( 'end', "Directory to be Processed:\n", '10b' );

$inDirFr = $inBody->Frame( -background  => "#EEEEEE" )
->pack(
   -fill => 'x',
   -pady => 2,
   -side => 'top' );
$inBody->windowCreate( 'end', -window => $inDirFr );

# Use a Dir in $wkgDir Radiobutton
$temp = $inDirFr->Radiobutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'nw',
   -background       => "#EEEEEE",
   -command          => sub
   {
      ScanWkgDir() if ( not $scanFlag );
      $rescanBtn->configure( -state => 'normal' ) if ( $rescanBtn->cget( -state ) eq "disabled" );
      CheckStartDirState();
   },
   -font             => "{Tahoma} 10 {normal}",
   -justify          => 'left',
   -text             => "Click a directory in $wkgDir:",
   -variable         => \$startDirFlag,
   -value            => "1" )
->grid(
   -column => 0,
   -row    => 0,
   -sticky => 'ne' );

# Directory Listing
$dirList = $inDirFr->Listbox(
   -background       => "#FFFFFF",
   -font             => "{Tahoma} 10 {bold}",
   -height           => 0,
   -relief           => 'flat',
   -selectbackground => "#F7DF8F",
   -selectforeground => "#000000",
   -selectmode       => "single" )
->grid(
   -column => 1,
   -row    => 0,
   -sticky => 'ne' );
$dirList->insert( 'end', @foundDirs );
$dirList->bind( "", sub
{
   return unless ( $startDirFlag );
   if ( $numDirs == 0 )
   {
      @msgItem = ( @msgItem, "ERROR: No directories!\n12b" );
      @msgItem = ( @msgItem, 
         "No directories were found in $wkgDir.\n10n" );
      ShowError();
      return;
   }
   $destDirName = $dirList->get( $dirList->curselection() );
   $initDir = $wkgDir . "\\" . $destDirName;
   $zipBase = $destDirName;
   CheckStartDir( $initDir );
} );

# Rescan Button
$rescanBtn = $inDirFr->Button(
   -activebackground => "#F7DF8F",
   -anchor           => 'nw',
   -background       => "#CCBB99",
   -command          => \&ScanWkgDir,
   -font             => "{Tahoma} 8 {bold}",
   -text             => "Rescan" )
->grid(
   -column => 2,
   -row    => 0,
   -sticky => 'ne' );
$rescanBtn->bind( "", sub
{
   if ( not $scanFlag )
   {
      if ( -d $wkgDir )
      {
         ScanWkgDir();
      } else
      {
         $startDirFlag = 0;
         $rescanBtn->configure( -state => 'disabled' );
         $initDirEn->configure( -background => "#FFFFFF", -state => 'normal' );
         $dirList->configure( -background => "#EEEEEE", -takefocus => '0' );
      }
   }
} );
$inBody->insert( 'end', "\n" );

# Use a User-Input Dir Radiobutton
$temp = $inBody->Radiobutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -command          => sub
   {
      $rescanBtn->configure( -state => 'disabled' ) if ( $rescanBtn->cget( -state ) eq "normal" );
      CheckStartDirState();
   },
   -font             => "{Tahoma} 10 {normal}",
   -justify          => 'left',
   -text             => "Enter the full path to a directory: ",
   -variable         => \$startDirFlag,
   -value            => "0" );
$inBody->windowCreate( 'end', -window => $temp );

# User-Input Dir Entry
$initDirEn = $inBody->Entry(
   -background   => "#EEEEEE",
   -font         => "{Tahoma} 10 {normal}",
   -state        => 'disabled',
   -textvariable => \$userDir,
   -width        => 40 );
$inBody->windowCreate( 'end', -window => $initDirEn );
$initDirEn->bind( "", sub
{
   return if ( $initDirEn->cget( -state ) eq 'disabled' );
   $initDirEn->selectionRange( 0, 'end' );
} );
$initDirEn->bind( "", sub 
{
   return if ( $initDirEn->cget( -state ) eq 'disabled' );
   $userDir =~ s/\//\\/g;   # Make sure dir uses Windows \ separators
   $userDir = "???" if ( $userDir eq "" );
   $initDir = $userDir;

   # Get the name for the zip file and the destination subdir.
   $tempStr = substr( "$initDir", (rindex( "$initDir", "\\" ) + 1) );
   $destDirName = $tempStr;

   # Guess the name for the zip file.
   $tempStr =~ s/\s//g;
   if ( length( $tempStr ) != 0 )
   {
      $zipBase = $tempStr;
   } else
   {
      $zipBase = "XXX";
   }
} );
$inBody->insert( 'end', "\n" );


# Processing Options
$inBody->insert( 'end', "\n", '8n' );
$inBody->insert( 'end', "Processing Options for ", '10b' );
$temp = $inBody->Label(
   -anchor       => 'w',
   -background   => "#EEEEEE",
   -font         => "{Tahoma} 10 {bold}",
   -foreground   => "#FF0000",
   -textvariable => \$destDirName );
$inBody->windowCreate( 'end', -window => $temp );
$inBody->insert( 'end', ":\n", '10b' );

# Clear ROA Checkbutton
$temp = $inBody->Checkbutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -font             => "{Tahoma} 10 {bold}",
   -justify          => 'left',
   -text             => "Clear Read-Only Attributes",
   -variable         => \$clearROA );
$inBody->windowCreate( 'end', -window => $temp );
$inBody->insert( 'end', "for files and directories.", '10n' );
$inBody->insert( 'end', "\n" );

# Zip Source Code Checkbutton
$temp = $inBody->Checkbutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -command          => \&CheckZipState,
   -font             => "{Tahoma} 10 {bold}",
   -justify          => 'left',
   -text             => "Zip Source Code",
   -variable         => \$zipSource );
$inBody->windowCreate( 'end', -window => $temp );
$inBody->insert( 'end', "(and include it in the directory).", '10n' );
$inBody->insert( 'end', "\n" );
$inBody->insert( 'end', "       Zip file name: ", '10n' );

$zipBaseEn = $inBody->Entry(
   -font         => "{Tahoma} 10 {normal}",
   -textvariable => \$zipBase,
   -width        => 30 );
$inBody->windowCreate( 'end', -window => $zipBaseEn );
$zipBaseEn->bind( "", sub
{
   return if ( $zipBaseEn->cget( -state ) eq 'disabled' );
   $zipBaseEn->selectionRange( 0, 'end' );
} );
$zipBaseEn->bind( "", sub 
{
   return if ( $zipBaseEn->cget( -state ) eq 'disabled' );
   $zipBase =~ s/\s//g;        # Lose whitespace in name
   $zipBase =~ s/(\.zip)+$//;   # Lose 1+ trailing .zip (.zip is automatically appended)
} );
$inBody->insert( 'end', ".zip\n" );


# Change Spaces to Underscore Checkbutton
$temp = $inBody->Checkbutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -font             => "{Tahoma} 10 {bold}",
   -justify          => 'left',
   -text             => "Change Spaces to Underscores",
   -variable         => \$fixSpaces );
$inBody->windowCreate( 'end', -window => $temp );
$inBody->insert( 'end', "in file and directory names.", '10n' );
$inBody->insert( 'end', "\n" );


# Change Upper Case to Lower Checkbutton
$temp = $inBody->Checkbutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -font             => "{Tahoma} 10 {bold}",
   -justify          => 'left',
   -text             => "Change Upper Case to Lower",
   -variable         => \$fixCase );
$inBody->windowCreate( 'end', -window => $temp );
$inBody->insert( 'end', "in file and directory names.", '10n' );
$inBody->insert( 'end', "\n" );


# Fix #include Formatting Checkbutton
$temp = $inBody->Checkbutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -command          => \&CheckExtsState,
   -font             => "{Tahoma} 10 {bold}",
   -justify          => 'left',
   -text             => "Fix \x23include Formatting",
   -variable         => \$fixIncludes );
$inBody->windowCreate( 'end', -window => $temp );
$inBody->insert( 'end', "in C/C++ files. Extensions to fix:", '10n' );
$inBody->insert( 'end', "\n" );

$inBody->insert( 'end', "      " );   # Indent line of checkbuttons.

# Fix .cpp Checkbutton
$fixCpp = $inBody->Checkbutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -font             => "{Tahoma} 10 {normal}",
   -justify          => 'left',
   -text             => ".cpp  ",
   -variable         => \$useCpp );
$inBody->windowCreate( 'end', -window => $fixCpp );

# Fix .c Checkbutton
$fixC = $inBody->Checkbutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -font             => "{Tahoma} 10 {normal}",
   -justify          => 'left',
   -text             => ".c  ",
   -variable         => \$useC );
$inBody->windowCreate( 'end', -window => $fixC );

# Fix .h Checkbutton
$fixH = $inBody->Checkbutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -font             => "{Tahoma} 10 {normal}",
   -justify          => 'left',
   -text             => ".h  ",
   -variable         => \$useH );
$inBody->windowCreate( 'end', -window => $fixH );

# Fix .hpp Checkbutton
$fixHpp = $inBody->Checkbutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -font             => "{Tahoma} 10 {normal}",
   -justify          => 'left',
   -text             => ".hpp  ",
   -variable         => \$useHpp );
$inBody->windowCreate( 'end', -window => $fixHpp );
$inBody->insert( 'end', "\n" );

# Fix Other Extensions Entry
$moreExtsIntro = $inBody->insert( 'end', "      Other:", '10n' );

$moreExtsEn = $inBody->Entry(
   -font         => "{Tahoma} 10 {normal}",
   -textvariable => \$moreExts,
   -width        => 40 );
$inBody->windowCreate( 'end', -window => $moreExtsEn );
$moreExtsEn->bind( "", sub 
{
   return if ( $moreExtsEn->cget( -state ) eq 'disabled' );

   $moreExts =~ s/,+|\.+/ /g;   # Convert all , and . to spaces.
   $moreExts =~ s/\s+/ /g;      # Use only single spaces.
   $moreExts =~ s/^\s|\s$//;    # Lose leading or trailing spaces.

   # Clean up moreExts: all exts begin with periods, are separated by one space,
   # and any needless repetition is tossed out. (This uses a hash with the exts
   # used as the KEYS of the hash, thus avoid duplicate names.)
   my %uniqueExts;
   foreach ( split( / /, $moreExts ))
   {
      $uniqueExts{ $_ } = 1;    # Value is immaterial; key is what we care about.
   }
   $moreExts = "";
   foreach ( keys( %uniqueExts ))
   {
      $moreExts .= " ." . $_;   # Keys are used to reconsistute $moreExts without duplicates.
   }
   $moreExts =~ s/^ //;         # Lose the initial space.
} );
$inBody->insert( 'end', "\n" );

$moreExtsExtro = $inBody->insert( 'end', "      Enter a comma and/or space separated list of extensions.\n", '10n' );


# Copy Processed Directory 
$inBody->insert( 'end', "\n", '8n' );
$inBody->insert( 'end', "Copy the Processed Directory, ", '10b' );
$temp = $inBody->Label(
   -anchor       => 'w',
   -background   => "#EEEEEE",
   -font         => "{Tahoma} 10 {bold}",
   -foreground   => "#FF0000",
   -textvariable => \$destDirName );
$inBody->windowCreate( 'end', -window => $temp );
$inBody->insert( 'end', ", into:\n", '10b' );

# L: Collective Dir Radiobutton
$temp = $inBody->Radiobutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -command          => \&CheckDestDirState,
   -font             => "{Tahoma} 10 {normal}",
   -justify          => 'left',
   -text             => "L:\\inetpub\\www\\wkg (THQ Collective).",
   -variable         => \$destDirFlag,
   -value            => "1" );
$inBody->windowCreate( 'end', -window => $temp );
$inBody->insert( 'end', "\n" );

# Use a User-Input Dir Radiobutton
$temp = $inBody->Radiobutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -command          => \&CheckDestDirState,
   -font             => "{Tahoma} 10 {normal}",
   -justify          => 'left',
   -text             => "Enter the full path to a directory: ",
   -variable         => \$destDirFlag,
   -value            => "2" );
$inBody->windowCreate( 'end', -window => $temp );

# User-Input Dest Dir Entry
$destDirEn = $inBody->Entry(
   -background   => "#EEEEEE",
   -font         => "{Tahoma} 10 {normal}",
   -state        => 'disabled',
   -textvariable => \$userDestDir,
   -width        => 40 );
$inBody->windowCreate( 'end', -window => $destDirEn );
$destDirEn->bind( "", sub
{
   return if ( $destDirEn->cget( -state ) eq 'disabled' );
   $destDirEn->selectionRange( 0, 'end' );
} );
$destDirEn->bind( "", sub 
{
   return if ( $destDirEn->cget( -state ) eq 'disabled' );
   $userDestDir =~ s/\//\\/g;   # Make sure dir uses Windows \ separators
   # Lose any trailing \ separators.
   chop $userDestDir while ( rindex( $userDestDir, "\\" ) == (length( $userDestDir ) - 1) );
   $userDestDir = "???" if ( $userDestDir eq "" );
} );
$inBody->insert( 'end', "\n" );

# Don't Copy Radiobutton
$temp = $inBody->Radiobutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -command          => \&CheckDestDirState,
   -font             => "{Tahoma} 10 {normal}",
   -justify          => 'left',
   -text             => "Do not copy.",
   -variable         => \$destDirFlag,
   -value            => "0" );
$inBody->windowCreate( 'end', -window => $temp );
$inBody->insert( 'end', "\n" );


# Overwrite Destination
$inBody->insert( 'end', "\n", '8n' );
$inBody->insert( 'end', "Overwrite ", '10b' );
$temp = $inBody->Label(
   -anchor       => 'w',
   -background   => "#EEEEEE",
   -font         => "{Tahoma} 10 {bold}",
   -foreground   => "#FF0000",
   -textvariable => \$destDirName );
$inBody->windowCreate( 'end', -window => $temp );
$inBody->insert( 'end', " if it already Exists in the Destination Directory:\n", '10b' );

# Don't Overwrite Radiobutton
$noOverRbn = $inBody->Radiobutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -font             => "{Tahoma} 10 {normal}",
   -justify          => 'left',
   -text             => "Do not overwrite (safest).",
   -variable         => \$overOld,
   -value            => "0" );
$inBody->windowCreate( 'end', -window => $noOverRbn );

# Overwrite Radiobutton
$yesOverRbn = $inBody->Radiobutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -font             => "{Tahoma} 10 {normal}",
   -justify          => 'left',
   -text             => "Overwrite.",
   -variable         => \$overOld,
   -value            => "1" );
$inBody->windowCreate( 'end', -window => $yesOverRbn );
$inBody->insert( 'end', "\n" );

# Echo Input Option
$inBody->insert( 'end', "\n", '8n' );
$inBody->insert( 'end', "Echo Input:\n", '10b' );

# Echo Input Checkbutton
$temp = $inBody->Checkbutton(
   -activebackground => "#F7DF8F",
   -anchor           => 'e',
   -background       => "#EEEEEE",
   -borderwidth      => 0,
   -font             => "{Tahoma} 8 {bold}",
   -justify          => 'left',
   -text             => "Echo input values during processing",
   -variable         => \$echoInput );
$inBody->windowCreate( 'end', -window => $temp );
$inBody->insert( 'end', "(you might notice an error).", '8n' );
$inBody->insert( 'end', "\n" );

# Final set of buttons.
$inBody->insert( 'end', "\n" );

# Process Files Button
$temp = $inBody->Button(
   -activebackground => "#F7DF8F",
   -background       => "#CCBB99",
   -command          => sub
   {
      my $statusFlag = 1;
      $outBody->configure( -state => 'normal' );
      $statusFlag = CheckForErrors();
      ProcessFiles() if ( $statusFlag );
      $outBody->configure( -state => 'disabled' );
   },
   -font             => "{Tahoma} 8 {bold}",
   -text             => "Process Files" );
$inBody->windowCreate( 'end', -window => $temp );

# Adds space between buttons
$inBody->insert( 'end', "   " );

# Reset Button
$temp = $inBody->Button(
   -activebackground => "#F7DF8F",
   -background       => "#CCBB99",
   -command          => sub 
   {
      $wkgDir        = 'C:\Astell-THQ\wkg';
      $initDir       = '???';
      $userDir       = "";
      $startDirFlag  = 1;
      $startDirState = 1;
      @foundDirs     = ();
      $numDirs       = 0;
      $clearROA      = 1;
      $zipSource     = 1;
      $zipBase       = "XXX";
      $zipName       = "";
      $fixSpaces     = 1;
      $fixCase       = 1;
      $fixIncludes   = 1;
      $useCpp        = 1;
      $useC          = 1;
      $useH          = 1;
      $useHpp        = 1;
      $moreExts      = "";
      $destDirFlag   = 1;
      $destDirName   = "XXX";
      $userDestDir   = "";
      $destDir       = "";
      $overOld       = 0;
      $echoInput     = 1;
      $tempStr       = "";

      ScanWkgDir();
   },
   -font             => "{Tahoma} 8 {bold}",
   -text             => "Reset Form" );
$inBody->windowCreate( 'end', -window => $temp );

# Adds space between buttons
$inBody->insert( 'end', "   " );

# Quit Button
$temp = $inBody->Button(
   -activebackground => "#F7DF8F",
   -background       => "#CCBB99",
   -command          => sub { exit; },
   -font             => "{Tahoma} 8 {bold}",
   -text             => "Quit" );
$inBody->windowCreate( 'end', -window => $temp );
$inBody->insert( 'end', "\n" );

# Output Window Button
$inBody->insert( 'end', "\n", '8n' );
$showOutWinBn = $inBody->Button(
   -activebackground   => "#F7DF8F",
   -background         => "#CCBB99",
   -disabledforeground => "#EEEEEE",
   -command            => \&OpenOutWin,
   -font               => "{Tahoma} 8 {bold}",
   -text               => "Show Ouput Window" )
->pack(
   -padx => 10,
   -side => 'top' );
$inBody->windowCreate( 'end', -window => $showOutWinBn );
$inBody->insert( 'end', "\n" );

# Affects static elements but not inserted interactive widgets.
$inBody->configure( -state => 'disabled' );   # Disables user mucking with non-interactive widgets.


# Output Window
$outWin = $inWin->Toplevel();
$outWin->title( "Windows File Processing: Output" );
$outWin->configure( -background => "#DDDDDD" );

# Prevent inadvertent destruction of the window (withdraw it instead).
$outWin->protocol( 'WM_DELETE_WINDOW', sub 
{ 
   $outWin->withdraw; 
} );

# Adjust window per screen resolution.
$outWin->geometry( $width . "x" . $height . "+" . ( 27 + $width ) . "+15" );

# Output Title Frame
$outTitleFr = $outWin->Frame( -background  => "#DDDDDD" )
->pack(
   -fill => 'x',
   -pady => 2,
   -side => 'top' );

# Output Title
$outTitleFr->Label(
   -anchor     => 'w',
   -background => "#DDDDDD",
   -font       => "{Tahoma} 12 {bold}",
   -text       => "Output" )
->pack( -side => 'left' );

# Clear Output Button
$outTitleFr->Button(
   -activebackground => "#F7DF8F",
   -background       => "#CCBB99",
   -font             => "{Tahoma} 8 {bold}",
   -command          => sub
   {
      $outBody->configure( -state => 'normal' );
      $outBody->delete( "1.0", 'end' );
      $outBody->configure( -state => 'disabled' );
   },
   -text             => "Clear" )
->pack(
   -padx => 10,
   -side => 'right' );

# Visual Separator
$outWin->Canvas(
   -background         => "#888888",
   -height             => 2,
   -highlightthickness => 0,
   -takefocus          => 0 )
->pack(
   -fill => 'x',
   -pady => 2,
   -side => 'top' );

# Output Area
$outBody = $outWin->Scrolled( "Text",
   -background => "#DDDDDD",
   -cursor     => 'arrow',
   -font       => "{Tahoma} 10 {normal}",
   -relief     => 'flat',
   -scrollbars => 'osoe',
   -wrap       => 'word' )
->pack(
   -expand => 1,
   -fill   => 'both',
   -side   => 'top' );

$outBody->tagConfigure( '10n',  -font => "{Tahoma} 10 {normal}" );
$outBody->tagConfigure( '10b',  -font => "{Tahoma} 10 {bold}" );
$outBody->tagConfigure( '12n',  -font => "{Tahoma} 12 {normal}" );
$outBody->tagConfigure( '12b',  -font => "{Tahoma} 12 {bold}" );
$outBody->tagConfigure( 'c10n', -font => "{Courier New} 10 {normal}" );
$outBody->tagConfigure( 'c10b', -font => "{Courier New} 10 {bold}" );

# Screen size warning
if ( $inWin->screenwidth() < 800 )
{
   $outBody->insert( 'end', "Screen Size\n", '12b' );
   $outBody->insert( 'end', "WARNING: ", '10b' );
   $outBody->insert( 'end', "Screen size is small! " .
      "You may have problems using this program at this resolution.\n", '10n' );
}

# Initial Message
$outBody->insert( 'end', "Output messages will appear here.\n" );

# User cannot change output contents.
$outBody->configure( -state => 'disabled' );


# Binding to toggle Show Output Window button (in Input Window) on and off.
# Binding is mapped to a specific widget in $outWin rather than $outWin itself
# (as otherwise the binding is triggered as each child in $outWin is mapped
# and unmapped).
$outBody->bind( "", sub
{
   $showOutWinBn->configure( 
      -activebackground => "#EEEEEE",
      -background       => "#EEEEEE",
      -relief           => 'flat',
      -state            => 'disabled',
      -takefocus        => 0 );
} );

$outBody->bind( "", sub
{
   $showOutWinBn->configure( 
      -activebackground => "#F7DF8F",
      -background       => "#CCBB99",
      -relief           => 'raised',
      -state            => 'normal',
      -takefocus        => 'undef' );
} );


# Help Window
$helpWin = $inWin->Toplevel();
$helpWin->withdraw;
$helpWin->title( "Input Help" );
$helpWin->configure( -background => "#CCCCCC" );

# Prevent inadvertent destruction of the window (withdraw it instead).
$helpWin->protocol( 'WM_DELETE_WINDOW', sub 
{ 
   $helpWin->withdraw; 
} );

# Adjust window per screen resolution.
$helpWin->geometry( $width . "x" . $height . "+" . ( 20 + $width ) . "+35" );

# Help Title Frame
$helpTitleFr = $helpWin->Frame( -background => "#CCCCCC" )
->pack(
   -fill => 'x',
   -pady => 2,
   -side => 'top' );

# Help Title
$helpTitleFr->Label(
   -anchor     => 'w',
   -background => "#CCCCCC",
   -font       => "{Tahoma} 12 {bold}",
   -text       => "Input Help" )
->pack( -side => 'left' );

# Close Button
$helpTitleFr->Button(
   -activebackground => "#F7DF8F",
   -background       => "#CCBB99",
   -command          => sub { $helpWin->withdraw; },
   -font             => "{Tahoma} 8 {bold}",
   -text             => "Close" )
->pack( -side => 'right' );

# Help Area
$helpBody = $helpWin->Scrolled( "Text",
   -background => "#EEEEEE",
   -cursor     => 'arrow',
   -font       => "{Tahoma} 10 {normal}",
   -relief     => 'flat',
   -scrollbars => 'osoe',
   -wrap       => 'word' )
->pack(
   -expand => 1,
   -fill   => 'both',
   -side   => 'top' );

$helpBody->tagConfigure( '10n', -font => "{Tahoma} 10 {normal}" );
$helpBody->tagConfigure( '10b', -font => "{Tahoma} 10 {bold}" );
$helpBody->tagConfigure( '12n', -font => "{Tahoma} 12 {normal}" );
$helpBody->tagConfigure( '12b', -font => "{Tahoma} 12 {bold}" );

# Get help contents -- stored as data in Part 5
$helpBody->configure( -state => 'normal' );
$helpBody->delete( "1.0", 'end' );
GetHelpContents();


# Final Bindings
# Place just before MainLoop to bind to all appropriate widgets.
foreach ( (qw(Tk::Button Tk::Checkbutton Tk::Radiobutton Tk::Entry)) )
{
   # Clicking Mouse Button 1 on interactive widgets causes the focus to shift to 
   # that widget (invoke() causes the regular effect of clicking).
   $inWin->bind( $_, "", sub
   {
      $temp = $_[0];                                          # First argument in @_ is the calling widget.
      $temp->invoke() unless ( $temp->class() eq "Entry" );   # Entry widgets do not have invoke() methods.
      $temp->focus();
   } );
}


# PART 2.2: The Main Loop
# Run the GUI
MainLoop;


# PART 2.3: GUI-Management Functions

# OpenHelpWin()
# Opens the Help window.
sub OpenHelpWin
{
   my $state = $helpWin->state();
   if ( $state eq "normal" )
   {
      # Apparenly user can't see window -- under another?
      $helpBody->focus();
   } elsif ( $state eq "iconic" )
   {
      $helpWin->deiconify();
      $helpBody->focus();
   } elsif ( $state eq "withdrawn" )
   {
      $helpWin->raise();
      $helpWin->deiconify();
      $helpBody->focus();
   } else
   {
      @msgItem = ( @msgItem, "ERROR: Could not find Help window!\n12b" );
      @msgItem = ( @msgItem, 
         "An invalid state was detected for the Help window. The window could not be " .
         "displayed. Something is very wrong.\n10n" );
      ShowError();
   }
}


# OpenOutWin()
# Checks the Output window. If it closed, reopen it.
sub OpenOutWin
{
   my $state = $outWin->state();
   if ( $state eq "normal" )
   {
      # Apparenly user can't see window -- under another?
      my $curFocus = $inWin->focusCurrent();
      $outBody->focus();
   } elsif ( $state eq "iconic" )
   {
      $outWin->deiconify();
      $outBody->focus();
   } elsif ( $state eq "withdrawn" )
   {
      $outWin->raise();
      $outWin->deiconify();
      $outBody->focus();
   } else
   {
      @msgItem = ( @msgItem, "ERROR: Could not find Output window!\n12b" );
      @msgItem = ( @msgItem, 
         "An invalid state was detected for the Output window. The window could not be " .
         "displayed. Something is very wrong.\n10n" );
      ShowError();
   }
}


# ShowError()
# Displays the Error dialog box.
sub ShowError
{
   my $errBox = $inWin->DialogBox(
      -buttons        => [ "OK" ],
      -default_button => "OK",
      -title          => "Error" );

   my $errMsg = $errBox->add( "Text",
      -background => "#FFFFCC",
      -cursor     => 'arrow',
      -font       => "{Tahoma} 10 {normal}",
      -relief     => 'flat',
      -wrap       => 'word' )
   ->pack;
   $errMsg->tagConfigure( '10n', -font => "{Tahoma} 10 {normal}" );
   $errMsg->tagConfigure( '10b', -font => "{Tahoma} 10 {bold}" );
   $errMsg->tagConfigure( '12n', -font => "{Tahoma} 12 {normal}" );
   $errMsg->tagConfigure( '12b', -font => "{Tahoma} 12 {bold}" );

   foreach ( @msgItem )
   {
      # Assumes text and tags are present and correct; no error checking done!
      ($text, $tag) = split( //, $_ );
      $errMsg->insert( 'end', $text, $tag );
   }

   # Clear the array for new input.
   @msgItem = ();

   $errBox->Show ( -global );
}


# ShowQuery()
# Displays the Query dialog box.
sub ShowQuery
{
   my $queryBox = $inWin->DialogBox(
      -buttons        => [ "YES", "NO" ],
      -default_button => "Yes",
      -title          => "Query" );

   my $queryMsg = $queryBox->add( "Text",
      -background => "#FFFFCC",
      -cursor     => 'arrow',
      -font       => "{Tahoma} 10 {normal}",
      -relief     => 'flat',
      -wrap       => 'word' )
   ->pack;
   $queryMsg->tagConfigure( '10n', -font => "{Tahoma} 10 {normal}" );
   $queryMsg->tagConfigure( '10b', -font => "{Tahoma} 10 {bold}" );
   $queryMsg->tagConfigure( '12n', -font => "{Tahoma} 12 {normal}" );
   $queryMsg->tagConfigure( '12b', -font => "{Tahoma} 12 {bold}" );

   foreach ( @msgItem )
   {
      # Assumes text and tags are present and correct; no error checking done!
      ($text, $tag) = split( //, $_ );
      $queryMsg->insert( 'end', $text, $tag );
   }

   # Clear the array for new input.
   @msgItem = ();

   $queryBox->Show ( -global );
}


# ScanWkgDir()
# Scans $wkgDir for possible initial directories
sub ScanWkgDir
{
   my $statusFlag = 1;

   if ( not $statusFlag = chdir( "$wkgDir" ))
   {
      @msgItem = ( @msgItem, "ERROR: Could not find $wkgDir\n10b" );
      @msgItem = ( @msgItem, "Could not find $wkgDir: $!\n" .
         "If this directory is supposed to exist, find out what is wrong!10n" );
      ShowError();
      return $statusFlag;
   }

   if ( not $statusFlag = opendir( WKG, "$wkgDir" ))
   {
      @msgItem = ( @msgItem, "ERROR: Could not open $wkgDir\n10b" );
      @msgItem = ( @msgItem, "Could not open $wkgDir: $!\n" .
         "The directory does exist; find out what is wrong!10n" );
      ShowError();
      return $statusFlag;
   }

   @foundDirs = ();
   foreach $tempStr ( sort readdir( WKG ))
   {
      next if ( $tempStr eq "." or $tempStr eq ".." );
      @foundDirs = ( @foundDirs, $tempStr ) if ( -d $tempStr );
   }

   if ( not $statusFlag = closedir( WKG ))
   {
      @msgItem = ( @msgItem, "WARNING: Could not close $wkgDir10b" );
      @msgItem = ( @msgItem, "Could not close $wkgDir: $!\n" .
         "This is NOT a fatal error, but you should find out what is wrong.10n" );
      ShowError();
      $statusFlag = 1;   # To make it fatal, change message, change flag to = 0, and add a return $statusFlag.
   }

   $numDirs = @foundDirs;   # Get number of entries in @foundDirs.
   $foundDirs[0] = "NONE!" if ( $numDirs == 0 );
   $scanFlag = 1;

   $dirList->delete( 0, 'end' );
   $dirList->insert( 'end', @foundDirs );

   return $statusFlag;
}


# CheckStartDirState()
# Sets values, etc., depending on whether user is choosing a dir from the list
# or is entering a dir.
sub CheckStartDirState
{
   if ( $startDirState and not $startDirFlag )
   {
      $initDirEn->configure( -background => "#FFFFFF", -state => 'normal' );
      $dirList->configure( -background => "#EEEEEE", -takefocus => '0' );
      $dirList->delete( 0, 'end' );
      $rescanBtn->configure( -state => 'disabled' );
      $startDirState = 0;
      $initDir       = "???";
      $userDir       = "???";
   } elsif ( not $startDirState and $startDirFlag )
   {
      $initDirEn->configure( -background => "#EEEEEE", -state => 'disabled' );
      $dirList->configure( -background => "#FFFFFF", -takefocus => 'undef' );
      $dirList->insert( 'end', @foundDirs );
      $rescanBtn->configure( -state => 'normal' );
      $startDirState = 1;
      $initDir       = "???";
      $userDir       = "";
   }
}


# CheckExtsState()
# Changes state and value of the extensions based on the user turning $fixIncludes on or off.
sub CheckExtsState
{
   if ( $fixIncludes )
   {
      # Reactivate widgets (may have been disabled).
      $fixCpp->configure( -state => 'normal' );
      $fixC->configure(   -state => 'normal' );
      $fixH->configure(   -state => 'normal' );
      $fixHpp->configure( -state => 'normal' );
      $moreExtsEn->configure( -background => "#FFFFFF", -state => 'normal' );

      # Check the usual suspects.
      $useCpp = $useC = $useH = $useHpp = 1;
   } else
   {
      # Uncheck all exts when user unchecks $fixIncludes
      $useCpp = $useC = $useH = $useHpp = 0;
      $moreExts = "";

      # Disable widgets
      $fixCpp->configure( -state => 'disabled' );
      $fixC->configure(   -state => 'disabled' );
      $fixH->configure(   -state => 'disabled' );
      $fixHpp->configure( -state => 'disabled' );
      $moreExtsEn->configure( -background => "#EEEEEE", -state => 'disabled' );
   }
}


# CheckZipState()
# Changes state of the zip file name entry based on the user turning $zipSource on or off.
sub CheckZipState
{
   if ( $zipSource )
   {
      # Reactivate widget (may have been disabled).
      $zipBaseEn->configure( -background => "#FFFFFF", -state => 'normal' );
   } else
   {
      # Disable widget
      $zipBaseEn->configure( -background => "#EEEEEE", -state => 'disabled' );
   }
}


# CheckDestDirState()
# Toggles $destDirEn state as appropriate, and changes state of the Overwrite radiobuttons 
# based on the user's settings.
sub CheckDestDirState
{

   if ( $destDirFlag == 2 and $destDirEn->cget( -state ) eq 'disabled' )
   {
      $destDirEn->configure( -background => "#FFFFFF", -state => 'normal' );
      $userDestDir = "???";
   } elsif ( $destDirFlag != 2 and $destDirEn->cget( -state ) eq 'normal' )
   {
      $destDirEn->configure( -background => "#EEEEEE", -state => 'disabled' );
      $userDestDir = "";
   }

   if ( $destDirFlag )
   {
      # Reactivate widgets (may have been disabled).
      $noOverRbn->configure(  -state => 'normal' );
      $yesOverRbn->configure( -state => 'normal' );
   } else
   {
      # Disable widgets
      $noOverRbn->configure(  -state => 'disabled' );
      $yesOverRbn->configure( -state => 'disabled' );
   }
}

# PART 3: Input Error Checking Functions
# Note: These are not GUI-independent. In particular, they format their output for 
# the error and query dialog boxes. Also, they assume errors in the input return the 
# user to the Input window.

# CheckForErrors()
# Called when Process Files button is pressed. Does extensive error checking beofre trying to
# process files. Structured to end when first error is encountered.
sub CheckForErrors
{
   my $statusFlag = 1;

   $statusFlag = CheckDirName()           if ( not $startDirFlag );
   $statusFlag = CheckStartDir( $initDir) if ( $statusFlag );
   $statusFlag = CheckZipName()           if ( $statusFlag and $zipSource );
   $statusFlag = CheckFixIncludes()       if ( $statusFlag and $fixIncludes );
   $statusFlag = BuildDestPath()          if ( $statusFlag );
   $statusFlag = CheckDestDir()           if ( $statusFlag and $destDirFlag and not $overOld );
   $statusFlag = CheckExts()              if ( $statusFlag and $fixIncludes and $moreExts ne "" );

   if ( not $statusFlag)
   {
      $outBody->insert( 'end', "\n", '8n' );
      $outBody->insert( 'end', "Processing Aborted!\n", '12b' );
      $outBody->insert( 'end', 
         "Errors were encounted before processing could begin. Please correct them and " .
         "try again.\n", '10n' );
   }

   return $statusFlag;
}


# CheckDirName()
# Checks input directory name, $initDir.
sub CheckDirName
{
   my $statusFlag = 1;
   my $testDir;          # Loses any drive specification (makes tests simpler).

   if ( $initDir =~ /^[a-z]:.*/i )
   {
      $testDir = substr( $initDir, 2 );
   } else
   {
      $testDir = $initDir;
   }

   if ( $testDir =~ /\\$/     or
             $testDir =~ /^\\win/i or
             $testDir =~ /^\\astell-thq\\wkg$/i )
   {
      # Reject entry if a dangerous or confusing directory name is entered.
      @msgItem = ( @msgItem, "ERROR: Incorrect directory name!\n12b" );
      @msgItem = ( @msgItem, 
         "You cannot specify: $initDir\n"                                 .
         "To prevent mistakes, you cannot specify:\n"                     .
         "   1. The root directory or an entire drive.\n"                 .
         "   2. Anything that looks likes a Windows system directory.\n"  .
         "   3. Any path that ends with \\.\n"                            .
         "   4. \\Astell-THQ\\wkg. (Specify a project dir!)\n"            .
         "   5. \\Astell-THQ\\wkg\\XXX. (Change XXX to a project dir!)\n10n" );
      ShowError();
      return $statusFlag = 0;
   }
   
   if ( not ( $testDir =~ /^\\/ ) )
   {
      # Reject entry if relative path is specified.
      @msgItem = ( @msgItem, "ERROR: Incomplete directory name!\n12b" );
      @msgItem = ( @msgItem, 
         "You cannot specify just: $initDir\n"                  .
         "Please specify an absolute, complete path, either:\n" .
         "   Drive:\\dir\\subdir\\projectdir\n"                 .
         " or:\n"                                               .
         "   \\dir\\subdir\\projectdir\n10n" );
      ShowError();
      return $statusFlag = 0;
   }
   
   if ( substr( $testDir, rindex( $testDir, /\\/ ) +1 =~ /\s+/ ))
   {
      # Reject entry if directory is whitespace.
      @msgItem = ( @msgItem, "ERROR: Improper directory name!\n12b" );
      @msgItem = ( @msgItem, 
         "You cannot have the final part of the name be blank.\n10n" );
      ShowError();
      return $statusFlag = 0;
   }
   
   if ( $testDir =~ /[\*\?":]/ )
   {
      # Reject illegal characters in names.
      @msgItem = ( @msgItem, "ERROR: Incorrect characters in directory name!\n12b" );
      @msgItem = ( @msgItem, 
         "You cannot have : * ? or \" in directory names.\n10n" );
      ShowError();
      return $statusFlag = 0;
   }
   
   if ( not $statusFlag = chdir( "$initDir" ))
   {
      # Reject entry if directory does not exist.
      @msgItem = ( @msgItem, "ERROR: Directory does not exist!\n12b" );
      @msgItem = ( @msgItem, 
         "You said to process $initDir, but it could not be found. " .
         "Is the name correct?\n10n" );
      ShowError();
      return $statusFlag;
   }
   
   if ( not $statusFlag = CheckDirContents( $initDir ))
   {
      # Reject entry if directory is empty.
      @msgItem = ( @msgItem, "ERROR: Directory is empty!\n12b" );
      @msgItem = ( @msgItem, 
         "You said to process $initDir, but it does not seem to contain anything. " .
         "Is the name correct? If so, find out why $initDir is empty.\n10n" );
      ShowError();
      return $statusFlag;
   }

   return $statusFlag;
}


# CheckStartDir()
# Checks dir to be processed.
sub CheckStartDir
{
   my $statusFlag = 1;
   my $testDir    = $_[0];

   if ( not $statusFlag = chdir( $testDir ))
   {
      @msgItem = ( @msgItem, "ERROR: Directory cannot be found!\n12b" );
      @msgItem = ( @msgItem, 
         "$testDir does not seem to exist. " .
         "Find out what is wrong.\n10n" );
      ShowError();
      return $statusFlag;
   }
   
   if ( not $statusFlag = CheckDirContents( $testDir ))
   {  
      # Reject if directory is empty.
      @msgItem = ( @msgItem, "ERROR: Directory is empty!\n12b" );
      @msgItem = ( @msgItem, 
         "$testDir does not seem to contain anything. " .
         "If this is supposed to be a project, find out why it is empty.\n10n" );
      ShowError();
      return $statusFlag;
   }

   return $statusFlag;
}


# CheckDirContents()
# Checks if input dir actually has anything in it.
sub CheckDirContents
{
   my $statusFlag = 1;
   my $testDir    = $_[0];
   my $numFiles   = 0;

   finddepth sub 
   {
      $numFiles += 1;
   }, $testDir;

   $statusFlag = 0 if ( $numFiles <= 1 );   # $numFiles = 1 means dir exists but has nothing in it.

   return $statusFlag;
}


# CheckFixIncludes()
# Checks if $fixSpaces or $fixCase is checked but not $fixIncludes. And,
# if user said to fix #includes, checks that at least one ext was entered.
# Also checks for odd stuff, like user saying check .c but not .h.
sub CheckFixIncludes
{
   my $statusFlag  = 1;

   # Check if $fixSpaces or $fixCase is checked but not $fixIncludes
   if ( ($fixSpaces == 1 or $fixCase == 1) and $fixIncludes == 0 )
   {
      @msgItem = ( @msgItem, "QUERY: Problem with fix #includes\n12b" );
      @msgItem = ( @msgItem, 
         "You specified to change spaces or upper case in files names, but you " .
         "did not specify to fix #includes. lxr will miss any included files "   .
         "that have had spaces or case changed. Is this what you want?\n\n"      .
         "(YES continues processing; NO returns to the Input window.)\n10n" );
      $tempStr = ShowQuery();
      return $statusFlag = 0 if ( $tempStr eq "NO" );
   }
   
   if ( $fixIncludes )
   {
      my $numExts = (8 * $useCpp) + (4 * $useC) + (2 * $useHpp) + (1 * $useH);

      if ( $numExts < 1 and $moreExts eq "" )
      {
         @msgItem = ( @msgItem, "QUERY: Fix #includes extensions\n12b" );
         @msgItem = ( @msgItem, 
            "You specified to fix #includes, but no extensions are specified! " .
            "This means no files will be examined. Is this what you want?\n\n"  .
            "(YES continues processing; NO returns to the Input window.)\n10n" );
         $tempStr = ShowQuery();
         return $statusFlag = 0 if ( $tempStr eq "NO" );
      }
      
      if ( $numExts >= 1 and $numExts <= 3 )
      {
         @msgItem = ( @msgItem, "QUERY: Fix #includes oddity\n12b" );
         @msgItem = ( @msgItem, 
            "You specified that only headers but no source files are to " .
            "be processed. Is this correct?\n\n"                          .
            "(YES continues processing; NO returns to the Input window.)\n10n" );
         $tempStr = ShowQuery();
         return $statusFlag = 0 if ( $tempStr eq "NO" );
      }
      
      if ( $numExts == 4 or $numExts == 8 or $numExts == 12 )
      {
         @msgItem = ( @msgItem, "QUERY: Fix #includes oddity\n12b" );
         @msgItem = ( @msgItem, 
            "You specified that only source files but no headers are to " .
            "be processed. Is this correct?\n\n"                          .
            "(YES continues processing; NO returns to the Input window.)\n10n" );
         $tempStr = ShowQuery();
         return $statusFlag = 0 if ( $tempStr eq "NO" );
      }
      
      if ( $numExts == 6 or $numExts == 7 or $numExts == 14 )
      {
         @msgItem = ( @msgItem, "QUERY: Fix #includes oddity\n12b" );
         @msgItem = ( @msgItem, 
            "You specified an atypical mix of source files and headers are to be processed. " .
            "Is this correct?\n\n"                                                            .
            "(YES continues processing; NO returns to the Input window.)\n10n" );
         $tempStr = ShowQuery();
         return $statusFlag = 0 if ( $tempStr eq "NO" );
      }
   }

   return $statusFlag;
}

# BuildDestPath()
# Build $destDir (must be done after CheckDirName() is done in CheckForErrors).
sub BuildDestPath
{
   my $statusFlag = 1;

   if ( $destDirFlag == 1 )
   {
      $destDir = "L:\\inetpub\\www\\wkg\\" . $destDirName;
   } elsif ( $destDirFlag == 2 )
   {
      if ( $statusFlag = -d "$userDestDir" )
      {
         $destDir = $userDestDir . "\\" . $destDirName;
      } else
      {
         @msgItem = ( @msgItem, "ERROR: Directory cannot be found!\n12b" );
         @msgItem = ( @msgItem, 
            "You said move the project into $userDestDir, but this dir does not seem to exist. " .
            "Find out what is wrong.\n10n" );
         ShowError();
         return $statusFlag;
      }
   } else
   {
      $destDir = "None";
   }

   return $statusFlag;
}


# CheckZipName()
# Loses spaces, rejects illegal characters in the zip file name and 
# rejects empty names.
sub CheckZipName
{
   my $statusFlag = 1;

   if ( $zipBase eq "" )
   {
      # Reject entry if empty.
      @msgItem = ( @msgItem, "ERROR: Improper zip name!\n12b" );
      @msgItem = ( @msgItem, 
         "You must enter something for the zip file name, and " .
         "it must be something other than spaces or \".zip\".\n10n" );
      ShowError();
      $zipBase = "XXX";
      $zipBaseEn->focus;
      return $statusFlag = 0;
   }
   
   if ( $zipBase =~ /[\\\/\*\?":]/ )
   {
      # Reject entry if it has illegal characters.
      @msgItem = ( @msgItem, "ERROR: Incorrect characters in file name!\n12b" );
      @msgItem = ( @msgItem, 
         "You cannot have \\ / : * ? or \" in the file name.\n10n" );
      ShowError();
      $zipBase = "XXX";
      $zipBaseEn->focus;
      return $statusFlag = 0;
   }

   # Everthing checked out OK.
   $zipName = $zipBase . ".zip";

   return $statusFlag;
}


# CheckExts()
# Rejects illegal characters in the extensions names and cleans up entry.
sub CheckExts
{
   my $statusFlag = 1;

   if ( $moreExts =~ /[\\\/\*\?":]/ )
   {
      @msgItem = ( @msgItem, "ERROR: Incorrect characters in file name!\n12b" );
      @msgItem = ( @msgItem, 
         "You cannot have \\ / : * ? or \" in the extension names.\n10n" );
      ShowError();
      $moreExts = "";
      $moreExtsEn->focus;
      return $statusFlag = 0;
   }

   return $statusFlag;
}

# CheckDestDir()
# Displays error if user said 1) to copy processed dir to a destination, 2) but
# not to overwrite destination if it already exits, and 3) dest does exist.
sub CheckDestDir
{
   my $statusFlag = 1;

   if ( -d "$destDir" )
   {
      @msgItem = ( @msgItem, "ERROR: Directory already exists at destination!\n12b" );
      @msgItem = ( @msgItem, 
         "You said to copy $initDir to $destDir without overwriting the destination, " .
         "but $destDir already exists at the destination. Something is not right; "    .
         "please change your settings.\n10n" );
      ShowError();
      return $statusFlag = 0;
   }

   return $statusFlag;
}


# PART 4: File Processing Functions
# Note: These are not GUI-independent. In particular, they format their output for 
# the Output window and the error and query dialog boxes. Also, they assume processing 
# errors put the user back in the Input Window.

# ProcessFiles()
# Called by the Process Files button. Calls the various individual processing functions.
sub ProcessFiles
{
   my $statusFlag = 1;

   OpenOutWin();
   $outBody->delete( "1.0", 'end' );
   $outBody->insert( 'end', "Processing Begun!\n", '12b' );
   $outBody->insert( 'end', "Note: ", '10b' );
   $outBody->insert( 'end', 
      "Large projects can take several minutes to process. " .
      "Please wait until the results appear.\n", '10n' );
   $outBody->update();   # Displays the above output now, rather than after function finishes.

   # Process stuff!
   EchoInput()                 if ( $echoInput );
   $statusFlag = ClearROA()    if ( $clearROA  );
   $statusFlag = ZipSource()   if ( $zipSource   and $statusFlag );
   $statusFlag = FixSpaces()   if ( $fixSpaces   and $statusFlag );
   $statusFlag = FixCase()     if ( $fixCase     and $statusFlag );
   $statusFlag = FixIncludes() if ( $fixIncludes and $statusFlag );
   $statusFlag = CopyToLinux() if ( $destDirFlag and $statusFlag );

   if ( $statusFlag )
   {
      # Tell use that processing succeeded without fatal error.
      $outBody->insert( 'end', "\n", '8n' );
      $outBody->insert( 'end', "Processing Done!\n", '12b' );
      $outBody->insert( 'end', 
         "Files were processing on the Windows PC without " . 
         "a fatal error being detected. ", '10n' );
      if ( $destDirFlag )
      {
         $outBody->insert( 'end', 
            "The files were copied to in $destDir and are ready for "     .
            "Linux processing there. (When everything is done there and " .
            "error checked, don't forget to delete $initDir or whatever.\n", '10n' );
      } else
      {
         $outBody->insert( 'end', 
            "The files were not copied anywhere and remain in $initDir.\n", '10n' );
      }
   }

   return $statusFlag;   # Not that the caller checks the return value in the current code...
}


# EchoInput()
# Echoes input to output. Who knows; maybe the user will notice
# something was specified incorrectly.
# Does not use or return $statusFlag like other functions as is not
# subject to failure due to user input.
sub EchoInput
{
   $outBody->insert( 'end', "\n", '8n' );
   $outBody->insert( 'end', "Input Settings\n", '12b' );
   $outBody->insert( 'end', "Directory to Process:       $initDir\n", 'c10n' );
   $tempStr = ($clearROA == 1) ? "Yes" : "No";
   $outBody->insert( 'end', "Clear Read-Only Attributes: $tempStr\n", 'c10n' );
   $tempStr = ($zipSource == 1) ? "Yes" : "No";
   $outBody->insert( 'end', "Zip Source Code:            $tempStr\n", 'c10n' );
   $outBody->insert( 'end', "  --Zip File Name:          $zipName\n", 'c10n' ) if ( $zipSource );
   $tempStr = ($fixSpaces == 1) ? "Yes" : "No";
   $outBody->insert( 'end', "Change Spaces to _:         $tempStr\n", 'c10n' );
   $tempStr = ($fixCase == 1) ? "Yes" : "No";
   $outBody->insert( 'end', "Change Upper Case to Lower: $tempStr\n", 'c10n' );
   $tempStr = ($fixIncludes == 1) ? "Yes" : "No";
   $outBody->insert( 'end', "Fix \x23include Formatting:    $tempStr\n", 'c10n' );
   if ( $fixIncludes )
   {
      my $exts;
      $exts .= ".cpp " if ( $useCpp );
      $exts .= ".c "   if ( $useC );
      $exts .= ".h "   if ( $useH );
      $exts .= ".hpp " if ( $useHpp );
      $exts .= $moreExts;
      $outBody->insert( 'end', "  --Extensions to fix:      $exts\n", 'c10n' );
   }
   $outBody->insert( 'end', "Destination Directory:      $destDir\n", 'c10n' );
   if ( $destDirFlag )
   {
      $tempStr = ($overOld == 1) ? "Yes" : "No";
      $outBody->insert( 'end', "Overwrite Destination:      $tempStr\n", 'c10n' );
   }
}


# ClearROA()
# Clears the read-only attribute for all files and directories in 
# $initDir. chmod( 700, $_ ) is used because chmod for Windows OS 
# is only valid for the "user" bit. Trying to set the "group" or 
# "other" bits fails to do what you want (e.g., chmod( 775, $_ ) does 
# NOT clear the read-only attribute).
# finddepth is used so that directories are done AFTER their contents;
# otherwise things can go wrong.
sub ClearROA
{
   my $statusFlag = 1;
   my $numFiles   = 0;
   my $numRO      = 0;
   my $numFailed  = 0;
   my $numDone    = 0;

   finddepth sub 
   {
      $numRO     += 1 if not -w;
      $numFailed += not chmod( 700, $_ ) or return;
      $numFiles  += 1;
   }, $initDir;

   $numDone = $numRO - $numFailed;

   $outBody->insert( 'end', "\n", '8n' );
   $outBody->insert( 'end', "Clearing Read-Only Attributes\n", '12b' );
   $outBody->insert( 'end', "Number of read-only files/directories: $numRO\n", '10n' );
   $outBody->insert( 'end', "Number that had RO cleared: $numDone\n", '10n' ) if ( $numRO );

   if ( $numFailed and not $fixIncludes )
   {
      $outBody->insert( 'end', "WARNING: ", '10b' );
      $outBody->insert( 'end', 
         "Number that failed to have RO cleared: $numFailed\n"                   .
         "Since you did not specify to fix #includes, processing will continue " .
         "and should be OK. (You should still try to find out what when wrong.)\n", '10n' );
   } elsif ( $numFailed )
   {
      $outBody->insert( 'end', "ERROR: ", '10b' );
      $outBody->insert( 'end', 
         "Failed to clear read-only attributes! Since read-only files " .
         "cannot have their #includes fixed, this is a fatal error.\n10n" );
      $outBody->insert( 'end', "Processing aborted.\n10b" );
      return $statusFlag = 0;
   }

   return $statusFlag;
}


# ZipSource()
# This calls a Windows command-line batch file in C:\Astell-THQ\Collective\scripts
# to zip the files using WinZip. (This is done to jigger things so that 
# WinZip will store relative path information for all files. When called
# via Perl's system, there's no convenient way to set the current directory
# the process occurs in -- chdir in Perl doesn't affect the cur dir setting
# of the WinZip process. Without controlling cur dir for the process, you 
# either only get path info for stuff in subdirs but not the base dir being
# zipped, or you get absolute path info on everything. Neither behavior is wanted.
# The batch file can chdir and thus achieve the desired behavior.)
sub ZipSource
{
   my $statusFlag = 1;
   my $size       = 0;   # To report size of zipped file in the output.
   my $zipResult;        # WARNING: The batch file will return 0 on success, the reverse 
                         # of the Perl convention.
   $outBody->insert( 'end', "\n", '8n' );
   $outBody->insert( 'end', "Zipping Source Code\n", '12b' );


   my $zipSpec = $initDir . "\\" . $zipName;   # Using '\' instead of "\\" fails.
   $zipResult = system( "C:\\Astell-THQ\\Collective\\scripts\\winzip.bat $zipSpec $initDir" );

   if ( $zipResult == 0 )
   {
      $outBody->insert( 'end', "Source code zipped as $zipSpec\n", '10n' );
      if ( $size = -s $zipSpec )
      {
         $size = int( $size / 1024 + .5 );
         $outBody->insert( 'end', "The zip file is $size KB\n", '10n' );
      } else
      {
         $outBody->insert( 'end', "WARNING: ", '10b' );
         $outBody->insert( 'end', "The zip file is 0 KB and thus has no contents. " .
         "This is not considered a fatal error, so processing continued. "          .
         "However, you have an empty zip file in your project. Check what went wrong!\n", '10n' );
      }
   } else
   {
      $outBody->insert( 'end', "WARNING: ", '10b' );
      $outBody->insert( 'end', "The source code was not zipped: $^E\n"     .
         "This is not considered a fatal error, so processing continued. " .
         "However, you have no zip file in your project. Check what went wrong!\n", '10n' );
      #$statusFlag = 0;   # Uncomment this line, change above message, and return the flag if you want it to a fatal error.
   }

   return $statusFlag;
}


# FixSpaces()
# Changes spaces to underscores in file and directory names in
# $initDir. (This is useful to avoid OS or processing problems 
# with "spaced" names.) Each space is changed to an underscore.
# finddepth is used so that directories are done AFTER their contents;
# otherwise things can go wrong.
sub FixSpaces
{
   my $statusFlag = 1;
   my $numFiles   = 0;
   my $numFailed  = 0;
   my $egFailName = "";
   my $numDone    = 0;

   finddepth sub 
   {
      my $old = $_;
      s/\s/_/g or return;   # Don't do anything if you don't have spaces.

      # See Win 95/98/ME notes at top for why a system call is included
      rename $old, $_ or not system( "ren $old $_" ) or ( $numFailed += 1 and $egFailName = $old );
      $numFiles += 1;
   }, $initDir;

   $numDone = $numFiles - $numFailed;

   $outBody->insert( 'end', "\n", '8n' );
   $outBody->insert( 'end', "Changing Spaces to Underscores\n", '12b' );
   $outBody->insert( 'end',
      "Number of file/directory names with spaces in their names: $numFiles\n" .
      "Number fixed: $numDone\n", '10n' );

   if ( $numFailed and not $fixIncludes )
   {
      $outBody->insert( 'end', "WARNING: ", '10b' );
      $outBody->insert( 'end', 
         "Number that failed to have spaces fixed: $numFailed\n"                 .
         "For example, $egFailName\n"                                            .
         "Since you did not specify to fix #includes, processing will continue " .
         "and should be OK. (You should still try to find out what when wrong.)\n", '10n' );
   } elsif ( $numFailed )
   {
      $outBody->insert( 'end', "ERROR: ", '10b' );
      $outBody->insert( 'end', 
         "Number that failed to have spaces fixed: $numFailed\n" .
         "For example, $egFailName\n"                            .
         "Since these files cannot have their #includes fixed, this is a fatal error.", '10n' );
      $outBody->insert( 'end', "Processing aborted.\n", '10b' );
      return $statusFlag = 0;
   }

   return $statusFlag;
}


# FixCase()
# Changes file and directory names to lower case in $initDir. (This is 
# useful since Windows is in effect case agnostic for file names and thus 
# many Win-based developers don't bother to maintain case, while Linux is 
# completely case senstive for file names).
# finddepth is used so that directories are done AFTER their contents;
# otherwise things can go wrong.
sub FixCase
{
   my $statusFlag = 1;
   my $numFiles   = 0;
   my $numFailed  = 0;
   my $egFailName = "";
   my $numDone    = 0;

   finddepth sub 
   {
      my $old = $_;
      tr/A-Z/a-z/ or return;   # Don't do anything if you don't have any upper case

      # See Win 95/98/ME notes at top for why a system call is included
      rename $old, $_ or not system( "ren $old $_" ) or ( $numFailed += 1 and $egFailName = $old );
      $numFiles += 1;
   }, $initDir;

   $numDone = $numFiles - $numFailed;

   $outBody->insert( 'end', "\n", '8n' );
   $outBody->insert( 'end', "Changing Upper Case to Lower\n", '12b' );
   $outBody->insert( 'end',
      "Number of file/directory names with upper case in their names: $numFiles\n" .
      "Number changed to lower case: $numDone\n", '10n' );

   if ( $numFailed and not $fixIncludes )
   {
      $outBody->insert( 'end', "WARNING: ", '10b' );
      $outBody->insert( 'end', 
         "Number that failed to have cased changed: $numFailed\n"                .
         "For example, $egFailName\n"                                            .
         "Since you did not specify to fix #includes, processing will continue " .
         "and should be OK. (You should still try to find out what when wrong.)\n", '10n' );
   } elsif ( $numFailed )
   {
      $outBody->insert( 'end', "ERROR: ", '10b' );
      $outBody->insert( 'end', 
         "Number that failed to have spaces fixed: $numFailed\n" .
         "For example, $egFailName\n"                            .
         "Since these files cannot have their #includes fixed, this is a fatal error.", '10n' );
      $outBody->insert( 'end', "Processing aborted.\n", '10b' );
      return $statusFlag = 0;
   }

   return $statusFlag;
}


#FixIncludes()
# Fixes the formatting of #include lines in user-specified files (.cpp, 
# .c, .h, .hpp, others as specified) to keep lxr happy.
# Also converts file and directory names in #include lines to lower case 
# if they were converted to lower case in earlier processing.
sub FixIncludes
{
   my $statusFlag  = 1;
   my $fixFlag     = 0;   # Flags whether a file requires changing/has been changed (1) or not (0).
   my $fixLine     = 0;   # Flags whether a line requires changing (1) or not (0).
   my $numFound    = 0;
   my $numTried    = 0;
   my $numFixed    = 0;
   my $numFunny    = 0;
   my $egFunnyName = "";
   my $numRO       = 0;
   my $egROName    = "";
   my $numZero     = 0;
   my $egZeroName  = "";

   # First build the useExt list
   my @useExt = ();

   @useExt = ( @useExt, ".cpp" ) if ( $useCpp );
   @useExt = ( @useExt, ".c" )   if ( $useC   );
   @useExt = ( @useExt, ".h" )   if ( $useH   );
   @useExt = ( @useExt, ".hpp" ) if ( $useHpp );
   @useExt = ( @useExt, split( /\s/, $moreExts ));

   $outBody->insert( 'end', "\n", '8n' );
   $outBody->insert( 'end', "Fixing #includes\n", '12b' );

   find sub 
   {
      return if ( -d );                  # Skip directories.
      my $tempExt = rindex( $_, "." );   # Get the location of the extension dot.
      return if ( $tempExt == -1 );      # Skip extensionless files. This is a potential 
                                         # problem if extensionless headers are used per 
                                         # the C++ spec, but in current practice this works.
      $tempExt = substr( $_, $tempExt ); # Get the extension.
      my $file = $_;                     # Remember file name.

      my $ext;
      foreach $ext ( @useExt )
      {
         if ( $tempExt eq $ext )
         {
            if ( not -f $file )        # Skip (and warn about) "funny" files.
            {
               $numFunny   += 1;
               $egFunnyName = $_;
               last;                # Break out of foreach loop.
            } elsif ( not -w $file )   # Skip (and warn about) read-only files.
            {
               $numRO     += 1;
               $egROName   = $_;
               last;               # Break out of foreach loop.
            } elsif ( -z $file )       # Skip (and mention existence of) zero-size files.
            {
               $numZero    += 1;
               $egZeroName  = $_;
               last;                # Break out of foreach loop.
            }

            # File passed the tests; try to process it.
            $numFound += 1;

            if ( not $statusFlag = open( TOFIX, "$file" ))
            {
               $outBody->insert( 'end', "ERROR: ", '10b' );
               $outBody->insert( 'end', 
                  "Fix #includes could not open $file: $!\n", '10n' );
               $outBody->insert( 'end', "Processing aborted.\n", '10b' );
               die;   # Apparently this works: abort processing without ending the GUI app!
            }

            if ( not $statusFlag = open( FIXED, ">fixinc.tmp" ))
            {
               $outBody->insert( 'end', "ERROR: ", '10b' );
               $outBody->insert( 'end', 
                  "Fix #includes could not open fixinc.tmp for fixing $file: $!\n", '10n' );
               $outBody->insert( 'end', "Processing aborted.\n", '10b' );
               die;   # Apparently this works: abort processing without ending the GUI app!
            }

            while (  )
            {
               my $line    = $_;
               my $incInc  = "";
               my $incFile = "";
               my $incCmt  = "";
               if ( $line =~ /(^\s*#\s*include\s+)(["|<]\s*.*)/ )
               {
                  $incInc  = $1;     # #include portion.
                  $tempStr = $2;     # Rest of line (except \n).

                  if ( $tempStr =~ m!( //.*)! )   # m!...! for clarity instead of /.../
                  {
                     $incCmt = $1;   # C++-style comment.
                  } elsif ( $tempStr =~ m!( /\*.*)! )   # m!...! for clarity instead of /.../
                  {
                     $incCmt = $1;   # C-style comment.
                  }

                  if ( $tempStr =~ /("[^"]+")/ )
                  {
                     $incFile = $1;  # File name. (Handles only "name" or ; 
                                     # cannot handle preprocessor tokens.
                  } elsif ( $tempStr =~ /(<[^>]+>)/ )
                  {
                     $incFile = $1;  # File name. (Handles only "name" or insert( 'end', "ERROR: ", '10b' );
               $outBody->insert( 'end', 
                  "Fix #includes could not close $file after reading it: $!\n", '10n' );
               $outBody->insert( 'end', "Processing aborted.\n", '10b' );
               die;   # Apparently this works: aborts without ending the GUI app!
            }

            if ( not $statusFlag = close( FIXED ))
            {
               $outBody->insert( 'end', "ERROR: ", '10b' );
               $outBody->insert( 'end', 
                  "Fix #includes could not close fixinc.tmp after reading $file: $!\n", '10n' );
               $outBody->insert( 'end', "Processing aborted.\n", '10b' );
               die;   # Apparently this works: aborts without ending the GUI app!
            }

            if ( $fixFlag )
            {
               $numTried += 1;

               # Original file was changed; lose old and rename new as old.
               if (not $statusFlag = unlink( "$file" ))
               {
                  $outBody->insert( 'end', "ERROR: ", '10b' );
                  $outBody->insert( 'end', 
                     "Fix #includes could not unlink $file after closing it: $!\n", '10n' );
                  $outBody->insert( 'end', "Processing aborted.\n", '10b' );
                  die;   # Apparently this works: aborts without ending the GUI app!
               }

               $statusFlag = rename( "fixinc.tmp", "$file" );
               if ( not $statusFlag )
               {
                  my $result = system( "ren fixinc.tmp $file" );
                  $statusFlag = 1 if ( $result == 0 );
               }
               if ( not $statusFlag )
               {
                  $outBody->insert( 'end', "ERROR: ", '10b' );
                  $outBody->insert( 'end', 
                     "Fix #includes could not rename fixinc.tmp as $file: perhaps $!\n", '10n' );
                  $outBody->insert( 'end', "Processing aborted.\n", '10b' );
                  die;   # Apparently this works: aborts without ending the GUI app!
               }
               $numFixed += 1;
            } else
            {
               # Original file was not changed; lose new and don't mess with old.
               if ( not $statusFlag = unlink( "fixinc.tmp" ))
               {
                  $outBody->insert( 'end', "ERROR: ", '10b' );
                  $outBody->insert( 'end', 
                     "Fix #includes could not unlink fixinc.tmp after closing it: $!\n", '10n' );
                  $outBody->insert( 'end', "Processing aborted.\n", '10b' );
                  die;   # Apparently this works: aborts without ending the GUI app!
               }
            }
            $fixFlag = 0;
         }
      }
   }, $initDir;

   if ( $statusFlag )
   {
      $outBody->insert( 'end', 
         "Fixed files with these extensions: @useExt\n" .
         "Total number of these files: $numFound\n"     .
         "Total that needed fixing: $numTried\n"        .
         "Number that were files fixed: $numFixed\n", '10n' );
   }

   if ( $numTried != $numFixed )
   {
      $outBody->insert( 'end', "ERROR: ", '10b' );
      $outBody->insert( 'end', 
         "Some files that needed fixing were not fixed. Find " .
         "out what when wrong.\n", '10n' );
   }

   if ( $numFunny )
   {
      $outBody->insert( 'end', "WARNING: ", '10b' );
      $outBody->insert( 'end', 
         "Funny files found!\n"                                       .
         "Number of files that failed the Perl -f check: $numFunny\n" .
         "For example: $egFunnyName\n"                                .
         "Fix #includes totally ignored these files. "                .
         "Things might be OK; check further!\n", '10n' );
   }

   if ( $numRO )
   {
      $outBody->insert( 'end', "WARNING: ", '10b' );
      $outBody->insert( 'end', 
         "Read-only files found!\n"                    .
         "Number of RO files: $numRO\n"                .
         "For example: $egROName\n"                    .
         "Fix #includes totally ignored these files. " .
         "Something probably is wrong! (Check if Clear Read-Only is off.)\n", '10n' );
   }

   if ( $numZero )
   {
      $outBody->insert( 'end', "WARNING: ", '10b' );
      $outBody->insert( 'end', 
         "Zero-size files found!\n"                    .
         "Number of zero-size files: $numZero\n"       .
         "For example: $egZeroName\n"                  .
         "Fix #includes totally ignored these files. " .
         "Things might be OK; check further!\n", '10n' );
   }

   return $statusFlag;
}


# CopyToLinux()
# Copies the files and directories in $initDir to a destination directory on
# a Linux box (either the Collective or the DevNet). User may specify whether
# or not to overwrite existing content in the destination. User may also 
# specify to delete $initDir when copying is done.
# WARNING: Makes a system() call to Window's xcopy command for mass transfer of
# files. This function can only be used on PCs running MSWin32. The sending PC must be 
# connected over a network to a Linux box running Samba, with the PC/user having the
# appropriate permissions. Map the Linux box to K: for the DevNet or L: for the
# Collective. (UNC connections, e.g., \\server\share, supposedly are possible, but the 
# need to enter username and password might thwart things.
# This function has been tested on Win2000. Should work on WinNT. Probably should work on
# WinXP. Theoretically should work on Win95/98/ME but not tested.
# TODO: Functionality to copy to both Linux boxes rather than just either one.
sub CopyToLinux 
{
   my $statusFlag  = 1;

   $outBody->insert( 'end', "\n", '8n' );
   $outBody->insert( 'end', "Copying to Linux\n", '12b' );

   if ( not -e $destDir ) 
   {
      # Make the dir if it doesn't exist.
      if ( not $statusFlag = mkdir( $destDir ))
      {
         $outBody->insert( 'end', "ERROR: ", '10b' );
         $outBody->insert( 'end', "$destDir could not be created: $!\n", '10n' );
         $outBody->insert( 'end', "Processing aborted.\n", '10b' );
         return $statusFlag;
      }
   } elsif ( -d $destDir and $overOld )
   {
      # Delete an existing dir and make a new one dir if user specified to overwrite.
      # WARNING: rmtree() becomes unsafe if the user can specify the directory (e.g.,
      # the root or an importante directory can be offed). The third arg is set to
      # true to try to limit damage, but this is insufficient for all situations.
      # If you change things and allow users to specify destination dirs other
      # than those currently allowed, consider changing this feature.
      if ( $statusFlag = rmtree( $destDir, 0, 1 ))
      {
         if ( not $statusFlag = mkdir( $destDir ))
         {
            $outBody->insert( 'end', "ERROR: ", '10b' );
            $outBody->insert( 'end', "$destDir could not be created: $!\n", '10n' );
            $outBody->insert( 'end', "Processing aborted.\n", '10b' );
            return $statusFlag;
         }
      } else
      {
         $outBody->insert( 'end', "ERROR: ", '10b' );
         $outBody->insert( 'end', "$destDir already exists and could not be deleted: $!\n", '10n' );
         $outBody->insert( 'end', "Processing aborted.\n", '10b' );
         return $statusFlag;
      }
   } elsif ( -d $destDir and not $overOld )
   {
      # Show error if there is an existing dir and user said not to overwrite.
      $outBody->insert( 'end', "ERROR: ", '10b' );
      $outBody->insert( 'end', 
         "$destDir already exists and you specified not to overwrite it!\n", '10n' );
      $outBody->insert( 'end', "Processing aborted.\n", '10b' );
      return $statusFlag = 0;
   } elsif ( -f $destDir )
   {
      # Show error if there is a file there with the dir's name.
      $outBody->insert( 'end', "ERROR: ", '10b' );
      $outBody->insert( 'end', 
         "$destDir already exists but says it is a file, " .
         "not a directory. Find out what is going on!\n", '10n' );
      $outBody->insert( 'end', "Processing aborted.\n", '10b' );
      return $statusFlag = 0;
   } else
   {
      # Show anonymous error.
      $outBody->insert( 'end', "ERROR: ", '10b' );
      $outBody->insert( 'end', "Something went wrong with $destDir, perhaps: $!\n", '10n' );
      $outBody->insert( 'end', "Processing aborted.\n", '10b' );
      return $statusFlag = 0;
   }
   my @xcopyArgs = qq( $initDir $destDir /q /i /s /e /h /y );

   my $result = system( "xcopy @xcopyArgs" );   # xcopy returns 0 on success, not 1.
   if ( $result == 0 )
   {
      $outBody->insert( 'end', "$initDir was copied to to $destDir.\n", '10n' );
   } else
   {
      $outBody->insert( 'end', "ERROR: ", '10b' );
      $outBody->insert( 'end', "Something went wrong when trying to copy $initDir to $destDir.\n", '10n' );
      $outBody->insert( 'end', "Processing aborted.\n", '10b' );
      return $statusFlag = 0;
   }

   return $statusFlag;
}


# PART 5: Help Data
# GetHelpContents()
# This originally was a separate file that was read in, but it was incorporated into the script
# so that only one file needed to be tracked. It is organized to be easily split out again
# should someone's mind change once again.
sub GetHelpContents
{
   my @helpContent;
   $helpContent[0] = 
      "Specify the options for processing files (source code, docs, etc.). Non-relevant files likes binaries, " .
      "source control, and make files should be removed from the source code before processing.\n10n";
   $helpContent[1] = 
      "\n10n";
   $helpContent[2] = 
      "Directory to be Processed:\n12b";
   $helpContent[3] = 
      "Choose the directory to be processed by selecting one found in the working directory " .
      "(C:\\Astell-THQ\\wkg) or by entering the full path to one.\n10n";
   $helpContent[4] = 
      "\n10n";
   $helpContent[5] = 
      "The directory to be processed should contain the directories and files to go on the Collective. "  .
      "Typically it will contain a src directory with the source code and may contain other directories " .
      "(such as a doc directory containing project documents) and files.\n10n";
   $helpContent[6] = 
      "\n10n";
   $helpContent[7] = 
      "You cannot specify the root directory or any directory that looks like a Windows system directory.\n10n";
   $helpContent[8] = 
      "\n10n";
   $helpContent[9] = 
      "The LAST subdirectory name you enter will be assumed to be the projectname directory. So, if "          .
      "you want to process a GAME1 directory in \\Somedir\\Anotherdir, specify \\Somedir\\Anotherdir\\GAME1. " .
      "If you just specify \\Somedir\\Anotherdir, Anotherdir and everything in it is processed and is moved "  .
      "to the Linux box, which is probably not whay you want!\n10n";
   $helpContent[10] = 
      "\n10n";
   $helpContent[11] = 
      "Processing Options:\n12b";
   $helpContent[12] = 
      "Clear Read-Only Attributes 10b";
   $helpContent[13] = 
      "for files and directories (highly recommended; in fact virtually mandatory).\n10n";
   $helpContent[14] = 
      "\n10n";
   $helpContent[15] = 
      "Zip Source Code 10b";
   $helpContent[16] = 
      "to include a downloadable zip file as part of the project. The code is zipped after read-only " .
      "attributes are cleared but before any other processing is done. (This preserves the original "  .
      "code, as the other processing is just to ensure lxr can handle it on the Linux box.)\n10n";
   $helpContent[17] = 
      "\n10n";
   $helpContent[18] = 
      "Change Spaces to Underscores 10b";
   $helpContent[19] = 
      "in file and directory names (highly recommended to prevent errors in further processing or " .
      "with lxr).\n10n";
   $helpContent[20] = 
      "\n10n";
   $helpContent[21] = 
      "Change Upper Case to Lower 10b";
   $helpContent[22] = 
      "in file and directory names (useful to avoid possible Windows/Linux silliness).\n10n";
   $helpContent[23] = 
      "\n10n";
   $helpContent[24] = 
      "Fix #include Formatting 10b";
   $helpContent[25] = 
      "in C/C++ source and header files. This fixes whitespace in #include lines so that lxr can "  .
      "parse the lines correctly. Also, if file names are made all lower case (see above option), " .
      "this option will also lower case file names in the includes.\n10n";
   $helpContent[26] = 
      "   Types (extensions) of files to fix #include formatting:\n10n";
   $helpContent[27] = 
      "   .cpp   .c   .h   .hpp\n10n";
   $helpContent[28] = 
      "   Other: Enter additional extensions in a comma and/or space separated list: " .
      ".ext1, .ext2, .ext3 (or ext1, ext2, ext3). (You can leave off the periods if you wish.)\n10n";
   $helpContent[29] = 
      "\n10n";
   $helpContent[30] = 
      "Copy Processed Files to:\n12b";
   $helpContent[31] = 
      "Choose one of:\n"                                                                                         .
      "1) L:\inetpub\www\wkg is on the THQ Collective Linux box.\n"                                               .
      "2) Enter the full path to a directory.\n"                                                                  .
      "3) Do not copy. (Allows you process the directory in place and then manually move it if you wish.)\n"      .
      "Note: To copy from a Windows PC to a Linux box, you should have the Linux box mapped to a network drive, " .
      "with and the Linux box using Samba allow you access via the Windows network.\n10n";
   $helpContent[32] = 
      "\n10n";
   $helpContent[33] = 
      "The projectname directory (and its contents) will be copied as a subdirectory of the same name under " .
      "the specified destination directory. Remember that the projectname directory is the final "            .
      "subdirectory you specify in the directory to be processed. For example, if you specify to process "    .
      "C:\\Somedir\\Anotherdir\\GAME1 and L:\\inetpub\\www\\wkg is the destination, then GAME1 and its "      .
      "contents are copied to L:\\inetpub\\www\\wkg\\GAME1.\n10n";
   $helpContent[34] = 
      "\n10n";
   $helpContent[35] = 
      "Note: Copying can take several minutes for a large project. Copying is done when the output " .
      "window updates.\n10n";
   $helpContent[36] = 
      "\n10n";
   $helpContent[37] = 
      "Overwrite Destination Directory:\n12b";
   $helpContent[38] = 
      "This lets you specify whether or not to overwrites the destination projectname directory " .
      "(and its contents) if it already exists. This is off by default since you may overwrite "  .
      "something you don't want to. If you turn it on, the old projectname directory and its "    .
      "contents are discarded before the new projectname directory and its contents are copied "  .
      "to the destination.\n10n";
   $helpContent[39] = 
      "\n10n";

   foreach ( @helpContent )
   {
      ($text, $tag) = split( //, $_ );
      $helpBody->insert( 'end', $text, $tag );
   }
}