Skip to content

Commit 54b3d68

Browse files
committed
added support for multiple files in one archive
1 parent 4da5e69 commit 54b3d68

File tree

14 files changed

+331
-67
lines changed

14 files changed

+331
-67
lines changed

Blazer.Exe/CommandLine/BlazerCommandLineOptions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ public class BlazerCommandLineOptions
3232
[CommandLineOption("nofilename", "Do not (re)store file name")]
3333
public bool NoFileName { get; set; }
3434

35+
[CommandLineOption("nopathname", "Do not (re)store information about paths")]
36+
public bool NoPathName { get; set; }
37+
3538
[CommandLineOption("mode", "Compression mode: none, block (default), stream, streamhigh")]
3639
public string Mode { get; set; }
3740

Blazer.Exe/Program.cs

Lines changed: 147 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.IO;
33
using System.Linq;
44
using System.Reflection;
5+
using System.Text;
56

67
using Force.Blazer.Algorithms;
78
using Force.Blazer.Exe.CommandLine;
@@ -81,7 +82,7 @@ private static int ProcessCompress(CommandLineParser<BlazerCommandLineOptions> o
8182
{
8283
var opt = options.Get();
8384

84-
var fileName = options.GetNonParamOptions(0) ?? string.Empty;
85+
string[] fileNamesMultiple = new[] { options.GetNonParamOptions(0) ?? string.Empty };
8586
string archiveName = null;
8687

8788
var listFile = options.GetNonParamOptions().FirstOrDefault(x => x[0] == '@');
@@ -94,25 +95,26 @@ private static int ProcessCompress(CommandLineParser<BlazerCommandLineOptions> o
9495
return 1;
9596
}
9697

97-
archiveName = fileName;
98-
// currently we support only one file
99-
fileName = File.ReadAllLines(listFile).FirstOrDefault();
98+
archiveName = fileNamesMultiple[0];
99+
fileNamesMultiple = File.ReadAllLines(listFile).Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).ToArray();
100100
}
101101

102-
if (!opt.Stdin && !File.Exists(fileName))
102+
if (!opt.Stdin && fileNamesMultiple.Any(x => !File.Exists(x) && !Directory.Exists(x)))
103103
{
104-
if (fileName == string.Empty)
104+
if (fileNamesMultiple[0] == string.Empty)
105105
{
106106
Console.WriteLine(options.GenerateHelp());
107107
return 0;
108108
}
109109

110-
Console.Error.WriteLine("Source file " + fileName + " does not exist");
110+
if (fileNamesMultiple.Length == 1)
111+
Console.Error.WriteLine("Source file " + fileNamesMultiple[0] + " does not exist");
112+
else Console.Error.WriteLine("One or more of files to compress does not exist");
111113
return 1;
112114
}
113115

