Pearson Software Consulting Services

    Cloning An Existing Folder To A New Folder

         This page describes the code to "clone" an existing folder to a new folder. The code will copy all of the files and subfolders from the specified source folder to the specified destination folder. The folder hierarchy (subfolders within subfolders within subfolders, etc) in the source folder is preserved in the destination folder. All subfolders, no matter how deeply nested, will be copied from the source folder to the destination folder.

The destination folder must not exist.  It will be created by the procedure. There are three functions in the code:

There are two versions of the Clone Folder code: CloneFolder and CloneFolderEx. The difference is that CloneFolderEx prompts for and accepts a procedure name that will be called automatically for each file copied from SourceFolder to DestinationFolder.  This function may change the name of the destination file. For example, you may which to append "_PREVIOUS" to each file name . The default mode is to leave the ConversionProcedure parameter a vbNullString, in which case CloneFolderEx functions identically to CloneFolder.

 CloneFolder

CloneFolder

Sub CloneFolder()

This procedure simply prompts the user for the Source and Destination folders and passes those folder names to CloneTheFolder. This step is separated into its own procedure, rather than being included in CloneTheFolder, so that you can automate the Clone operation without user input.

CloneTheFolder

Sub CloneTheFolder(SourceFolderName As String, DestFolderName As String)

This procedure sets up the Folder objects for the Source Folder and Destination Folder, and then calls CreateCopyItem to do the cloning. SourceFolderName is the name of the folder to clone, and DestFolderName is the name of the new destination folder. The procedure will create DestFolderName.   DestFolderName must not exist when you run the code.

SourceFolderName and DestFolderName may name a folder on a local drive, a mapped network drive, to a UNC folder specification.

CreateCopyItem

Private Sub CreateCopyItem(FSO As Scripting.FileSystemObject, _
    SourceFolderObj As Scripting.Folder, DestFolderObj As Scripting.Folder)

This procedure does the actual work.  In order to support folders nested to any depth, it calls itself recursively, processing one subfolder at a time.

The complete code is shown below. You can also download a module file here.

Option Explicit
Option Compare Text
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' modCloneFolder
' By Chip Pearson, www.cpearson.com chip@cpearson.com
' This module requires a reference to the Microsoft Scripting Runtime (typically located
' at C:\WINDOWS\system32\scrrun.dll).
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Sub CloneFolder()
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' CloneFolder
' This procedure prompts the user for the source and destination folders and
' passes them to the procedure CloneTheFolder. CloneTheFolder does the
' actual work of cloning the folder.
' The user-input code takes place in this procedure rather than in the
' CloneTheFolder function in order to allow automation of the
' CloneTheFolder procedure without user input.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Dim SourceFolderName As String
Dim DestFolderName  As String

''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Prompt user for Source Folder Name.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
SourceFolderName = InputBox("Enter the Full Name of the Source folder", "Clone Folder")
If Trim(SourceFolderName) = vbNullString Then
    '''''''''''''''''''''
    ' User cancelled out.
    '''''''''''''''''''''
    Exit Sub
End If

''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Prompt user for Destination Folder Name.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
DestFolderName = InputBox("Enter the Full Name of the destination folder." & vbCrLf & _
    "The Destination Folder must not exist.")
If Trim(DestFolderName) = vbNullString Then
    '''''''''''''''''''''
    ' User cancelled out.
    '''''''''''''''''''''
    Exit Sub
End If

CloneTheFolder SourceFolderName:=SourceFolderName, DestFolderName:=DestFolderName


End Sub


Sub CloneTheFolder(SourceFolderName As String, DestFolderName As String)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' CloneFolder
' This makes a complete copy of one folder, including all files and subfolder
' The folder hierarchy of the Source folder is replicated in the new folder.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim SourceFolderObj As Scripting.Folder
Dim DestFolderObj As Scripting.Folder
Dim FSO As Scripting.FileSystemObject

''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Create an instance of the FileSystemObjectx
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Set FSO = New Scripting.FileSystemObject

'''''''''''''''''''''''''''''
' Ensure SourceFolder exists.
'''''''''''''''''''''''''''''
If FSO.FolderExists(SourceFolderName) = False Then
    MsgBox "Source Folder Does Not Exist", vbOKOnly, "Close Folder"
    Exit Sub
