stripSafeHeredocSubstitutions function
Strip safe $(cat <<'DELIM'...DELIM) heredoc substitutions from a command. Returns the command with heredocs stripped, or null if none found.
Implementation
String? stripSafeHeredocSubstitutions(String command) {
if (!_heredocInSubstitution.hasMatch(command)) return null;
final heredocPattern = RegExp(
r"""\$\(cat[ \t]*<<(-?)[ \t]*(?:'+([A-Za-z_]\w*)'+|\\([A-Za-z_]\w*))""",
);
var result = command;
var found = false;
final ranges = <({int start, int end})>[];
for (final match in heredocPattern.allMatches(command)) {
if (match.start > 0 && command[match.start - 1] == r'\') continue;
final delimiter = match.group(2) ?? match.group(3);
if (delimiter == null) continue;
final isDash = match.group(1) == '-';
final operatorEnd = match.start + match.group(0)!.length;
final afterOperator = command.substring(operatorEnd);
final openLineEnd = afterOperator.indexOf('\n');
if (openLineEnd == -1) continue;
if (!RegExp(
r'^[ \t]*$',
).hasMatch(afterOperator.substring(0, openLineEnd))) {
continue;
}
final bodyStart = operatorEnd + openLineEnd + 1;
final bodyLines = command.substring(bodyStart).split('\n');
for (var i = 0; i < bodyLines.length; i++) {
final rawLine = bodyLines[i];
final line = isDash ? rawLine.replaceFirst(RegExp(r'^\t*'), '') : rawLine;
if (line.startsWith(delimiter)) {
final after = line.substring(delimiter.length);
var closePos = -1;
if (RegExp(r'^[ \t]*\)').hasMatch(after)) {
final lineStart =
bodyStart +
bodyLines.sublist(0, i).join('\n').length +
(i > 0 ? 1 : 0);
closePos = command.indexOf(')', lineStart);
} else if (after.isEmpty) {
final nextLine = i + 1 < bodyLines.length ? bodyLines[i + 1] : null;
if (nextLine != null && RegExp(r'^[ \t]*\)').hasMatch(nextLine)) {
final nextLineStart =
bodyStart + bodyLines.sublist(0, i + 1).join('\n').length + 1;
closePos = command.indexOf(')', nextLineStart);
}
}
if (closePos != -1) {
ranges.add((start: match.start, end: closePos + 1));
found = true;
}
break;
}
}
}
if (!found) return null;
for (var i = ranges.length - 1; i >= 0; i--) {
final r = ranges[i];
result = result.substring(0, r.start) + result.substring(r.end);
}
return result;
}