#!/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.