فهرست منبع

Merge branch 'external-excludes'

Martijn Laan 4 ماه پیش
والد
کامیت
67b5068824

+ 1 - 1
Examples/CodeDownloadFiles.iss

@@ -35,7 +35,7 @@ Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme;
 ; These files will be downloaded
 Source: "{tmp}\innosetup-latest.exe"; DestDir: "{app}"; Flags: external ignoreversion issigverify
 Source: "{tmp}\ISCrypt.dll"; DestDir: "{app}"; Flags: external ignoreversion
-Source: "{tmp}\MyProg-ExtraReadmes\*.txt"; DestDir: "{app}"; Flags: external recursesubdirs ignoreversion issigverify  
+Source: "{tmp}\MyProg-ExtraReadmes\*"; Excludes: "*.issig"; DestDir: "{app}"; Flags: external recursesubdirs ignoreversion issigverify  
 
 [Icons]
 Name: "{group}\My Program"; Filename: "{app}\MyProg.exe"

+ 1 - 1
ISHelp/isetup.xml

@@ -1580,7 +1580,7 @@ DestDir: "{app}\subdir"
 </param>
 
 <param name="Excludes">
-<p>Specifies a list of patterns to exclude, separated by commas. This parameter cannot be combined with the <tt>external</tt> flag.</p>
+<p>Specifies a list of patterns to exclude, separated by commas.</p>
 <p>Patterns may include wildcard characters ("*" and "?"). Note that unlike the <tt>Source</tt> parameter, a simple Unix-style pattern matching routine is used for <tt>Excludes</tt>. Dots in the pattern are always significant, thus "*.*" will not exclude a file with no extension (instead, use just "*"). Also, question marks always match exactly one character, thus "?????" will not exclude files with names less than five characters long.</p>
 <p>If a pattern starts with a backslash ("\") it is matched against the start of a path name, otherwise it is matched against the end of a path name. Thus "\foo" will only exclude a file named "foo" at the base of the tree. On the other hand, "foo" will exclude any file named "foo" anywhere in the tree.</p>
 <p>The patterns may include backslashes. "foo\bar" will exclude both "foo\bar" and "subdir\foo\bar". "\foo\bar" will only exclude "foo\bar".</p>

+ 0 - 2
Projects/Src/Compiler.Messages.pas

@@ -287,8 +287,6 @@ const
     'the "Source" parameter contains wildcards';
   SCompilerFilesStrongAssemblyNameMustBeSpecified = 'Parameter "StrongAssemblyName" must be specified if ' +
     'the flag "gacinstall" is used';
-  SCompilerFilesCantHaveExternalExclude = 'Parameter "Excludes" may not be used when ' +
-    'the "external" flag is used';
   SCompilerFilesCantHaveNonExternalExternalSize = 'Parameter "ExternalSize" may only be used when ' +
     'the "external" flag is used';
   SCompilerFilesExcludeTooLong = 'Parameter "Excludes" contains a pattern that is too long';

+ 7 - 80
Projects/Src/Compiler.SetupCompiler.pas

@@ -4762,80 +4762,6 @@ type
     end;
   end;
 
-  function IsExcluded(Text: String): Boolean;
-
-    function CountBackslashes(S: PChar): Integer;
-    begin
-      Result := 0;
-      while True do begin
-        S := PathStrScan(S, '\');
-        if S = nil then
-          Break;
-        Inc(Result);
-        Inc(S);
-      end;
-    end;
-
-  var
-    I, J, TB, PB: Integer;
-    T, P, TStart, TEnd: PChar;
-    MatchFront: Boolean;
-  begin
-    if AExcludes.Count > 0 then begin
-      Text := PathLowercase(Text);
-      UniqueString(Text);
-      T := PChar(Text);
-      TB := CountBackslashes(T);
-
-      for I := 0 to AExcludes.Count-1 do begin
-        P := PChar(AExcludes[I]);
-
-        { Leading backslash in an exclude pattern means 'match at the front
-          instead of the end' }
-        MatchFront := False;
-        if P^ = '\' then begin
-          MatchFront := True;
-          Inc(P);
-        end;
-
-        PB := CountBackslashes(P);
-        { The text must contain at least as many backslashes as the pattern
-          for a match to be possible }
-        if TB >= PB then begin
-          TStart := T;
-          if not MatchFront then begin
-            { If matching at the end, advance TStart so that TStart and P point
-              to the same number of components }
-            for J := 1 to TB - PB do
-              TStart := PathStrScan(TStart, '\') + 1;
-            TEnd := nil;
-          end
-          else begin
-            { If matching at the front, clip T to the same number of
-              components as P }
-            TEnd := T;
-            for J := 1 to PB do
-              TEnd := PathStrScan(TEnd, '\') + 1;
-            TEnd := PathStrScan(TEnd, '\');
-            if Assigned(TEnd) then
-              TEnd^ := #0;
-          end;
-
-          if WildcardMatch(TStart, P) then begin
-            Result := True;
-            Exit;
-          end;
-
-          { Put back any backslash that was temporarily null'ed }
-          if Assigned(TEnd) then
-            TEnd^ := '\';
-        end;
-      end;
-    end;
-
-    Result := False;
-  end;
-
   procedure AddToFileList(const FileList: TList; const Filename: String;
     const SizeLo, SizeHi: LongWord);
   var
@@ -4888,7 +4814,7 @@ type
           else
             FileName := SearchWildcard;  { use the case specified in the script }
 
-          if IsExcluded(SearchSubDir + FileName) then
+          if IsExcluded(SearchSubDir + FileName, AExcludes) then
             Continue;
 
           AddToFileList(FileList, SearchSubDir + FileName, FindData.nFileSizeLow,
@@ -4911,7 +4837,7 @@ type
                (FindData.dwFileAttributes and FILE_ATTRIBUTE_HIDDEN = 0) and
                (StrComp(FindData.cFileName, '.') <> 0) and
                (StrComp(FindData.cFileName, '..') <> 0) and
-               not IsExcluded(SearchSubDir + FindData.cFileName) then
+               not IsExcluded(SearchSubDir + FindData.cFileName, AExcludes) then
               BuildFileList(SearchBaseDir, SearchSubDir + FindData.cFileName + '\',
                 SearchWildcard, FileList, DirList, CreateAllSubDirs);
           until not FindNextFile(H, FindData);
@@ -5221,6 +5147,8 @@ begin
 
   AExcludes := TStringList.Create();
   try
+    AExcludes.StrictDelimiter := True;
+    AExcludes.Delimiter := ',';
     PrevFileEntry := nil;
     NewFileEntry := AllocMem(SizeOf(TSetupFileEntry));
     try
@@ -5369,7 +5297,7 @@ begin
                AStrongAssemblyName := Values[paStrongAssemblyName].Data;
 
                { Excludes }
-               ProcessWildcardsParameter(Values[paExcludes].Data, AExcludes, SCompilerFilesExcludeTooLong);
+               ProcessWildcardsParameter(Values[paExcludes].Data, AExcludes, SCompilerFilesExcludeTooLong); { for an external file the Excludes field is set below }
 
                { ExternalSize }
                if Values[paExternalSize].Found then begin
@@ -5458,11 +5386,10 @@ begin
           AbortCompileFmt(SCompilerParamFlagMissing, ['nocompression', 'dontverifychecksum']);
 
         if ExternalFile then begin
-          if (AExcludes.Count > 0) then
-            AbortCompile(SCompilerFilesCantHaveExternalExclude)
-          else if Sign <> fsNoSetting then
+          if Sign <> fsNoSetting then
             AbortCompileFmt(SCompilerParamErrorBadCombo2,
               [ParamCommonFlags, 'external', SignFlags[Sign]]);
+          Excludes := AExcludes.DelimitedText;
         end;
 
         if (ISSigKeyEntries.Count = 0) and (foISSigVerify in Options) then

+ 16 - 5
Projects/Src/Setup.Install.pas

@@ -1787,7 +1787,7 @@ var
 
     function RecurseExternalCopyFiles(const DisableFsRedir: Boolean;
       const SearchBaseDir, SearchSubDir, SearchWildcard: String; const SourceIsWildcard: Boolean;
-      const CurFile: PSetupFileEntry; const FileLocationFilenames: TStringList;
+      const Excludes: TStringList; const CurFile: PSetupFileEntry; const FileLocationFilenames: TStringList;
       var ExpectedBytesLeft: Integer64; var ConfirmOverwriteOverwriteAll, PromptIfOlderOverwriteAll: TOverwriteAll;
       var WarnedPerUserFonts: Boolean): Boolean;
     var
@@ -1815,6 +1815,9 @@ var
               else
                 FileName := SearchWildcard;  { use the case specified in the script }
 
+              if IsExcluded(SearchSubDir + FileName, Excludes) then
+                Continue;
+
               Result := True;
               SourceFile := SearchBaseDir + SearchSubDir + FileName;
               DestName := ExpandConst(CurFile^.DestName);
@@ -1848,7 +1851,7 @@ var
               if IsRecurseableDirectory(FindData) then
                 Result := RecurseExternalCopyFiles(DisableFsRedir, SearchBaseDir,
                   SearchSubDir + FindData.cFileName + '\', SearchWildcard,
-                  SourceIsWildcard, CurFile, FileLocationFileNames,
+                  SourceIsWildcard, Excludes, CurFile, FileLocationFileNames,
                   ExpectedBytesLeft, ConfirmOverwriteOverwriteAll, PromptIfOlderOverwriteAll,
                   WarnedPerUserFonts) or Result;
             until not FindNextFile(H, FindData);
@@ -1882,7 +1885,6 @@ var
     end;
 
   var
-    FileLocationFilenames: TStringList;
     I: Integer;
     CurFileNumber: Integer;
     CurFile: PSetupFileEntry;
@@ -1897,10 +1899,17 @@ var
     PromptIfOlderOverwriteAll := oaUnknown;
     WarnedPerUserFonts := False;
 
-    FileLocationFilenames := TStringList.Create;
+    var FileLocationFilenames: TStringList := nil;
+    var Excludes: TStringList := nil;
     try
+      FileLocationFilenames := TStringList.Create;
       for I := 0 to Entries[seFileLocation].Count-1 do
         FileLocationFilenames.Add('');
+
+      Excludes := TStringList.Create;
+      Excludes.StrictDelimiter := True;
+      Excludes.Delimiter := ',';
+
       for CurFileNumber := 0 to Entries[seFile].Count-1 do begin
         CurFile := PSetupFileEntry(Entries[seFile][CurFileNumber]);
         if ((CurFile^.FileType <> ftUninstExe) or Uninstallable) and
@@ -1932,13 +1941,14 @@ var
             end
             else
               SourceWildcard := ExpandConst(CurFile^.SourceFilename);
+            Excludes.DelimitedText := CurFile^.Excludes;
             ProgressBefore := CurProgress;
             repeat
               SetProgress(ProgressBefore);
               ExpectedBytesLeft := CurFile^.ExternalSize;
               FoundFiles := RecurseExternalCopyFiles(DisableFsRedir,
                 PathExtractPath(SourceWildcard), '', PathExtractName(SourceWildcard),
-                IsWildcard(SourceWildcard), CurFile, FileLocationFileNames,
+                IsWildcard(SourceWildcard), Excludes, CurFile, FileLocationFileNames,
                 ExpectedBytesLeft, ConfirmOverwriteOverwriteAll, PromptIfOlderOverwriteAll,
                 WarnedPerUserFonts);
             until FoundFiles or
@@ -1956,6 +1966,7 @@ var
         end;
       end;
     finally
+      Excludes.Free;
       FileLocationFilenames.Free;
     end;
   end;

+ 33 - 21
Projects/Src/Setup.MainFunc.pas

@@ -1744,7 +1744,7 @@ function EnumFiles(const EnumFilesProc: TEnumFilesProc;
 
   function RecurseExternalFiles(const DisableFsRedir: Boolean;
     const SearchBaseDir, SearchSubDir, SearchWildcard: String;
-    const SourceIsWildcard: Boolean; const CurFile: PSetupFileEntry): Boolean;
+    const SourceIsWildcard: Boolean; const Excludes: TStringList; const CurFile: PSetupFileEntry): Boolean;
   var
     SearchFullPath, DestName: String;
     H: THandle;
@@ -1764,6 +1764,9 @@ function EnumFiles(const EnumFilesProc: TEnumFilesProc;
               if FindData.dwFileAttributes and FILE_ATTRIBUTE_HIDDEN <> 0 then
                 Continue;
 
+            if IsExcluded(SearchSubDir + FindData.cFileName, Excludes) then
+              Continue;
+
             DestName := ExpandConst(CurFile^.DestName);
             if not(foCustomDestName in CurFile^.Options) then
               DestName := DestName + SearchSubDir + FindData.cFileName
@@ -1788,7 +1791,7 @@ function EnumFiles(const EnumFilesProc: TEnumFilesProc;
             if IsRecurseableDirectory(FindData) then
               if not RecurseExternalFiles(DisableFsRedir, SearchBaseDir,
                  SearchSubDir + FindData.cFileName + '\', SearchWildcard,
-                 SourceIsWildcard, CurFile) then begin
+                 SourceIsWildcard, Excludes, CurFile) then begin
                 Result := False;
                 Exit;
               end;
@@ -1809,28 +1812,37 @@ begin
   Result := True;
 
   { [Files] }
-  for I := 0 to Entries[seFile].Count-1 do begin
-    CurFile := PSetupFileEntry(Entries[seFile][I]);
-    if (CurFile^.FileType = ftUserFile) and
-       ShouldProcessFileEntry(WizardComponents, WizardTasks, CurFile, False) then begin
-      DisableFsRedir := ShouldDisableFsRedirForFileEntry(CurFile);
-      if CurFile^.LocationEntry <> -1 then begin
-        { Non-external file }
-        if not EnumFilesProc(DisableFsRedir, ExpandConst(CurFile^.DestName), Param) then begin
-          Result := False;
-          Exit;
-        end;
-      end
-      else begin
-        { External file }
-        SourceWildcard := ExpandConst(CurFile^.SourceFilename);
-        if not RecurseExternalFiles(DisableFsRedir, PathExtractPath(SourceWildcard), '',
-           PathExtractName(SourceWildcard), IsWildcard(SourceWildcard), CurFile) then begin
-          Result := False;
-          Exit;
+  const Excludes = TStringList.Create;
+  try
+    Excludes.StrictDelimiter := True;
+    Excludes.Delimiter := ',';
+
+    for I := 0 to Entries[seFile].Count-1 do begin
+      CurFile := PSetupFileEntry(Entries[seFile][I]);
+      if (CurFile^.FileType = ftUserFile) and
+         ShouldProcessFileEntry(WizardComponents, WizardTasks, CurFile, False) then begin
+        DisableFsRedir := ShouldDisableFsRedirForFileEntry(CurFile);
+        if CurFile^.LocationEntry <> -1 then begin
+          { Non-external file }
+          if not EnumFilesProc(DisableFsRedir, ExpandConst(CurFile^.DestName), Param) then begin
+            Result := False;
+            Exit;
+          end;
+        end
+        else begin
+          { External file }
+          SourceWildcard := ExpandConst(CurFile^.SourceFilename);
+          Excludes.DelimitedText := CurFile^.Excludes;
+          if not RecurseExternalFiles(DisableFsRedir, PathExtractPath(SourceWildcard), '',
+             PathExtractName(SourceWildcard), IsWildcard(SourceWildcard), Excludes, CurFile) then begin
+            Result := False;
+            Exit;
+          end;
         end;
       end;
     end;
+  finally
+    Excludes.Free;
   end;
 
   { [InstallDelete] }

+ 73 - 1
Projects/Src/Shared.SetupTypes.pas

@@ -58,11 +58,12 @@ procedure GenerateEncryptionKey(const Password: String; const Salt: TSetupKDFSal
 procedure SetISSigAllowedKey(var ISSigAllowedKeys: AnsiString; const KeyIndex: Integer);
 function GetISSigAllowedKeys([Ref] const ISSigAvailableKeys: TArrayOfECDSAKey;
   const ISSigAllowedKeys: AnsiString): TArrayOfECDSAKey;
+function IsExcluded(Text: String; const AExcludes: TStringList): Boolean;
 
 implementation
 
 uses
-  PBKDF2, Shared.CommonFunc;
+  PBKDF2, PathFunc, Shared.CommonFunc;
 
 function QuoteStringIfNeeded(const S: String): String;
 { Used internally by StringsToCommaString. Adds quotes around the string if
@@ -350,4 +351,75 @@ begin
     Result := ISSigAvailableKeys;
 end;
 
+function IsExcluded(Text: String; const AExcludes: TStringList): Boolean;
+
+  function CountBackslashes(S: PChar): Integer;
+  begin
+    Result := 0;
+    while True do begin
+      S := PathStrScan(S, '\');
+      if S = nil then
+        Break;
+      Inc(Result);
+      Inc(S);
+    end;
+  end;
+
+begin
+  if AExcludes.Count > 0 then begin
+    Text := PathLowercase(Text);
+    UniqueString(Text);
+    const T = PChar(Text);
+    const TB = CountBackslashes(T);
+
+    for var AExclude in AExcludes do begin
+      var P := PChar(AExclude);
+
+      { Leading backslash in an exclude pattern means 'match at the front
+        instead of the end' }
+      var MatchFront := False;
+      if P^ = '\' then begin
+        MatchFront := True;
+        Inc(P);
+      end;
+
+      const PB = CountBackslashes(P);
+      { The text must contain at least as many backslashes as the pattern
+        for a match to be possible }
+      if TB >= PB then begin
+        var TStart := T;
+        var TEnd: PChar;
+        if not MatchFront then begin
+          { If matching at the end, advance TStart so that TStart and P point
+            to the same number of components }
+          for var I := 1 to TB - PB do
+            TStart := PathStrScan(TStart, '\') + 1;
+          TEnd := nil;
+        end
+        else begin
+          { If matching at the front, clip T to the same number of
+            components as P }
+          TEnd := T;
+          for var J := 1 to PB do
+            TEnd := PathStrScan(TEnd, '\') + 1;
+          TEnd := PathStrScan(TEnd, '\');
+          if Assigned(TEnd) then
+            TEnd^ := #0;
+        end;
+
+        if WildcardMatch(TStart, P) then begin
+          Result := True;
+          Exit;
+        end;
+
+        { Put back any backslash that was temporarily null'ed }
+        if Assigned(TEnd) then
+          TEnd^ := '\';
+      end;
+    end;
+  end;
+
+  Result := False;
+end;
+
 end.

+ 3 - 3
Projects/Src/Shared.Struct.pas

@@ -226,13 +226,13 @@ type
     PublicX, PublicY: String;
   end;
 const
-  SetupFileEntryStrings = 10;
+  SetupFileEntryStrings = 11;
   SetupFileEntryAnsiStrings = 1;
 type
   PSetupFileEntry = ^TSetupFileEntry;
   TSetupFileEntry = packed record
-    SourceFilename, DestName, InstallFontName, StrongAssemblyName: String;
-    Components, Tasks, Languages, Check, AfterInstall, BeforeInstall: String;
+    SourceFilename, DestName, InstallFontName, StrongAssemblyName, Components,
+    Tasks, Languages, Check, AfterInstall, BeforeInstall, Excludes: String;
     ISSigAllowedKeys: AnsiString;
     MinVersion, OnlyBelowVersion: TSetupVersionData;
     LocationEntry: Integer;

+ 7 - 2
whatsnew.htm

@@ -64,8 +64,12 @@ Name: bosskey; KeyFile: "boss.ispubkey"</pre>
     <li>Note: The <tt>issigverify</tt> flag cannot be combined with the <tt>sign</tt> or <tt>signonce</tt> flags. Use <tt>signcheck</tt> instead.</li>
     <li>Example section:
       <pre>[Files]
-Source: "MyProg.exe"; DestDir: "{app}"; Flags: issigverify; ISSigAllowedKeys: exesigner bosskey
-Source: "MyProg.chm"; DestDir: "{app}"; Flags: issigverify; ISSigAllowedKeys: docsigner bosskey</pre>
+Source: "MyProg.exe"; DestDir: "{app}"; Flags: issigverify; \
+  ISSigAllowedKeys: exesigner bosskey
+Source: "MyProg.chm"; DestDir: "{app}"; Flags: issigverify; \
+  ISSigAllowedKeys: docsigner bosskey
+Source: "{src}\Extra\*.chm"; DestDir: "{app}"; Flags: issigverify external; \
+  ISSigAllowedKeys: docsigner bosskey; Excludes: "*.issig"</pre>
     </li>
   </ul>
   </li>
@@ -94,6 +98,7 @@ issigtool --key-file="MyKey.ispublickey" verify "MyProg.dll"</pre>
 <span class="head2">Other changes</span>
 <ul>
   <li>Compiler IDE: the <i>Find in Files</i> result list will now update its line numbers when you add or delete lines.</li>
+  <li><tt>[Files]</tt> section parameter <tt>Excludes</tt> can now be combined with the <tt>external</tt> flag.</li>
   <li>Example script <i>CodeDownloadFiles.iss</i> now also demonstrates how to use the <tt>CreateExtractionPage</tt> support function to extract a 7-Zip archive. See the <a href="https://jrsoftware.org/ishelp/index.php?topic=isxfunc_extract7ziparchive">Extract7ZipArchive help topic</a> for information about this function's limitations.</li>
 </ul>