114116
if (archiveName == null)
115-
archiveName = fileName + ".blz";
117+
archiveName = fileNamesMultiple[0] + ".blz";
116118
var truncateOutFile = false;
117119
if (!opt.Stdout && File.Exists(archiveName))
118120
{
@@ -134,7 +136,18 @@ private static int ProcessCompress(CommandLineParser<BlazerCommandLineOptions> o
134136
compressionOptions.Comment = opt.Comment;
135137

136138
if (!opt.NoFileName)
137-
compressionOptions.FileInfo = BlazerFileInfo.FromFileName(fileName, false);
139+
{
140+
if (fileNamesMultiple.Length == 1) compressionOptions.FileInfo = BlazerFileInfo.FromFileName(fileNamesMultiple[0], false);
141+
else compressionOptions.MultipleFiles = true;
142+
}
143+
else
144+
{
145+
if (fileNamesMultiple.Length > 1)
146+
{
147+
Console.Error.WriteLine("No File Name option cannot be used with multiple files");
148+
return 1;
149+
}
150+
}
138151

139152
if (mode == "none")
140153
compressionOptions.SetEncoderByAlgorithm(BlazerAlgorithm.NoCompress);
@@ -165,25 +178,52 @@ private static int ProcessCompress(CommandLineParser<BlazerCommandLineOptions> o
165178

166179
var outStream = opt.Stdout ? Console.OpenStandardOutput() : new FileStream(archiveName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read);
167180

168-
Stream blazerStream = opt.DataArray ? (Stream)new MemoryStream() : new BlazerInputStream(outStream, compressionOptions);
169-
170-
using (var inFile = new StatStream(opt.Stdin ? Console.OpenStandardInput() : File.OpenRead(fileName), !opt.Stdout))
171-
using (var outFile = blazerStream)
172-
{
173-
inFile.CopyTo(outFile);
174-
}
175-
176181
if (opt.DataArray)
177182
{
178-
var sourceData = (blazerStream as MemoryStream).ToArray();
183+
byte[] sourceData;
184+
using (var sourceStream = opt.Stdin ? Console.OpenStandardInput() : File.OpenRead(fileNamesMultiple[0]))
185+
{
186+
var tmpOutStream = new MemoryStream();
187+
sourceStream.CopyTo(tmpOutStream);
188+
sourceData = tmpOutStream.ToArray();
189+
}
190+
179191
var encoder = compressionOptions.Encoder;
180192
encoder.Init(sourceData.Length);
181193
var res = encoder.Encode(sourceData, 0, sourceData.Length);
182194
outStream.Write(new[] { (byte)sourceData.Length, (byte)(sourceData.Length >> 8), (byte)(sourceData.Length >> 16), (byte)(sourceData.Length >> 24) }, 0, 4);
183195
outStream.Write(res.Buffer, res.Offset, res.Count);
184196
outStream.Close();
185197
}
186-
198+
else
199+
{
200+
using (var outFile = new BlazerInputStream(outStream, compressionOptions))
201+
{
202+
if (opt.Stdin)
203+
{
204+
using (var inFile = new StatStream(Console.OpenStandardInput(), !opt.Stdout)) inFile.CopyTo(outFile);
205+
}
206+
else
207+
{
208+
foreach (var fileName in fileNamesMultiple)
209+
{
210+
if (fileNamesMultiple.Length > 1)
211+
{
212+
var blazerFileInfo = BlazerFileInfo.FromFileName(fileName, !opt.NoPathName);
213+
outFile.WriteFileInfo(blazerFileInfo);
214+
if ((blazerFileInfo.Attributes & FileAttributes.Directory) != 0)
215+
continue;
216+
}
217+
218+
using (var inFile = new StatStream(File.OpenRead(fileName), !opt.Stdout))
219+
{
220+
inFile.CopyTo(outFile);
221+
}
222+
}
223+
}
224+
}
225+
}
226+
187227
return 0;
188228
}
189229

@@ -193,7 +233,7 @@ private static int ProcessDecompress(CommandLineParser<BlazerCommandLineOptions>
193233

194234
var archiveName = options.GetNonParamOptions(0) ?? string.Empty;
195235

196-
string customOutFileName = null;
236+
string[] customOutFileNames = null;
197237

198238
var listFile = options.GetNonParamOptions().FirstOrDefault(x => x[0] == '@');
199239
if (listFile != null)
@@ -205,8 +245,7 @@ private static int ProcessDecompress(CommandLineParser<BlazerCommandLineOptions>
205245
return 1;
206246
}
207247

208-
// currently we support only one file
209-
customOutFileName = File.ReadAllLines(listFile).FirstOrDefault();
248+
customOutFileNames = File.ReadAllLines(listFile).Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).ToArray();
210249
}
211250

212251
if (!opt.Stdin && !File.Exists(archiveName))
@@ -226,7 +265,50 @@ private static int ProcessDecompress(CommandLineParser<BlazerCommandLineOptions>
226265
var decOptions = new BlazerDecompressionOptions(opt.Password) { EncyptFull = opt.EncryptFull };
227266

228267
BlazerOutputStream outBlazerStream = null;
229-
Stream outStream = null;
268+
Stream outStream;
269+
BlazerFileInfo prevFile = null;
270+
Stream[] outFile = { null };
271+
decOptions.FileInfoCallback = fInfo =>
272+
{
273+
if (prevFile != null)
274+
{
275+
outFile[0].Flush();
276+
outFile[0].Close();
277+
outFile[0] = null;
278+
prevFile.ApplyToFile();
279+
prevFile = null;
280+
}
281+
282+
var fInfoFileName = fInfo.FileName;
283+
if (customOutFileNames != null && !customOutFileNames.Contains(fInfoFileName))
284+
return;
285+
286+
prevFile = fInfo;
287+
if ((fInfo.Attributes & FileAttributes.Directory) != 0)
288+
{
289+
if (!opt.NoPathName && !Directory.Exists(fInfoFileName)) Directory.CreateDirectory(fInfoFileName);
290+
}
291+
else
292+
{
293+
if (opt.NoPathName) fInfoFileName = Path.GetFileName(fInfoFileName);
294+
295+
if (File.Exists(fInfoFileName))
296+
{
297+
if (!opt.Force)
298+
{
299+
Console.WriteLine("Target " + fInfoFileName + " already exists. Overwrite? (Y)es (N)o");
300+
var readLine = Console.ReadLine();
301+
if (readLine.Trim().ToLowerInvariant().IndexOf('y') != 0) return;
302+
}
303+
304+
new FileStream(fInfoFileName, FileMode.Truncate, FileAccess.Write).Close();
305+
}
306+
307+
var directoryName = Path.GetDirectoryName(fInfoFileName);
308+
if (!string.IsNullOrEmpty(directoryName)) Directory.CreateDirectory(directoryName);
309+
outFile[0] = new StatStream(new FileStream(fInfoFileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read), true);
310+
}
311+
};
230312

231313
if (opt.DataArray)
232314
{
@@ -251,38 +333,38 @@ private static int ProcessDecompress(CommandLineParser<BlazerCommandLineOptions>
251333
}
252334

253335
var fileName = archiveName;
254-
var applyFileInfoAfterComplete = false;
255336
if (archiveName.EndsWith(".blz")) fileName = fileName.Substring(0, fileName.Length - 4);
256337
else fileName += ".unpacked";
257338

258339
if (outBlazerStream != null && outBlazerStream.FileInfo != null && !opt.NoFileName)
259340
{
260341
fileName = outBlazerStream.FileInfo.FileName;
261-
applyFileInfoAfterComplete = true;
262342
}
263343

264-
if (customOutFileName != null) fileName = customOutFileName;
344+
if (opt.Stdout) outFile[0] = Console.OpenStandardOutput();
345+
// we haven't received an file info from callback
346+
if (outFile[0] == null)
347+
outFile[0] = new StatStream(new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read), true);
265348

266-
if (!opt.Stdout && File.Exists(fileName))
349+
using (var inFile = outStream)
267350
{
268-
if (!opt.Force)
351+
var buf = new byte[81920];
352+
int cnt = 1;
353+
while (cnt > 0)
269354
{
270-
Console.WriteLine("Target " + fileName + " already exists. Overwrite? (Y)es (N)o");
271-
var readLine = Console.ReadLine();
272-
if (readLine.Trim().ToLowerInvariant().IndexOf('y') != 0) return 1;
355+
cnt = inFile.Read(buf, 0, buf.Length);
356+
if (outFile[0] != null)
357+
outFile[0].Write(buf, 0, cnt);
273358
}
274-
275-
new FileStream(fileName, FileMode.Truncate, FileAccess.Write).Close();
276359
}
277360

278-
using (var inFile = outStream)
279-
using (var outFile = opt.Stdout ? Console.OpenStandardOutput() : new StatStream(new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read), true))
361+
if (prevFile != null)
280362
{
281-
inFile.CopyTo(outFile);
363+
outFile[0].Flush();
364+
outFile[0].Close();
365+
prevFile.ApplyToFile();
282366
}
283367

284-
if (applyFileInfoAfterComplete) outBlazerStream.FileInfo.ApplyToFile();
285-
286368
return 0;
287369
}
288370

