3

Can I use Inno Setup PreProcessor to get the files and size of the source path and its subdirs?,

I am doing a Batch Compiler and I need the size to auto-set in [Setup] DiskSpanning True or False

Only can get the size of source,

Somebody can help me?


#define FindHandle
#define FindResult
#define Mask "*.*"
#define size 0
#define allfiles ""


#sub ProcessFoundFile
 #define FileName FindGetFileName(FindHandle)
  #if direxists(Filename) && Filename!="." && Filename!=".."
   #Define Public Mask AddBackSlash(Filename)+"*.*"  
  #else
   #Define Mask "*.*"
  #endif
 #define public allfiles allfiles + " - " +Filename
 #define public size size + FileSize(FileName)  
#endsub


#for {FindHandle = FindResult = FindFirst(Mask, faDirectory); FindResult; FindResult = FindNext(FindHandle)} ProcessFoundFile
#if FindHandle
;  FindClose(FindHandle)
#endif


#IF Size > 2100000000
#DEFINE Span "True"
#ELSE
#DEFINE Span "False"
#ENDIF

[Setup]
DiskSpanning={#Span}
InternalCompressLevel=ultra
DiskClusterSize=2048
CompressionThreads=2
Compression=lzma2/ultra64
SolidCompression=no
Newbee
  • 1,379
  • 2
  • 16
  • 36
  • Are you aware that the [`FileSize`](http://www.jrsoftware.org/ispphelp/index.php?topic=filesize) function returns correct values only for files up to 2GB ? Anyway, good question! – TLama Jan 02 '14 at 13:30
  • sorry, I forgot to put the opposite condition –  Jan 02 '14 at 14:10
  • I'm afraid that it is either impossible or very hard task because there seems to be no way to write recursion (since there is no way to have subroutine in a subroutine) and for the non-recursive way you cannot write a loop (since there is no `while` loop available and `for` loop evaluates its condition when the loop is entered). But maybe I'm wrong... – TLama Jan 02 '14 at 15:49
  • Also I thought of other subroutines, always gives the size of the home directory –  Jan 02 '14 at 16:14
  • The nature of the `FindFirst` and `FindNext` is to list files (and/or directories) from the given path. It doesn't include items from subdirectories. You'd need to list all subdirectories and for each of them call a routine like yours. And I can't figure out how to write it in a general way to list subdirectories of all levels due to limitations in syntax. For a given level of depth it is quite easy though... – TLama Jan 02 '14 at 16:22
  • I thought `DiskSpanning=yes` only actually split it over the slice size? IOW, if it's smaller than the slice size then it will still be one file. – Deanna Aug 18 '14 at 11:39

2 Answers2

4

Ok, so this is a bit old but I want to share my solution though because I came across the same problem and found some kind of a solution:

#define FindHandle
#define FindResult 
#dim InnerMask[65536]
#define InnerMask[0] ""
#define size 0     

#sub ProcessFoundFile
    #define InnerFileName FindGetFileName(FindHandle)
    #define fileName InnerMask[InnerMaskWorkPosition] + InnerFileName
    #if InnerFileName!="." && InnerFileName!=".."
        #if direxists(FileName)
            #define Public InnerMask[InnerMaskPosition] FileName+"\"
            #define Public InnerMaskPosition InnerMaskPosition + 1
        #else
            #define Public size size + FileSize(FileName)
        #endif
    #endif 
#endsub

#sub ProcessInnerMaskPosition 
    #for {FindHandle = FindResult = FindFirst(InnerMask[InnerMaskWorkPosition]+"*", faAnyFile); FindResult; FindResult = FindNext(FindHandle)} ProcessFoundFile
    #if FindHandle
        #expr FindClose(FindHandle)
    #endif
#endsub

#sub RunSizeScan
    #define Public InnerMaskPosition 1
    #define Public InnerMaskWorkPosition 0
    #expr size=0
    #for {InnerMaskWorkPosition = 0; InnerMaskWorkPosition < InnerMaskPosition; InnerMaskWorkPosition++} ProcessInnerMaskPosition
    #undef Public InnerMaskPosition
    #undef Public InnerMaskWorkPosition
#endsub

#expr InnerMask[0]="some-dir-name-you-want-the-size-of\"
#expr RunSizeScan

#if size > 2100000000
    #define Span "True"
#else
    #define Span "False"
#endif

What it does is to scan the given directories in the array "InnerMask" for everything which isn't "." or "..". Files are added to the already calculated size and directories are added to the array "InnerMask". This Process will end once there are no more subdirectories to evaluate.

Note: As the limitation of the array is set to 65536, you should not have more than this amount of folders nested in you scanned directory. Otherwise you could try to reuse the first already processed array slots or work with multiple arrays.

René Martin
  • 518
  • 4
  • 16
  • Very nice indeed! However, it returns incorrect results. I have tried to make a few subdirectories with randomly placed 40B files and the result differed by 8 bytes (size calculated by preprocessor was smaller). With another folder, the difference was 152 bytes which is, as before, a number divisible by 8. Which is weird, and points to a possible problem with `FileSize` function. [unfortunately I don't have time to dig into this deeeper] – TLama Aug 15 '14 at 13:31
  • Ah, got the problem. The FileSize of a folder equals -1 and if a directory is evaluated which is named '.' or '..' it jumps in the else branch. Gonna edit my solution. – René Martin Aug 16 '14 at 15:12
  • Oh, so that 8 was just a coincidence. Thanks for the update! [already upvoted] – TLama Aug 16 '14 at 16:58
2

Here's the recursive version, broken into several macros for readability:

#define private CalcDirSize(str path, int size = 0) \
    CalcFileSize(path, FindFirst(AddBackSlash(path) + '*.*', faAnyFile), size)

#define private CalcFileSize(str path, int handle, int size) \
    handle ? CalcFileSizeFilterPath(path, handle, size) : size

#define private CalcFileSizeFilterPath(str path, int handle, int size) \
    FindGetFilename(handle) == '.' || FindGetFilename(handle) == '..' ? \
        GoToNextFile(path, handle, size) : \
        CalcFileSizeTestIfDir(path, handle, size, AddBackSlash(path) + FindGetFilename(handle))

#define private GoToNextFile(str path, int handle, int size) \
    FindNext(handle) ? CalcFileSizeFilterPath(path, handle, size) : size

#define private CalcFileSizeTestIfDir(str path, int handle, int size, str filename) \
    DirExists(filename) ? CalcDirSize(filename) + GoToNextFile(path, handle, size) : \
        GoToNextFile(path, handle, size + FileSize(filename))

Doesn't support too many files (poor ISPP will run out of memory) or large sizes (>2gb), but it should work well for smaller setups.