End If

    
'''''''''''''''''''''''''''''''''''''''''''
' Ensure Destination folder does NOT exist.
'''''''''''''''''''''''''''''''''''''''''''
If FSO.FolderExists(DestFolderName) = True Then
    MsgBox "The destination folder already exists.", vbOKOnly, "Clone Folder"
    Exit Sub
End If

''''''''''''''''''''''''''''''''''''''''''
' Create the Destination Folder
'''''''''''''''''''''''''''''''''''''''''''
On Error Resume Next
MkDir DestFolderName
If Err.Number <> 0 Then
    MsgBox "Unable To Create Destination Folder:" & vbCrLf & _
        "'" & DestFolderName & "'" & vbCrLf & _
        "Err: " & CStr(Err.Number) & " " & Err.Description
    Exit Sub
End If
On Error GoTo 0
''''''''''''''''''''''''''''''''''''''''''''''
' Set the scripting variables.
''''''''''''''''''''''''''''''''''''''''''''''
Set SourceFolderObj = FSO.GetFolder(SourceFolderName)
Set DestFolderObj = FSO.GetFolder(DestFolderName)

'''''''''''''''''''''''''''''''''''''''''''''''
' Copy the items, starting with the top-level
' folder named in SourceFolderName to
' DestFolderName. The procedure CreateCopyItem
' will call itself for subfolders within
' SourceFolderObj.
''''''''''''''''''''''''''''''''''''''''''''''''
CreateCopyItem FSO:=FSO, SourceFolderObj:=SourceFolderObj, DestFolderObj:=DestFolderObj

End Sub

Private Sub CreateCopyItem(FSO As Scripting.FileSystemObject, _
    SourceFolderObj As Scripting.Folder, DestFolderObj As Scripting.Folder)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' CreateCopyItem
' This function copies all files in SourceFolderObj to DestFolderObj. It
' then calls itself to handle the SubFolders of SourceFolderObj. It will
' preserve the folder heirarachy as it exists in the SourceFolderObj
' folder.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim SubFolder As Scripting.Folder
Dim OneFile As Scripting.File
Dim FileNameOnly As String
Dim CopyFileName As String
Dim DestFileName As String
Dim DestFolder As Scripting.Folder
Dim NewSubFolderObj As Scripting.Folder
Dim NewSubFolderName As String

''''''''''''''''''''''''''''''''''''''''''
' For each file, copy it from the
' source folder to the destination
' folder.
''''''''''''''''''''''''''''''''''''''''''
For Each OneFile In SourceFolderObj.Files
    '''''''''''''''''''''''''''''''''''''''''''''''''
    ' Get the file name without any Path information.
    '''''''''''''''''''''''''''''''''''''''''''''''''
    FileNameOnly = OneFile.Name
    
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ' Set CopyFileName to the FileNameOnly in SourceFolderObj.Path.
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    CopyFileName = SourceFolderObj.Path & "\" & FileNameOnly
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ' Set DestFileName to the FileNameOnly in DestFolderObj.Path
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    DestFileName = DestFolderObj.Path & "\" & FileNameOnly
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ' Copy the file from Source Folder to Destintaion Folder.
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    FileCopy Source:=CopyFileName, Destination:=DestFileName
Next OneFile

''''''''''''''''''''''''''''''''''''''''''''''''''''''
' For each SubFolder of SourceFolderObj, create
' a subfolder of Destination Folder with the same name.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
For Each SubFolder In SourceFolderObj.SubFolders
    NewSubFolderName = DestFolderObj.Path & "\" & SubFolder.Name
    ''''''''''''''''''''''''''
    ' Create the new directory
    ''''''''''''''''''''''''''
    MkDir NewSubFolderName
    Set NewSubFolderObj = FSO.GetFolder(NewSubFolderName)
    '''''''''''''''''''''''''''''''''''''''''''''''''''
    ' The following line of code call this same function,
    ' recursively, for the subfolders.
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''
    CreateCopyItem FSO:=FSO, SourceFolderObj:=SubFolder, DestFolderObj:=NewSubFolderObj
    
Next SubFolder

End Sub

CloneFolderEx

CloneFolderEx

Sub CloneFolderEx()

This procedure simply prompts the user for the Source and Destination folders and the name of the Conversion Procedure and passes those names to CloneTheFolderEx. This step is separated into its own procedure, rather than being included in CloneTheFolderEx, so that you can automate the Clone operation without user input.

