Contributor: GUY MCLOUGHLIN           


              (* Compiler directives.                               *)
 {$A+,B-,D-,E-,F-,I+,N-,O-,R-,S-,V+}

              (* STACK, HEAP memory directives.                     *)
 {$M 1024, 0, 0}

              (* Public domain file-copy program.                   *)
              (* Guy McLoughlin - August 23, 1992.                  *)
program MCopy;

uses          (* We need this unit for the paramcount, paramstr,    *)
  Dos;        (* fsearch, fexpand, fsplit routines.                 *)

const
              (* Carridge-return + Line-feed constant.              *)
  coCrLf = #13#10;

              (* Size of the buffer we're going to use.             *)
  coBuffSize = 61440;

type
              (* User defined file read/write buffer.               *)
  arBuffSize = array[1..coBuffSize] of byte;

var
              (* Path display width.                                *)
  byDispWidth : byte;

              (* Variable to record the number of files copied.     *)
  woCopyCount,
              (* Variable to record the number of bytes read.       *)
  woBytesRead,
              (* Variable to record the number of bytes written.    *)
  woBytesWritten : word;

              (* Variable to record the size in bytes of IN-file.   *)
  loInSize,
              (* Variable to record the number of bytes copied.     *)
  loByteProc : longint;

              (* Variables for TP "Fsplit" routine.                 *)
  stName : namestr;
  stExt  : extstr;

              (* Directory-string variables.                        *)
  stDirTo,
  stDirFrom : dirstr;

              (* Path-string variables.                             *)
  stPathTo,
  stPathFrom,
  stPathTemp : pathstr;

              (* Array used to buffer file reads/writes.            *)
  arBuffer : arBuffSize;

              (* Directory search-record.                           *)
  rcSearchTemp : searchrec;

              (* IN file-variable.                                  *)
  fiIN,
              (* OUT file-variable.                                 *)
  fiOUT : file;


   (***** Handle file errors.                                       *)
   procedure ErrorHandler( byErrorNum : byte);
   begin
     case byErrorNum of

       1 : begin
             writeln(coCrLf, ' (SYNTAX) MCOPY ' +
                             ' ');
             writeln(coCrLf, ' (USAGE)  MCOPY c:\utils\*.doc' +
                             ' c:\temp\master.doc');
             writeln('          MCOPY   \utils\*.doc    ' +
                     '\temp\master.doc');
             writeln(coCrLf, ' (Copies all files with the ''.doc''' +
                             ' extension from ''c:\utils'')');
             writeln(' (directory, to ''master.doc'' in the ' +
                     '''c:\temp'' directory.    )');
             writeln(coCrLf, ' ( Public-domain utility by Guy ' +
                     'McLoughlin  \  August 1992  )')
           end;

       2 : writeln(coCrLf,
                  ' Error :  = ');

       3 : writeln(coCrLf, ' Directory not found ---> ', stDirFrom);

       4 : writeln(coCrLf, ' Directory not found ---> ', stDirTo);

       5 : writeln(coCrLf, ' Error opening ---> ', stPathTo);

       6 : writeln(coCrLf, ' File copy aborted');

       7 : writeln(coCrLf, ' Error creating ---> ', stPathTo);

       8 : writeln(coCrLf, ' Error opening ---> ', stPathTemp);

       9 : writeln(coCrLf, ' Error with disk I/O ')

     end;     (* case byErrorNum.                                   *)

     halt
   end;       (* ErrorHandler.                                      *)


   (***** Determine if a file exists.                               *)
   function FileExist(FileName : pathstr) : boolean;
   begin
     FileExist := (FSearch(FileName, '') <> '')
   end;       (* FileExist.                                         *)


   (***** Determine if a directory exists.                          *)
   function DirExist(stDir : dirstr) : boolean;
   var
     woFattr : word;
     fiTemp  : file;
   begin
     assign(fiTemp, (stDir + '.'));
     getfattr(fiTemp, woFattr);
     if (doserror <> 0) then
       DirExist := false
     else
       DirExist := ((woFattr and directory) <> 0)
   end;       (* DirExist.                                          *)


   (***** Clear the keyboard-buffer.                                *)
   procedure ClearKeyBuff; assembler;
   asm
     @1: mov ah, 01h
         int 16h
         jz  @2
         mov ah, 00h
         int 16h
         jmp @1
     @2:
   end;       (* ClearKeyBuff                                       *)


   (***** Read a key-press.                                         *)
   function ReadKeyChar : char; assembler;
   asm
     mov ah, 00h
     int 16h
   end;        (* ReadKeyChar.                                      *)


   (***** Obtain user's choice.                                     *)
   function UserChoice : char;
   var
     Key : char;
   begin
     ClearKeyBuff;
     repeat
       Key := upcase(ReadKeyChar)
     until (Key in ['A', 'O', 'Q']);
     writeln(Key);
     UserChoice := Key
   end;       (* UserChoice.                                        *)


   (***** Returns all valid wildcard names for a specific directory.*)
   (*     When the last file is found, the next call will return an *)
   (*     empty string.                                             *)
   (*                                                               *)
   (* NOTE: Standard TP DOS unit must be listed in your program's   *)
   (*       "uses" directive, for this routine to compile.          *)

   function WildCardNames({ input}     stPath   : pathstr;
                                       woAttr   : word;
                          {update} var stDir    : dirstr;
                                   var rcSearch : searchrec)
                          {output}              : pathstr;
   var
              (* Fsplit variables.                                  *)
     stName : namestr;
     stExt  : extstr;
   begin
              (* If the search-record "name" field is empty, then   *)
              (* initialize it with the first matching file found.  *)
     if (rcSearch.name = '') then
       begin
              (* Obtain directory-string from passed path-string.   *)
         fsplit(stPath, stDir, stName, stExt);

              (* Find first match of path-string.                   *)
         findfirst(stPath, woAttr, rcSearch);

              (* If a matching file was found, then return full     *)
              (* path-name.                                         *)
         if (doserror = 0) and (rcSearch.name <> '') then
           WildCardNames := (stDir + rcSearch.name)
         else
              (* No match found, return empty string.               *)
           WildCardNames := ''
       end
     else
              (* Search-record "name" field is not empty, so        *)
              (* continue searching for matches.                    *)
       begin
         findnext(rcSearch);

              (* If no error occurred, then match was found...      *)
         if (doserror = 0) then
           WildCardNames := (stDir + rcSearch.name)
         else
              (* No match found. Re-set search-record "name" field, *)
              (* and return empty path-string.                      *)
           begin
             rcSearch.name := '';
             WildCardNames := ''
           end
       end
   end;


   (***** Pad a string with extras spaces on the right.             *)
   function PadR(stIn : string; bySize : byte) : string;
   begin
     fillchar(stIn[succ(length(stIn))], (bySize - length(stIn)) ,' ');
     inc(stIn[0], (bySize - length(stIn)));
     PadR := stIn
   end;       (* PadR.                                              *)


              (* Main program execution block.                      *)
BEGIN
              (* If too many or too few parameters, display syntax. *)
  if (paramcount <> 2) then
    ErrorHandler(1);

              (* Assign program parameters to string variables.     *)
  stPathFrom := paramstr(1);
  stPathTo   := paramstr(2);

              (* Make sure full path-string is used.                *)
  stPathFrom := fexpand(stPathFrom);
  stPathTo   := fexpand(stPathTo);
  stPathTemp := stPathFrom;

              (* Check if IN-Filename is the same as OUT-Filename.  *)
  if (stPathFrom = stPathTo) then
    ErrorHandler(2);

              (* Seperate directory-strings from path-strings.      *)
  fsplit(stPathFrom, stDirFrom, stName, stExt);
  fsplit(stPathTo, stDirTo, stName, stExt);

              (* Make sure that "From" directory exists.            *)
  if NOT DirExist(stDirFrom) then
    ErrorHandler(3);

              (* Make sure that "To" directory exists.              *)
  if NOT DirExist(stDirTo) then
    ErrorHandler(4);

              (* Determine the full path display width.             *)
  if (stDirFrom[0] > stDirTo[0]) then
    byDispWidth := length(stDirFrom) + 12
  else
    byDispWidth := length(stDirTo) + 12;

              (* Check if the OUT-File does exist, then...          *)
  if FileExist(stPathTo) then
    begin
              (* Ask if user wants to append/overwrite file or quit.*)
      writeln(coCrLf, ' File exists ---> ', stPathTo);
      write(coCrLf, ' Append / Overwrite / Quit  [A,O,Q]? ');

              (* Obtain user's response.                            *)
      case UserChoice of
        'A' : begin
              (* Open the OUT-file to write to it.                  *)
                assign(fiOUT, stPathTo);
                {$I-}
                reset(fiOUT, 1);
                {$I+}

              (* If there is an error opening the OUT-file, inform  *)
              (* the user of it, and halt the program.              *)
                if (ioresult <> 0) then
                  ErrorHandler(5);

              (* Seek to end of file, so that data can be appended. *)
                seek(fiOUT, filesize(fiOUT))
              end;

        'O' : begin
              (* Open the OUT-file to write to it.                  *)
                assign(fiOUT, stPathTo);
                {$I-}
                rewrite(fiOUT, 1);
                {$I+}

              (* If there is an error opening the OUT-file, inform  *)
              (* the user of it, and halt the program.              *)
                if (ioresult <> 0) then
                  ErrorHandler(5)
              end;

        'Q' : ErrorHandler(6)

      end     (* case UserChoice.                                   *)

    end

  else        (* OUT-file does not exist.                           *)

    begin
              (* Create the OUT-file to write to.                   *)
      assign(fiOUT, stPathTo);
      {$I-}
      rewrite(fiOUT, 1);
      {$I+}

              (* If there is an error creating the OUT-file, inform *)
              (* the user of it, and halt the program.              *)
      if (ioresult <> 0) then
        ErrorHandler(7)
    end;

              (* Clear the search-record, before begining.          *)
  fillchar(rcSearchTemp, sizeof(rcSearchTemp), 0);

              (* Initialize copy-counter.                           *)
  woCopyCount := 0;

              (* Set current file-mode to "read-only".              *)
  filemode := 0;

  writeln;

              (* Repeat... ...Until (stPathTemp = '').              *)
  repeat
              (* Search for vaild filenames.                        *)
    stPathTemp := WildCardNames(stPathTemp, archive, stDirFrom,
                                rcSearchTemp);

              (* If file search was successful, then...             *)
    if (stPathTemp <> '') then
      begin
              (* Open the IN-file to read it.                       *)
        assign(fiIN, stPathTemp);
        {$I-}
        reset(fiIN, 1);
        {$I+}

              (* If there is an error opening the IN-file, inform   *)
              (* the user of it, and halt the program.              *)
        if (ioresult <> 0) then
          begin
            close(fiOUT);
            erase(fiOUT);
            ErrorHandler(8)
          end;

              (* Determine the size of the IN-file.                 *)
        loInSize := filesize(fiIN);

              (* Set the number of bytes processed to 0.            *)
        loByteProc := 0;

              (* Repeat... ...Until the IN-file has been completely *)
              (* copied.                                            *)
        repeat

              (* Read the IN-file into the file-buffer.             *)
          blockread(fiIN, arBuffer, coBuffSize, woBytesRead);

              (* Write the file-buffer to the OUT-file.             *)
          blockwrite(fiOUT, arBuffer, woBytesRead, woBytesWritten);

              (* If there is a problem writing the bytes to the     *)
              (* OUT-file, let the user know, and halt the program. *)
          if (woBytesWritten <> woBytesRead) then
            begin
              close(fiIN);
              close(fiOUT);
              erase(fiOut);
              ErrorHandler(9)
            end
          else
              (* Advance the bytes-processed variable by the        *)
              (* number of bytes written to the OUT-file.           *)
            inc(loByteProc, woBytesWritten)

              (* Repeat... ...Until the complete IN-file has been   *)
              (* processed.                                         *)
        until (loByteProc = loInSize);

              (* Close the IN-file that has been copied.            *)
        close(fiIN);

              (* Increment copy-counter by 1.                       *)
        inc(woCopyCount);

              (* Let the user know that we've finished copying file.*)
        writeln(' ', PadR(stPathTemp, byDispWidth),' COPIED TO ---> ',
                stPathTo);

      end     (* If (stPathTemp <> '') then...                      *)

              (* Repeat... ...Until no more files are found.        *)
  until (stPathTemp = '');

              (* Close the OUT-file.                                *)
  close(fiOUT);

              (* Display the number of files copied.                *)
  if (woCopyCount = 0) then
    begin
      erase(fiOut);
      writeln(coCrLf, ' No matching files found ---> ', stPathFrom)
    end
  else
    writeln(coCrLf, ' ', woCopyCount, ' Files copied')
END.