@@ -367,6 +449,26 @@ private static int ProcessList(CommandLineParser<BlazerCommandLineOptions> optio
367449
Stream inStreamSource = opt.Stdin ? Console.OpenStandardInput() : File.OpenRead(archiveName);
368450

369451
var decOptions = new BlazerDecompressionOptions(opt.Password) { EncyptFull = opt.EncryptFull };
452+
StringBuilder header = new StringBuilder();
453+
header.AppendLine(" Date Time Attr Size Name");
454+
header.AppendLine("------------------- ----- ------------ ------------------------");
455+
bool[] headerWritten = { false };
456+
decOptions.FileInfoCallback = fi =>
457+
{
458+
var s = string.Format(
459+
"{0:yyyy-MM-dd} {1:HH:mm:ss} {2}{3}{4}{5}{6} {7,12} {8}",
460+
fi.CreationTimeUtc.ToLocalTime(),
461+
fi.CreationTimeUtc.ToLocalTime(),
462+
(fi.Attributes & FileAttributes.Directory) != 0 ? "D" : ".",
463+
(fi.Attributes & FileAttributes.ReadOnly) != 0 ? "R" : ".",
464+
(fi.Attributes & FileAttributes.Hidden) != 0 ? "H" : ".",
465+
(fi.Attributes & FileAttributes.System) != 0 ? "S" : ".",
466+
(fi.Attributes & FileAttributes.Archive) != 0 ? "A" : ".",
467+
fi.Length,
468+
fi.FileName);
469+
if (!headerWritten[0]) header.AppendLine(s);
470+
else Console.WriteLine(s);
471+
};
370472

371473
if (opt.DataArray)
372474
{
@@ -386,29 +488,21 @@ private static int ProcessList(CommandLineParser<BlazerCommandLineOptions> optio
386488
}
387489

388490
Console.WriteLine();
491+
Console.Write(header);
492+
headerWritten[0] = true;
389493
var fi = outStream.FileInfo;
390-
if (fi == null)
494+
if (fi == null && !outStream.HaveMultipleFiles)
391495
{
392496
Console.WriteLine("Missing file information in archive.");
393497
return 1;
394498
}
395499
else
396500
{
397-
Console.WriteLine(" Date Time Attr Size Name");
398-
Console.WriteLine("------------------- ----- ------------ ------------------------");
399-
Console.WriteLine(
400-
"{0:yyyy-MM-dd} {1:HH:mm:ss} {2}{3}{4}{5}{6} {7,12} {8}",
401-
fi.CreationTimeUtc.ToLocalTime(),
402-
fi.CreationTimeUtc.ToLocalTime(),
403-
(fi.Attributes & FileAttributes.Directory) != 0 ? "D" : ".",
404-
(fi.Attributes & FileAttributes.ReadOnly) != 0 ? "R" : ".",
405-
(fi.Attributes & FileAttributes.Hidden) != 0 ? "H" : ".",
406-
(fi.Attributes & FileAttributes.System) != 0 ? "S" : ".",
407-
(fi.Attributes & FileAttributes.Archive) != 0 ? "A" : ".",
408-
fi.Length,
409-
fi.FileName);
501+
// while we read, we will write info
502+
if (outStream.HaveMultipleFiles)
503+
outStream.CopyTo(new NullStream());
410504
Console.WriteLine("------------------- ----- ------------ ------------------------");
411-
// now, we have only one file, so there are no sense to write total
505+
// todo: think about total line
412506
// 4854 1018 2 files, 1 folders
413507
}
414508
}

Blazer.Exe/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@
3232
// by using the '*' as shown below:
3333
// [assembly: AssemblyVersion("1.0.*")]
3434
[assembly: AssemblyVersion("0.9.0.0")]
35-
[assembly: AssemblyFileVersion("0.9.0.8")]
35+
[assembly: AssemblyFileVersion("0.9.1.9")]

Blazer.Net.Tests/Blazer.Net.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
<Compile Include="EncryptionTests.cs" />
5353
<Compile Include="IntegrityHelper.cs" />
5454
<Compile Include="IntegrityTests.cs" />
55+
<Compile Include="MultipleFilesTests.cs" />
5556
<Compile Include="OptionsTests.cs" />
5657
<Compile Include="Properties\AssemblyInfo.cs" />
5758
<Compile Include="PatternedCompressionTests.cs" />

0 commit comments

Comments
 (0)