CloneTheFolderEx

Sub CloneTheFolderEx(SourceFolderName As String, DestFolderName As String, _
        Optional ConversionProcedure As String = vbNullString)

This procedure sets up the Folder objects for the Source Folder and Destination Folder, and then calls CreateCopyItemEx to do the cloning.  SourceFolderName is the name of the folder to clone, and DestFolderName is the name of the new destination folder, and ConversionProcedure is then name of the file name conversion procedure.

The procedure will create DestFolderName.   DestFolderName must not exist when you run the code.  SourceFolderName and DestFolderName may name a folder on a local drive, a mapped network drive, to a UNC folder specification.

CreateCopyItemEx

Private Sub CreateCopyItemEx(FSO As Scripting.FileSystemObject, _
    SourceFolderObj As Scripting.Folder, DestFolderObj As Scripting.Folder, _
    Optional ConversionProcedure As String = vbNullString)

This procedure does the actual work.  In order to support folders nested to any depth, it calls itself recursively, processing one subfolder at a time. If ConversionProcedure is not an empty string , it must name an existing VBA function that will return a string with the DestinationFileName, fully qualified with drive and folder information (e.g., "C:\Temp\Folder\FileName.ext"). If the Conversion Procedure returns an unqualified or partially qualified file name (e.g,. "Folder\Filename.ext"),  the results are unpredictable. The Conversion Procedure is called only when copying a file, not when creating a folder. The Conversion Procedure's declaration must be the following:

Public Function ConversionProcedure(ByVal FileNameOnly As String, _
        SourceFolderName As String, DestFolderName As String) As String

You may, of course, change the name of the procedure, but its parameter types and return value must be declared as shown above.

If you do not want to use a conversion procedure, use CloneFolder rather than CloneFolderEx, or in CloneFolderEx set the ConverionProcedure parameter to vbNullString.

The complete code is shown below. You can also download a module file here.

Option Explicit
Option Compare Text
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' modCloneFolderEx
' By Chip Pearson, www.cpearson.com chip@cpearson.com
' This module requires a reference to the Microsoft Scripting Runtime (typically located
' at C:\WINDOWS\system32\scrrun.dll).
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Sub CloneFolderEx()
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' CloneFolderEx
' This procedure prompts the user for the source and destination folders and
' the name of the filename conversion procedure, if any, and passes them to the
' procedure CloneTheFolder. CloneTheFolder does the actual work of cloning the folder.
' The user-input code takes place in this procedure rather than in the
' CloneTheFolder function in order to allow automation of the CloneTheFolder
' procedure without user input.
'
' If a ConversionProcedure was specified, that procedure
' is called and it is responsible for creating the
' DestFileName. No testing is done on the result of
' ConversionProcedure. The source file will be copied to a file
' whose name is the result of ConversionProcedure. ConversionProcedure
' is called for each file being copied (files only, not folders).
' The ConversionProcedure must declared as
'
'        Function ConversionProcedure(ByVal FileNameOnly As String, _
'           ByVal SourceFolderName As String, ByVal DestFolderName As String) As String
'
' Where FileNameOnly is the name without any path information
' of the file being copied, SourceFolderName is the name of the folder
' that FileNameOnly is being copied FROM, and DestFolderName
' is the name of the folder than FileNameOnly is being copied TO.
'
' You may change the name of the ConversionProcedure to any valid procedure
' name, but it MUST be declare as shown above.
'
' This procedure should return the fully qualified name of the
' destination file, including complete path information. If
' ConversionProcedure returns a filename without path information,
' the results are unpredictable.
'
' The conversion procedure must be in the same workbook as this code.
'
' These procedures support local drives, mapped drives, and UNC specification
' locations.
'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Dim SourceFolderName As String
Dim DestFolderName  As String
Dim ConversionProcedureName As String
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Prompt user for Source Folder Name.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
SourceFolderName = InputBox("Enter the Full Name of the Source folder", "Clone Folder")
If Trim(SourceFolderName) = vbNullString Then
    '''''''''''''''''''''
    ' User cancelled out.
    '''''''''''''''''''''
    Exit Sub
End If

''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Prompt user for Destination Folder Name.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
DestFolderName = InputBox("Enter the Full Name of the destination folder." & vbCrLf & _
    "The Destination Folder must not exist.")
