Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Copying folders 5

Status
Not open for further replies.

gwar2k1

Programmer
Apr 17, 2003
387
GB
I have script to copy single files but I need a script to copy whole directories. Does any one know how this can be achieved?

Here's the code I have at the moment:

assign(source,main); {the file to be copied is assigned a path passed by reference}
assign(dest,backup); {where the file is copied to is defined in the procedure}
reset(source,1); {opens the file at the begining and sets the number of records to 1}
rewrite(dest,1); {creates a new file with one record}
getmem(buffer,1024); {gives the pointer (buffer^) 1024 bytes of memory}
REPEAT {until size of data being read is < 1Kb}
blockread(source,buffer^,1024,actually_read);
blockwrite(dest,buffer^,actually_read,written);
UNTIL actually_read < 1024;
close(source);
close(dest);
writexy(3,48,'File copied :: <return>');
readln;

~*Gwar3k1*~
&quot;To the pressure, everything's just like: an illusion. I'll be losing you before long...&quot;
 
Borland?
FindFirst&FindNext (Example for your OS can be found in help system)
 
If you want to make a new directory, you can use the pascal &quot;mkdir&quot; (help on syntax in pascal's help)

And yes, Nopik is right, you can use findfirst and findnext to copy all the files (using filename *.*) to your nice new directory! Findfirst and findnext are a bit of a pain in that they use a special data structure, but the point is they accept dos wildcards (* and ?) which makes them very useful. They can also be set up to copy all files of a particular attribute.

You are slightly misleading in your comments on reset(name, 1) and rewrite.
The &quot;1&quot; is the record size, not the number of records. By default it isn't 1, which is very confusing. By putting the 1 in you are measuring your file sizes in single bytes, which is of course what you want to do.

 
Thanks. With that, I've come up with this program... but it doesn't work and I can't think why not =(

PROGRAM david_loche;

{
by Gwar3k1 me_as_I_am@hotmail.com
4th June 2003

help: NOPIK
to: Copy files from a chosen location to another chosen location
}

USES crt,dos;

PROCEDURE writexy(x,y:byte; s:string);
{ displays text strings at specified co-ordinates; removes the need for
gotoxy before writeln }

VAR
i,j : word;
color : word;
sreenwidth : integer;
screenheight : integer;

BEGIN
IF (x = 0) OR (y = 0) THEN
exit;
x := x + lo(Windmin)-1;
y := y + hi(Windmin)-1;
IF (x > lo(windmax)) OR (y > hi(windmax)) THEN
exit;
IF x+length(s)-1 > lo(windmax) THEN
s := copy(s, 1, lo(windmax)-x+1);
sreenwidth := memw[Seg0040:$4a];
i := ( y * SreenWidth + x ) * sizeof(word);
color := textattr;
color := color shl 8;
FOR j := 1 TO length(s) DO
BEGIN
memw[segB800:i] := color OR ord(s[j]);
inc(i,2);
END;
END;

PROCEDURE copy_files(s,f:string; name:searchrec);
{copies files 1 by 1}

VAR
source,dest : FILE;
buffer : pointer;
act_read,written : word;

BEGIN
assign(source,s+name.name);
assign(dest,f+name.name);
reset(source,1);
rewrite(dest,1);
getmem(buffer,1024);
REPEAT
blockread(source,buffer^,1024,act_read);
blockwrite(dest,buffer^,act_read,written);
UNTIL act_read < 1024;
close(source);
close(dest);
END;

PROCEDURE find_files(s,f:string);

VAR
dirinfo : searchrec;
count : integer;
num : string;

BEGIN
count := 0;
clrscr;
FindFirst(s+'*.*',Archive, DirInfo); { Same as DIR *.PAS }
WHILE DosError = 0 DO
BEGIN
copy_files(s,f,dirinfo);
count:= count + 1;
FindNext(DirInfo);
END;
str(count,num);
writexy(1,49,'Copied '+num+' files - <return>');
readln;
END;

PROCEDURE trailing_slash(s:string; VAR slash:boolean);
{ all important validation ;) }


VAR
c : string;

