#!/usr/bin/env --split-string=${JDK_HOME}/bin/java @Makeshift/java_arguments @Makeshift/java_javac_arguments \c [SS]
// This command runs directly from the present source file, it needs no compiling.
import Breccia.Web.imager.CleaningOptions;
import java.io.Console;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import static Breccia.Web.imager.Project.sourceSibling;
import static java.lang.System.err;
import static java.lang.System.out;
import static java.lang.System.exit;
import static java.nio.file.Files.exists;
import static java.nio.file.Files.isDirectory;
import static java.nio.file.Files.isRegularFile;
import static java.nio.file.FileVisitResult.CONTINUE;
/** A shell command to clean a Web image.
*
* @see
* The `web-image-clean` command
*/
public final class WebImageCleanCommand extends SimpleFileVisitor { // [AFN]
/** @param argsN Nominal arguments, aka options.
* @param argsP Positional arguments.
*/
private WebImageCleanCommand( final List argsN, final List argsP ) {
opt.initialize( argsN );
toAsk = !opt.toForce();
final int n = argsP.size();
if( n != 1 ) {
err.println( commandName + ": Expecting 1 argument, found " + n );
exitWithUsage( err, 1 ); }
boundaryPath = Path.of(argsP.get(0)).toAbsolutePath().normalize(); }
/** Takes a `web-image-clean` command from the shell and executes it.
*/
public static void main( final String[] arguments ) throws IOException {
final var argsN = new ArrayList();
final var argsP = new ArrayList();
for( final String arg: arguments ) {
exitOnDemand( arg );
(arg.charAt(0) == '-' ? argsN : argsP).add( arg ); }
if( !new WebImageCleanCommand(argsN,argsP).run() ) exit( 1 ); }
// ━━━ F i l e V i s i t o r ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
public @Override FileVisitResult visitFile( final Path file, BasicFileAttributes _a )
throws IOException {
clean( file );
return CONTINUE; }
//// P r i v a t e ////////////////////////////////////////////////////////////////////////////////////
private final Path boundaryPath;
/** @param f The path of a potential image file.
*/
private void clean( final Path f ) throws IOException {
if( !f.getFileName().toString().endsWith( ".brec.xht" )) return; // Not an image file.
if( isRegularFile( sourceSibling( f ))) return; // Not an orphan.
boolean toDelete = true;
if( toAsk ) for( ;; ) {
out.print( "Delete orphan image file " + f + "? (n, y, !) " );
final String answer = console.readLine(); /* The user must press the `Enter` key.
Unfortunately Java makes no provision for single character (e.g. ‘raw’) input. */
if( "n".equals( answer )) toDelete = false;
else if( "!".equals( answer )) toAsk = false;
else if( !"y".equals( answer )) continue;
break; }
if( toDelete ) {
opt.out(toAsk? 2:1).println( "Deleting orphan image file " + f );
Files.delete( f );
++count; }}
private static final String commandName = "web-image-clean";
private static final Console console = System.console(); /* It yields ‘the unique Console object’,
a single object, as source code confirms (JDK 18), which allows for the `static` modifier here. */
private int count; // Of orphan image files deleted.
private static void exitOnDemand( final String arg ) {
if( arg.equals("-?") || arg.equals("-help") ) exitWithUsage( out, 0 ); }
private static void exitWithUsage( final PrintStream sP, final int status ) {
sP.println( "Usage: " + commandName + " [] " );
sP.println( " " + commandName + " -help | -?" );
sP.println( "Options, one or more of:" );
sP.println( " -force" );
sP.println( " -speak" );
sP.println( " -stifle" );
sP.println( " -verbosity=0|1|2" );
exit( status ); }
private final CleaningOptions opt = new CleaningOptions( commandName );
/** @return True on success, false on failure.
*/
private boolean run() throws IOException {
if( isDirectory( boundaryPath )) Files.walkFileTree( boundaryPath, WebImageCleanCommand.this );
else {
if( !exists( boundaryPath )) {
err.println( commandName + ": No such file or directory: " + boundaryPath );
return false; }
clean( boundaryPath ); }
if( count > 0 ) {
final PrintStream sP = opt.out( toAsk? 2:1 );
sP.print( count );
sP.print( " orphan image file" );
if( count > 1 ) sP.print( 's' );
sP.println( " deleted" ); }
return true; }
private boolean toAsk; } // To ask before deleting, that is.
// NOTES
// ─────
// AFN Atypical file naming is allowed here, as explained in `./breccia-web-image`.
//
// SS · Long-form option `--split-string` is for Emacs, as explained in `./breccia-web-image`.
// Copyright © 2022, 2024 Michael Allan. Licence MIT.