If Trim(DestFolderName) = vbNullString Then
    '''''''''''''''''''''
    ' User cancelled out.
    '''''''''''''''''''''
    Exit Sub
End If

''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Prompt the user for the name of the Name Conversion
' procedure.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
ConversionProcedureName = InputBox("Enter the name of the procedure that will" & _
    " used to modify the destination file names. Click Cancel if you won't be" & _
    " converting file names.")
   

CloneTheFolderEx SourceFolderName:=SourceFolderName, DestFolderName:=DestFolderName, _
    ConversionProcedure:=ConversionProcedureName


End Sub


Sub CloneTheFolderEx(SourceFolderName As String, DestFolderName As String, _
        Optional ConversionProcedure As String = vbNullString)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' CloneFolder
' This makes a complete copy of one folder, including all files and subfolder
' The folder hierarchy of the Source folder is replicated in the new folder.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim SourceFolderObj As Scripting.Folder
Dim DestFolderObj As Scripting.Folder
Dim FSO As Scripting.FileSystemObject

''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Create an instance of the FileSystemObjectx
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Set FSO = New Scripting.FileSystemObject

'''''''''''''''''''''''''''''
' Ensure SourceFolder exists.
'''''''''''''''''''''''''''''
If FSO.FolderExists(SourceFolderName) = False Then
    MsgBox "Source Folder Does Not Exist", vbOKOnly, "Close Folder"
    Exit Sub
End If

    
'''''''''''''''''''''''''''''''''''''''''''
' Ensure Destination folder does NOT exist.
'''''''''''''''''''''''''''''''''''''''''''
If FSO.FolderExists(DestFolderName) = True Then
    MsgBox "The destination folder already exists.", vbOKOnly, "Clone Folder"
    Exit Sub
End If

''''''''''''''''''''''''''''''''''''''''''
' Create the Destination Folder
'''''''''''''''''''''''''''''''''''''''''''
On Error Resume Next
MkDir DestFolderName
If Err.Number <> 0 Then
    MsgBox "Unable To Create Destination Folder:" & vbCrLf & _
        "'" & DestFolderName & "'" & vbCrLf & _
        "Err: " & CStr(Err.Number) & " " & Err.Description
    Exit Sub
End If
On Error GoTo 0
''''''''''''''''''''''''''''''''''''''''''''''
' Set the scripting variables.
''''''''''''''''''''''''''''''''''''''''''''''
Set SourceFolderObj = FSO.GetFolder(SourceFolderName)
Set DestFolderObj = FSO.GetFolder(DestFolderName)