BEGIN
c := copy(s,length(s),1);
IF c = '\' THEN
slash := true
ELSE
BEGIN
writexy(1,49,'You must include the last ''\'' at part of the DIR - <return>');
readln;
END;
END;

PROCEDURE enter_directories;
{waits for user input of directories}

VAR
start,finish : string;
slash : boolean;

BEGIN
slash := false;
writexy(1,1,'Please enter the DIR of files to be copied >');
writexy(1,2,'e.g. M:\24hours\');
writexy(1,4,'Please enter the destination DIR >');
writexy(1,5,'e.g. C:\backup\');
REPEAT
gotoxy(46,1); clreol;
gotoxy(46,1); readln(start);
trailing_slash(start,slash);
UNTIL slash;
slash := false;
REPEAT
gotoxy(46,4); clreol;
gotoxy(46,4); readln(start);
trailing_slash(start,slash);
UNTIL slash;
find_files(start,finish);
END;

{ main program }

BEGIN
clrscr;
enter_directories;
END.

~*Gwar3k1*~
&quot;To the pressure, everything's just like: an illusion. I'll be losing you before long...&quot;
 
Thanks. With that, I've come up with this program... but it doesn't work and I can't think why not =(

PROGRAM file_backup;

{
by Gwar3k1 me_as_I_am@hotmail.com
4th June 2003

help: NOPIK
to: Copy files from a chosen location to another chosen location
}

USES crt,dos;

PROCEDURE writexy(x,y:byte; s:string);
{ displays text strings at specified co-ordinates; removes the need for
gotoxy before writeln }

VAR
i,j : word;
color : word;
sreenwidth : integer;
screenheight : integer;

BEGIN
IF (x = 0) OR (y = 0) THEN
exit;
x := x + lo(Windmin)-1;
y := y + hi(Windmin)-1;
IF (x > lo(windmax)) OR (y > hi(windmax)) THEN
exit;
IF x+length(s)-1 > lo(windmax) THEN
s := copy(s, 1, lo(windmax)-x+1);
sreenwidth := memw[Seg0040:$4a];
i := ( y * SreenWidth + x ) * sizeof(word);
color := textattr;
color := color shl 8;
FOR j := 1 TO length(s) DO
BEGIN
memw[segB800:i] := color OR ord(s[j]);
inc(i,2);
END;
END;

PROCEDURE copy_files(s,f:string; name:searchrec);
{copies files 1 by 1}

VAR
source,dest : FILE;
buffer : pointer;
act_read,written : word;

BEGIN
assign(source,s+name.name);
assign(dest,f+name.name);
reset(source,1);
rewrite(dest,1);
getmem(buffer,1024);
REPEAT
blockread(source,buffer^,1024,act_read);
blockwrite(dest,buffer^,act_read,written);
UNTIL act_read < 1024;
close(source);
close(dest);
END;

PROCEDURE find_files(s,f:string);

VAR
dirinfo : searchrec;
count : integer;
num : string;

BEGIN
count := 0;
clrscr;
FindFirst(s+'*.*',Archive, DirInfo); { Same as DIR *.PAS }
WHILE DosError = 0 DO
BEGIN
copy_files(s,f,dirinfo);
count:= count + 1;
FindNext(DirInfo);
END;
str(count,num);
writexy(1,49,'Copied '+num+' files - <return>');
readln;
END;

PROCEDURE trailing_slash(s:string; VAR slash:boolean);
{ all important validation ;) }


VAR
c : string;

BEGIN
c := copy(s,length(s),1);
IF c = '\' THEN
slash := true
ELSE
BEGIN
writexy(1,49,'You must include the last ''\'' at part of the DIR - <return>');
readln;
END;
END;

PROCEDURE enter_directories;
{waits for user input of directories}

VAR
start,finish : string;
slash : boolean;

BEGIN
slash := false;
writexy(1,1,'Please enter the DIR of files to be copied >');
writexy(1,2,'e.g. M:\24hours\');
writexy(1,4,'Please enter the destination DIR >');
writexy(1,5,'e.g. C:\backup\');
REPEAT
gotoxy(46,1); clreol;
gotoxy(46,1); readln(start);
trailing_slash(start,slash);
UNTIL slash;
slash := false;
REPEAT
gotoxy(46,4); clreol;
gotoxy(46,4); readln(start);
trailing_slash(start,slash);
UNTIL slash;
find_files(start,finish);
END;

{ main program }

BEGIN
clrscr;
enter_directories;
END.

~*Gwar3k1*~
&quot;To the pressure, everything's just like: an illusion. I'll be losing you before long...&quot;
 
Whoops double post sorry, forgot to change program name and inlude ppl that have helped - i clicked stop but clearly that does nothing ;) And the misleading comments are cause I dug this code up from college days when I was still unsure about a lot of things... now Im only unsure of a few things ;)

If i change Archive to Anyfile, it then produces an error: file not found on the reset(s) line in the Copy_files procedure.

=S

~*Gwar3k1*~
&quot;To the pressure, everything's just like: an illusion. I'll be losing you before long...&quot;
 
Get it to print out the name of the file it can't find... (use {$I-} etc and IOresult)
 
Ive been playing with chdir and mkdir. I wrote a program where the user inputs a drive and a folder WITH OUT TRAILING '\' and it can check the directory fine but wont make the DIR =S
It may just be me being stupid but can some one please point out where Ive gone wrong. I suspect its the {$I-}mkdir line

Thanx

PROGRAM test;

USES crt,dos;

VAR
drv,fldr,dir : string;

BEGIN
clrscr;
readln(drv);
readln(fldr);
dir := drv + '\' + fldr + '\';
writeln(dir);
readln;
REPEAT
{$I-}
chdir(dir);
{$I+}
IF IORESULT <> 0 THEN
BEGIN
writeln('Did not find ',dir);
{$I-}
mkdir(dir);
{$I+}
IF IORESULT <> 0 THEN
writeln('Cannot create DIR tree')
ELSE
writeln('Created ',dir);
END
ELSE
writeln('Found ',dir);
UNTIL IORESULT = 0;
writeln(dir);
readln;
END.

~*Gwar3k1*~
&quot;To the pressure, everything's just like: an illusion. I'll be losing you before long...&quot;
 
I wouldn't swear to this, but off-hand I'd guess mkdir needs a string containing a single up-to-8-letters bit of text that it will create as a directory at the current level in the tree. So if you are at c:\user\ you could call mkdir('me') and it would create a directory c:\user\me, but leave you in c:\user. I don't think you can call it with c:\user\me, because I think then it would try to add a subdirectory called &quot;c:\user\me&quot; to where it is at the moment (c:\user) and get upset because this isn't a valid name.

Does that make any sense? Am I talking rubbish?

By the way, findfirst and findnext will find the &quot;files&quot; called . and .. that you see in the dos dir command, which may be why you were having problems before. You can get them as names, but you can't open them!
 
i understood. maybe fools understand other fools? lol nah its useful *gives it a whirl*

~*Gwar3k1*~
&quot;To the pressure, everything's just like: an illusion. I'll be losing you before long...&quot;
 
Well I did the mkdir thing and it worked like you said. I also dont need the trailing slash. =D
So then when that worked, I tried the name thing and now i have that working too =D
Now just to put em all together... thus in 5 minutes ill be back crying for help ;)

~*Gwar3k1*~
&quot;To the pressure, everything's just like: an illusion. I'll be losing you before long...&quot;
 
Told ya! The following code keeps making folder in folder. The maximum itll make is 2 e.g. C:\backup\backup =S

Yeah ^^; Also, is there any way I can determin a sub directory in the source folder then go ahead and make a new folder in the backup 1?

PROGRAM test;

USES crt,dos;

FUNCTION destination:string;

VAR
drv,fldr,dir : string;

BEGIN
clrscr;
write('Please enter the drive letter e.g. M: > ');
readln(drv);
write('Please enter the folder name > ');
readln(fldr);
dir := drv+fldr;
REPEAT
{$I-}
chdir(dir);
{$I+}
IF IORESULT <> 0 THEN
BEGIN
chdir(drv);
writeln('Did not find ',dir);
{$I-}
mkdir(fldr);
{$I+}
IF IORESULT <> 0 THEN
writeln('Cannot create DIR tree')
ELSE
writeln('Created ',dir);
END
ELSE
writeln('Found ',dir);
UNTIL IORESULT = 0;
destination := dir;
END;

FUNCTION source:string;

VAR
drv,fldr,dir : string;

BEGIN
clrscr;
write('Please enter the drive letter e.g. C: > ');
readln(drv);
write('Please enter the folder name > ');
readln(fldr);
source := drv+fldr;
END;

PROCEDURE copy_files(source,destination,name:string);

VAR
s,d : FILE;
buffer : pointer;
act_read,written : word;

BEGIN
assign(s,source+name);
assign(d,destination+name);
reset(s,1);
rewrite(d,1);
getmem(buffer,1024);
REPEAT
blockread(s,buffer^,1024,act_read);
blockwrite(d,buffer^,act_read,written);
UNTIL act_read < 1024;
close(s);
close(d);
END;

PROCEDURE file_stuff(source,destination:string);

VAR
name : string;
dirinfo : searchrec;

BEGIN
chdir(source);
findfirst('*.*', archive, dirinfo);
WHILE DOSERROR = 0 DO
BEGIN
name := dirinfo.name;
copy_files(source,destination,name);
END;
findnext(dirinfo);
END;

BEGIN
source;
destination;
file_stuff(source,destination);
END.

=P

~*Gwar3k1*~
&quot;To the pressure, everything's just like: an illusion. I'll be losing you before long...&quot;
 
I haven't gone through in detail, but I'm not sure what your repeat until loop in destination is supposed to do. Each time it goes round it will use the same &quot;dir&quot; string for chdir...
Also I'm not sure about IOresult, but if it works like doserror, it is reset every time you read it. Therefore it is not safe to read it twice after doing something IOish, which is what you do in the until statement. If you must read ioresult twice, read once into a variable, and test the variable.
But please someone correct me if I'm wrong about that.
 
Ioresult is destroyed after each readout (because of an XCHG operation somewhere) and receives a new value after the next I/O operation.

Regards,
Bert Vingerhoets
vingerhoetsbert@hotmail.com
Don't worry what people think about you. They're too busy wondering what you think about them.
 
An old chum on another forum helped me make this:

PROGRAM test4;

USES
crt,DOS;

VAR
source: File;
dest: File;
main: String; {Storing source filename}
backup: String; {Storing destination filename}
buffer: Pointer;
actually_read: Word;
written: Word;
sfolder: String; {Storing source folder}
dfolder: String; {Storing destination folder}
DirInfo: SearchRec; {Var needed for FindFirst & FindNext}

BEGIN
{$I-}
getmem(buffer,1024);
write('Source folder: ');
readln(sfolder);
chdir(sfolder); {Test folder if it is valid}
IF IOResult <> 0 THEN
BEGIN
writeln('Cannot find source directory!');
writeln('Exiting...');
END
ELSE
BEGIN
write('Destination folder: ');
readln(dfolder);
chdir(dfolder); {Test folder if it is valid}
IF IOResult <> 0 THEN
BEGIN
writeln('Cannot find destination directory!');
writeln('Exiting...');
END
ELSE
BEGIN
chdir(sfolder);
FindFirst('*.*',Archive,DirInfo); {Looks for any file(s)}
WHILE DosError = 0 DO {While found}
BEGIN
assign(source,DirInfo.Name);
writeln(dirinfo.name);
assign(dest,DirInfo.Name);
reset(source,1);
chdir(dfolder);
writeln(dfolder); {Change to destination folder}
rewrite(dest,1);
REPEAT
blockread(source,buffer^,1024,actually_read);
blockwrite(dest,buffer^,actually_read,written);
UNTIL actually_read < 1024;
close(source);
close(dest);
chdir(sfolder); {Return to source folder}
FindNext(DirInfo); {Looks for any more file(s)}
END;
writeln('Files copied :: ');
END;
END;
readln;
END.

Its great, it copies files from 1 folder to another but Im still stuck for reading folders within folders then continuing to copy. You see my over all aim is to copy the History.IE5 folder from 1 PC to another (security reasons).

We've mapped the M drive so the folder on C can be copied to M

Any ideas?

Oh and Ive never had a problem with IOResult in a repeat loop. IOR is the same as DOS error, the results are exactly the same. I prefer to use IO because it makes it easier to understand for other programmers (obviously not hehe) as its an IO interupt after all.


~*Gwar3k1*~
&quot;To the pressure, everything's just like: an illusion. I'll be losing you before long...&quot;
 
I'm not going to plough through any sourcecode posted, but that last piece looks worse than the ones before (it lacks a certain touch of procedural abstraction).

Personally I would tackle the problem as follows:
1) write a procedure that copies a single file from a given source to a given destination.
2) write a procedure that copies a whole directory from a given source to a given destination.
3) write a user interface that requests source and destination and checks them for validity.

