- NSIS Discussion
- Recursive File/Directory Operations
Archive: Recursive File/Directory Operations
RobGrant
7th December 2004 12:34 UTC
Recursive File/Directory Operations
Guys. I've been looking at this stuff for a while now, and getting all tangled up in knots, hope someone can help me through this one a bit :)
I need to move the contents of a directory (within which there are directories and files, each of which can contain directories and/or files, etc). I need to not move it en masse, but one at a time, as I need to perform an extra operation on each file that I move.
I need to retain the folder structure as well. Currently I have this, which a) doesn't work properly, and b) doesn't retain the folder structure:
Push $1
Push $8
Push $9
StrCpy $9 "$INSTDIR\scripts-temp\"
startSearch:
FindFirst $0 $1 "$9\*"
StrCpy $8 "0"
loop:
StrCmp $1 "" dirdone
IfFileExists "$9\$1\*.*" IsDir NotDir
IsDir:
StrCmp $1 "." bleh
StrCmp $1 ".." bleh
Push "$9$1\"
IntOp $8 $8 + "1"
Goto bleh
NotDir:
!insertmacro WRITE_TO_MAIN_LOG "Copying file $9$1 to $INSTDIR\scripts\" "**"
!insertmacro COPY_FILE $9$1 "$INSTDIR\scripts\"
bleh:
FindNext $0 $1
Goto loop
dirdone:
IntCmp $8 "0" done
IntOp $8 $8 - 1
Pop $9
Goto startSearch
done:
Pop $9
Pop $8
Pop $1
For completeness, here is the COPY_FILE macro:
!macro COPY_FILE OLD_FILEPATH NEW_FILEPATH
ClearErrors
Rename "${OLD_FILEPATH}" "${NEW_FILEPATH}"
IfErrors +4
!insertmacro WRITE_TO_MAIN_LOG "Wrote file ${NEW_FILEPATH}" "**"
!insertmacro WRITE_TO_FILELIST_LOG "${NEW_FILEPATH}"
Goto +2
!insertmacro WRITE_TO_MAIN_LOG "Unable to copy file '${OLD_FILEPATH}' to '${NEW_FILEPATH}'" "EE"
!macroend
Thanks guys, sorry to lumber you with a nasty one like this :)
-rob-
Afrow UK
7th December 2004 13:49 UTC
I have a function which I've been using over many installers in the past which you can use. You'll have to wait till I get home though because the function isn't on the Archive.
-Stu
RobGrant
7th December 2004 13:56 UTC
Brilliant :) Thanks.
RobGrant
7th December 2004 14:08 UTC
I'm getting there, just a quick bugfix and it copies it properly, but again without recreating the directory structure:
Push $1
Push $8
Push $9
StrCpy $9 "$INSTDIR\scripts-temp\"
StrCpy $8 "0"
startSearch:
FindFirst $0 $1 "$9\*"
loop:
StrCmp $1 "" dirdone
IfFileExists "$9\$1\*.*" IsDir NotDir
IsDir:
StrCmp $1 "." bleh
StrCmp $1 ".." bleh
Push "$9$1\"
IntOp $8 $8 + 1
Goto bleh
NotDir:
!insertmacro COPY_FILE $9$1 "$INSTDIR\scripts\$1"
bleh:
FindNext $0 $1
Goto loop
dirdone:
IntCmp $8 "0" done
IntOp $8 $8 - 1
Pop $9
Goto startSearch
done:
Pop $9
Pop $8
Pop $1
Afrow UK
7th December 2004 17:28 UTC
StrCpy $DirPath "C:\blah"
StrCpy $Location "D:\blah"
Function GetFiles
Push $R1
Push $R2
Push $R3
Push $R4
StrCpy $R3 1
Push ""
nextDir:
Pop $R4
IntOp $R3 $R3 - 1
ClearErrors
FindFirst $R1 $R2 "$DirPath$R4\*.*"
checkLoop:
IfFileExists "$DirPath$R4\$R2\*.*" +3
### Create directory
CreateDirectory "$Location$R4"
Goto checkDone
FindNext $R1 $R2
IfErrors 0 checkLoop
checkDone:
FindClose $R1
FindFirst $R1 $R2 "$DirPath$R4\*.*"
nextFile:
StrCmp $R2 "." gotoNextFile
StrCmp $R2 ".." gotoNextFile
IfFileExists "$DirPath$R4\$R2\*.*" 0 notDir
IntOp $R3 $R3 + 1
Push "$R4\$R2"
Goto gotoNextFile
notDir:
### Copy a file
CopyFiles "$DirPath$R4\$R2" "$Location$R2"
gotoNextFile:
FindNext $R1 $R2
IfErrors 0 nextFile
FindClose $R1
StrCmp $R3 0 0 nextDir
Pop $R4
Pop $R3
Pop $R2
Pop $R1
FunctionEnd
You just need to adapt it now.
-Stu
Instructor
8th December 2004 08:52 UTC
Function Locate v1.2
http://nsis.sourceforge.net/archive/...34&instances=0
Function Locate
;.....
FunctionEnd
Section
StrCpy $R0 "C:\CM" ;Directory copy from
StrCpy $R1 "C:\CM2" ;Directory copy into
StrLen $R2 $R1
!insertmacro Locate "$R0" "/L=F" "CallBack"
IfErrors 0 +2
MessageBox MB_OK 'error'
SectionEnd
Function CallBack
StrCpy $1 $R8 '' $R2
IfFileExists '$R1\$1\*.*' +2
CreateDirectory '$R1\$1'
CopyFiles /SILENT $R9 '$R1\$1'
;Your code ...
;$R9 "path\name" (source)
;$R8 "path" (source)
;$R1\$1 "path" (destination)
;$R7 "name"
Push $0
FunctionEnd
RobGrant
8th December 2004 10:02 UTC
Instructor, Afrow, cheers guys much appreciated.
One question - in the Locate Callback, I want to be able to just copy the directories first, so I can log the file copying afterwards. If I comment out this line:
CopyFiles /SILENT $R9 '$R1\$1'
...then it doesn't make all of the directory structure, but it DOES make some of it!
Any reason? CopyFiles docs don't seem to really say whether or not the function will create directories as well as files...
Cheers
-rob-
Instructor
8th December 2004 10:35 UTC
Function Locate
;.....
FunctionEnd
Section
StrCpy $R0 "C:\CM" ;Directory copy from
StrCpy $R1 "C:\CM2" ;Directory copy into
StrLen $R2 $R1
GetTempFileName $0
FileOpen $R3 $0 w
!insertmacro Locate "$R0" "/L=F" "CallBack"
FileClose $R4
IfErrors 0 +2
MessageBox MB_OK 'error'
Exec '"notepad.exe" "$0"' ;view log
SectionEnd
Function CallBack
StrCpy $1 $R8 '' $R2
StrCmp $1 '' +2
StrCpy $1 '\$1'
IfFileExists '$R1\$1\*.*' +2
CreateDirectory '$R1$1'
CopyFiles /SILENT $R9 '$R1$1'
;Your code ...
;$R9 "path\name" (source)
;$R8 "path" (source)
;$R1$1 "path" (destination)
;$R7 "name"
IfFileExists '$R1$1\$R7' 0 +3
FileWrite $R3 "-old:$R9 -new:$R1$1\$R7 -success$\r$\n"
goto +2
FileWrite $R3 "-old:$R9 -new:$R1$1\$R7 -failed$\r$\n"
Push $0
FunctionEnd
RobGrant
8th December 2004 11:43 UTC
Is that FileClose $R4 meant to be $R3? I'm not being funny but this isn't working properly either way so it could well be right...
Afrow UK
8th December 2004 13:13 UTC
Yes it should be.
-Stu
RobGrant
8th December 2004 14:12 UTC
Instructor - I tried to get that to work but it was weird, the $0 variable seemed to be very temperamental.
Not sure what the problem was, so I went back to the solution I posted.
Thanks anyway both of you, much appreciated!
-rob-
P.S. if anyone has helpful comments for other people who need to do this feel free to post...
Instructor
9th December 2004 08:07 UTC
Sorry, I had little time for answer yesterday and I have done the pair of absurd mistakes.
Copy files with log:
Section
StrCpy $R0 "C:\CM" ;Directory copy from
StrCpy $R1 "C:\CM2" ;Directory copy into
StrLen $R2 $R0
GetTempFileName $0
FileOpen $R3 $0 w
!insertmacro Locate "$R0" "/L=FDE" "CallBack"
FileClose $R3
IfErrors 0 +2
MessageBox MB_OK 'error'
Exec '"notepad.exe" "$0"' ;view log
SectionEnd
Function CallBack
StrCpy $1 $R8 '' $R2
StrCmp $R6 '' 0 +3
CreateDirectory '$R1$1\$R7'
goto end
CreateDirectory '$R1$1'
CopyFiles /SILENT $R9 '$R1$1'
IfFileExists '$R1$1\$R7' 0 +3
FileWrite $R3 "-old:$R9 -new:$R1$1\$R7 -success$\r$\n"
goto +2
FileWrite $R3 "-old:$R9 -new:$R1$1\$R7 -failed$\r$\n"
end:
Push $0
FunctionEnd
If you want recreate directory structure:
Section
StrCpy $R0 "C:\CM" ;Directory structure from
StrCpy $R1 "C:\CM2" ;Directory structure into
StrLen $R2 $R0
!insertmacro Locate "$R0" "/L=D" "CallBack"
IfErrors 0 +2
MessageBox MB_OK 'error'
SectionEnd
Function CallBack
StrCpy $1 $R9 '' $R2
StrCmp $1 '' +2
CreateDirectory '$R1$1'
Push $0
FunctionEnd
RobGrant
9th December 2004 10:53 UTC
Instructor - thanks so much that was exactly what I needed. Here's the code just for reference purposes (the 2 logging macros aren't necessary) - it assumes that the original directory is $INSTDIR\<var>-temp and the new one is $INSTDIR\<var>, and that you want to delete the temp directory afterwards. Just Push the <var> having created the zip file and the 2 directories and go for it!
Function unzipFiles
Exch $R9
CreateDirectory "$INSTDIR\$R9"
NsExec::Exec '"unzip.exe" -o $R9.zip'
StrCpy $R0 "$INSTDIR\$R9-temp" ;Directory structure from
StrCpy $R1 "$INSTDIR\$R9" ;Directory structure into
StrLen $R2 $R0
StrCpy $0 $FILE_LOG_TEMP_FILELIST
FileOpen $R3 $0 a
FileSeek $R3 "0" END
ClearErrors
!insertmacro Locate "$R0" "/L=D" "CallBack-Directory"
IfErrors 0 noscriptsdirerrors
!insertmacro WRITE_TO_MAIN_LOG "Error writing directories within $R9\ directory" "EE"
Goto writeScriptsFiles
noscriptsdirerrors:
!insertmacro WRITE_TO_MAIN_LOG "Finished writing directories within $R9\ directory" "**"
writeScriptsFiles:
ClearErrors
!insertmacro Locate "$R0" "/L=FDE" "CallBack"
FileClose $R3
IfErrors 0 uzEnd
!insertmacro WRITE_TO_MAIN_LOG "Error writing files within $R9\ directory" "EE"
uzEnd:
SetOutPath "$INSTDIR"
RMDir /r "$INSTDIR\$R9-temp"
Exch $R9
FunctionEnd
Function CallBack
StrCpy $1 $R8 '' $R2
StrCmp $R6 '' 0 +3
CreateDirectory '$R1$1\$R7'
goto end
CreateDirectory '$R1$1'
CopyFiles /SILENT $R9 '$R1$1'
IfFileExists '$R1$1\$R7' 0 c-errors
FileWrite $R3 "$R1$1\$R7$\r$\n"
!insertmacro WRITE_TO_MAIN_LOG "Wrote file $R1$1\$R7" "**"
goto end
c-errors:
!insertmacro WRITE_TO_MAIN_LOG "Failed to write $R1$1\$R7" "EE"
end:
Push $0
FunctionEnd
Function CallBack-Directory
StrCpy $1 $R9 '' $R2
StrCmp $1 '' +2
ClearErrors
CreateDirectory '$R1$1'
IfErrors c-dir-errors
FileWrite $R3 '$R1$1$\r$\n'
!insertmacro WRITE_TO_MAIN_LOG "Created directory $R1$1\" "**"
Goto end
c-dir-errors:
!insertmacro WRITE_TO_MAIN_LOG "Failed to create directory $R1$1\" "EE"
end:
Push $0
FunctionEnd
RobGrant
10th December 2004 12:32 UTC
While we're on the subject of Locate, I thought I'd have a go at listing all Windows users of a computer, by listing the names of the directories which are immediate children of the C:\Documents and Settings folder (it's a Win2k or above installer so that's fine, AFAIK).
To do that I do this:
Function
...
Push "$PROFILE"
Call GetParent
Pop $R0
!insertmacro Locate "$R0" "/L=D /G=0" "findusers"
...
FunctionEnd
Function "findusers"
MessageBox MB_OK "$R7"
FunctionEnd
Rather than giving me a nice sequence of messageboxes teling me user names, it crashes after the first one (Administrator in my case). Any ideas why?
Instructor
11th December 2004 10:43 UTC
Function "findusers"
MessageBox MB_OK "$R7"
Push $var ; If $var=StopLocate Then exit from function
FunctionEnd
atsuk
12th December 2004 16:08 UTC
Afrow UK's example seems to be quite good short and easy, but the problem is that it does not complitelly support wildcards. suppose i need to find *.txt instead of *.*
in this case it only copyes files from $Location, but not from its subdirectories. And in case you only have $Location\subdir\file.txt it does not copy anything.
does anybody have some ideas how to make it work with wildcards also?
one thing should be different:
original line:
### Copy a file
CopyFiles "$DirPath$R4\$R2" "$Location$R2"
should be replaced with
CopyFiles "$DirPath$R4\$R2" "$Location$R4\$R2"
..but still very good script(y)
Yathosho
12th December 2004 16:30 UTC
great, was just about to ask about such a function :)