'''''''''''''''''''''''''''''''''''''''''''''''
' Copy the items, starting with the top-level
' folder named in SourceFolderName to
' DestFolderName. The procedure CreateCopyItem
' will call itself for subfolders within
' SourceFolderObj.
''''''''''''''''''''''''''''''''''''''''''''''''
CreateCopyItemEx FSO:=FSO, SourceFolderObj:=SourceFolderObj, DestFolderObj:=DestFolderObj, _
        ConversionProcedure:=ConversionProcedure

End Sub

Private Sub CreateCopyItemEx(FSO As Scripting.FileSystemObject, _
    SourceFolderObj As Scripting.Folder, DestFolderObj As Scripting.Folder, _
    Optional ConversionProcedure As String = vbNullString)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' CreateCopyItem
' This function copies all files in SourceFolderObj to DestFolderObj. It
' then calls itself to handle the SubFolders of SourceFolderObj. It will
' preserve the folder heirarachy as it exists in the SourceFolderObj
' folder.
'
' If a ConversionProcedure was specified, that procedure
' is called and it is responsible for creating the
' DestFileName. No testing is done on the result of
' ConversionProcedure. This procedure is declared as
'
'        Function ConversionProcedure(ByVal FileNameOnly As String, _
'           ByVal SourceFolderName As String, ByVal DestFolderName As String) As String
'
' Where FileNameOnly is the name without any path information
' of the file being copied, SourceFolderName is the name of the folder
' that FileNameOnly is being copied FROM, and DestFolderName
' is the name of the folder than FileNameOnly is being copied TO.
'
' You may change the name of the ConversionProcedure to any valid procedure
' name, but it MUST be declare as shown above.
'
' This procedure should return the fully qualified name of the
' destination file, including complete path information. If
' ConversionProcedure returns a filename without path information,
' the results are unpredictable.
'
' The conversion procedure must be in the same workbook as this code.
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim SubFolder As Scripting.Folder
Dim OneFile As Scripting.File
Dim FileNameOnly As String
Dim CopyFileName As String
Dim DestFileName As String
Dim DestFolder As Scripting.Folder
Dim NewSubFolderObj As Scripting.Folder
Dim NewSubFolderName As String

''''''''''''''''''''''''''''''''''''''''''
' For each file, copy it from the
' source folder to the destination
' folder.
''''''''''''''''''''''''''''''''''''''''''
For Each OneFile In SourceFolderObj.Files
    '''''''''''''''''''''''''''''''''''''''''''''''''
    ' Get the file name without any Path information.
    '''''''''''''''''''''''''''''''''''''''''''''''''
    FileNameOnly = OneFile.Name
    
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ' Set CopyFileName to the FileNameOnly in SourceFolderObj.Path.
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    CopyFileName = SourceFolderObj.Path & "\" & FileNameOnly
    
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ' Set DestFileName to the FileNameOnly in DestFolderObj.Path.
    '
    ' If a ConversionProcedure was specified, that procedure
    ' is called and it is responsible for creating the
    ' DestFileName. No testing is done on the result of
    ' ConversionProcedure. This procedure is declared as
    '
    '        Function ConversionProcedure(ByVal FileNameOnly As String, _
    '           ByVal SourceFolderName As String, ByVal DestFolderName As String) As String
    '
    ' Where FileNameOnly is the name without any path information
    ' of the file being copied, SourceFolderName is the name of the folder
    ' that FileNameOnly is being copied FROM, and DestFolderName
    ' is the name of the folder than FileNameOnly is being copied TO.
    ' This procedure should return the fully qualified name of the
    ' destination file, including complete path information. If
    ' ConversionProcedure returns a filename without path information,
    ' the results are unpredictable.
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    If Trim(ConversionProcedure) <> vbNullString Then
        DestFileName = Application.Run(ConversionProcedure, FileNameOnly, SourceFolderObj.Path, DestFolderObj.Path)
    Else
        DestFileName = DestFolderObj.Path & "\" & FileNameOnly
    End If
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ' Copy the file from Source Folder to Destintaion Folder.
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    FileCopy Source:=CopyFileName, Destination:=DestFileName
Next OneFile

''''''''''''''''''''''''''''''''''''''''''''''''''''''
' For each SubFolder of SourceFolderObj, create
' a subfolder of Destination Folder with the same name.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
For Each SubFolder In SourceFolderObj.SubFolders
    NewSubFolderName = DestFolderObj.Path & "\" & SubFolder.Name
    ''''''''''''''''''''''''''
    ' Create the new directory
    ''''''''''''''''''''''''''
    MkDir NewSubFolderName
    Set NewSubFolderObj = FSO.GetFolder(NewSubFolderName)
    '''''''''''''''''''''''''''''''''''''''''''''''''''
    ' The following line of code call this same function,
    ' recursively, for the subfolders.
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''
    CreateCopyItemEx FSO:=FSO, SourceFolderObj:=SubFolder, DestFolderObj:=NewSubFolderObj
    
Next SubFolder

End Sub

Public Function ConversionProcedure(ByVal FileNameOnly As String, _
        SourceFolderName As String, DestFolderName As String) As String
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' ConversionProcedure
' This is an example ConversionProcedure. It appends "_PREVIOUS" at the end of the
' filename, right before the exentsion
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Dim ExtPos As Integer
Dim FName As String
ExtPos = InStrRev(FileNameOnly, ".", -1)
If ExtPos Then
    FName = DestFolderName & "\" & Left(FileNameOnly, ExtPos - 1) & "_PREVIOUS" & Mid(FileNameOnly, ExtPos)
Else
    FName = DestFolderName & "\" & FileNameOnly
End If

ConversionProcedure = FName

End Function

	

 

 

     
     

 

 Created By Chip Pearson and Pearson Software Consulting, LLC 
This Page:                Updated: November 06, 2013     
MAIN PAGE    About This Site    Consulting    Downloads  
Page Index     Search    Topic Index    What's New   
Links   Legalese And Disclaimers
chip@cpearson.com

Copyright 1997-2007  Charles H. Pearson