From df9448c277aa15f906670ac4eccb686a4f6cd4e7 Mon Sep 17 00:00:00 2001 From: a2geek Date: Sun, 11 Oct 2026 10:03:18 -0500 Subject: [PATCH 1/6] Moved GlobGenerator into org.applecommander.util. --- .../util/filestreamer/FileStreamer.java | 4 +- .../applecommander/util/GlobGenerator.java | 37 +++++----- .../util/GlobGeneratorTest.java | 68 ------------------- .../util/GlobGeneratorTest.java | 43 ++++++++++++ 4 files changed, 61 insertions(+), 91 deletions(-) rename lib/ac-api/src/main/java/{com/webcodepro => org}/applecommander/util/GlobGenerator.java (69%) delete mode 100644 lib/ac-api/src/test/java/com/webcodepro/applecommander/util/GlobGeneratorTest.java create mode 100644 lib/ac-api/src/test/java/org/applecommander/util/GlobGeneratorTest.java diff --git a/lib/ac-api/src/main/java/com/webcodepro/applecommander/util/filestreamer/FileStreamer.java b/lib/ac-api/src/main/java/com/webcodepro/applecommander/util/filestreamer/FileStreamer.java index a66d6327..b0b0b4f8 100644 --- a/lib/ac-api/src/main/java/com/webcodepro/applecommander/util/filestreamer/FileStreamer.java +++ b/lib/ac-api/src/main/java/com/webcodepro/applecommander/util/filestreamer/FileStreamer.java @@ -20,7 +20,7 @@ package com.webcodepro.applecommander.util.filestreamer; import com.webcodepro.applecommander.storage.*; -import com.webcodepro.applecommander.util.GlobGenerator; +import org.applecommander.util.GlobGenerator; import org.applecommander.source.Source; import java.io.File; @@ -140,7 +140,7 @@ public class FileStreamer { String filePath = tuple.fileEntry.getFilename(); String fullPath = tuple.fullPath(); for (String glob : this.globs) { - String regex = GlobGenerator.globToRegex(glob, tuple.formattedDisk); + String regex = GlobGenerator.globToRegex(glob, tuple.formattedDisk.canHaveDirectories()); if (filePath.matches(regex) || fullPath.matches(regex)) return true; } return false; diff --git a/lib/ac-api/src/main/java/com/webcodepro/applecommander/util/GlobGenerator.java b/lib/ac-api/src/main/java/org/applecommander/util/GlobGenerator.java similarity index 69% rename from lib/ac-api/src/main/java/com/webcodepro/applecommander/util/GlobGenerator.java rename to lib/ac-api/src/main/java/org/applecommander/util/GlobGenerator.java index 5be592ce..1dd7e001 100644 --- a/lib/ac-api/src/main/java/com/webcodepro/applecommander/util/GlobGenerator.java +++ b/lib/ac-api/src/main/java/org/applecommander/util/GlobGenerator.java @@ -17,30 +17,25 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -package com.webcodepro.applecommander.util; - -import com.webcodepro.applecommander.storage.FormattedDisk; +package org.applecommander.util; import java.util.Set; -/** - * Utility methods to support glob-style patterns with Apple file names. - *
- * Glob patterns supported: - */ +/// Utility methods to support glob-style patterns with Apple file names. +/// +/// Glob patterns supported: +/// +/// - `*` = matches zero or more characters +/// - `?` = matches exactly one character +/// - `**` = match any number of directories +/// - `\x` = escape character public class GlobGenerator { - /** - * Transform glob pattern into a regex expression. This needs to take into effect the "valid" - * character set used in the filesystem as well as if directories are supported. - */ - public static String globToRegex(String glob, FormattedDisk disk) { + private static final Set REGEX_SPECIAL_CHARS = Set.of('?', '*', '^', '$'); + + /// Transform glob pattern into a regex expression. This needs to take into effect the "valid" + /// character set used in the filesystem as well as if directories are supported. + public static String globToRegex(String glob, boolean supportsDirectories) { StringBuilder regex = new StringBuilder(); - Set regexSpecialChars = Set.of('?', '*', '^', '$'); regex.append("(?i)^"); for (int i = 0; i < glob.length(); i++) { char c = glob.charAt(i); @@ -54,7 +49,7 @@ public class GlobGenerator { i++; } // if we have directories (currently just ProDOS) exclude the separator '/' - else if (disk.canHaveDirectories()) { + else if (supportsDirectories) { regex.append("[^/]*"); } // otherwise we want everything @@ -66,7 +61,7 @@ public class GlobGenerator { case '?' -> regex.append("."); case '\\' -> { if (nextc != 0) { - if (regexSpecialChars.contains(nextc)) regex.append("\\\\"); + if (REGEX_SPECIAL_CHARS.contains(nextc)) regex.append("\\\\"); regex.append(nextc); i++; } diff --git a/lib/ac-api/src/test/java/com/webcodepro/applecommander/util/GlobGeneratorTest.java b/lib/ac-api/src/test/java/com/webcodepro/applecommander/util/GlobGeneratorTest.java deleted file mode 100644 index a9048d22..00000000 --- a/lib/ac-api/src/test/java/com/webcodepro/applecommander/util/GlobGeneratorTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * AppleCommander - An Apple ][ image utility. - * Copyright (C) 2026 by Robert Greene and others - * robgreene at users.sourceforge.net - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -package com.webcodepro.applecommander.util; - -import com.webcodepro.applecommander.storage.DiskConstants; -import com.webcodepro.applecommander.storage.os.dos33.DosFormatDisk; -import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk; -import org.applecommander.device.BlockDevice; -import org.applecommander.device.DosOrderedTrackSectorDevice; -import org.applecommander.device.ProdosOrderedBlockDevice; -import org.applecommander.device.TrackSectorDevice; -import org.applecommander.hint.Hint; -import org.applecommander.source.DataBufferSource; -import org.applecommander.source.Source; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class GlobGeneratorTest { - public static final ProdosFormatDisk prodosFormatDisk; - static { - Source source = DataBufferSource.create(DiskConstants.APPLE_140KB_DISK, "new-disk").get(); - BlockDevice blockDevice = new ProdosOrderedBlockDevice(source, BlockDevice.STANDARD_BLOCK_SIZE); - ProdosFormatDisk[] disks = ProdosFormatDisk.create("deleteme.po", "nothere", blockDevice); - prodosFormatDisk = disks[0]; - } - public static final DosFormatDisk dosFormatDisk; - static { - Source source = DataBufferSource.create(DiskConstants.APPLE_140KB_DISK, "new-disk").get(); - TrackSectorDevice device = new DosOrderedTrackSectorDevice(source, Hint.DOS_SECTOR_ORDER); - DosFormatDisk[] disks = DosFormatDisk.create("deleteme.do", device); - dosFormatDisk = disks[0]; - } - - @Test - public void testNoDirectories() { - assertEquals("(?i)^.*$", GlobGenerator.globToRegex("*", dosFormatDisk)); - assertEquals("(?i)^.*\\.txt$", GlobGenerator.globToRegex("*.txt", dosFormatDisk)); - assertEquals("(?i)^file.\\.txt$", GlobGenerator.globToRegex("file?.txt", dosFormatDisk)); - assertEquals("(?i)^asterisks\\\\*$", GlobGenerator.globToRegex("asterisks\\*", dosFormatDisk)); - } - - @Test - public void testWithDirectories() { - assertEquals("(?i)^[^/]*$", GlobGenerator.globToRegex("*", prodosFormatDisk)); - assertEquals("(?i)^[^/]*\\.txt$", GlobGenerator.globToRegex("*.txt", prodosFormatDisk)); - assertEquals("(?i)^.*/[^/]*\\.txt$", GlobGenerator.globToRegex("**/*.txt", prodosFormatDisk)); - assertEquals("(?i)^file.\\.txt$", GlobGenerator.globToRegex("file?.txt", prodosFormatDisk)); - assertEquals("(?i)^asterisks\\\\*$", GlobGenerator.globToRegex("asterisks\\*", prodosFormatDisk)); - } -} diff --git a/lib/ac-api/src/test/java/org/applecommander/util/GlobGeneratorTest.java b/lib/ac-api/src/test/java/org/applecommander/util/GlobGeneratorTest.java new file mode 100644 index 00000000..62561270 --- /dev/null +++ b/lib/ac-api/src/test/java/org/applecommander/util/GlobGeneratorTest.java @@ -0,0 +1,43 @@ +/* + * AppleCommander - An Apple ][ image utility. + * Copyright (C) 2026 by Robert Greene and others + * robgreene at users.sourceforge.net + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.applecommander.util; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class GlobGeneratorTest { + @Test + public void testNoDirectories() { + assertEquals("(?i)^.*$", GlobGenerator.globToRegex("*", false)); + assertEquals("(?i)^.*\\.txt$", GlobGenerator.globToRegex("*.txt", false)); + assertEquals("(?i)^file.\\.txt$", GlobGenerator.globToRegex("file?.txt", false)); + assertEquals("(?i)^asterisks\\\\*$", GlobGenerator.globToRegex("asterisks\\*", false)); + } + + @Test + public void testWithDirectories() { + assertEquals("(?i)^[^/]*$", GlobGenerator.globToRegex("*", true)); + assertEquals("(?i)^[^/]*\\.txt$", GlobGenerator.globToRegex("*.txt", true)); + assertEquals("(?i)^.*/[^/]*\\.txt$", GlobGenerator.globToRegex("**/*.txt", true)); + assertEquals("(?i)^file.\\.txt$", GlobGenerator.globToRegex("file?.txt", true)); + assertEquals("(?i)^asterisks\\\\*$", GlobGenerator.globToRegex("asterisks\\*", true)); + } +} From d91fb58b8481c8cedf0f6ff76f369c1ec5602fe6 Mon Sep 17 00:00:00 2001 From: a2geek Date: Sun, 11 Oct 2026 10:09:11 -0500 Subject: [PATCH 2/6] Realized search should just use globs instead of a one-off solution. --- .../applecommander/acx/command/SearchCommand.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/app/cli-acx/src/main/java/io/github/applecommander/acx/command/SearchCommand.java b/app/cli-acx/src/main/java/io/github/applecommander/acx/command/SearchCommand.java index 7544782d..c246a235 100644 --- a/app/cli-acx/src/main/java/io/github/applecommander/acx/command/SearchCommand.java +++ b/app/cli-acx/src/main/java/io/github/applecommander/acx/command/SearchCommand.java @@ -25,6 +25,7 @@ import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk; import io.github.applecommander.acx.base.ReusableCommandOptions; import io.github.applecommander.acx.converter.IntegerTypeConverter; import org.applecommander.source.Source; +import org.applecommander.util.GlobGenerator; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; @@ -45,9 +46,9 @@ import static picocli.CommandLine.*; will (essentially) search for likely HGR candidates. Note that size is the only optional allowed to repeat due to ANDing of the criteria. - For filename comparisons, the '%%' can be used similar to the SQL 'LIKE' operator. + For filename comparisons, simple 'glob' patterns are allowed of '*' or '?'. There is no keyword and it is implicitly case insensitive. An example using this is: - --name='%%myname%%' + --name='*myname*' which will match 'MYNAME', 'myname.txt', and 'Zmyname'. Number value support allows the '$' or '0x' prefix for hex numbers. @@ -64,14 +65,8 @@ public class SearchCommand extends ReusableCommandOptions { @Option(names = "--name", description = "Filename (case insensitive).", paramLabel = "FILENAME") private void applyNameFilter(String filename) { Objects.requireNonNull(filename); - if (filename.contains("%")) { - // This is a one-off for the LIKE operation - Pattern pattern = Pattern.compile("(?i)" + filename.replace("%", ".*")); - criteria = criteria.and(fileEntry -> pattern.matcher(fileEntry.getFilename()).matches()); - } - else { - addCriteria(String.CASE_INSENSITIVE_ORDER, filename, s -> s, FileEntry::getFilename); - } + Pattern pattern = Pattern.compile(GlobGenerator.globToRegex(filename, false)); + criteria = criteria.and(fileEntry -> pattern.matcher(fileEntry.getFilename()).matches()); } @Option(names = "--size", description = "File size.", paramLabel = "SIZE") private void applySizeFilter(String[] filesizes) { From 64d67f2bf530ef3e6bce9ab861cd18ff6ec82338 Mon Sep 17 00:00:00 2001 From: a2geek Date: Sun, 11 Oct 2026 10:51:53 -0500 Subject: [PATCH 3/6] Ignoring package-info as well. --- scripts/license-check.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/license-check.sh b/scripts/license-check.sh index 63c92139..578edc70 100755 --- a/scripts/license-check.sh +++ b/scripts/license-check.sh @@ -1,3 +1,3 @@ #!/bin/bash -find . -name "*.java" -not -name "Sleak.java" -not -name module-info.java | xargs grep -L "GNU General Public License" +find . -name "*.java" -not -name "Sleak.java" -not -name module-info.java -not -name package-info.java | xargs grep -L "GNU General Public License" From 2292f1532f5a420edb09425a8c7d487d8d2f6e6e Mon Sep 17 00:00:00 2001 From: a2geek Date: Sun, 11 Oct 2026 10:52:00 -0500 Subject: [PATCH 4/6] Removing. --- lib/ac-api/src/main/java/org/applecommander/NOTES.txt | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 lib/ac-api/src/main/java/org/applecommander/NOTES.txt diff --git a/lib/ac-api/src/main/java/org/applecommander/NOTES.txt b/lib/ac-api/src/main/java/org/applecommander/NOTES.txt deleted file mode 100644 index db1db115..00000000 --- a/lib/ac-api/src/main/java/org/applecommander/NOTES.txt +++ /dev/null @@ -1,5 +0,0 @@ -Yet to do -========== -1. Need some logging capability to capture messages (instead of a single exception) across an entire "file store". -2. There are coded assertions that likely should be actual logging. -3. Review exceptions and see if there should be some structure; now defaulting to runtime exceptions. \ No newline at end of file From 4af9cdcfdb870196187cdc6cee511332c55b8f63 Mon Sep 17 00:00:00 2001 From: a2geek Date: Sun, 11 Oct 2026 10:52:16 -0500 Subject: [PATCH 5/6] Some documentation updates. --- .../applecommander/device/package-info.java | 25 +++++++++++++ .../os/gamedos/package-info.java | 35 +++++++++++++++++++ .../org/applecommander/util/DataBuffer.java | 20 ++++++----- .../applecommander/util/FileExtensions.java | 6 ++++ 4 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 lib/ac-api/src/main/java/org/applecommander/device/package-info.java create mode 100644 lib/ac-api/src/main/java/org/applecommander/os/gamedos/package-info.java diff --git a/lib/ac-api/src/main/java/org/applecommander/device/package-info.java b/lib/ac-api/src/main/java/org/applecommander/device/package-info.java new file mode 100644 index 00000000..019eef04 --- /dev/null +++ b/lib/ac-api/src/main/java/org/applecommander/device/package-info.java @@ -0,0 +1,25 @@ +/// Contains most of the device interfaces and implementation. +/// +/// Some creational patterns for `TrackSectorDevice`: +/// +/// * 140K DOS-ordered sectors: +/// ``` +/// Source source = DataBufferSource.create(DiskConstants.APPLE_140KB_DISK, "filename.do").get(); +/// TrackSectorDevice device = new DosOrderedTrackSectorDevice(source); +/// ``` +/// +/// * 140K ProDOS-ordered sectors: +/// ``` +/// Source source = DataBufferSource.create(DiskConstants.APPLE_140KB_DISK, "filename.po").get(); +/// TrackSectorDevice device = new BlockToTrackSectorAdapter(new ProdosOrderedBlockDevice(source, +/// BlockDevice.STANDARD_BLOCK_SIZE), new ProdosBlockToTrackSectorAdapterStrategy()); +/// ``` +/// +/// * 140K Nibble-ordered sectors: +/// ``` +/// Source source = DataBufferSource.create(DiskConstants.APPLE_140KB_NIBBLE_DISK, "filename.nib").get(); +/// TrackSectorDevice device = SkewedTrackSectorDevice.physicalToDosSkew( +/// TrackSectorNibbleDevice.create(new NibbleImage(source), 16)); +/// ``` +/// +package org.applecommander.device; \ No newline at end of file diff --git a/lib/ac-api/src/main/java/org/applecommander/os/gamedos/package-info.java b/lib/ac-api/src/main/java/org/applecommander/os/gamedos/package-info.java new file mode 100644 index 00000000..22297128 --- /dev/null +++ b/lib/ac-api/src/main/java/org/applecommander/os/gamedos/package-info.java @@ -0,0 +1,35 @@ +/// GameDOS, Written by usotsuki. +/// +/// This is a single-load read-only program launcher that does not require DOS. +/// It only resides on track 0, freeing up the rest of the disk for ... games. +/// There dos not appear to be any presence on the internet, but the source and +/// some example disks do exist in the Asimov archives. +/// +/// GameDOS requires a DOS-ordered 140K disk. +/// +/// There is a readme that gives information on the disk format and (expected) +/// file types: +/// +/// ```c +/// struct gd_dirent { +/// char type; /* 'A' = Applesoft, 'B' = Binary */ +/// unsigned char track1; +/// unsigned char sector1; +/// unsigned short int meta; /* load addr for Binary, +/// end of code addr for Applesoft */ +/// char filename[26]; /* space-padded */ +/// unsigned char len; /* in sectors; file must be contiguous */ +/// }; +/// ``` +/// +/// There are 16 directory entries, stored from $BE00 up (track 00 sectors E and F). +/// +/// The following types are defined but not yet implemented: +/// +/// * T - Text +/// * S - System, loads at $0800 (NOT $2000 as ProDOS!) +/// * P - Picture, loads at $2000 +/// * I - Integer Basic +/// +/// The AppleCommander implementation assumes all types are currently valid. +package org.applecommander.os.gamedos; \ No newline at end of file diff --git a/lib/ac-api/src/main/java/org/applecommander/util/DataBuffer.java b/lib/ac-api/src/main/java/org/applecommander/util/DataBuffer.java index 2191d6e4..aaee18fe 100644 --- a/lib/ac-api/src/main/java/org/applecommander/util/DataBuffer.java +++ b/lib/ac-api/src/main/java/org/applecommander/util/DataBuffer.java @@ -27,14 +27,18 @@ import java.util.Arrays; import java.util.Objects; import java.util.Optional; -/** - * A DataBuffer is a bunch of bytes with Apple II knowledge. - * This shares the technical details as well as the actual image data - * across all components in the application. There are two sets of - * accessors get/put the index the buffer and read/write that - * navigate via position. Note that read and write can overrun - * the buffer and generate an error. - */ +/// A `DataBuffer` is a bunch of bytes with Apple II knowledge. +/// This shares the technical details as well as the actual image data +/// across all components in the application. +/// +/// There are two sets of accessors: +/// +/// * `get`/`put` access the buffer via an index (explicit positioning); and +/// * `read`/`write` that navigate via position. Note that read and write +/// can overrun the buffer and generate an error. +/// +/// Creational patterns are via the `wrap` methods for existing data +/// or via the `create` method. public class DataBuffer { private static final int[] BIT_MASKS = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; diff --git a/lib/ac-api/src/main/java/org/applecommander/util/FileExtensions.java b/lib/ac-api/src/main/java/org/applecommander/util/FileExtensions.java index 2a1355f0..411aafb6 100644 --- a/lib/ac-api/src/main/java/org/applecommander/util/FileExtensions.java +++ b/lib/ac-api/src/main/java/org/applecommander/util/FileExtensions.java @@ -23,6 +23,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +/// `FileExtensions` is the one place that holds all possible file extensions. +/// It can be used in a user-interface context by using the description in +/// conjunction with the glob-pattern file extensions or just by utilizing +/// all the file extensions. +/// +/// There are no accessors, just the static variables. public class FileExtensions { public static final List FILE_FILTERS; public static final List DISK_IMAGE_EXTENSIONS; From 591d145f5319b595887c114fbe3c4a7164f4f8b2 Mon Sep 17 00:00:00 2001 From: a2geek Date: Sun, 11 Oct 2026 10:52:41 -0500 Subject: [PATCH 6/6] Set version. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index bb7734d5..e0e39c2c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,4 @@ # - Naming JAR file. # - The build will insert this into a file that is read at run time as well. # - Use SNAPSHOT for development and remove for a release. -version=15.0-SNAPSHOT +version=15.0