Skip to content

Commit

Permalink
Make filename extensions on exports optional
Browse files Browse the repository at this point in the history
Exported files are converted to a new format, such as PNG or CSV,
so they usually need a new filename extension.  This is not the case
when exporting source code files, which already have a meaningful
filename extension (.c, .asm, etc.), so adding ".txt" is unnecessary
and counter-productive.

This adds the "--[no-]add-ext" flag to the CLI, and the "Add Filename
Ext to Exports" checkbox to the GUI.  Both default to true.

This affects the export operation and the export-mode drag & drop
operation.  "Host" exports (e.g. exporting a GIF) are unaffected,
since those did not have an extension added previously.
  • Loading branch information
fadden committed Jan 3, 2025
1 parent 9884f1c commit a50fdd5
Show file tree
Hide file tree
Showing 14 changed files with 95 additions and 32 deletions.
9 changes: 7 additions & 2 deletions AppCommon/ClipFileSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class ClipFileSet {
// Innards.
//

private bool mAddExportExt;
private bool mStripPaths;
private bool mEnableMacZip;
private ExtractFileWorker.PreserveMode mPreserveMode;
Expand Down Expand Up @@ -99,6 +100,7 @@ public class ClipFileSet {
/// <param name="entries">List of entries to add.</param>
/// <param name="baseDir">For disk images, the effective root of the filesystem.</param>
/// <param name="preserveMode">Preservation mode to use for extracted files.</param>
/// <param name="addExportExt">If true, add a file extension to exported files.</param>
/// <param name="useRawData">If true, use "raw" mode on appropriate disks.</param>
/// <param name="stripPaths">If true, strip paths while adding to set.</param>
/// <param name="enableMacZip">If true, handle Mac ZIP entries.</param>
Expand All @@ -107,13 +109,14 @@ public class ClipFileSet {
/// <param name="appHook">Application hook reference.</param>
public ClipFileSet(object archiveOrFileSystem, List<IFileEntry> entries,
IFileEntry baseDir, ExtractFileWorker.PreserveMode preserveMode,
bool useRawData, bool stripPaths, bool enableMacZip,
bool addExportExt, bool useRawData, bool stripPaths, bool enableMacZip,
ConvConfig.FileConvSpec? exportSpec,
Dictionary<string, ConvConfig.FileConvSpec>? defaultSpecs, AppHook appHook) {

// Stuff the simple items into the object so we don't have to pass them around.
mBaseDir = baseDir;
mPreserveMode = preserveMode;
mAddExportExt = addExportExt;
mUseRawData = useRawData;
mStripPaths = stripPaths;
mEnableMacZip = enableMacZip;
Expand Down Expand Up @@ -591,7 +594,9 @@ private void CreateForExport(object archiveOrFileSystem, IFileEntry entry,
}
Debug.Assert(!string.IsNullOrEmpty(attrs.FileNameOnly));
string ext;
if (expectedType == typeof(SimpleText)) {
if (!mAddExportExt) {
ext = string.Empty;
} else if (expectedType == typeof(SimpleText)) {
ext = TXTGenerator.FILE_EXT;
} else if (expectedType == typeof(FancyText)) {
ext = RTFGenerator.FILE_EXT;
Expand Down
34 changes: 22 additions & 12 deletions AppCommon/ExtractFileWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ public enum PreserveMode {
public delegate CallbackFacts.Results CallbackFunc(CallbackFacts what);

/// <summary>
/// If set, files added to a ZIP archive that have resource forks or HFS types will
/// be stored as AppleDouble with a "__MACOSX" prefix.
/// If set, add a filename extension to exported files.
/// </summary>
public bool AddExportExt { get; set; }

/// <summary>
/// If set, handle AppleDouble files stored with the __MACOSX prefix.
/// </summary>
public bool IsMacZipEnabled { get; set; }

Expand All @@ -54,13 +58,12 @@ public enum PreserveMode {
public PreserveMode Preserve { get; set; }

/// <summary>
/// If set, use raw mode when adding files to filesystems (notably DOS 3.x).
/// If set, use raw mode when extracting files from filesystems (notably DOS 3.x).
/// </summary>
public bool RawMode { get; set; }

/// <summary>
/// If set, strip pathnames off of files before adding them. For a filesystem, all
/// files will be added to the target directory.
/// If set, strip pathnames off of files before extracting them.
/// </summary>
public bool StripPaths { get; set; }

Expand All @@ -83,16 +86,19 @@ public enum PreserveMode {
/// </summary>
/// <param name="func">Callback function, for progress messages and file overwrite
/// queries.</param>
/// <param name="addExportExt">True if filename extensions should be added to exported
/// files.</param>
/// <param name="macZip">True if MacZip is enabled.</param>
/// <param name="preserve">Attribute preservation mode to use during extract.</param>
/// <param name="rawMode">True if data forks should be opened in "raw" mode.</param>
/// <param name="stripPaths">True if pathnames should be stripped down to filenames.</param>
/// <param name="defaultSpecs">Default export specs, for "best" mode.</param>
/// <param name="appHook">Application hook reference.</param>
public ExtractFileWorker(CallbackFunc func, bool macZip, PreserveMode preserve,
bool rawMode, bool stripPaths,
public ExtractFileWorker(CallbackFunc func, bool addExportExt, bool macZip,
PreserveMode preserve, bool rawMode, bool stripPaths,
Dictionary<string, ConvConfig.FileConvSpec>? defaultSpecs, AppHook appHook) {
mFunc = func;
AddExportExt = addExportExt;
IsMacZipEnabled = macZip;
Preserve = preserve;
RawMode = rawMode;
Expand Down Expand Up @@ -685,7 +691,8 @@ private bool DoExport(string extractPath, FileAttribs attrs, Stream? dataStream,
((ErrorText)convOutput).Text.ToString());
return false;
} else if (convOutput is FancyText && !((FancyText)convOutput).PreferSimple) {
string rtfPath = extractPath + RTFGenerator.FILE_EXT;
string rtfPath = extractPath;
if (AddExportExt) { rtfPath += RTFGenerator.FILE_EXT; }
if (!PrepareOutputFile(rtfPath, dataProgEnt, out bool doCancel)) {
return !doCancel;
}
Expand All @@ -695,7 +702,8 @@ private bool DoExport(string extractPath, FileAttribs attrs, Stream? dataStream,
RTFGenerator.Generate((FancyText)convOutput, outStream);
}
} else if (convOutput is SimpleText) {
string txtPath = extractPath + TXTGenerator.FILE_EXT;
string txtPath = extractPath;
if (AddExportExt) { txtPath += TXTGenerator.FILE_EXT; }
if (!PrepareOutputFile(txtPath, dataProgEnt, out bool doCancel)) {
return !doCancel;
}
Expand All @@ -705,7 +713,8 @@ private bool DoExport(string extractPath, FileAttribs attrs, Stream? dataStream,
TXTGenerator.Generate((SimpleText)convOutput, outStream);
}
} else if (convOutput is CellGrid) {
string csvPath = extractPath + CSVGenerator.FILE_EXT;
string csvPath = extractPath;
if (AddExportExt) { csvPath += CSVGenerator.FILE_EXT; }
if (!PrepareOutputFile(csvPath, dataProgEnt, out bool doCancel)) {
return !doCancel;
}
Expand All @@ -715,7 +724,8 @@ private bool DoExport(string extractPath, FileAttribs attrs, Stream? dataStream,
CSVGenerator.Generate((CellGrid)convOutput, outStream);
}
} else if (convOutput is IBitmap) {
string pngPath = extractPath + PNGGenerator.FILE_EXT;
string pngPath = extractPath;
if (AddExportExt) { pngPath += PNGGenerator.FILE_EXT; }
if (!PrepareOutputFile(pngPath, dataProgEnt, out bool doCancel)) {
return !doCancel;
}
Expand All @@ -725,7 +735,7 @@ private bool DoExport(string extractPath, FileAttribs attrs, Stream? dataStream,
PNGGenerator.Generate((IBitmap)convOutput, outStream);
}
} else if (convOutput is HostConv) {
// Copy these to the output unmodified
// Copy these to the output unmodified. No file extension added.
if (dataCopy == null) {
ReportConvFailure("weird: HostConv but no data fork");
return false;
Expand Down
6 changes: 3 additions & 3 deletions cp2/Extract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ private static bool HandleExtractExport(string extArchive, string[] paths,
ExtractFileWorker.CallbackFunc cbFunc = delegate (CallbackFacts what) {
return Misc.HandleCallback(what, opStr, parms);
};
ExtractFileWorker worker = new ExtractFileWorker(cbFunc, macZip: parms.MacZip,
preserve: parms.Preserve, rawMode: parms.Raw, stripPaths: parms.StripPaths,
ParamsBag.sExportSpecs, parms.AppHook);
ExtractFileWorker worker = new ExtractFileWorker(cbFunc, addExportExt: parms.AddExt,
macZip: parms.MacZip, preserve: parms.Preserve, rawMode: parms.Raw,
stripPaths: parms.StripPaths, ParamsBag.sExportSpecs, parms.AppHook);
if (parms.ShowNotes && exportSpec != null) {
worker.NotesOut = Console.Out;
}
Expand Down
6 changes: 6 additions & 0 deletions cp2/ParamsBag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public enum ScanDepth {
/// </summary>
private Dictionary<string, Option> mOptions = InitOptions();

private static Option OptAddExt = new Option("add-ext", OptionType.Bool, true);
private static Option OptClassic = new Option("classic", OptionType.Bool, false);
private static Option OptCompress = new Option("compress", OptionType.Bool, true);
private static Option OptConvertDOSText = new Option("convert-dos-text",
Expand Down Expand Up @@ -117,6 +118,7 @@ public enum ScanDepth {
/// </summary>
private static Dictionary<string, Option> InitOptions() {
Dictionary<string, Option> opts = new Dictionary<string, Option> {
{ OptAddExt.Name, OptAddExt },
{ OptClassic.Name, OptClassic },
{ OptCompress.Name, OptCompress },
{ OptConvertDOSText.Name, OptConvertDOSText },
Expand Down Expand Up @@ -168,6 +170,10 @@ private void SetOption(string name, object value) {
mOptions[name].CurValue = value;
}

public bool AddExt {
get => GetBoolOption(OptAddExt.Name);
set => SetOption(OptAddExt.Name, value);
}
public bool Classic {
get => GetBoolOption(OptClassic.Name);
set => SetOption(OptClassic.Name, value);
Expand Down
15 changes: 8 additions & 7 deletions cp2/Tests/TestClip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static void RunTest(ParamsBag parms) {
parms.StripPaths = false;
parms.Raw = false;
parms.MacZip = true;
parms.AddExt = true;

using NuFX nufxSrc = NuFX.CreateArchive(parms.AppHook);
CreateTestArchive(nufxSrc, sSampleFiles, parms);
Expand All @@ -86,7 +87,7 @@ public static void RunTest(ParamsBag parms) {
private static void TestForeignExtract(IArchive arc, ParamsBag parms) {
List<IFileEntry> entries = GatherArchiveEntries(arc, false, parms);
ClipFileSet clipSet = new ClipFileSet(arc, entries, IFileEntry.NO_ENTRY,
parms.Preserve, parms.Raw, parms.StripPaths, parms.MacZip,
parms.Preserve, parms.AddExt, parms.Raw, parms.StripPaths, parms.MacZip,
null, null, parms.AppHook);

CheckForeign(clipSet, sForeignExtractNAPS, parms);
Expand All @@ -96,7 +97,7 @@ private static void TestBasicXfer(IFileSystem srcFs, ParamsBag parms) {
List<IFileEntry> entries = GatherDiskEntries(srcFs, srcFs.GetVolDirEntry());

ClipFileSet clipSet = new ClipFileSet(srcFs, entries, IFileEntry.NO_ENTRY,
parms.Preserve, parms.Raw, parms.StripPaths, parms.MacZip,
parms.Preserve, parms.AddExt, parms.Raw, parms.StripPaths, parms.MacZip,
null, null, parms.AppHook);
List<MemoryStream?> xferStreams = GenerateXferStreams(srcFs, clipSet);
Debug.Assert(clipSet.XferEntries.Count == xferStreams.Count);
Expand All @@ -116,7 +117,7 @@ private static void TestRerootXfer(IFileSystem srcFs, ParamsBag parms) {
List<IFileEntry> entries = GatherDiskEntries(srcFs, srcRootDir);

ClipFileSet clipSet = new ClipFileSet(srcFs, entries, srcBaseDir,
parms.Preserve, parms.Raw, parms.StripPaths, parms.MacZip,
parms.Preserve, parms.AddExt, parms.Raw, parms.StripPaths, parms.MacZip,
null, null, parms.AppHook);
List<MemoryStream?> xferStreams = GenerateXferStreams(srcFs, clipSet);
Debug.Assert(clipSet.XferEntries.Count == xferStreams.Count);
Expand All @@ -135,7 +136,7 @@ private static void TestZipToProDOS(Zip zip, ParamsBag parms) {
List<IFileEntry> entries = GatherArchiveEntries(zip, false, parms);

ClipFileSet clipSet = new ClipFileSet(zip, entries, IFileEntry.NO_ENTRY,
parms.Preserve, parms.Raw, parms.StripPaths, parms.MacZip,
parms.Preserve, parms.AddExt, parms.Raw, parms.StripPaths, parms.MacZip,
null, null, parms.AppHook);
List<MemoryStream?> xferStreams = GenerateXferStreams(zip, clipSet);
Debug.Assert(clipSet.XferEntries.Count == xferStreams.Count);
Expand All @@ -151,7 +152,7 @@ private static void TestProDOSToZip(IFileSystem srcFs, ParamsBag parms) {
List<IFileEntry> entries = GatherDiskEntries(srcFs, srcFs.GetVolDirEntry());

ClipFileSet clipSet = new ClipFileSet(srcFs, entries, IFileEntry.NO_ENTRY,
parms.Preserve, parms.Raw, parms.StripPaths, parms.MacZip,
parms.Preserve, parms.AddExt, parms.Raw, parms.StripPaths, parms.MacZip,
null, null, parms.AppHook);
List<MemoryStream?> xferStreams = GenerateXferStreams(srcFs, clipSet);
Debug.Assert(clipSet.XferEntries.Count == xferStreams.Count);
Expand Down Expand Up @@ -205,7 +206,7 @@ private static void TestDOSConversion(ParamsBag parms) {
//
List<IFileEntry> dosSrcEntries = new List<IFileEntry>() { dosText };
ClipFileSet dosClipSet = new ClipFileSet(dosFs, dosSrcEntries, IFileEntry.NO_ENTRY,
parms.Preserve, parms.Raw, parms.StripPaths, parms.MacZip,
parms.Preserve, parms.AddExt, parms.Raw, parms.StripPaths, parms.MacZip,
null, null, parms.AppHook);

ClipPasteWorker.ClipStreamGenerator dosGen = delegate (ClipFileEntry clipEntry) {
Expand All @@ -224,7 +225,7 @@ private static void TestDOSConversion(ParamsBag parms) {
//
List<IFileEntry> proSrcEntries = new List<IFileEntry>() { proText };
ClipFileSet proClipSet = new ClipFileSet(proFs, proSrcEntries, IFileEntry.NO_ENTRY,
parms.Preserve, parms.Raw, parms.StripPaths, parms.MacZip,
parms.Preserve, parms.AddExt, parms.Raw, parms.StripPaths, parms.MacZip,
null, null, parms.AppHook);

ClipPasteWorker.ClipStreamGenerator proGen = delegate (ClipFileEntry clipEntry) {
Expand Down
13 changes: 13 additions & 0 deletions cp2/Tests/TestExport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public static void RunTest(ParamsBag parms) {
Controller.PrepareTestTmp(parms);

parms.EraseImportExportSpecs();
parms.AddExt = true;

//
// Scenarios to test:
Expand Down Expand Up @@ -124,6 +125,18 @@ private static void TestBASConfig(ParamsBag parms) {
if (!File.Exists("HRBARS.rtf")) {
throw new Exception("Failed to extract BAS as .rtf");
}

// Test the --no-add-ext option.
parms.AddExt = false;
if (!Extract.HandleExport("xp",
new string[] { arcPath, convSpec + "false", testFile }, parms)) {
throw new Exception("export spec=" + convSpec + " " + testFile +
" --no-add-ext failed");
}
if (!File.Exists("HRBARS")) {
throw new Exception("Failed to extract BAS without extension");
}
parms.AddExt = true;
} finally {
Environment.CurrentDirectory = oldCurrentDir;
}
Expand Down
7 changes: 4 additions & 3 deletions cp2_wpf/Actions/ExtractProgress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ internal class ExtractProgress : WorkProgress.IWorker {
private AppHook mAppHook;

public ExtractFileWorker.PreserveMode Preserve { get; set; }
public bool AddExportExt { get; set; }
public bool EnableMacOSZip { get; set; }
public bool StripPaths { get; set; }
public bool RawMode { get; set; }
Expand Down Expand Up @@ -69,9 +70,9 @@ public object DoWork(BackgroundWorker bkWorker) {
ExtractFileWorker.CallbackFunc cbFunc = delegate (CallbackFacts what) {
return ProgressUtil.HandleCallback(what, "extract", choices, null, bkWorker);
};
ExtractFileWorker extWorker = new ExtractFileWorker(cbFunc, macZip: EnableMacOSZip,
preserve: Preserve, rawMode: RawMode, stripPaths: StripPaths, DefaultSpecs,
mAppHook);
ExtractFileWorker extWorker = new ExtractFileWorker(cbFunc,
addExportExt: AddExportExt, macZip: EnableMacOSZip, preserve: Preserve,
rawMode: RawMode, stripPaths: StripPaths, DefaultSpecs, mAppHook);

// Switch to output directory.
Environment.CurrentDirectory = mOutputDir;
Expand Down
3 changes: 2 additions & 1 deletion cp2_wpf/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ internal static class AppSettings {
public const string AUTO_OPEN_DEPTH = "auto-open-depth";
public const string AUDIO_DECODE_ALG = "audio-decode-alg";

// Add/extract/import/export.
// Add/extract/import/export. NOTE: update PublishSideOptions().
public const string DDCP_ADD_EXTRACT = "ddcp-add-extract";

public const string ADD_RECURSE_ENABLED = "add-recurse-enabled";
Expand All @@ -69,6 +69,7 @@ internal static class AppSettings {
public const string ADD_PRESERVE_AS = "add-preserve-as";
public const string ADD_PRESERVE_NAPS = "add-preserve-naps";

public const string EXT_ADD_EXPORT_EXT = "ext-add-export-ext";
public const string EXT_RAW_ENABLED = "ext-raw-enabled";
public const string EXT_STRIP_PATHS_ENABLED = "ext-strip-paths-enabled";
public const string EXT_PRESERVE_MODE = "ext-preserve-mode";
Expand Down
6 changes: 4 additions & 2 deletions cp2_wpf/MainController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1915,6 +1915,7 @@ internal VirtualFileDataObject GenerateVFDO() {
ExtractFileWorker.PreserveMode preserve =
settings.GetEnum(AppSettings.EXT_PRESERVE_MODE,
ExtractFileWorker.PreserveMode.None);
bool addExportExt = settings.GetBool(AppSettings.EXT_ADD_EXPORT_EXT, true);
bool rawMode = settings.GetBool(AppSettings.EXT_RAW_ENABLED, false);
bool doStrip = settings.GetBool(AppSettings.EXT_STRIP_PATHS_ENABLED, false);
bool doMacZip = settings.GetBool(AppSettings.MAC_ZIP_ENABLED, true);
Expand All @@ -1932,8 +1933,8 @@ internal VirtualFileDataObject GenerateVFDO() {
// may have to open file contents.
Mouse.OverrideCursor = Cursors.Wait;
clipSet = new ClipFileSet(CurrentWorkObject!, entries, baseDir,
preserve, useRawData: rawMode, stripPaths: doStrip, enableMacZip: doMacZip,
exportSpec, defaultSpecs, AppHook);
preserve, addExportExt: addExportExt, useRawData: rawMode, stripPaths: doStrip,
enableMacZip: doMacZip, exportSpec, defaultSpecs, AppHook);
} finally {
Mouse.OverrideCursor = null;
}
Expand Down Expand Up @@ -2050,6 +2051,7 @@ private void HandleExtractExport(ConvConfig.FileConvSpec? exportSpec) {
selected, outputDir, exportSpec, AppHook) {
Preserve = settings.GetEnum(AppSettings.EXT_PRESERVE_MODE,
ExtractFileWorker.PreserveMode.None),
AddExportExt = settings.GetBool(AppSettings.EXT_ADD_EXPORT_EXT, true),
EnableMacOSZip = settings.GetBool(AppSettings.MAC_ZIP_ENABLED, true),
StripPaths = settings.GetBool(AppSettings.EXT_STRIP_PATHS_ENABLED, false),
RawMode = settings.GetBool(AppSettings.EXT_RAW_ENABLED, false),
Expand Down
Loading

0 comments on commit a50fdd5

Please sign in to comment.