1) use blockread and blockwrite, see help how to use them
2) (this is the interesting procedure); create a list of filenames of files to copy using findfirst and findnext; loop through the list, copying the files and removing them from the list as you go along; loop a second time through the list (which now contains only direcory names) and for each name, call this directory-copying procedure recursively.
3) trivial

Note that you can split up procedure #2 into subroutines (e.g. one that creates the list, one that copies the files and one that copies the directories). This list I refer to is any abstraction for an array or a linked list; personally I would use a linked list, because I already have such abstractions, but a large enough array (if possible) will do for a low-end application like this.

Remember that recursion is a very powerful way to solve complicated, self-similar problems like this.

Regards,
Bert Vingerhoets
vingerhoetsbert@hotmail.com
Don't worry what people think about you. They're too busy wondering what you think about them.
 
Good plan =D
Recursion however isnt my friend =( I usually end up with stack overflow errors. Do you know the syntax for $M?

I tried {$M 65...,1,64....} but it didnt work :S error in syntax (below PROGRAM}



~*Gwar3k1*~
&quot;To the pressure, everything's just like: an illusion. I'll be losing you before long...&quot;
 
If you don't like recursion, you don't have to handle problems like this using it. Instead you can make a list structure holding the names of directories whose contents need to be copied.
While copying, every time you encounter a directory instead of a file, you put the directory name on the list. Then, every time you finish copying where you are, you take another directory off the list and start copying that one.
When the list is finally empty, you are done.

If you are getting stack overflows when you use recursion, you are unlikely to solve the problem by defining a bigger stack. Yes, it might solve it for the current file-copying situation, but next week when you try to copy a directory structure that is twice as deep, you'll be back to square one. The stack overflow is probably because you have a lot of very large local variables (big arrays etc), or because your recursion isn't working properly, and is carrying on for ever.
 
Observations:

The copy routine posted is slow. 1024 bytes a chunk?! Try 65520 bytes. (Allocate it off the heap.)

The copy routine preserves neither the timestamp nor the file attributes.

I would scrap the directory changes. Use complete paths like you did at the start. My impression is that chdir won't change the current drive, either--making it fail when the drive isn't right. (It's been so long since I used a chdir that I'm not totally sure of how it operates.)

The search attributes are wrong. You aren't going to see every file that way. I think you want AnyFile - Directory - Volume. (I won't swear what the flags are called.) (Note that omitting volume won't matter so long as you are on a dos or 9x OS. On 2k, though--oops!) Archive won't catch files with the modify bit cleared and it won't catch hidden or system files.

If you are copying entire trees, recursion is the answer. Just watch how much you have in local variables. Since you seem to be working in real mode, the list approach is not always useable. I've written a program that's a much fancier version of what you are doing. The Windows version of it has been ordered to copy in excess of 100,000 files with a single command. Making a list: 100,000 files x 80 chars/filename x 2 filenames = 16mb.

Incidently, if you're looking for a solution as opposed to looking to WRITE a solution, how about xcopy?
 
List of files copied or recursive calls are bad ideas. The list of files and directories can be more, than a gigabyte (even if you forget about modern HDD, you can eventually catch network drive).
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top