Skip to content

Commit

Permalink
Merge pull request #177 from ascopes/task/pretty-invocations
Browse files Browse the repository at this point in the history
Make Shlex emit line continutations to improve command line output visually
  • Loading branch information
ascopes authored Apr 21, 2024
2 parents bad345b + 7b17632 commit 4f46e87
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,57 +16,78 @@

package io.github.ascopes.protobufmavenplugin.utils;

import java.util.function.BiConsumer;
import java.util.function.Function;

/**
* Shell/batch file quoting.
*
* <p>Losely based on Python's {@code shlex} module.
*
* <p>This is far from perfect but should work in the majority of use cases
* to ensure scripts do not interpret special characters in paths in strange and unexpected ways.
* to ensure scripts do not interpret special characters in paths in strange
* and unexpected ways.
*
* <p>Long lines will be split up with line continuations.
*
* @author Ashley Scopes
*/
public final class Shlex {

private static final int LINE_LENGTH_TARGET = 99;
private static final String INDENT = " ".repeat(4);

private Shlex() {
// Static-only class
}

public static String quoteShellArgs(Iterable<String> args) {
return quote(args, Shlex::quoteShellArg);
return quote(args, Shlex::quoteShellArg, " \\\n");
}

public static String quoteBatchArgs(Iterable<String> args) {
return quote(args, Shlex::quoteBatchArg);
return quote(args, Shlex::quoteBatchArg, " ^\r\n");
}

private static String quote(Iterable<String> args, BiConsumer<StringBuilder, String> quoter) {
private static String quote(
Iterable<String> args,
Function<String, String> quoter,
String continuation
) {
var iter = args.iterator();

if (!iter.hasNext()) {
// Probably won't ever happen.
return "";
}

var sb = new StringBuilder();
quoter.accept(sb, iter.next());
var sb = new StringBuilder(quoter.apply(iter.next()));
var lineLength = sb.length();

while (iter.hasNext()) {
sb.append(' ');
quoter.accept(sb, iter.next());
var next = quoter.apply(iter.next());

if (lineLength + next.length() >= LINE_LENGTH_TARGET) {
sb.append(continuation);
lineLength = INDENT.length();
sb.append(INDENT);
} else {
sb.append(" ");
lineLength += 1;
}

sb.append(next);
lineLength += next.length();
}

return sb.toString();
}

private static void quoteShellArg(StringBuilder sb, String arg) {
private static String quoteShellArg(String arg) {
if (isSafe(arg)) {
sb.append(arg);
return;
return arg;
}

var sb = new StringBuilder();
sb.append('\'');
for (var i = 0; i < arg.length(); ++i) {
var c = arg.charAt(i);
Expand All @@ -89,14 +110,16 @@ private static void quoteShellArg(StringBuilder sb, String arg) {
}
}
sb.append('\'');

return sb.toString();
}

private static void quoteBatchArg(StringBuilder sb, String arg) {
private static String quoteBatchArg(String arg) {
if (isSafe(arg)) {
sb.append(arg);
return;
return arg;
}

var sb = new StringBuilder();
for (var i = 0; i < arg.length(); ++i) {
var c = arg.charAt(i);
switch (c) {
Expand All @@ -121,6 +144,8 @@ private static void quoteBatchArg(StringBuilder sb, String arg) {
break;
}
}

return sb.toString();
}

private static boolean isSafe(String arg) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,16 @@ static Stream<Arguments> quoteShellArgsTestCases() {
arguments(list("'potato'"), "''\"'\"'potato'\"'\"''"),
arguments(list("foo\nbar", "baz"), "'foo'$'\\n''bar' baz"),
arguments(list("foo\rbar", "baz"), "'foo'$'\\r''bar' baz"),
arguments(list("foo\tbar", "baz"), "'foo'$'\\t''bar' baz")
arguments(list("foo\tbar", "baz"), "'foo'$'\\t''bar' baz"),
arguments(
list("a".repeat(100), "b".repeat(100), "c".repeat(100)),
String.join(
" \\\n ",
"a".repeat(100),
"b".repeat(100),
"c".repeat(100)
)
)
);
}

Expand Down Expand Up @@ -115,7 +124,16 @@ static Stream<Arguments> quoteBatchArgsTestCases() {
arguments(list("<"), "^<"),
arguments(list(">"), "^>"),
arguments(list("|"), "^|"),
arguments(list("100% complete", "0% incomplete"), "100%%^ complete 0%%^ incomplete")
arguments(list("100% complete", "0% incomplete"), "100%%^ complete 0%%^ incomplete"),
arguments(
list("a".repeat(100), "b".repeat(100), "c".repeat(100)),
String.join(
" ^\r\n ",
"a".repeat(100),
"b".repeat(100),
"c".repeat(100)
)
)
);
}

Expand Down

0 comments on commit 4f46e87

Please sign in to comment.